| /******************************************************************************* |
| * Copyright (c) 2001, 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.wst.wsdl.validation.internal.xml; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.io.StringReader; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.apache.xerces.impl.XMLErrorReporter; |
| import org.apache.xerces.parsers.SAXParser; |
| import org.apache.xerces.parsers.StandardParserConfiguration; |
| import org.apache.xerces.xni.XMLResourceIdentifier; |
| import org.apache.xerces.xni.XNIException; |
| import org.apache.xerces.xni.grammars.XMLGrammarPool; |
| import org.apache.xerces.xni.parser.XMLEntityResolver; |
| import org.apache.xerces.xni.parser.XMLErrorHandler; |
| import org.apache.xerces.xni.parser.XMLInputSource; |
| import org.apache.xerces.xni.parser.XMLParseException; |
| import org.eclipse.wst.wsdl.validation.internal.ValidationMessageImpl; |
| import org.eclipse.wst.wsdl.validation.internal.resolver.IURIResolutionResult; |
| import org.eclipse.wst.wsdl.validation.internal.resolver.URIResolver; |
| import org.eclipse.wst.wsdl.validation.internal.wsdl11.xsd.XSDValidator; |
| import org.w3c.dom.DOMError; |
| 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.helpers.DefaultHandler; |
| |
| import com.ibm.wsdl.Constants; |
| |
| /** |
| * The default implementation of IXMLValidator. |
| */ |
| public class DefaultXMLValidator implements IXMLValidator |
| { |
| protected String uri; |
| protected URIResolver uriResolver = null; |
| protected List errors = new ArrayList(); |
| protected StringBuffer schemaLocationString = new StringBuffer(); |
| protected List ignoredNamespaceList = new ArrayList(); |
| |
| protected InputStream inputStream = null; |
| |
| protected String currentErrorKey = null; |
| protected Object[] currentMessageArguments = null; |
| |
| protected boolean isChildOfDoc = false; |
| protected XMLGrammarPool grammarPool = null; |
| |
| /** |
| * Constructor. |
| */ |
| public DefaultXMLValidator() |
| { |
| super(); |
| |
| ignoredNamespaceList.add(Constants.NS_URI_XSD_1999); |
| ignoredNamespaceList.add(Constants.NS_URI_XSD_2000); |
| ignoredNamespaceList.add(Constants.NS_URI_XSD_2001); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.wsdl.validation.internal.xml.IXMLValidator#setFile(java.lang.String) |
| */ |
| public void setFile(String uri) |
| { |
| this.uri = uri; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.wsdl.validation.internal.xml.IXMLValidator#setURIResolver(org.eclipse.wst.wsdl.validation.internal.resolver.URIResolver) |
| */ |
| public void setURIResolver(URIResolver uriResolver) |
| { |
| this.uriResolver = uriResolver; |
| } |
| |
| /** |
| * @param grammarPool |
| */ |
| public void setGrammarPool(XMLGrammarPool grammarPool) |
| { |
| this.grammarPool = grammarPool; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.wsdl.validation.internal.xml.IXMLValidator#run() |
| */ |
| public void run() |
| { |
| // Validate the XML file. |
| try |
| { |
| Reader reader1 = null; // Used for validation parse. |
| |
| InputSource validateInputSource; |
| |
| validateInputSource = new InputSource(uri); |
| if (this.inputStream != null) |
| { |
| String string = createStringForInputStream(inputStream); |
| reader1 = new StringReader(string); |
| |
| validateInputSource.setByteStream(inputStream); |
| validateInputSource.setCharacterStream(reader1); |
| } |
| |
| XMLReader reader = createXMLReader(); |
| reader.parse(validateInputSource); |
| } |
| catch (SAXParseException e) |
| { |
| // No need to add an error here. SAXParseExceptions are reported by the error reporter. |
| } |
| catch (IOException e) |
| { |
| // TODO: Log exception. |
| } |
| catch (Exception e) |
| { |
| // TODO: Log exception. |
| //System.out.println(e); |
| } |
| } |
| |
| /** |
| * @param inputStream |
| * @return |
| */ |
| 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); |
| 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(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.wsdl.validation.internal.xml.IXMLValidator#hasErrors() |
| */ |
| public boolean hasErrors() |
| { |
| return !errors.isEmpty(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.wst.wsdl.validation.internal.xml.IXMLValidator#getErrors() |
| */ |
| public List getErrors() |
| { |
| return errors; |
| } |
| |
| /** |
| * @param message |
| * @param line |
| * @param column |
| * @param uri |
| */ |
| public void addError(String message, int line, int column, String uri) |
| { |
| errors.add(new ValidationMessageImpl(message, line, column, ValidationMessageImpl.SEV_WARNING, uri, currentErrorKey, currentMessageArguments)); |
| } |
| |
| /** |
| * The handler for the SAX parser. This handler will obtain the WSDL |
| * namespace, handle errors and resolve entities. |
| */ |
| protected class XMLConformanceDefaultHandler extends DefaultHandler |
| { |
| /** |
| * @see org.xml.sax.ErrorHandler#error(SAXParseException) |
| */ |
| public void error(SAXParseException arg0) throws SAXException |
| { |
| String tempURI = arg0.getSystemId(); |
| if (inputStream!= null && arg0.getSystemId() == null) |
| { |
| //mh: In this case we are validating a stream so the URI may be null in this exception |
| tempURI = uri; |
| } |
| addError(arg0.getMessage(), arg0.getLineNumber(), arg0.getColumnNumber(), tempURI); |
| } |
| |
| /** |
| * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException) |
| */ |
| public void fatalError(SAXParseException arg0) throws SAXException |
| { |
| addError(arg0.getMessage(), arg0.getLineNumber(), arg0.getColumnNumber(), arg0.getSystemId()); |
| } |
| |
| /** |
| * @see org.xml.sax.ErrorHandler#warning(SAXParseException) |
| */ |
| public void warning(SAXParseException arg0) throws SAXException |
| { |
| addError(arg0.getMessage(), arg0.getLineNumber(), arg0.getColumnNumber(), arg0.getSystemId()); |
| } |
| |
| /** |
| * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String) |
| */ |
| public void endElement(String uri, String localName, String qName) |
| throws SAXException { |
| if(localName.equals("documentation") && (uri.equals(Constants.NS_URI_WSDL) || uri.equals(Constants.NS_URI_XSD_2001)|| uri.equals(Constants.NS_URI_XSD_1999) || uri.equals(Constants.NS_URI_XSD_2000))) |
| { |
| isChildOfDoc = false; |
| } |
| super.endElement(uri, localName, qName); |
| } |
| /** |
| * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) |
| */ |
| public void startElement(String uri, String localName, String qName, |
| Attributes atts) throws SAXException { |
| if(localName.equals("documentation") && (uri.equals(Constants.NS_URI_WSDL) || uri.equals(Constants.NS_URI_XSD_2001)|| uri.equals(Constants.NS_URI_XSD_1999) || uri.equals(Constants.NS_URI_XSD_2000))) |
| { |
| isChildOfDoc = true; |
| } |
| super.startElement(uri, localName, qName, atts); |
| } |
| |
| /** |
| * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String, |
| * java.lang.String) |
| */ |
| public InputSource resolveEntity(String publicId, String systemId) throws SAXException |
| { |
| // If we're currently examining a subelement of the |
| // WSDL or schema documentation element we don't want to resolve it |
| // as anything is allowed as a child of documentation. |
| if(isChildOfDoc) |
| { |
| return new InputSource(); |
| } |
| if(publicId == null) |
| { |
| publicId = systemId; |
| } |
| |
| IURIResolutionResult result = uriResolver.resolve("", publicId, systemId); |
| String uri = result.getPhysicalLocation(); |
| if (uri != null && !uri.equals("")) |
| { |
| try |
| { |
| // String entity = systemId; |
| // if(publicId != null) |
| // { |
| // entity = publicId; |
| // } |
| URL entityURL = new URL(uri); |
| InputSource is = new InputSource(result.getLogicalLocation()); |
| is.setByteStream(entityURL.openStream()); |
| if (is != null) |
| { |
| return is; |
| } |
| } |
| catch(Exception e) |
| { |
| // Do nothing. |
| } |
| } |
| // This try/catch block with the IOException is here to handle a difference |
| // between different versions of the EntityResolver interface. |
| try |
| { |
| InputSource is = super.resolveEntity(publicId, systemId); |
| if(is == null) |
| { |
| throw new IOException(); |
| } |
| return is; |
| } |
| catch(IOException e) |
| { |
| } |
| return null; |
| } |
| |
| } |
| |
| /** |
| * @see org.eclipse.validate.wsdl.xmlconformance.IXMLValidator#setSchemaLocation(String, |
| * String) |
| */ |
| public void setSchemaLocation(String namespace, String location) |
| { |
| if (namespace != null && location != null && !namespace.equalsIgnoreCase("") && !location.equalsIgnoreCase("")) |
| { |
| schemaLocationString.append(" ").append(namespace).append(" ").append(formatURI(location)); |
| } |
| } |
| |
| |
| /** |
| * @param inputStream - set the inputStream to validate |
| */ |
| public void setInputStream(InputStream inputStream) |
| { |
| this.inputStream = inputStream; |
| } |
| |
| /** |
| * Remove space characters from a String and replace them with %20. |
| * |
| * @param uri - |
| * the uri to format |
| * @return the formatted string |
| */ |
| protected String formatURI(String uri) |
| { |
| String space = "%20"; |
| StringBuffer newUri = new StringBuffer(uri); |
| int newUriLength = newUri.length(); |
| |
| // work backwards through the stringbuffer in case it grows to the |
| // point |
| // where the last space would be missed. |
| for (int i = newUriLength - 1; i >= 0; i--) |
| { |
| if (newUri.charAt(i) == ' ') |
| { |
| newUri.replace(i, i + 1, space); |
| } |
| } |
| |
| return newUri.toString(); |
| } |
| |
| protected class XMLValidatorParserConfiguration extends StandardParserConfiguration |
| { |
| public XMLErrorHandler getErrorHandler() |
| { |
| return new XMLValidatorErrorHandler(); |
| } |
| |
| public XMLEntityResolver getEntityResolver() { |
| return new XMLEntityResolver() |
| { |
| |
| |
| |
| /* (non-Javadoc) |
| * @see org.apache.xerces.xni.parser.XMLEntityResolver#resolveEntity(org.apache.xerces.xni.XMLResourceIdentifier) |
| */ |
| public XMLInputSource resolveEntity(XMLResourceIdentifier rid) throws XNIException, IOException |
| { |
| // XMLInputSource result = null; |
| // |
| // // TODO cs : Lawrence's XMLConformanceDefaultHandler seems to need to ingore some entities |
| // // that are part of documentation etc. I think this resolver needs to do that |
| // // since the XMLConformanceDefaultHandler resolver is no longer active. |
| // if (uriResolver != null) |
| // { |
| // String pid = rid.getPublicId() != null ? rid.getPublicId() : rid.getNamespace(); |
| // String systemId = uriResolver.resolve(rid.getBaseSystemId(), pid, rid.getLiteralSystemId()); |
| // result = new XMLInputSource(rid.getPublicId(), systemId, rid.getBaseSystemId()); |
| // } |
| // return result; |
| |
| // If we're currently examining a subelement of the |
| // WSDL or schema documentation element we don't want to resolve it |
| // as anything is allowed as a child of documentation. |
| if(isChildOfDoc) |
| { |
| return new XMLInputSource(rid); |
| } |
| String systemId = rid.getLiteralSystemId(); |
| if(systemId == null) |
| { |
| systemId = rid.getNamespace(); |
| } |
| String publicId = rid.getPublicId(); |
| if(publicId == null) |
| { |
| publicId = systemId; |
| } |
| |
| IURIResolutionResult result = uriResolver.resolve("", publicId, systemId); |
| String uri = result.getPhysicalLocation(); |
| if (uri != null && !uri.equals("")) |
| { |
| try |
| { |
| // String entity = systemId; |
| // if(publicId != null) |
| // { |
| // entity = publicId; |
| // } |
| URL entityURL = new URL(uri); |
| XMLInputSource is = new XMLInputSource(rid.getPublicId(), systemId, result.getLogicalLocation()); |
| is.setByteStream(entityURL.openStream()); |
| if (is != null) |
| { |
| return is; |
| } |
| } |
| catch(Exception e) |
| { |
| // Do nothing. |
| } |
| } |
| // This try/catch block with the IOException is here to handle a difference |
| // between different versions of the EntityResolver interface. |
| // try |
| // { |
| // InputSource is = super.resolveEntity(publicId, systemId); |
| // if(is == null) |
| // { |
| // throw new IOException(); |
| // } |
| // return is; |
| // } |
| // catch(IOException e) |
| // { |
| // } |
| return null; |
| } |
| |
| }; |
| } |
| |
| /* (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 |
| { |
| currentErrorKey = key; |
| currentMessageArguments = arguments; |
| super.reportError(domain, key, arguments, severity); |
| } |
| }; |
| } |
| } |
| |
| /** |
| * Create an XML Reader. |
| * |
| * @return The newly created XML reader or null if unsuccessful. |
| * @throws Exception |
| */ |
| protected XMLReader createXMLReader() throws Exception |
| { |
| SAXParser reader = null; |
| try |
| { |
| reader = new org.apache.xerces.parsers.SAXParser(new XMLValidatorParserConfiguration()); |
| |
| XMLConformanceDefaultHandler conformanceDefaultHandler = new XMLConformanceDefaultHandler(); |
| reader.setErrorHandler(conformanceDefaultHandler); |
| reader.setContentHandler(conformanceDefaultHandler); |
| |
| // Older Xerces versions will thrown a NPE if a null grammar pool is set. |
| if(grammarPool != null) |
| { |
| reader.setProperty(org.apache.xerces.impl.Constants.XERCES_PROPERTY_PREFIX + org.apache.xerces.impl.Constants.XMLGRAMMAR_POOL_PROPERTY, grammarPool); |
| } |
| reader.setProperty(org.apache.xerces.impl.Constants.XERCES_PROPERTY_PREFIX + org.apache.xerces.impl.Constants.ENTITY_RESOLVER_PROPERTY, new MyEntityResolver(uriResolver)); |
| reader.setFeature(org.apache.xerces.impl.Constants.XERCES_FEATURE_PREFIX + org.apache.xerces.impl.Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE, false); |
| reader.setFeature(org.apache.xerces.impl.Constants.SAX_FEATURE_PREFIX + org.apache.xerces.impl.Constants.NAMESPACES_FEATURE, true); |
| reader.setFeature(org.apache.xerces.impl.Constants.SAX_FEATURE_PREFIX + org.apache.xerces.impl.Constants.NAMESPACE_PREFIXES_FEATURE, true); |
| reader.setFeature(org.apache.xerces.impl.Constants.SAX_FEATURE_PREFIX + org.apache.xerces.impl.Constants.VALIDATION_FEATURE, true); |
| reader.setFeature(org.apache.xerces.impl.Constants.XERCES_FEATURE_PREFIX + org.apache.xerces.impl.Constants.SCHEMA_VALIDATION_FEATURE, true); |
| } |
| catch(Exception e) |
| { |
| // TODO: Log error. |
| //e.printStackTrace(); |
| } |
| return reader; |
| } |
| |
| /** |
| * A custom entity resolver that uses the URI resolver specified to resolve entities. |
| */ |
| protected class MyEntityResolver implements XMLEntityResolver |
| { |
| private URIResolver uriResolver; |
| |
| /** |
| * Constructor. |
| * |
| * @param uriResolver The URI resolver to use with this entity resolver. |
| */ |
| public MyEntityResolver(URIResolver uriResolver) |
| { |
| this.uriResolver = uriResolver; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.xerces.xni.parser.XMLEntityResolver#resolveEntity(org.apache.xerces.xni.XMLResourceIdentifier) |
| */ |
| public XMLInputSource resolveEntity(XMLResourceIdentifier rid) throws XNIException, IOException |
| { |
| // If we're currently examining a subelement of the |
| // WSDL or schema documentation element we don't want to resolve it |
| // as anything is allowed as a child of documentation. |
| if(isChildOfDoc) |
| { |
| return new XMLInputSource(rid); |
| } |
| |
| boolean nsUsed = false; |
| |
| String ns = rid.getNamespace(); |
| if(ns != null && ignoredNamespaceList.contains(ns)) |
| { |
| return new XMLInputSource(rid); |
| } |
| |
| String systemId = rid.getLiteralSystemId(); |
| if(systemId == null) |
| { |
| systemId = ns; |
| nsUsed = true; |
| } |
| String publicId = rid.getPublicId(); |
| if(publicId == null) |
| { |
| publicId = systemId; |
| } |
| |
| // Xerces tends to try to resolve locations with no information. |
| // No need to do any processing if we have no information. |
| if(publicId != null || systemId != null) |
| { |
| IURIResolutionResult result = uriResolver.resolve("", publicId, systemId); |
| String uri = result.getPhysicalLocation(); |
| if (uri != null && !uri.equals("")) |
| { |
| // If the namespace was used to resolve this reference ensure a schema |
| // has been returned. Namespaces tend to point to Web resources that |
| // may or may not be schemas. |
| boolean createEntityResult = true; |
| if(nsUsed) |
| { |
| XSDValidator xsdVal = new XSDValidator(); |
| xsdVal.validate(uri, uriResolver, null); |
| if(!xsdVal.isValid()) |
| createEntityResult = false; |
| } |
| |
| if(createEntityResult) |
| { |
| try |
| { |
| URL entityURL = new URL(uri); |
| XMLInputSource is = new XMLInputSource(rid.getPublicId(), systemId, result.getLogicalLocation()); |
| is.setByteStream(entityURL.openStream()); |
| if (is != null) |
| { |
| return is; |
| } |
| } |
| catch(Exception e) |
| { |
| // No need to report this error. Simply continue below. |
| } |
| } |
| } |
| } |
| return null; |
| } |
| } |
| |
| protected class XMLValidatorErrorHandler implements XMLErrorHandler |
| { |
| |
| /** |
| * Add an error message. |
| * |
| * @param error The error message to add. |
| * @param line The line location of the error. |
| * @param column The column location of the error. |
| * @param uri The URI of the file containing the error. |
| */ |
| private void addValidationMessage(String key, XMLParseException exception, int severity) |
| { |
| if (severity == DOMError.SEVERITY_WARNING) |
| { |
| errors.add(new ValidationMessageImpl(exception.getLocalizedMessage(), exception.getLineNumber(), exception.getColumnNumber(), ValidationMessageImpl.SEV_WARNING, uri, key, currentMessageArguments)); |
| } |
| else |
| { |
| errors.add(new ValidationMessageImpl(exception.getLocalizedMessage(), exception.getLineNumber(), exception.getColumnNumber(), ValidationMessageImpl.SEV_ERROR, uri, key, currentMessageArguments)); |
| } |
| |
| } |
| |
| public void error(String domain, String key, XMLParseException exception) throws XNIException |
| { |
| addValidationMessage(key, exception, DOMError.SEVERITY_ERROR); |
| |
| } |
| |
| public void fatalError(String domain, String key, XMLParseException exception) throws XNIException |
| { |
| addValidationMessage(key, exception, DOMError.SEVERITY_FATAL_ERROR); |
| |
| } |
| |
| public void warning(String domain, String key, XMLParseException exception) throws XNIException |
| { |
| addValidationMessage(key, exception, DOMError.SEVERITY_WARNING); |
| |
| } |
| |
| } |
| |
| protected class MyXMLErrorReporter extends XMLErrorReporter |
| { |
| |
| public XMLErrorHandler getErrorHandler() { |
| // TODO Auto-generated method stub |
| return new XMLValidatorErrorHandler(); |
| } |
| |
| /* (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 |
| { |
| currentErrorKey = key; |
| currentMessageArguments = arguments; |
| super.reportError(domain, key, arguments, severity); |
| } |
| } |
| |
| |
| |
| } |