blob: 96c578cf68026cffea40db4c3a56d62472b0e614 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2008 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.xml.core.internal.validation;
import java.io.InputStream;
import java.io.Reader;
import java.net.URL;
import java.util.List;
import com.ibm.icu.util.StringTokenizer;
import java.util.Vector;
import org.eclipse.wst.common.uriresolver.internal.provisional.URIResolver;
import org.eclipse.wst.common.uriresolver.internal.provisional.URIResolverPlugin;
import org.eclipse.wst.common.uriresolver.internal.util.URIHelper;
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.LexicalHandler;
/**
* A helper class for the XML validator.
*
* @author Craig Salter, IBM
* @author Lawrence Mandel, IBM
*/
public class ValidatorHelper
{
public List namespaceURIList = new Vector();
public boolean isGrammarEncountered = false;
public boolean isDTDEncountered = false;
public boolean isNamespaceEncountered = false;
public String schemaLocationString = ""; //$NON-NLS-1$
public int numDTDElements = 0;
/**
* Constructor.
*/
public ValidatorHelper()
{
}
/**
* Create an XML Reader.
*
* @return An XML Reader if one can be created or null.
* @throws Exception
*/
protected XMLReader createXMLReader(String uri) throws Exception
{
XMLReader reader = null;
reader = new org.apache.xerces.parsers.SAXParser();
reader.setFeature("http://apache.org/xml/features/continue-after-fatal-error", false); //$NON-NLS-1$
reader.setFeature("http://xml.org/sax/features/namespace-prefixes", true); //$NON-NLS-1$
reader.setFeature("http://xml.org/sax/features/namespaces", false); //$NON-NLS-1$
reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); //$NON-NLS-1$
reader.setContentHandler(new MyContentHandler(uri));
reader.setErrorHandler(new InternalErrorHandler());
LexicalHandler lexicalHandler = new LexicalHandler()
{
public void startDTD (String name, String publicId, String systemId)
{
isGrammarEncountered = true;
isDTDEncountered = true;
}
public void endDTD() throws SAXException
{
}
public void startEntity(String name) throws SAXException
{
}
public void endEntity(String name) throws SAXException
{
}
public void startCDATA() throws SAXException
{
}
public void endCDATA() throws SAXException
{
}
public void comment (char ch[], int start, int length) throws SAXException
{
}
};
reader.setProperty("http://xml.org/sax/properties/lexical-handler", lexicalHandler); //$NON-NLS-1$
return reader;
}
/**
* An error handler to suppress error and warning information.
*/
private class InternalErrorHandler implements org.xml.sax.ErrorHandler
{
public InternalErrorHandler()
{
super();
}
public void error(SAXParseException exception) throws SAXException
{
}
public void fatalError(SAXParseException exception) throws SAXException
{
}
public void warning(SAXParseException exception) throws SAXException
{
}
}
/**
* Figures out the information needed for validation.
*
* @param uri The uri of the file to validate.
* @param uriResolver A helper to resolve locations.
*/
public void computeValidationInformation(String uri, Reader characterStream, URIResolver uriResolver)
{
try
{
XMLReader reader = createXMLReader(uri);
InputSource inputSource = new InputSource(uri);
inputSource.setCharacterStream(characterStream);
reader.parse(inputSource);
}
catch (Exception e)
{
//System.out.println(e);
}
}
/**
* Handle the content while parsing the file.
*/
class MyContentHandler extends org.xml.sax.helpers.DefaultHandler
{
/* (non-Javadoc)
* @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException)
*/
boolean isRootElement = true;
String baseURI;
MyContentHandler(String uri)
{
this.baseURI = uri;
}
public void error(SAXParseException e) throws SAXException
{
}
/* (non-Javadoc)
* @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException)
*/
public void fatalError(SAXParseException e) throws SAXException
{
}
/* (non-Javadoc)
* @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException)
*/
public void warning(SAXParseException e) throws SAXException
{
}
public String getPrefix(String name)
{
String prefix = null;
int index = name.indexOf(":"); //$NON-NLS-1$
if (index != -1)
{
prefix = name.substring(0, index);
}
return prefix;
}
public String getUnprefixedName(String name)
{
int index = name.indexOf(":"); //$NON-NLS-1$
if (index != -1)
{
name = name.substring(index + 1);
}
return name;
}
public String getPrefixedName(String prefix, String localName)
{
return prefix != null && prefix.length() > 0 ? prefix + ":" + localName : localName; //$NON-NLS-1$
}
public void startElement(String namespaceURI, String localName, String rawName, Attributes atts)
{
//String explicitLocation = null;
if (isRootElement)
{
isRootElement = false;
int nAtts = atts.getLength();
String schemaInstancePrefix = null;
for (int i =0; i < nAtts; i++)
{
String attributeName = atts.getQName(i);
if (attributeName.equals("xmlns") || attributeName.startsWith("xmlns:")) //$NON-NLS-1$ //$NON-NLS-2$
{
isNamespaceEncountered = true;
String value = atts.getValue(i);
if (value.startsWith("http://www.w3.org/") && value.endsWith("/XMLSchema-instance")) //$NON-NLS-1$ //$NON-NLS-2$
{
schemaInstancePrefix = attributeName.equals("xmlns") ? "" : getUnprefixedName(attributeName); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
String prefix = getPrefix(rawName);
String rootElementNamespaceDeclarationName = (prefix != null && prefix.length() > 0) ? "xmlns:" + prefix : "xmlns"; //$NON-NLS-1$ //$NON-NLS-2$
String rootElementNamespace = rootElementNamespaceDeclarationName != null ? atts.getValue(rootElementNamespaceDeclarationName) : null;
String location = null;
// first we use any 'xsi:schemaLocation' or 'xsi:noNamespaceSchemaLocation' attribute
// to determine a location
if (schemaInstancePrefix != null)
{
location = atts.getValue(getPrefixedName(schemaInstancePrefix, "noNamespaceSchemaLocation")); //$NON-NLS-1$
if (location == null)
{
String schemaLoc = atts.getValue(getPrefixedName(schemaInstancePrefix, "schemaLocation")); //$NON-NLS-1$
location = getSchemaLocationForNamespace(schemaLoc, rootElementNamespace);
}
}
if (rootElementNamespace == null)
{
rootElementNamespace = "";
}
location = URIResolverPlugin.createResolver().resolve(baseURI, rootElementNamespace, location);
location = URIResolverPlugin.createResolver().resolvePhysicalLocation(baseURI, rootElementNamespace, location);
location = URIHelper.addImpliedFileProtocol(location);
schemaLocationString = location;
if (location != null)
{
InputStream is = null;
try
{
URL url = new URL(location);
is = url.openStream();
isGrammarEncountered = true;
}
catch(Exception e)
{
// Do nothing.
}
finally
{
if(is != null)
{
try
{
is.close();
}
catch(Exception e)
{
// Do nothing.
}
}
}
}
}
}
/* (non-Javadoc)
* @see org.xml.sax.ext.DeclHandler#elementDecl(java.lang.String, java.lang.String)
*/
public void elementDecl(String name, String model)
{
numDTDElements++;
}
// The xsiSchemaLocationValue is a list of namespace/location pairs that are separated by whitespace
// this method walks the list of pairs looking for the specified namespace and returns the associated
// location.
//
protected String getSchemaLocationForNamespace(String xsiSchemaLocationValue, String namespace)
{
String result = null;
if (xsiSchemaLocationValue != null && namespace != null)
{
StringTokenizer st = new StringTokenizer(xsiSchemaLocationValue);
while(st.hasMoreTokens())
{
if(st.nextToken().equals(namespace))
{
if(st.hasMoreTokens())
{
result = st.nextToken();
}
}
else
{
if(st.hasMoreTokens())
{
st.nextToken();
}
}
}
}
return result;
}
}
/**
* Replace all instances in the string of the old pattern with the new pattern.
*
* @param string The string to replace the patterns in.
* @param oldPattern The old pattern to replace.
* @param newPattern The pattern used for replacement.
* @return The modified string with all occurrances of oldPattern replaced by new Pattern.
*/
protected static String replace(String string, String oldPattern, String newPattern)
{
int index = 0;
while (index != -1)
{
index = string.indexOf(oldPattern, index);
if (index != -1)
{
string = string.substring(0, index) + newPattern + string.substring(index + oldPattern.length());
index = index + oldPattern.length();
}
}
return string;
}
}