blob: fdd8444570f5950c5e1eb478037f92f4639049f7 [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.xsd.core.internal.validation;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import org.apache.xerces.parsers.SAXParser;
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.wst.common.uriresolver.internal.provisional.URIResolver;
import org.eclipse.wst.xml.core.internal.validation.XMLValidator;
import org.eclipse.wst.xml.core.internal.validation.core.ValidationInfo;
import org.eclipse.wst.xml.core.internal.validation.core.ValidationReport;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
/**
* The XSDValidator will validate XSD files.
*
* @author Lawrence Mandel, IBM
*/
public class XSDValidator
{
private final boolean WRAPPER_ERROR_SUPPORT_ENABLED = true;
private final String XML_INSTANCE_DOC_TOP = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<root \n"
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n";
private final String XML_INSTANCE_DOC_MID = " xsi:noNamespaceSchemaLocation=\"";
private final String XML_INSTANCE_DOC_BOT = "\">\n" + "</root>\n";
private final String DUMMY_URI = "http://example.org/dummy";
private URIResolver uriresolver = null;
public ValidationReport validate(String uri)
{
return validate(uri, null);
}
/**
* Validate the XSD file specified by the URI.
*
* @param uri
* The URI of the XSD file to validate.
*/
public ValidationReport validate(String uri, InputStream inputStream)
{
ValidationInfo valinfo = new ValidationInfo(uri);
try
{
String ns = null;
String schemaLocationString = "";
URL url = new URL(uri);
if (url != null)
{
BufferedReader bufreader = new BufferedReader(new InputStreamReader(url.openStream()));
if (bufreader != null)
{
String source = "";
while (bufreader.ready())
{
source += bufreader.readLine();
}
bufreader.close();
int tn = source.indexOf("targetNamespace");
if (tn != -1)
{
int firstquote = source.indexOf("\"", tn) + 1;
int secondquote = source.indexOf("\"", firstquote);
ns = source.substring(firstquote, secondquote);
}
}
bufreader.close();
}
XSDErrorHandler errorHandler = new XSDErrorHandler(valinfo);
try
{
String instanceDoc = XML_INSTANCE_DOC_TOP;
if (ns != null && !ns.equals(""))
{
instanceDoc += " xmlns=\"" + ns + "\"\n";
instanceDoc += " xsi:schemaLocation=\"";
instanceDoc += ns;
instanceDoc += " ";
} else
{
instanceDoc += " xsi:noNamespaceSchemaLocation=\"";
}
instanceDoc += uri.replaceAll(" ", "%20");
if (!schemaLocationString.equals(""))
{
instanceDoc += " " + schemaLocationString;
}
instanceDoc += XML_INSTANCE_DOC_BOT;
InputSource is = new InputSource(new StringReader(instanceDoc));
is.setSystemId(DUMMY_URI);
String soapFile = "platform:/plugin/org.eclipse.wst.wsdl.validation./xsd/xml-soap.xsd";
XMLReader reader = new SAXParser();
try
{
reader.setFeature("http://apache.org/xml/features/continue-after-fatal-error", false);
reader.setFeature("http://xml.org/sax/features/namespaces", true);
reader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
reader.setFeature("http://xml.org/sax/features/validation", true);
reader.setFeature("http://apache.org/xml/features/validation/schema", true);
reader.setFeature("http://apache.org/xml/features/validation/schema-full-checking", true);
reader.setFeature("http://xml.org/sax/features/external-general-entities", true);
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", true);
reader.setFeature("http://apache.org/xml/features/validation/warn-on-duplicate-attdef", true);
reader.setProperty("http://apache.org/xml/properties/schema/external-schemaLocation",
"http://xml.apache.org/xml-soap " + soapFile);
reader.setErrorHandler(errorHandler);
if (uriresolver != null)
{
XSDEntityResolver resolver = new XSDEntityResolver(uriresolver, uri, inputStream);
try
{
reader.setProperty("http://apache.org/xml/properties/internal/entity-resolver", resolver);
}
catch (Exception e)
{
reader.setEntityResolver(resolver);
}
}
reader.parse(is);
} catch (SAXException e)
{
//LoggerFactory.getLoggerInstance().logError("XSD Validator Exception: ", e);
}
} catch (IOException except)
{
//LoggerFactory.getLoggerInstance().logError("XSD Validator Exception: ", except);
}
} catch (Exception e)
{
//LoggerFactory.getLoggerInstance().logError("XSD Validator Exception: ", e);
}
return valinfo;
}
/**
* Set the URI resolver to use with XSD validation.
*
* @param uriresolver
* The URI resolver to use.
*/
public void setURIResolver(URIResolver uriresolver)
{
this.uriresolver = uriresolver;
}
/**
* The XSDErrorHandler handle Xerces parsing errors and puts the errors
* into the given ValidationInfo object.
*
* @author Lawrence Mandel, IBM
*/
protected class XSDErrorHandler implements org.xml.sax.ErrorHandler
{
private final int ERROR = 0;
private final int WARNING = 1;
private final ValidationInfo valinfo;
public XSDErrorHandler(ValidationInfo valinfo)
{
this.valinfo = valinfo;
}
/**
* Add a validation message with the given severity.
*
* @param exception The exception that contains the information about the message.
* @param severity The severity of the validation message.
*/
protected void addValidationMessage(SAXParseException exception, int severity)
{
// get the error key by taking the substring of what is before the ':' in the error message:
String errorKey = exception.getLocalizedMessage();
if (errorKey != null)
{
int index = errorKey.indexOf(':');
if (index != -1)
{ errorKey = errorKey.substring(0, index);
}
}
if (exception.getSystemId() != null && !exception.getSystemId().equals(DUMMY_URI))
{
if (severity == WARNING)
{
valinfo.addWarning(exception.getLocalizedMessage(), exception.getLineNumber(), exception.getColumnNumber(), exception.getSystemId());
}
else
{
valinfo.addError(exception.getLocalizedMessage(), exception.getLineNumber(), exception.getColumnNumber(), exception.getSystemId(), errorKey, null);
}
}
}
/* (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);
}
}
/**
* The XSDEntityResolver wraps an idresolver to provide entity resolution to
* the XSD validator.
*
* @author Lawrence Mandel, IBM
*/
protected class XSDEntityResolver implements EntityResolver, XMLEntityResolver
{
private URIResolver uriresolver = null;
private String baselocation = null;
private InputStream inputStream;
/**
* Constructor.
*
* @param idresolver
* The idresolver this entity resolver wraps.
* @param baselocation The base location to resolve with.
*/
public XSDEntityResolver(URIResolver uriresolver, String baselocation, InputStream inputStream)
{
this.uriresolver = uriresolver;
this.baselocation = baselocation;
this.inputStream = inputStream;
}
/*
* (non-Javadoc)
*
* @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String,
* java.lang.String)
*/
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException
{
String location = null;
if (baselocation.equals(systemId))
{
location = systemId;
}
else
{
location = uriresolver.resolve(baselocation, publicId, systemId);
}
InputSource is = null;
if (location != null && !location.equals(""))
{
try
{
URI uri = URI.create(location);
URL url = uri.toURL();
is = new InputSource(location);
is.setByteStream(url.openStream());
} catch (MalformedURLException e)
{
throw new IOException(e.getMessage());
}
}
return is;
}
/* (non-Javadoc)
* @see org.apache.xerces.xni.parser.XMLEntityResolver#resolveEntity(org.apache.xerces.xni.XMLResourceIdentifier)
*/
public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier) throws XNIException, IOException
{
// consider the resourceIdentifier's fields to see we're actually in the dummy xml file
// and if this is the reference to the schema we want to validate
// ... if so return an XMLInputSource built from the dirty copy of the schema
boolean isDummyXML = DUMMY_URI.equals(resourceIdentifier.getBaseSystemId());
if (isDummyXML && inputStream != null)
{
XMLInputSource inputSource = new XMLInputSource(null, resourceIdentifier.getLiteralSystemId(), null, inputStream, null);
return inputSource;
}
else
{
// TODO cs: In revision 1.1 we explicitly opened a stream to ensure
// file I/O problems produced messages. I've remove this fudge for now
// since I can't seem to reproduce the problem it was intended to fix.
// I'm hoping the newer Xerces code base has fixed this problem and the fudge is defunct.
return XMLValidator._internalResolveEntity(uriresolver, resourceIdentifier);
}
}
}
}