| /******************************************************************************* |
| * Copyright (c) 2001, 2010 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 |
| * David Carver - STAR - [205989] - [validation] validate XML after XInclude resolution |
| *******************************************************************************/ |
| |
| package org.eclipse.wst.xml.core.internal.validation; |
| |
| import java.io.BufferedReader; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.io.StringReader; |
| import java.net.ConnectException; |
| import java.net.URL; |
| import java.net.UnknownHostException; |
| import java.util.ArrayList; |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| import org.apache.xerces.impl.XMLErrorReporter; |
| import org.apache.xerces.impl.msg.XMLMessageFormatter; |
| import org.apache.xerces.parsers.XIncludeAwareParserConfiguration; |
| import org.apache.xerces.xni.Augmentations; |
| import org.apache.xerces.xni.NamespaceContext; |
| import org.apache.xerces.xni.QName; |
| import org.apache.xerces.xni.XMLAttributes; |
| import org.apache.xerces.xni.XMLLocator; |
| import org.apache.xerces.xni.XMLResourceIdentifier; |
| import org.apache.xerces.xni.XNIException; |
| import org.apache.xerces.xni.parser.XMLEntityResolver; |
| import org.apache.xerces.xni.parser.XMLInputSource; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspace; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.wst.common.uriresolver.internal.provisional.URIResolver; |
| import org.eclipse.wst.common.uriresolver.internal.util.URIHelper; |
| import org.eclipse.wst.validation.ValidationResult; |
| import org.eclipse.wst.validation.internal.ValOperation; |
| import org.eclipse.wst.validation.internal.operations.LocalizedMessage; |
| import org.eclipse.wst.validation.internal.provisional.core.IReporter; |
| import org.eclipse.wst.xml.core.internal.Logger; |
| import org.eclipse.wst.xml.core.internal.XMLCorePlugin; |
| import org.eclipse.wst.xml.core.internal.preferences.XMLCorePreferenceNames; |
| import org.eclipse.wst.xml.core.internal.validation.core.LazyURLInputStream; |
| import org.eclipse.wst.xml.core.internal.validation.core.NestedValidatorContext; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.SAXParseException; |
| import org.xml.sax.XMLReader; |
| import org.xml.sax.ext.DeclHandler; |
| import org.xml.sax.helpers.DefaultHandler; |
| |
| /** |
| * This class performs validation using a xerces sax parser. |
| * Here's a quick overview of the details : |
| * - an ErrorHandler is used to collect errors into a list (so they may be displayed by the UI) |
| * - an EntityResolver is used along with the xerces "external-schemaLocation" property to implement XML Catalog support |
| */ |
| public class XMLValidator |
| { |
| protected URIResolver uriResolver = null; |
| protected Hashtable ingoredErrorKeyTable = new Hashtable(); |
| protected Set adjustLocationErrorKeySet = new TreeSet(); |
| |
| protected static final String IGNORE_ALWAYS = "IGNORE_ALWAYS"; //$NON-NLS-1$ |
| protected static final String IGNORE_IF_DTD_WITHOUT_ELEMENT_DECL = "IGNORE_IF_DTD_WITHOUT_ELEMENT_DECL"; //$NON-NLS-1$ |
| protected static final String PREMATURE_EOF = "PrematureEOF"; //$NON-NLS-1$ |
| protected static final String ROOT_ELEMENT_TYPE_MUST_MATCH_DOCTYPEDECL = "RootElementTypeMustMatchDoctypedecl"; //$NON-NLS-1$ |
| protected static final String MSG_ELEMENT_NOT_DECLARED = "MSG_ELEMENT_NOT_DECLARED"; //$NON-NLS-1$ |
| |
| // WTP XML validator specific key. |
| protected static final String NO_GRAMMAR_FOUND = "NO_GRAMMAR_FOUND"; //$NON-NLS-1$ |
| |
| private static final String FILE_NOT_FOUND_KEY = "FILE_NOT_FOUND"; //$NON-NLS-1$ |
| |
| private MarkupValidator val = new MarkupValidator(); |
| |
| private final String ANNOTATIONMSG = AnnotationMsg.class.getName(); |
| |
| /** |
| * Constructor. |
| */ |
| public XMLValidator() |
| { |
| // Here we add some error keys that we need to filter out when we're validation |
| // against a DTD without any element declarations. |
| ingoredErrorKeyTable.put(PREMATURE_EOF, IGNORE_ALWAYS); |
| ingoredErrorKeyTable.put(ROOT_ELEMENT_TYPE_MUST_MATCH_DOCTYPEDECL, IGNORE_IF_DTD_WITHOUT_ELEMENT_DECL); |
| ingoredErrorKeyTable.put(MSG_ELEMENT_NOT_DECLARED, IGNORE_IF_DTD_WITHOUT_ELEMENT_DECL); |
| |
| // Here we add some error keys that we need to adjust the location information for. |
| // The location information will be adjusted to place the message on the line of the starting |
| // element instead of on the line of the closing element. |
| adjustLocationErrorKeySet.add("MSG_CONTENT_INVALID"); //$NON-NLS-1$ |
| adjustLocationErrorKeySet.add("MSG_CONTENT_INCOMPLETE"); //$NON-NLS-1$ |
| adjustLocationErrorKeySet.add("cvc-complex-type.2.4.b"); //$NON-NLS-1$ |
| adjustLocationErrorKeySet.add("cvc-complex-type.2.3"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Set the URI Resolver to use. |
| * |
| * @param uriResolver The URI Resolver to use. |
| */ |
| public void setURIResolver(URIResolver uriResolver) |
| { |
| this.uriResolver = uriResolver; |
| //entityResolver = new MyEntityResolver(uriResolver); |
| } |
| |
| |
| /** |
| * Create an XML Reader. |
| * |
| * @return The newly created XML reader or null if unsuccessful. |
| * @throws Exception |
| */ |
| protected XMLReader createXMLReader(final XMLValidationInfo valinfo, XMLEntityResolver entityResolver) throws Exception |
| { |
| XMLReader reader = null; |
| // move to Xerces-2... add the contextClassLoader stuff |
| ClassLoader prevClassLoader = Thread.currentThread().getContextClassLoader(); |
| try |
| { |
| Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); |
| MyStandardParserConfiguration configuration = new MyStandardParserConfiguration(valinfo); |
| reader = new org.apache.xerces.parsers.SAXParser(configuration) |
| { |
| private XMLLocator locator = null; |
| |
| /* (non-Javadoc) |
| * @see org.apache.xerces.parsers.AbstractSAXParser#startDocument(org.apache.xerces.xni.XMLLocator, java.lang.String, org.apache.xerces.xni.NamespaceContext, org.apache.xerces.xni.Augmentations) |
| */ |
| public void startDocument(org.apache.xerces.xni.XMLLocator theLocator, java.lang.String encoding, NamespaceContext nscontext, org.apache.xerces.xni.Augmentations augs) |
| { |
| locator = theLocator; |
| valinfo.setXMLLocator(theLocator); |
| super.startDocument(theLocator, encoding, nscontext, augs); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.xerces.parsers.AbstractSAXParser#startElement(org.apache.xerces.xni.QName, org.apache.xerces.xni.XMLAttributes, org.apache.xerces.xni.Augmentations) |
| */ |
| public void startElement(QName arg0, XMLAttributes arg1, Augmentations arg2) throws XNIException |
| { |
| valinfo.getStartElementLocations().push(new LocationCoordinate(locator.getLineNumber(), locator.getColumnNumber())); |
| super.startElement(arg0, arg1, arg2); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.xerces.parsers.AbstractSAXParser#endElement(org.apache.xerces.xni.QName, org.apache.xerces.xni.Augmentations) |
| */ |
| public void endElement(QName arg0, Augmentations arg1) throws XNIException { |
| super.endElement(arg0, arg1); |
| valinfo.getStartElementLocations().pop(); |
| } |
| }; |
| |
| reader.setFeature("http://apache.org/xml/features/continue-after-fatal-error", false); //$NON-NLS-1$ |
| reader.setFeature("http://xml.org/sax/features/namespace-prefixes", valinfo.isNamespaceEncountered()); //$NON-NLS-1$ |
| reader.setFeature("http://xml.org/sax/features/namespaces", valinfo.isNamespaceEncountered()); //$NON-NLS-1$ |
| reader.setFeature("http://xml.org/sax/features/validation", valinfo.isGrammarEncountered()); //$NON-NLS-1$ |
| reader.setFeature("http://apache.org/xml/features/validation/schema", valinfo.isGrammarEncountered()); //$NON-NLS-1$ |
| reader.setContentHandler(new DefaultHandler() |
| { |
| public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { |
| valinfo.getErrorCustomizationManager().startElement(uri, localName); |
| } |
| |
| public void endElement(String uri, String localName, String qName) throws SAXException { |
| valinfo.getErrorCustomizationManager().endElement(uri, localName); |
| } |
| }); |
| |
| // MH make sure validation works even when a customer entityResolver is note set (i.e. via setURIResolver()) |
| if (entityResolver != null) |
| { |
| reader.setProperty("http://apache.org/xml/properties/internal/entity-resolver", entityResolver); //$NON-NLS-1$ |
| } |
| reader.setProperty("http://xml.org/sax/properties/declaration-handler", new MyDeclHandler()); //$NON-NLS-1$ |
| } |
| catch(Exception e) |
| { |
| Logger.logException(e); |
| //e.printStackTrace(); |
| } |
| finally |
| { |
| Thread.currentThread().setContextClassLoader(prevClassLoader); |
| } |
| return reader; |
| } |
| |
| /** |
| * Validate the file located at the given URI. |
| * |
| * @param uri The URI of the file to validate. |
| * @return Returns an XML validation report. |
| */ |
| public XMLValidationReport validate(String uri) |
| { |
| return validate(uri, null, new XMLValidationConfiguration()); |
| } |
| |
| final String createStringForInputStream(InputStream inputStream) |
| { |
| // Here we are reading the file and storing to a stringbuffer. |
| StringBuffer fileString = new StringBuffer(); |
| try |
| { |
| InputStreamReader inputReader = new InputStreamReader(inputStream, "UTF-8"); //$NON-NLS-1$ |
| BufferedReader reader = new BufferedReader(inputReader); |
| char[] chars = new char[1024]; |
| int numberRead = reader.read(chars); |
| while (numberRead != -1) |
| { |
| fileString.append(chars, 0, numberRead); |
| numberRead = reader.read(chars); |
| } |
| } |
| catch (Exception e) |
| { |
| //TODO: log error message |
| //e.printStackTrace(); |
| } |
| return fileString.toString(); |
| } |
| /** |
| * Validate the inputStream |
| * |
| * @param uri The URI of the file to validate. |
| * @param the inputStream of the file to validate |
| * @return Returns an XML validation report. |
| */ |
| public XMLValidationReport validate(String uri, InputStream inputStream) |
| { |
| return validate(uri, inputStream, new XMLValidationConfiguration()); |
| } |
| /** |
| * Validate the inputStream |
| * |
| * @param uri |
| * The URI of the file to validate. |
| * @param inputstream |
| * The inputStream of the file to validate |
| * @param configuration |
| * A configuration for this validation session. |
| * @return |
| * Returns an XML validation report. |
| */ |
| public XMLValidationReport validate(String uri, InputStream inputStream, XMLValidationConfiguration configuration) |
| { |
| return validate(uri, inputStream, configuration, null); |
| } |
| |
| /** |
| * Validate the inputStream |
| * |
| * @param uri |
| * The URI of the file to validate. |
| * @param inputstream |
| * The inputStream of the file to validate |
| * @param configuration |
| * A configuration for this validation session. |
| * @param result |
| * The validation result |
| * @return |
| * Returns an XML validation report. |
| */ |
| public XMLValidationReport validate(String uri, InputStream inputStream, XMLValidationConfiguration configuration, ValidationResult result) |
| { |
| return validate(uri, inputStream, configuration, null, null); |
| } |
| |
| /** |
| * Validate the inputStream |
| * |
| * @param uri |
| * The URI of the file to validate. |
| * @param inputstream |
| * The inputStream of the file to validate |
| * @param configuration |
| * A configuration for this validation session. |
| * @param result |
| * The validation result |
| * @param context |
| * The validation context |
| * @return |
| * Returns an XML validation report. |
| */ |
| public XMLValidationReport validate(String uri, InputStream inputStream, XMLValidationConfiguration configuration, ValidationResult result, NestedValidatorContext context) |
| { |
| String grammarFile = ""; //$NON-NLS-1$ |
| Reader reader1 = null; // Used for the preparse. |
| Reader reader2 = null; // Used for validation parse. |
| |
| if (inputStream != null) |
| { |
| String string = createStringForInputStream(inputStream); |
| reader1 = new StringReader(string); |
| reader2 = new StringReader(string); |
| } |
| |
| XMLValidationInfo valinfo = new XMLValidationInfo(uri); |
| MyEntityResolver entityResolver = new MyEntityResolver(uriResolver, context); |
| ValidatorHelper helper = new ValidatorHelper(); |
| try |
| { |
| helper.computeValidationInformation(uri, reader1, uriResolver); |
| valinfo.setDTDEncountered(helper.isDTDEncountered); |
| valinfo.setElementDeclarationCount(helper.numDTDElements); |
| valinfo.setNamespaceEncountered(helper.isNamespaceEncountered); |
| valinfo.setGrammarEncountered(helper.isGrammarEncountered); |
| |
| XMLReader reader = createXMLReader(valinfo, entityResolver); |
| // Set the configuration option |
| if (configuration.getFeature(XMLValidationConfiguration.HONOUR_ALL_SCHEMA_LOCATIONS)) |
| { |
| reader.setFeature("http://apache.org/xml/features/honour-all-schemaLocations", true); //$NON-NLS-1$ |
| } |
| if (configuration.getFeature(XMLValidationConfiguration.USE_XINCLUDE)) |
| { |
| reader.setFeature("http://apache.org/xml/features/xinclude", true); //$NON-NLS-1$ |
| } |
| XMLErrorHandler errorhandler = new XMLErrorHandler(valinfo); |
| reader.setErrorHandler(errorhandler); |
| |
| InputSource inputSource = new InputSource(uri); |
| inputSource.setCharacterStream(reader2); |
| reader.parse(inputSource); |
| if(configuration.getIntFeature(XMLValidationConfiguration.INDICATE_NO_GRAMMAR) > 0 && |
| valinfo.isValid() && !helper.isGrammarEncountered) |
| { |
| if(configuration.getIntFeature(XMLValidationConfiguration.INDICATE_NO_GRAMMAR) == 1) |
| valinfo.addWarning(XMLValidationMessages._WARN_NO_GRAMMAR, 1, 0, uri, NO_GRAMMAR_FOUND, null); |
| else // 2 |
| valinfo.addError(XMLValidationMessages._WARN_NO_GRAMMAR, 1, 0, uri, NO_GRAMMAR_FOUND, null); |
| } |
| |
| if (helper.isDTDEncountered) |
| grammarFile = entityResolver.getLocation(); |
| else |
| grammarFile = helper.schemaLocationString; |
| } |
| catch (SAXParseException saxParseException) |
| { |
| // These errors are caught by the error handler. |
| //addValidationMessage(valinfo, saxParseException); |
| } |
| catch (IOException ioException) |
| { |
| addValidationMessage(valinfo, ioException); |
| } |
| catch (Exception exception) |
| { |
| Logger.logException(exception.getLocalizedMessage(), exception); |
| } |
| |
| // Now set up the dependencies |
| // Wrap with try catch so that if something wrong happens, validation can |
| // still proceed as before |
| if (result != null) |
| { |
| try |
| { |
| IResource resource = getWorkspaceFileFromLocation(grammarFile); |
| ArrayList resources = new ArrayList(); |
| if (resource != null) |
| resources.add(resource); |
| result.setDependsOn((IResource [])resources.toArray(new IResource [0])); |
| } |
| catch (Exception e) |
| { |
| Logger.logException(e.getLocalizedMessage(), e); |
| } |
| } |
| |
| if ( XMLCorePlugin.getDefault().getPluginPreferences().getBoolean(XMLCorePreferenceNames.MARKUP_VALIDATION)){ |
| IReporter reporter = executeMarkupValidator(uri); |
| if (reporter != null){ |
| List msgList = reporter.getMessages(); |
| for (int i = 0;i < msgList.size();i++){ |
| LocalizedMessage msg = (LocalizedMessage)msgList.get(i); |
| if (msg.getSeverity() == 2) |
| valinfo.addError(msg.getLocalizedMessage(), msg.getLineNumber(), msg.getOffset(),valinfo.getFileURI(),"null",getMsgArguments(msg) ); //$NON-NLS-1$ |
| else if (msg.getSeverity() == 1) |
| valinfo.addWarning(msg.getLocalizedMessage(), msg.getLineNumber(), msg.getOffset(),valinfo.getFileURI(),"null", getMsgArguments(msg)); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| return valinfo; |
| |
| } |
| |
| private Object[] getMsgArguments(LocalizedMessage msg){ |
| Object obj = msg.getAttribute(ANNOTATIONMSG); |
| return new Object[]{obj}; |
| } |
| |
| |
| private IReporter executeMarkupValidator(String uri){ |
| Path path = new Path(uri); |
| String fileProtocol = "file://"; //$NON-NLS-1$ |
| int index = uri.indexOf(fileProtocol); |
| |
| IFile resource = null; |
| if (index == 0){ |
| String transformedUri = uri.substring(fileProtocol.length()); |
| Path transformedPath = new Path(transformedUri); |
| resource = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(transformedPath); |
| } |
| else { |
| resource = ResourcesPlugin.getWorkspace().getRoot().getFile(path); |
| |
| } |
| IReporter reporter = null; |
| if (resource != null){ |
| reporter = val.validate(resource, 0, new ValOperation().getState()) ; |
| } |
| return reporter; |
| } |
| |
| /** |
| * Add a validation message to the specified list. |
| * |
| * @param valinfo The validation info object to add the error to. |
| * @param exception The exception that contains the validation information. |
| */ |
| protected void addValidationMessage(XMLValidationInfo valinfo, IOException exception) |
| { |
| String validationMessageStr = exception.getMessage(); |
| Throwable cause = exception.getCause() != null ? exception.getCause() : exception; |
| while(validationMessageStr == null && cause != null){ |
| String localizedMessage = cause.getLocalizedMessage(); |
| cause = cause.getCause(); |
| if(cause == null && localizedMessage != null ) |
| { |
| validationMessageStr = localizedMessage; |
| } |
| } |
| |
| if (validationMessageStr != null) |
| { |
| if (cause instanceof FileNotFoundException) |
| { |
| validationMessageStr = NLS.bind(XMLValidationMessages._UI_PROBLEMS_VALIDATING_FILE_NOT_FOUND, new Object [] { validationMessageStr }); |
| } |
| else if (cause instanceof UnknownHostException) |
| { |
| validationMessageStr = NLS.bind(XMLValidationMessages._UI_PROBLEMS_VALIDATING_UNKNOWN_HOST, new Object [] { validationMessageStr }); |
| } |
| else if(cause instanceof ConnectException) |
| { |
| validationMessageStr = XMLValidationMessages._UI_PROBLEMS_CONNECTION_REFUSED; |
| } |
| } |
| |
| if (validationMessageStr != null) |
| { |
| XMLLocator locator = valinfo.getXMLLocator(); |
| valinfo.addWarning(validationMessageStr, locator != null ? locator.getLineNumber() : 1, locator != null ? locator.getColumnNumber() : 0, valinfo.getFileURI(), FILE_NOT_FOUND_KEY, null); |
| } |
| } |
| |
| /** |
| * Add a validation message to the specified list. |
| * |
| * @param valinfo The validation info object to add the error to. |
| * @param exception The exception that contains the validation information. |
| */ |
| protected void addValidationMessage(XMLValidationInfo valinfo, SAXParseException exception) |
| { |
| if (exception.getMessage() != null) |
| { |
| valinfo.addError(exception.getLocalizedMessage(), exception.getLineNumber(), exception.getColumnNumber(), exception.getSystemId()); |
| } |
| } |
| |
| |
| /** |
| * A custom entity resolver that uses the URI resolver specified to resolve entities. |
| */ |
| protected class MyEntityResolver implements XMLEntityResolver |
| { |
| private URIResolver uriResolver; |
| private String resolvedDTDLocation; |
| private NestedValidatorContext context; |
| |
| /** |
| * Constructor. |
| * |
| * @param uriResolver The URI resolver to use with this entity resolver. |
| * @param context The XML validator context. |
| */ |
| public MyEntityResolver(URIResolver uriResolver, NestedValidatorContext context) |
| { |
| this.uriResolver = uriResolver; |
| this.context = context; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.xerces.xni.parser.XMLEntityResolver#resolveEntity(org.apache.xerces.xni.XMLResourceIdentifier) |
| */ |
| public XMLInputSource resolveEntity(XMLResourceIdentifier rid) throws XNIException, IOException |
| { |
| XMLInputSource inputSource = _internalResolveEntity(uriResolver, rid, context); |
| if (inputSource != null) |
| { |
| resolvedDTDLocation = inputSource.getSystemId(); |
| } |
| return inputSource; |
| } |
| |
| public String getLocation() |
| { |
| return resolvedDTDLocation; |
| } |
| } |
| |
| // cs : I've refactored the common SAX based resolution code into this method for use by other validators |
| // (i.e. XML Schema, WSDL etc). The other approach is maintain a copy for each validator that has |
| // identical code. In any case we should strive to ensure that the validators perform resolution consistently. |
| public static XMLInputSource _internalResolveEntity(URIResolver uriResolver, XMLResourceIdentifier rid) throws IOException |
| { |
| return _internalResolveEntity(uriResolver, rid, null); |
| } |
| |
| public static XMLInputSource _internalResolveEntity(URIResolver uriResolver, XMLResourceIdentifier rid, NestedValidatorContext context) throws IOException |
| { |
| XMLInputSource is = null; |
| |
| if (uriResolver != null) |
| { |
| String id = rid.getPublicId(); |
| if(id == null) |
| { |
| id = rid.getNamespace(); |
| } |
| |
| String location = null; |
| if (id != null || rid.getLiteralSystemId() != null) |
| { |
| location = uriResolver.resolve(rid.getBaseSystemId(), id, rid.getLiteralSystemId()); |
| } |
| |
| if (location != null) |
| { |
| String physical = uriResolver.resolvePhysicalLocation(rid.getBaseSystemId(), id, location); |
| |
| // if physical is already a known bad uri, just go ahead and throw an exception |
| if (context instanceof XMLNestedValidatorContext) |
| { |
| XMLNestedValidatorContext xmlContext = ((XMLNestedValidatorContext)context); |
| |
| if (xmlContext.isURIMarkedInaccessible(physical)) |
| { |
| throw new FileNotFoundException(physical); |
| } |
| } |
| |
| is = new XMLInputSource(rid.getPublicId(), location, location); |
| |
| // This block checks that the file exists. If it doesn't we need to throw |
| // an exception so Xerces will report an error. note: This may not be |
| // necessary with all versions of Xerces but has specifically been |
| // experienced with the version included in IBM's 1.4.2 JDK. |
| InputStream isTemp = null; |
| try |
| { |
| isTemp = new URL(physical).openStream(); |
| } |
| catch (IOException e) |
| { |
| // physical was a bad url, so cache it so we know next time |
| if (context instanceof XMLNestedValidatorContext) |
| { |
| XMLNestedValidatorContext xmlContext = ((XMLNestedValidatorContext)context); |
| xmlContext.markURIInaccessible(physical); |
| } |
| throw e; |
| } |
| finally |
| { |
| if(isTemp != null) |
| { |
| isTemp.close(); |
| } |
| } |
| is.setByteStream(new LazyURLInputStream(physical)); |
| } |
| } |
| return is; |
| } |
| |
| /** |
| * An error handler to catch errors encountered while parsing the XML document. |
| */ |
| protected class XMLErrorHandler implements org.xml.sax.ErrorHandler |
| { |
| |
| private final int ERROR = 0; |
| private final int WARNING = 1; |
| private XMLValidationInfo valinfo; |
| |
| /** |
| * Constructor. |
| * |
| * @param valinfo The XML validation info object that will hold the validation messages. |
| */ |
| public XMLErrorHandler(XMLValidationInfo valinfo) |
| { |
| this.valinfo = valinfo; |
| } |
| |
| /** |
| * Add a validation message with the given severity. |
| * |
| * @param exception The exception that contains the message. |
| * @param severity The severity of the message. |
| */ |
| |
| protected void addValidationMessage(SAXParseException exception, int severity) |
| { |
| if(exception.getSystemId() != null) |
| { |
| int lineNumber = exception.getLineNumber(); |
| int columnNumber = exception.getColumnNumber(); |
| |
| // For the following three errors the line number will be modified to use that of the start |
| // tag instead of the end tag. |
| String currentErrorKey = valinfo.currentErrorKey; |
| if (currentErrorKey != null && adjustLocationErrorKeySet.contains(currentErrorKey)) |
| { |
| LocationCoordinate adjustedCoordinates = (LocationCoordinate)valinfo.getStartElementLocations().peek(); |
| lineNumber = adjustedCoordinates.getLineNumber(); |
| columnNumber = adjustedCoordinates.getColumnNumner(); |
| } |
| |
| if(severity == WARNING) |
| { |
| valinfo.addWarning(exception.getLocalizedMessage(), lineNumber, columnNumber, exception.getSystemId()); |
| } |
| else |
| { |
| valinfo.addError(exception.getLocalizedMessage(), lineNumber, columnNumber, exception.getSystemId(), valinfo.getCurrentErrorKey(), valinfo.getMessageArguments()); |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException) |
| */ |
| public void error(SAXParseException exception) throws SAXException |
| { |
| addValidationMessage(exception, ERROR); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException) |
| */ |
| public void fatalError(SAXParseException exception) throws SAXException |
| { |
| addValidationMessage(exception, ERROR); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException) |
| */ |
| public void warning(SAXParseException exception) throws SAXException |
| { |
| addValidationMessage(exception, WARNING); |
| } |
| } |
| |
| /** |
| * This class is used to count the elementDecls that are encountered in a DTD. |
| */ |
| protected class MyDeclHandler implements DeclHandler |
| { |
| |
| /** |
| * Constructor. |
| * |
| * @param valinfo The XMLValidationInfo object that will count the declarations. |
| */ |
| public MyDeclHandler() |
| { |
| } |
| |
| /* (non-Javadoc) |
| * @see org.xml.sax.ext.DeclHandler#attributeDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String) |
| */ |
| public void attributeDecl(String eName, String aName, String type, String valueDefault, String value) |
| { |
| } |
| |
| /* (non-Javadoc) |
| * @see org.xml.sax.ext.DeclHandler#elementDecl(java.lang.String, java.lang.String) |
| */ |
| public void elementDecl(String name, String model) |
| { |
| //valinfo.increaseElementDeclarationCount(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.xml.sax.ext.DeclHandler#externalEntityDecl(java.lang.String, java.lang.String, java.lang.String) |
| */ |
| public void externalEntityDecl(String name, String publicId, String systemId) |
| { |
| } |
| |
| /* (non-Javadoc) |
| * @see org.xml.sax.ext.DeclHandler#internalEntityDecl(java.lang.String, java.lang.String) |
| */ |
| public void internalEntityDecl(String name, String value) |
| { |
| } |
| } |
| |
| /** |
| * A XIncludeAwareParserConfiguration that creates an error reporter which can ignore |
| * DTD error messages for DTD's with no elements defined. |
| */ |
| |
| protected class MyStandardParserConfiguration extends XIncludeAwareParserConfiguration |
| { |
| XMLValidationInfo valinfo = null; |
| List reportedExceptions = new ArrayList(); |
| |
| /** |
| * Constructor. |
| * |
| * @param valinfo The XMLValidationInfo object to use. |
| */ |
| public MyStandardParserConfiguration(XMLValidationInfo valinfo) |
| { |
| this.valinfo = valinfo; |
| |
| XMLErrorReporter errorReporter = createErrorReporter(); |
| if (errorReporter.getMessageFormatter(XMLMessageFormatter.XML_DOMAIN) == null) { |
| XMLMessageFormatter xmft = new XMLMessageFormatter(); |
| errorReporter.putMessageFormatter(XMLMessageFormatter.XML_DOMAIN, xmft); |
| errorReporter.putMessageFormatter(XMLMessageFormatter.XMLNS_DOMAIN, xmft); |
| } |
| fErrorReporter = errorReporter; |
| setProperty(ERROR_REPORTER, errorReporter); |
| fCommonComponents.remove(fErrorReporter); |
| fCommonComponents.add(fErrorReporter); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.xerces.parsers.DTDConfiguration#createErrorReporter() |
| */ |
| protected XMLErrorReporter createErrorReporter() |
| { |
| return new XMLErrorReporter() |
| { |
| /* (non-Javadoc) |
| * @see org.apache.xerces.impl.XMLErrorReporter#reportError(java.lang.String, java.lang.String, java.lang.Object[], short) |
| */ |
| public void reportError(String domain, String key, Object[] arguments, short severity) throws XNIException |
| { |
| boolean reportError = true; |
| valinfo.setCurrentErrorKey(key); |
| valinfo.setMessageArguments(arguments); |
| String ignoreCondition = (String)ingoredErrorKeyTable.get(key); |
| if (ignoreCondition != null) |
| { |
| if (ignoreCondition.equals(XMLValidator.IGNORE_IF_DTD_WITHOUT_ELEMENT_DECL)) |
| { |
| boolean isDTDWithoutElementDeclarationEncountered = valinfo.isDTDWithoutElementDeclarationEncountered(); |
| reportError = !isDTDWithoutElementDeclarationEncountered; |
| } |
| else |
| { |
| reportError = false; |
| } |
| } |
| if ("schema_reference.4".equals(key) && arguments.length > 0) //$NON-NLS-1$ |
| { |
| Object location = arguments[0]; |
| if (location != null) |
| { |
| if(reportedExceptions.contains(location)) |
| { |
| reportError = false; |
| } |
| else |
| { |
| reportedExceptions.add(location); |
| } |
| } |
| } |
| if (reportError) |
| { |
| super.reportError(domain, key, arguments, severity); |
| valinfo.getErrorCustomizationManager().considerReportedError(valinfo, key, arguments); |
| } |
| } |
| }; |
| } |
| } |
| |
| /** |
| * A line and column number coordinate. |
| */ |
| protected class LocationCoordinate |
| { |
| private int lineNo = -1; |
| private int columnNo = -1; |
| |
| public LocationCoordinate(int lineNumber, int columnNumber) |
| { |
| this.lineNo = lineNumber; |
| this.columnNo = columnNumber; |
| } |
| |
| public int getLineNumber() |
| { |
| return this.lineNo; |
| } |
| |
| public int getColumnNumner() |
| { |
| return this.columnNo; |
| } |
| } |
| |
| protected IResource getWorkspaceFileFromLocation(String location) |
| { |
| if (location == null) return null; |
| IWorkspace workspace = ResourcesPlugin.getWorkspace(); |
| // To canonicalize the EMF URI |
| IPath canonicalForm = new Path(location); |
| // Need to convert to absolute location... |
| IPath pathLocation = new Path(URIHelper.removeProtocol(canonicalForm.toString())); |
| // ...to find the resource file that is in the workspace |
| IResource resourceFile = workspace.getRoot().getFileForLocation(pathLocation); |
| // If the resource is resolved to a file from http, or a file outside |
| // the workspace, then we will just ignore it. |
| return resourceFile; |
| } |
| } |