blob: f89094276ac36717626fc5507a56c807becf5a7e [file] [log] [blame]
/*******************************************************************************
* 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.core.resources.IFile;
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);
}
/**
* @see org.eclipse.validate.wsdl.xmlconformance.IXMLValidatorAction#setFile(IFile)
*/
public void setFile(String uri)
{
this.uri = uri;
}
public void setURIResolver(URIResolver uriResolver)
{
this.uriResolver = uriResolver;
}
public void setGrammarPool(XMLGrammarPool grammarPool)
{
this.grammarPool = grammarPool;
}
/**
* @see org.eclipse.validate.wsdl.xmlconformance.IXMLValidatorAction#run()
*/
public void run()
{
// Validate the XML file.
try
{
Reader reader1 = null; // Used for validation parse.
InputSource validateInputSource;
if (this.inputStream != null)
{
String string = createStringForInputStream(inputStream);
reader1 = new StringReader(string);
validateInputSource = new InputSource(inputStream);
validateInputSource.setCharacterStream(reader1);
} else
{ validateInputSource = new InputSource(uri);
}
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.
// System.out.println(e);
}
catch (Exception e)
{
// TODO: Log exception.
//System.out.println(e);
}
}
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();
}
/**
* @see org.eclipse.validate.wsdl.xmlconformance.IXMLValidatorAction#hasErrors()
*/
public boolean hasErrors()
{
return !errors.isEmpty();
}
/**
* @see org.eclipse.validate.wsdl.xmlconformance.IXMLValidatorAction#getErrorList()
*/
public List getErrors()
{
return errors;
}
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);
}
}
}