diff --git a/plugin/src/main/org/apache/maven/JaxpMsvBean.java b/plugin/src/main/org/apache/maven/JaxpMsvBean.java index 93c85bf2..178aa05b 100644 --- a/plugin/src/main/org/apache/maven/JaxpMsvBean.java +++ b/plugin/src/main/org/apache/maven/JaxpMsvBean.java @@ -17,22 +17,25 @@ package org.apache.maven; * ==================================================================== */ - -import com.sun.msv.verifier.jaxp.SAXParserFactoryImpl; - import java.io.File; +import java.io.FileInputStream; import java.io.ByteArrayInputStream; -import javax.xml.parsers.*; +import javax.xml.parsers.SAXParserFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.xml.sax.helpers.DefaultHandler; +import org.iso_relax.verifier.Verifier; +import org.iso_relax.verifier.VerifierFactory; +import org.iso_relax.verifier.VerifierHandler; + +import org.xml.sax.ErrorHandler; +import org.xml.sax.EntityResolver; import org.xml.sax.SAXParseException; import org.xml.sax.SAXException; import org.xml.sax.InputSource; - +import org.xml.sax.XMLReader; /** * JaxpMsvBean Bean: Uses JAXP implementation of MSV. @@ -52,86 +55,47 @@ public class JaxpMsvBean /** For debug output. */ private Log log = LogFactory.getLog(JaxpMsvBean.class); - private static String EMPTY = ""; - private static ByteArrayInputStream EMPTY_STREAM = + private static final String EMPTY = ""; + private static final ByteArrayInputStream EMPTY_STREAM = new ByteArrayInputStream(EMPTY.getBytes()); - private static int MSV_WARNING = 0; - private static int MSV_ERROR = 1; - private static int MSV_FATAL_ERROR = 2; + private static final int MSV_WARNING = 0; + private static final int MSV_ERROR = 1; + private static final int MSV_FATAL_ERROR = 2; + + private boolean isValid = true; + private XPathLocationTracker tracker; //~ Methods -------------------------------------------------------------- /** * Performs validation. + * @throws Exception Exception */ - public void validate() throws Exception + public void validate() throws Exception { - SAXParserFactory factory = - new SAXParserFactoryImpl(new File(schema)); + + VerifierFactory verifierFactory = + new com.sun.msv.verifier.jarv.TheFactoryImpl(); + Verifier verifier = verifierFactory.newVerifier(new File(schema)); + + + SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setNamespaceAware(true); - SAXParser parser = factory.newSAXParser(); - parser.parse(new File(file), new DefaultHandler() - { - boolean isValid = true; - public void warning(SAXParseException e) throws SAXException - { - errorMessage(e, MSV_WARNING); - } - public void error(SAXParseException e) throws SAXException - { - errorMessage(e, MSV_ERROR); - isValid = false; - } - public void fatalError(SAXParseException e) throws SAXException - { - errorMessage(e, MSV_FATAL_ERROR); - isValid = false; - } - public InputSource resolveEntity(String publicId, - String systemId) throws SAXException - { - log.warn("WARNING: External entity " + systemId - + " won't be resolved!"); - return new InputSource(EMPTY_STREAM); - } - public void endDocument() - { - if(isValid) - { - log.info(file + " verified: OK"); - } else { - log.info("WARNING: " + file + " is NOT valid"); - } - } - }); - } + XMLReader reader = factory.newSAXParser().getXMLReader(); - private void errorMessage(SAXParseException e, int type) - { - File xmlFile = new File(file); + VerifierHandler handler = verifier.getVerifierHandler(); + tracker = new XPathLocationTracker(handler); + reader.setContentHandler(tracker); + reader.setEntityResolver( new EntityResolverImpl() ); + verifier.setErrorHandler( new ErrorHandlerImpl() ); - if (type == MSV_ERROR) - { - log.error( "com.sun.msv.verifier.ValidityViolation on line " - + e.getLineNumber() + " of " + xmlFile.getName() + ":" ); - log.error( e.getMessage() ); - } else if (type == MSV_FATAL_ERROR) - { - log.error( "Non-recoverable parsing error on line " - + e.getLineNumber() + " of " + xmlFile.getName() + ":" ); - log.error( e.getMessage() ); - } else if (type == MSV_WARNING) - { - log.warn( "Warning on line " - + e.getLineNumber() + " of " + xmlFile.getName() + ":" ); - log.warn( e.getMessage() ); - } + reader.parse(new InputSource(new FileInputStream(file))); + endMessage(); } /** * Sets the schema. - * * @param newSchema The schema to set */ public void setSchema(String newSchema) @@ -141,7 +105,6 @@ public class JaxpMsvBean /** * Sets the file. - * * @param newFile The file to set */ public void setFile(String newFile) @@ -151,7 +114,6 @@ public class JaxpMsvBean /** * Gets the schema. - * * @return The schema */ public String getSchema() @@ -161,7 +123,6 @@ public class JaxpMsvBean /** * Gets the file. - * * @return The file */ public String getFile() @@ -169,5 +130,76 @@ public class JaxpMsvBean return file; } + private void endMessage() + { + if ( isValid ) + { + log.info(file + " verified: OK"); + } else { + log.info(file + " is NOT valid!"); + } + } + + private void setValid(boolean valid) + { + this.isValid = valid; + } + + private void errorMessage(SAXParseException e, int type) + { + File xmlFile = new File(file); + + if (type == MSV_ERROR) + { + log.error( " ERROR on line " + e.getLineNumber() + + " of file " + xmlFile.getName() + "," ); + log.error( " XPath location " + tracker.getXPath() + ":" ); + log.error( " " + e.getMessage() ); + } else if (type == MSV_FATAL_ERROR) + { + log.error( " Non-recoverable parsing error on line " + + e.getLineNumber() + " of file " + xmlFile.getName() + "," ); + log.error( " XPath location " + tracker.getXPath() + ":" ); + log.error( " " + e.getMessage() ); + } else if (type == MSV_WARNING) + { + log.warn( " WARNING on line " + e.getLineNumber() + + " of file " + xmlFile.getName() + "," ); + log.warn( " XPath location " + tracker.getXPath() + ":" ); + log.warn( e.getMessage() ); + } + } + + private class ErrorHandlerImpl implements ErrorHandler + { + public void warning(SAXParseException e) throws SAXException + { + errorMessage(e, MSV_WARNING); + } + + public void error(SAXParseException e) throws SAXException + { + errorMessage(e, MSV_ERROR); + setValid(false); + } + + public void fatalError(SAXParseException e) throws SAXException + { + errorMessage(e, MSV_FATAL_ERROR); + setValid(false); + } + } + + + private class EntityResolverImpl implements EntityResolver + { + public InputSource resolveEntity(String publicId, + String systemId) throws SAXException + { + log.warn(" WARNING: External entity " + systemId + + " won't be resolved"); + return new InputSource(EMPTY_STREAM); + } + } } diff --git a/plugin/src/main/org/apache/maven/XPathLocationTracker.java b/plugin/src/main/org/apache/maven/XPathLocationTracker.java new file mode 100644 index 00000000..7b0b9184 --- /dev/null +++ b/plugin/src/main/org/apache/maven/XPathLocationTracker.java @@ -0,0 +1,206 @@ +package org.apache.maven; + +/* ==================================================================== + * Copyright 2001-2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + */ + +import java.util.HashMap; +import java.util.Map; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.XMLFilterImpl; + +/** + * Constructs an XPath expression in the form: + * /root/child[3]/grandchild[2] ... + * + * @author Lukas Theussl + */ + +public class XPathLocationTracker extends XMLFilterImpl +{ + + private State state; + private static final Integer[] ints = new Integer[] + { + new Integer(0), + new Integer(1), + new Integer(2), + new Integer(3), + new Integer(4) + }; + + /** + * Constructor: sets the ContentHandler. + * @param handler The ContentHandler + */ + public XPathLocationTracker( ContentHandler handler ) + { + setContentHandler(handler); + } + + /** + * Overriding ContentHandler. + * @throws SAXException SAXException + */ + public void startDocument() throws SAXException + { + state = new State(null); + super.startDocument(); + } + + /** + * Overriding ContentHandler. + * @throws SAXException SAXException + */ + public void endDocument() throws SAXException + { + super.endDocument(); + state = null; + } + + /** + * Overriding ContentHandler. + * @param uri uri + * @param localName localName + * @param qName qName + * @param atts atts + * @throws SAXException SAXException + */ + public void startElement(String uri, String localName, + String qName, Attributes atts) throws SAXException + { + state = state.push(qName); + super.startElement(uri, localName, qName, atts); + } + + /** + * Overriding ContentHandler. + * @param uri uri + * @param localName localName + * @param qName qName + * @throws SAXException SAXException + */ + public void endElement(String uri, String localName, String qName) + throws SAXException + { + super.endElement(uri, localName, qName); + state = state.pop(); + } + + /** + * Gets the XPath expression that points to the current location. + * Throws a new IllegalStateException if the component is not + * parsing a document. + * @return The XPath expression + */ + public final String getXPath() + { + if ( state == null ) + { + throw new IllegalStateException( + "startDocument event is not invoked"); + } + return state.getXPath(); + } + + private static Integer getInt(int i) + { + if ( i < ints.length ) + { + return ints[i]; + } else { + return new Integer(i); + } + } + + private static final class State + { + + private final Map counter = new HashMap(); + private final State parent; + private State child; + private String currentName; + + State( State newParent ) + { + this.parent = newParent; + } + + protected State push( String rawName ) + { + count(rawName); + currentName = rawName; + if ( child == null ) + { + child = new State(this); + } else { + child.reset(); + } + return child; + } + + protected State pop() + { + parent.currentName = null; + return parent; + } + + private void count( String rawName ) + { + Integer i = (Integer) counter.get(rawName); + if ( i == null ) + { + i = getInt(1); + } else { + i = getInt(i.intValue() + 1); + } + counter.put(rawName, i); + } + + private void reset() + { + counter.clear(); + currentName = null; + } + + private String getXPath() + { + String xPath; + if ( parent == null ) // root + { + xPath = "/"; + if ( currentName != null) + { + xPath += currentName; + } + } else { // child node + xPath = parent.getXPath(); + if ( currentName != null ) + { + xPath += '/' + currentName; + Integer i = (Integer) counter.get(currentName); + xPath += '[' + i.toString() + ']'; + } + } + return xPath; + } + + } + +}