| /******************************************************************************* |
| * Copyright (c) 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jst.jsp.core.internal.encoding; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.Reader; |
| import java.io.UnsupportedEncodingException; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.jface.text.IDocumentPartitioner; |
| import org.eclipse.jst.jsp.core.internal.Assert; |
| import org.eclipse.jst.jsp.core.internal.JSPCoreMessages; |
| import org.eclipse.jst.jsp.core.internal.document.PageDirectiveWatcherFactory; |
| import org.eclipse.jst.jsp.core.internal.parser.JSPReParser; |
| import org.eclipse.jst.jsp.core.internal.parser.JSPSourceParser; |
| import org.eclipse.jst.jsp.core.internal.provisional.JSP11Namespace; |
| import org.eclipse.jst.jsp.core.internal.provisional.contenttype.ContentTypeIdForJSP; |
| import org.eclipse.jst.jsp.core.internal.text.StructuredTextPartitionerForJSP; |
| import org.eclipse.wst.sse.core.internal.PropagatingAdapter; |
| import org.eclipse.wst.sse.core.internal.document.AbstractDocumentLoader; |
| import org.eclipse.wst.sse.core.internal.document.IDocumentCharsetDetector; |
| import org.eclipse.wst.sse.core.internal.document.IDocumentLoader; |
| import org.eclipse.wst.sse.core.internal.document.StructuredDocumentFactory; |
| import org.eclipse.wst.sse.core.internal.encoding.CodedIO; |
| import org.eclipse.wst.sse.core.internal.encoding.CodedReaderCreator; |
| import org.eclipse.wst.sse.core.internal.encoding.ContentTypeEncodingPreferences; |
| import org.eclipse.wst.sse.core.internal.encoding.util.BufferedLimitedReader; |
| import org.eclipse.wst.sse.core.internal.ltk.modelhandler.EmbeddedTypeHandler; |
| import org.eclipse.wst.sse.core.internal.ltk.parser.JSPCapableParser; |
| import org.eclipse.wst.sse.core.internal.ltk.parser.RegionParser; |
| import org.eclipse.wst.sse.core.internal.ltk.parser.TagMarker; |
| import org.eclipse.wst.sse.core.internal.modelhandler.EmbeddedTypeRegistry; |
| import org.eclipse.wst.sse.core.internal.modelhandler.EmbeddedTypeRegistryImpl; |
| import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; |
| import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; |
| import org.eclipse.wst.sse.core.internal.provisional.document.IEncodedDocument; |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; |
| import org.eclipse.wst.sse.core.internal.text.BasicStructuredDocument; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; |
| import org.eclipse.wst.xml.core.internal.ssemodelquery.ModelQueryAdapter; |
| import org.w3c.dom.Document; |
| |
| public class JSPDocumentLoader extends AbstractDocumentLoader { |
| private final static String DEFAULT_MIME_TYPE = "text/html"; //$NON-NLS-1$ |
| private final static String SPEC_DEFAULT_ENCODING = "ISO-8859-1"; //$NON-NLS-1$ |
| |
| protected static IFile getFileFor(IStructuredModel model) { |
| if (model == null) |
| return null; |
| String path = model.getBaseLocation(); |
| if (path == null || path.length() == 0) { |
| Object id = model.getId(); |
| if (id == null) |
| return null; |
| path = id.toString(); |
| } |
| // TODO needs rework for linked resources |
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| IFile file = root.getFileForLocation(new Path(path)); |
| return file; |
| } |
| |
| private EmbeddedTypeRegistry fEmbeddedContentTypeRegistry; |
| |
| public JSPDocumentLoader() { |
| super(); |
| } |
| |
| protected void addNestablePrefix(JSPSourceParser parser, String tagName) { |
| TagMarker bm = new TagMarker(tagName); |
| parser.addNestablePrefix(bm); |
| } |
| |
| synchronized public IEncodedDocument createNewStructuredDocument(IFile iFile) throws IOException, CoreException { |
| IStructuredDocument structuredDocument = null; |
| try { |
| structuredDocument = createCodedDocument(iFile); |
| |
| EmbeddedTypeHandler embeddedType = getEmbeddedType(iFile); |
| if (embeddedType != null) |
| embeddedType.initializeParser((JSPCapableParser) structuredDocument.getParser()); |
| |
| fFullPreparedReader.reset(); |
| setDocumentContentsFromReader(structuredDocument, fFullPreparedReader); |
| |
| } |
| finally { |
| if (fFullPreparedReader != null) { |
| fFullPreparedReader.close(); |
| } |
| } |
| return structuredDocument; |
| } |
| |
| private IStructuredDocument createCodedDocument(IFile iFile) throws CoreException, UnsupportedEncodingException, IOException { |
| IStructuredDocument structuredDocument = (IStructuredDocument) createNewStructuredDocument(); |
| |
| getCodedReaderCreator().set(iFile); |
| |
| fFullPreparedReader = getCodedReaderCreator().getCodedReader(); |
| fEncodingMemento = getCodedReaderCreator().getEncodingMemento(); |
| |
| structuredDocument.setEncodingMemento(getCodedReaderCreator().getEncodingMemento()); |
| |
| return structuredDocument; |
| } |
| |
| public IEncodedDocument createNewStructuredDocument(String filename, InputStream inputStream) throws UnsupportedEncodingException, IOException { |
| if (filename == null && inputStream == null) { |
| throw new IllegalArgumentException("can not have both null filename and inputstream"); //$NON-NLS-1$ |
| } |
| IEncodedDocument structuredDocument = createNewStructuredDocument(); |
| CodedReaderCreator codedReaderCreator = new CodedReaderCreator(); |
| try { |
| codedReaderCreator.set(filename, inputStream); |
| fFullPreparedReader = codedReaderCreator.getCodedReader(); |
| fEncodingMemento = codedReaderCreator.getEncodingMemento(); |
| structuredDocument.setEncodingMemento(fEncodingMemento); |
| // the fact that file is null means this method/code path is no |
| // good for JSP fragments |
| EmbeddedTypeHandler embeddedType = getEmbeddedType((IFile) null); |
| fFullPreparedReader.reset(); |
| if (embeddedType != null) |
| embeddedType.initializeParser((JSPCapableParser) ((IStructuredDocument) structuredDocument).getParser()); |
| setDocumentContentsFromReader(structuredDocument, fFullPreparedReader); |
| } |
| catch (CoreException e) { |
| // impossible in this context |
| throw new Error(e); |
| } |
| finally { |
| if (fFullPreparedReader != null) { |
| fFullPreparedReader.close(); |
| } |
| } |
| return structuredDocument; |
| |
| } |
| |
| /** |
| * Method getDefaultDocumentPartitioner. |
| * |
| * @return IDocumentPartitioner |
| */ |
| public IDocumentPartitioner getDefaultDocumentPartitioner() { |
| return new StructuredTextPartitionerForJSP(); |
| } |
| |
| /** |
| * Method getDefaultMimeType. |
| * |
| * @return String |
| */ |
| private String getDefaultMimeType() { |
| return DEFAULT_MIME_TYPE; |
| } |
| |
| |
| public IDocumentCharsetDetector getDocumentEncodingDetector() { |
| if (fDocumentEncodingDetector == null) { |
| fDocumentEncodingDetector = new JSPDocumentHeadContentDetector(); |
| } |
| return fDocumentEncodingDetector; |
| } |
| |
| /** |
| * Gets the embeddedContentTypeRegistry. |
| * |
| * @return Returns a EmbeddedContentTypeRegistry |
| */ |
| private EmbeddedTypeRegistry getEmbeddedContentTypeRegistry() { |
| if (fEmbeddedContentTypeRegistry == null) { |
| fEmbeddedContentTypeRegistry = EmbeddedTypeRegistryImpl.getInstance(); |
| } |
| return fEmbeddedContentTypeRegistry; |
| } |
| |
| /** |
| * Determine the MIME content type specified in a page directive. This |
| * should appear "as early as possible in the JSP page" according to the |
| * JSP v1.2 specification. |
| */ |
| private EmbeddedTypeHandler getEmbeddedType(IFile file) throws UnsupportedEncodingException, CoreException, IOException { |
| EmbeddedTypeHandler handler = null; |
| if (fFullPreparedReader == null) { |
| handler = getJSPDefaultEmbeddedType(); |
| } |
| else { |
| String mimeType = null; |
| |
| IDocumentCharsetDetector jspProvider = getDocumentEncodingDetector(); |
| Reader fullPreparedReader = getFullPreparedReader(); |
| BufferedLimitedReader limitedReader = new BufferedLimitedReader(fullPreparedReader, CodedIO.MAX_BUF_SIZE); |
| jspProvider.set(limitedReader); |
| if (jspProvider instanceof IJSPHeadContentDetector) { |
| mimeType = ((IJSPHeadContentDetector) jspProvider).getContentType(); |
| fullPreparedReader.reset(); |
| } |
| |
| EmbeddedTypeRegistry reg = getEmbeddedContentTypeRegistry(); |
| if (mimeType == null || mimeType.length() == 0) { |
| handler = getJSPDefaultEmbeddedType(); |
| } |
| else { |
| handler = reg.getTypeFor(mimeType); |
| } |
| } |
| return handler; |
| } |
| |
| /** |
| * For JSP files, text/html is the default content type. This may want |
| * this different for types like jsv (jsp for voice xml) For now, hard |
| * code to new instance. In future, should get instance from registry. |
| * Specification cites HTML as the default contentType. |
| */ |
| private EmbeddedTypeHandler getJSPDefaultEmbeddedType() { |
| EmbeddedTypeRegistry reg = getEmbeddedContentTypeRegistry(); |
| return reg.getTypeFor(getDefaultMimeType()); |
| } |
| |
| public RegionParser getParser() { |
| // remember, the Loader |
| // will need to finish initialization of parser |
| // based on "embedded content" |
| JSPSourceParser parser = new JSPSourceParser(); |
| // add default nestable tag list |
| addNestablePrefix(parser, JSP11Namespace.JSP_TAG_PREFIX); |
| return parser; |
| } |
| |
| protected String getPreferredNewLineDelimiter() { |
| return ContentTypeEncodingPreferences.getPreferredNewLineDelimiter(ContentTypeIdForJSP.ContentTypeID_JSP); |
| } |
| |
| /** |
| * Specification cites ISO-8859-1/Latin-1 as the default charset. |
| */ |
| protected String getSpecDefaultEncoding() { |
| return SPEC_DEFAULT_ENCODING; |
| } |
| |
| /** |
| * This method must return a new instance of IStructuredDocument, that has |
| * been initialized with appropriate parser. For many loaders, the |
| * (default) parser used is known for any input. For others, the correct |
| * parser (and its initialization) is normall dependent on the content of |
| * the file. This no-argument method should assume "empty input" and would |
| * therefore return the default parser for the default contentType. If the |
| * parser is to handle tag libraries, it must have a TaglibSupport object |
| * with a valid URIResolver and this IStructuredDocument attached to it |
| * before the contents are set on the IStructuredDocument. |
| */ |
| protected IEncodedDocument newEncodedDocument() { |
| IStructuredDocument structuredDocument = StructuredDocumentFactory.getNewStructuredDocumentInstance(getParser()); |
| ((BasicStructuredDocument) structuredDocument).setReParser(new JSPReParser()); |
| // structuredDocument.setDocumentPartitioner(new |
| // JSPJavaDocumentPartioner()); |
| // even though this is an "empty model" ... we want it to have at |
| // least |
| // the |
| // default embeddeded content type handler |
| EmbeddedTypeHandler embeddedType = getJSPDefaultEmbeddedType(); |
| embeddedType.initializeParser((JSPCapableParser) structuredDocument.getParser()); |
| return structuredDocument; |
| } |
| |
| public IDocumentLoader newInstance() { |
| return new JSPDocumentLoader(); |
| } |
| |
| protected void preLoadAdapt(IStructuredModel structuredModel) { |
| IDOMModel domModel = (IDOMModel) structuredModel; |
| // |
| // document must have already been set for this to |
| // work. |
| Document document = domModel.getDocument(); |
| Assert.isNotNull(document, JSPCoreMessages.JSPDocumentLoader_1); |
| // if there is a model in the adapter, this will adapt it to |
| // first node. After that the PropagatingAdater spreads over the |
| // children being |
| // created. Each time that happends, a side effect is to |
| // also "spread" sprecific registered adapters, |
| // they two can propigate is needed. |
| // This 'get' causes first to be be attached. |
| PropagatingAdapter propagatingAdapter = (PropagatingAdapter) ((INodeNotifier) document).getAdapterFor(PropagatingAdapter.class); |
| // may make this easier to use in futue |
| propagatingAdapter.addAdaptOnCreateFactory(new PageDirectiveWatcherFactory()); |
| // For JSPs, the ModelQueryAdapter must be "attached" to the document |
| // before content is set in the model, so taglib initization can |
| // take place. |
| ((INodeNotifier) document).getAdapterFor(ModelQueryAdapter.class); |
| |
| } |
| |
| } |