| /******************************************************************************* |
| * Copyright (c) 2001, 2005 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.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); |
| |
| 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; |
| } |
| } |