### Eclipse Workspace Patch 1.0 #P org.eclipse.core.contenttype Index: src/org/eclipse/core/internal/content/ContentTypeCatalog.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeCatalog.java,v retrieving revision 1.8 diff -u -r1.8 ContentTypeCatalog.java --- src/org/eclipse/core/internal/content/ContentTypeCatalog.java 31 Aug 2009 11:28:15 -0000 1.8 +++ src/org/eclipse/core/internal/content/ContentTypeCatalog.java 25 Sep 2009 12:55:08 -0000 @@ -16,6 +16,7 @@ import org.eclipse.core.runtime.content.*; import org.eclipse.core.runtime.content.IContentTypeManager.ISelectionPolicy; import org.eclipse.core.runtime.preferences.IScopeContext; +import org.eclipse.osgi.util.NLS; public final class ContentTypeCatalog { private static final IContentType[] NO_CONTENT_TYPES = new IContentType[0]; @@ -177,7 +178,7 @@ existing.add(contentType); } - private int collectMatchingByContents(int valid, IContentType[] subset, List destination, ILazySource contents) throws IOException { + private int collectMatchingByContents(int valid, IContentType[] subset, List destination, ILazySource contents, Map properties) throws IOException { for (int i = 0; i < subset.length; i++) { ContentType current = (ContentType) subset[i]; IContentDescriber describer = current.getDescriber(); @@ -186,7 +187,7 @@ if (contents.isText() && !(describer instanceof ITextContentDescriber)) // for text streams we skip content types that do not provide text-based content describers continue; - status = current.describe(describer, contents, null); + status = describe(current, contents, null, properties); if (status == IContentDescriber.INVALID) continue; } @@ -198,6 +199,48 @@ return valid; } + int describe(ContentType type, ILazySource contents, ContentDescription description, Map properties) throws IOException { + IContentDescriber describer = type.getDescriber(); + try { + if (contents.isText()) { + if (describer instanceof XMLRootElementContentDescriber2) { + return ((XMLRootElementContentDescriber2) describer).describe((Reader) contents, description, properties); + } else if (describer instanceof XMLRootElementContentDescriber) { + return ((XMLRootElementContentDescriber) describer).describe((Reader) contents, description, properties); + } + return ((ITextContentDescriber) describer).describe((Reader) contents, description); + } else { + if (describer instanceof XMLRootElementContentDescriber2) { + return ((XMLRootElementContentDescriber2) describer).describe((InputStream) contents, description, properties); + } else if (describer instanceof XMLRootElementContentDescriber) { + return ((XMLRootElementContentDescriber) describer).describe((InputStream) contents, description, properties); + } + return (describer).describe((InputStream) contents, description); + } + } catch (RuntimeException re) { + // describer seems to be buggy. just disable it (logging the reason) + type.invalidateDescriber(re); + } catch (Error e) { + // describer got some serious problem. disable it (logging the reason) and throw the error again + type.invalidateDescriber(e); + throw e; + } catch (LowLevelIOException llioe) { + // throw the actual exception + throw llioe.getActualException(); + } catch (IOException ioe) { + // bugs 67841/ 62443 - non-low level IOException should be "ignored" + if (ContentTypeManager.DEBUGGING) { + String message = NLS.bind(ContentMessages.content_errorReadingContents, type.getId()); + ContentType.log(message, ioe); + } + // we don't know what the describer would say if the exception didn't occur + return IContentDescriber.INDETERMINATE; + } finally { + contents.rewind(); + } + return IContentDescriber.INVALID; + } + synchronized void dissociate(ContentType contentType, String text, int type) { Map fileSpecMap = ((type & IContentType.FILE_NAME_SPEC) != 0) ? fileNames : fileExtensions; String mappingKey = FileSpec.getMappingKeyFor(text); @@ -353,10 +396,11 @@ } private IContentType[] internalFindContentTypesFor(ILazySource buffer, IContentType[][] subset, Comparator validPolicy, Comparator indeterminatePolicy) throws IOException { + Map properties = new HashMap(); final List appropriate = new ArrayList(5); - final int validFullName = collectMatchingByContents(0, subset[0], appropriate, buffer); + final int validFullName = collectMatchingByContents(0, subset[0], appropriate, buffer, properties); final int appropriateFullName = appropriate.size(); - final int validExtension = collectMatchingByContents(validFullName, subset[1], appropriate, buffer) - validFullName; + final int validExtension = collectMatchingByContents(validFullName, subset[1], appropriate, buffer, properties) - validFullName; final int appropriateExtension = appropriate.size() - appropriateFullName; IContentType[] result = (IContentType[]) appropriate.toArray(new IContentType[appropriate.size()]); if (validFullName > 1) Index: src/org/eclipse/core/internal/content/ContentType.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentType.java,v retrieving revision 1.4 diff -u -r1.4 ContentType.java --- src/org/eclipse/core/internal/content/ContentType.java 4 Apr 2006 20:49:21 -0000 1.4 +++ src/org/eclipse/core/internal/content/ContentType.java 25 Sep 2009 12:55:07 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2006 IBM Corporation and others. + * Copyright (c) 2004, 2009 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 @@ -496,7 +496,7 @@ return false; } - private IContentDescriber invalidateDescriber(Throwable reason) { + public IContentDescriber invalidateDescriber(Throwable reason) { String message = NLS.bind(ContentMessages.content_invalidContentDescriber, id); log(message, reason); return (IContentDescriber) (describer = new InvalidDescriber()); Index: src/org/eclipse/core/runtime/content/XMLContentDescriber.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.contenttype/src/org/eclipse/core/runtime/content/XMLContentDescriber.java,v retrieving revision 1.5 diff -u -r1.5 XMLContentDescriber.java --- src/org/eclipse/core/runtime/content/XMLContentDescriber.java 20 Feb 2009 11:04:07 -0000 1.5 +++ src/org/eclipse/core/runtime/content/XMLContentDescriber.java 25 Sep 2009 12:55:08 -0000 @@ -11,6 +11,8 @@ package org.eclipse.core.runtime.content; import java.io.*; +import java.util.HashMap; +import java.util.Map; import org.eclipse.core.internal.content.TextContentDescriber; import org.eclipse.core.internal.content.Util; import org.eclipse.core.runtime.QualifiedName; @@ -49,7 +51,39 @@ private static final String XML_PREFIX = ""; //$NON-NLS-1$ + private static final String BOM = "org.eclipse.core.runtime.content.XMLContentDescriber.bom"; //$NON-NLS-1$ + private static final String CHARSET = "org.eclipse.core.runtime.content.XMLContentDescriber.charset"; //$NON-NLS-1$ + private static final String FULL_XML_DECL = "org.eclipse.core.runtime.content.XMLContentDescriber.fullXMLDecl"; //$NON-NLS-1$ + private static final String RESULT = "org.eclipse.core.runtime.content.XMLContentDescriber.processed"; //$NON-NLS-1$ + public int describe(InputStream input, IContentDescription description) throws IOException { + return describe2(input, description, new HashMap()); + } + + int describe2(InputStream input, IContentDescription description, Map properties) throws IOException { + if (!isProcessed(properties)) + fillContentProperties(input, description, properties); + return internalDescribe(description, properties); + } + + public int describe(Reader input, IContentDescription description) throws IOException { + return describe2(input, description, new HashMap()); + } + + int describe2(Reader input, IContentDescription description, Map properties) throws IOException { + if (!isProcessed(properties)) + fillContentProperties(readXMLDecl(input), description, properties); + return internalDescribe(description, properties); + } + + private boolean isProcessed(Map properties) { + Boolean result = (Boolean) properties.get(RESULT); + if (result != null) + return true; + return false; + } + + private void fillContentProperties(InputStream input, IContentDescription description, Map properties) throws IOException { byte[] bom = Util.getByteOrderMark(input); String xmlDeclEncoding = "UTF-8"; //$NON-NLS-1$ input.reset(); @@ -60,64 +94,76 @@ xmlDeclEncoding = "UTF-16LE"; //$NON-NLS-1$ // skip BOM to make comparison simpler input.skip(bom.length); - // set the BOM in the description if requested - if (description != null && description.isRequested(IContentDescription.BYTE_ORDER_MARK)) - description.setProperty(IContentDescription.BYTE_ORDER_MARK, bom); + properties.put(BOM, bom); } - return internalDescribe(readXMLDecl(input, xmlDeclEncoding), description); - } - - public int describe(Reader input, IContentDescription description) throws IOException { - return internalDescribe(readXMLDecl(input), description); + fillContentProperties(readXMLDecl(input, xmlDeclEncoding), description, properties); } - - private int internalDescribe(String line, IContentDescription description) throws IOException { - // end of stream - if (line == null) - return INDETERMINATE; + + private void fillContentProperties(String line, IContentDescription description, Map properties) throws IOException { // XMLDecl should be the first string (no blanks allowed) - if (!line.startsWith(XML_PREFIX)) + if (line != null && line.startsWith(XML_PREFIX)) + properties.put(FULL_XML_DECL, new Boolean(true)); + String charset = getCharset(line); + if (charset != null) + properties.put(CHARSET, charset); + properties.put(RESULT, new Boolean(true)); + } + + private int internalDescribe(IContentDescription description, Map properties) { + if (description != null) { + byte[] bom = (byte[]) properties.get(BOM); + if (bom != null && description.isRequested(IContentDescription.BYTE_ORDER_MARK)) + description.setProperty(IContentDescription.BYTE_ORDER_MARK, bom); + } + Boolean fullXMLDecl = (Boolean) properties.get(FULL_XML_DECL); + if (fullXMLDecl == null || !fullXMLDecl.booleanValue()) return INDETERMINATE; if (description == null) return VALID; - // describe charset if requested - if ((description.isRequested(IContentDescription.CHARSET))) { - String charset = getCharset(line); + String charset = (String) properties.get(CHARSET); + if (description.isRequested(IContentDescription.CHARSET)) { if (charset != null && !isCharsetValid(charset)) return INVALID; - if (charset != null && !charset.equalsIgnoreCase("utf8") && !charset.equalsIgnoreCase("utf-8")) //$NON-NLS-1$ //$NON-NLS-2$ - // only set property if value is not default (avoid using a non-default content description) + if (isNonDefaultCharset(charset)) description.setProperty(IContentDescription.CHARSET, charset); } return VALID; } - + + private boolean isNonDefaultCharset(String charset) { + if (charset == null) + return false; + if (charset.equalsIgnoreCase("utf8") || charset.equalsIgnoreCase("utf-8")) //$NON-NLS-1$ //$NON-NLS-2$ + return false; + return true; + } + private boolean isFullXMLDecl(String xmlDecl) { return xmlDecl.endsWith(XML_DECL_END); } - + private String readXMLDecl(InputStream input, String encoding) throws IOException { byte[] xmlDeclEndBytes = XML_DECL_END.getBytes(encoding); - + // allocate an array for the input - int xmlDeclSize = 100 * xmlDeclEndBytes.length/2; + int xmlDeclSize = 100 * xmlDeclEndBytes.length / 2; byte[] xmlDecl = new byte[xmlDeclSize]; - + // looks for XMLDecl end (?>) int c = 0; int read = 0; - + // count is incremented when subsequent read characters match the xmlDeclEnd bytes, // the end of xmlDecl is reached, when count equals the xmlDeclEnd length int count = 0; - - while (read < xmlDecl.length && (c = input.read()) != -1){ + + while (read < xmlDecl.length && (c = input.read()) != -1) { if (c == xmlDeclEndBytes[count]) count++; else count = 0; xmlDecl[read++] = (byte) c; - if (count == xmlDeclEndBytes.length) + if (count == xmlDeclEndBytes.length) break; } return new String(xmlDecl, 0, read, encoding); Index: src/org/eclipse/core/runtime/content/XMLRootElementContentDescriber2.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.contenttype/src/org/eclipse/core/runtime/content/XMLRootElementContentDescriber2.java,v retrieving revision 1.5 diff -u -r1.5 XMLRootElementContentDescriber2.java --- src/org/eclipse/core/runtime/content/XMLRootElementContentDescriber2.java 23 Sep 2009 09:52:27 -0000 1.5 +++ src/org/eclipse/core/runtime/content/XMLRootElementContentDescriber2.java 25 Sep 2009 12:55:09 -0000 @@ -14,7 +14,8 @@ import java.io.*; import java.util.*; import javax.xml.parsers.ParserConfigurationException; -import org.eclipse.core.internal.content.*; +import org.eclipse.core.internal.content.ContentMessages; +import org.eclipse.core.internal.content.XMLRootHandler; import org.eclipse.core.internal.runtime.RuntimeLog; import org.eclipse.core.runtime.*; import org.eclipse.osgi.util.NLS; @@ -65,8 +66,14 @@ * @noinstantiate This class is not intended to be instantiated by clients. */ public final class XMLRootElementContentDescriber2 extends XMLContentDescriber implements IExecutableExtension { + + static final String DTD = "org.eclipse.core.runtime.content.XMLRootElementContentDescriber2.dtd"; //$NON-NLS-1$ + static final String NAMESPACE = "org.eclipse.core.runtime.content.XMLRootElementContentDescriber2.namespace"; //$NON-NLS-1$ + static final String ELEMENT = "org.eclipse.core.runtime.content.XMLRootElementContentDescriber2.element"; //$NON-NLS-1$ + static final String RESULT = "org.eclipse.core.runtime.content.XMLRootElementContentDescriber2.result"; //$NON-NLS-1$ + private static final String ELEMENT_TO_FIND = "element"; //$NON-NLS-1$ - + /* (Intentionally not included in javadoc) * The top-level elements we are looking for. This value will be initialized * by the setInitializationData method. If no value is @@ -78,23 +85,23 @@ /* (Intentionally not included in javadoc) * Simple value holder for root element name, its namespace and dtd. */ - private class QualifiedElement { + private class QualifiedElement { private String namespace; private String element; private String dtd; - + public QualifiedElement(String qualifiedElement) { // Extract namespace part int openBrace = qualifiedElement.indexOf('{'); int closeBrace = qualifiedElement.indexOf('}'); - if (openBrace == 0 && closeBrace >=1 ) { + if (openBrace == 0 && closeBrace >= 1) { namespace = qualifiedElement.substring(1, closeBrace); - qualifiedElement = qualifiedElement.substring(closeBrace+1); + qualifiedElement = qualifiedElement.substring(closeBrace + 1); } // Extract dtd part int dtdSlash = qualifiedElement.indexOf('/'); if (dtdSlash > 0) { - dtd = qualifiedElement.substring(dtdSlash+1); + dtd = qualifiedElement.substring(dtdSlash + 1); qualifiedElement = qualifiedElement.substring(0, dtdSlash); } // Check if the name is a wildcard @@ -108,7 +115,7 @@ return nsMatch && elementEquals && dtdEquals; } } - + /* (Intentionally not included in javadoc) * Determines the validation status for the given contents. * @@ -120,25 +127,24 @@ * * @throws IOException */ - private int checkCriteria(InputSource contents) throws IOException { - XMLRootHandler xmlHandler = new XMLRootHandler(elementsToFind != null); - try { - if (!xmlHandler.parseContents(contents)) - return INDETERMINATE; - } catch (SAXException e) { - // we may be handed any kind of contents... it is normal we fail to parse + private int checkCriteria(InputSource contents, Map properties) throws IOException { + if (!isProcessed(properties)) + fillContentProperties(contents, properties); + return checkCriteria(properties); + } + + private int checkCriteria(Map properties) throws IOException { + Boolean result = (Boolean) properties.get(RESULT); + if (!result.booleanValue()) return INDETERMINATE; - } catch (ParserConfigurationException e) { - // some bad thing happened - force this describer to be disabled - String message = ContentMessages.content_parserConfiguration; - RuntimeLog.log(new Status(IStatus.ERROR, ContentMessages.OWNER_NAME, 0, message, e)); - throw new RuntimeException(message); - } // Check to see if we matched our criteria. if (elementsToFind != null) { boolean foundOne = false; for (int i = 0; i < elementsToFind.length && !foundOne; ++i) { - foundOne |= elementsToFind[i].matches(xmlHandler.getRootNamespace(), xmlHandler.getRootName(), xmlHandler.getDTD()); + String dtd = (String) properties.get(DTD); + String namespace = (String) properties.get(NAMESPACE); + String element = (String) properties.get(ELEMENT); + foundOne |= elementsToFind[i].matches(namespace, element, dtd); } if (!foundOne) return INDETERMINATE; @@ -151,26 +157,77 @@ * @see IContentDescriber#describe(InputStream, IContentDescription) */ public int describe(InputStream contents, IContentDescription description) throws IOException { + return describe(contents, description, new HashMap()); + } + + /** + * @noreference This method is not intended to be referenced by clients. + */ + public int describe(InputStream contents, IContentDescription description, Map properties) throws IOException { // call the basic XML describer to do basic recognition - if (super.describe(contents, description) == INVALID) + if (super.describe2(contents, description, properties) == INVALID) return INVALID; // super.describe will have consumed some chars, need to rewind contents.reset(); // Check to see if we matched our criteria. - return checkCriteria(new InputSource(contents)); + return checkCriteria(new InputSource(contents), properties); } /* (Intentionally not included in javadoc) * @see IContentDescriber#describe(Reader, IContentDescription) */ public int describe(Reader contents, IContentDescription description) throws IOException { + return describe(contents, description, new HashMap()); + } + + /** + * @noreference This method is not intended to be referenced by clients. + */ + public int describe(Reader contents, IContentDescription description, Map properties) throws IOException { // call the basic XML describer to do basic recognition - if (super.describe(contents, description) == INVALID) + if (super.describe2(contents, description, properties) == INVALID) return INVALID; // super.describe will have consumed some chars, need to rewind contents.reset(); // Check to see if we matched our criteria. - return checkCriteria(new InputSource(contents)); + return checkCriteria(new InputSource(contents), properties); + } + + static boolean isProcessed(Map properties) { + Boolean result = (Boolean) properties.get(RESULT); + // It can be set to false which means that content can't be parsed + if (result != null) + return true; + return false; + } + + static void fillContentProperties(InputSource input, Map properties) throws IOException { + XMLRootHandler xmlHandler = new XMLRootHandler(true); + try { + if (!xmlHandler.parseContents(input)) { + properties.put(RESULT, new Boolean(false)); + return; + } + } catch (SAXException e) { + // we may be handed any kind of contents... it is normal we fail to parse + properties.put(RESULT, new Boolean(false)); + return; + } catch (ParserConfigurationException e) { + // some bad thing happened - force this describer to be disabled + String message = ContentMessages.content_parserConfiguration; + RuntimeLog.log(new Status(IStatus.ERROR, ContentMessages.OWNER_NAME, 0, message, e)); + throw new RuntimeException(message); + } + String element = xmlHandler.getRootName(); + if (element != null) + properties.put(ELEMENT, element); + String dtd = xmlHandler.getDTD(); + if (dtd != null) + properties.put(DTD, dtd); + String namespace = xmlHandler.getRootNamespace(); + if (namespace != null) + properties.put(NAMESPACE, namespace); + properties.put(RESULT, new Boolean(true)); } /* (Intentionally not included in javadoc) Index: src/org/eclipse/core/runtime/content/XMLRootElementContentDescriber.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.contenttype/src/org/eclipse/core/runtime/content/XMLRootElementContentDescriber.java,v retrieving revision 1.5 diff -u -r1.5 XMLRootElementContentDescriber.java --- src/org/eclipse/core/runtime/content/XMLRootElementContentDescriber.java 23 Sep 2009 09:52:27 -0000 1.5 +++ src/org/eclipse/core/runtime/content/XMLRootElementContentDescriber.java 25 Sep 2009 12:55:08 -0000 @@ -11,14 +11,11 @@ package org.eclipse.core.runtime.content; import java.io.*; -import java.util.Hashtable; -import javax.xml.parsers.ParserConfigurationException; -import org.eclipse.core.internal.content.*; -import org.eclipse.core.internal.runtime.RuntimeLog; +import java.util.*; +import org.eclipse.core.internal.content.ContentMessages; import org.eclipse.core.runtime.*; import org.eclipse.osgi.util.NLS; import org.xml.sax.InputSource; -import org.xml.sax.SAXException; /** * A content describer for detecting the name of the top-level element or the @@ -70,24 +67,20 @@ * * @throws IOException */ - private int checkCriteria(InputSource contents) throws IOException { - XMLRootHandler xmlHandler = new XMLRootHandler(elementToFind != null); - try { - if (!xmlHandler.parseContents(contents)) - return INDETERMINATE; - } catch (SAXException e) { - // we may be handed any kind of contents... it is normal we fail to parse + private int checkCriteria(InputSource contents, Map properties) throws IOException { + if (!XMLRootElementContentDescriber2.isProcessed(properties)) + XMLRootElementContentDescriber2.fillContentProperties(contents, properties); + return checkCriteria(properties); + } + + private int checkCriteria(Map properties) throws IOException { + Boolean result = (Boolean) properties.get(XMLRootElementContentDescriber2.RESULT); + if (!result.booleanValue()) return INDETERMINATE; - } catch (ParserConfigurationException e) { - // some bad thing happened - force this describer to be disabled - String message = ContentMessages.content_parserConfiguration; - RuntimeLog.log(new Status(IStatus.ERROR, ContentMessages.OWNER_NAME, 0, message, e)); - throw new RuntimeException(message); - } // Check to see if we matched our criteria. - if ((elementToFind != null) && (!elementToFind.equals(xmlHandler.getRootName()))) + if ((dtdToFind != null) && (!dtdToFind.equals(properties.get(XMLRootElementContentDescriber2.DTD)))) return INDETERMINATE; - if ((dtdToFind != null) && (!dtdToFind.equals(xmlHandler.getDTD()))) + if ((elementToFind != null) && (!elementToFind.equals(properties.get(XMLRootElementContentDescriber2.ELEMENT)))) return INDETERMINATE; // We must be okay then. return VALID; @@ -97,26 +90,40 @@ * @see IContentDescriber#describe(InputStream, IContentDescription) */ public int describe(InputStream contents, IContentDescription description) throws IOException { + return describe(contents, description, new HashMap()); + } + + /** + * @noreference This method is not intended to be referenced by clients. + */ + public int describe(InputStream contents, IContentDescription description, Map properties) throws IOException { // call the basic XML describer to do basic recognition - if (super.describe(contents, description) == INVALID) + if (super.describe2(contents, description, properties) == INVALID) return INVALID; // super.describe will have consumed some chars, need to rewind contents.reset(); // Check to see if we matched our criteria. - return checkCriteria(new InputSource(contents)); + return checkCriteria(new InputSource(contents), properties); } /* (Intentionally not included in javadoc) * @see IContentDescriber#describe(Reader, IContentDescription) */ public int describe(Reader contents, IContentDescription description) throws IOException { + return describe(contents, description, new HashMap()); + } + + /** + * @noreference This method is not intended to be referenced by clients. + */ + public int describe(Reader contents, IContentDescription description, Map properties) throws IOException { // call the basic XML describer to do basic recognition - if (super.describe(contents, description) == INVALID) + if (super.describe2(contents, description, properties) == INVALID) return INVALID; // super.describe will have consumed some chars, need to rewind contents.reset(); // Check to see if we matched our criteria. - return checkCriteria(new InputSource(contents)); + return checkCriteria(new InputSource(contents), properties); } /* (Intentionally not included in javadoc)