package org.codehaus.mojo.xml;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.io.File;
import java.io.IOException;
import javax.xml.XMLConstants;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.codehaus.mojo.xml.validation.ValidationSet;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
/**
* The ValidatorMojo's task is the validation of XML files against a given schema.
*
* @goal validate
* @phase test
*/
public class ValidateMojo
extends AbstractXmlMojo
{
/**
* Specifies a set of document types, which are being
* validated.
* @parameter
*/
private ValidationSet[] validationSets;
/**
* Reads a validation sets schema.
* @param pResolver The resolver to use for loading external entities.
* @param pValidationSet The validation set to configure.
* @return The validation sets schema, if any, or null.
* @throws MojoExecutionException Loading the schema failed.
*/
private Schema getSchema( Resolver pResolver, ValidationSet pValidationSet )
throws MojoExecutionException
{
final String publicId = pValidationSet.getPublicId();
final String systemId = pValidationSet.getSystemId();
if ( ( publicId == null || "".equals( publicId ) ) && ( systemId == null || "".equals( systemId ) ) )
{
return null;
}
getLog().debug( "Loading schema with public Id " + publicId + ", system Id " + systemId );
InputSource inputSource = null;
if ( pResolver != null )
{
try
{
inputSource = pResolver.resolveEntity( publicId, systemId );
}
catch ( SAXException e )
{
throw new MojoExecutionException( e.getMessage(), e );
}
catch ( IOException e )
{
throw new MojoExecutionException( e.getMessage(), e );
}
}
if ( inputSource == null )
{
inputSource = new InputSource();
inputSource.setPublicId( publicId );
inputSource.setSystemId( systemId );
}
final SAXSource saxSource = new SAXSource( inputSource );
String schemaLanguage = pValidationSet.getSchemaLanguage();
if ( schemaLanguage == null || "".equals( schemaLanguage ) )
{
schemaLanguage = XMLConstants.W3C_XML_SCHEMA_NS_URI;
}
try
{
SchemaFactory schemaFactory = SchemaFactory.newInstance( schemaLanguage );
if ( pResolver != null )
{
schemaFactory.setResourceResolver( pResolver );
}
return schemaFactory.newSchema( saxSource );
}
catch ( SAXException e )
{
throw new MojoExecutionException( "Failed to load schema with public ID " + publicId + ", system ID "
+ systemId + ": " + e.getMessage(), e );
}
}
/**
* Called for parsing or validating a single file.
* @param pResolver The resolver to use for loading external entities.
* @param pValidationSet The parsers or validators configuration.
* @param pSchema The schema to use.
* @param pFile The file to parse or validate.
* @throws MojoExecutionException Parsing or validating the file failed.
*/
private void validate( final Resolver pResolver, ValidationSet pValidationSet, Schema pSchema, File pFile )
throws MojoExecutionException
{
try
{
if ( pSchema == null )
{
getLog().debug( "Parsing " + pFile.getPath() );
parse( pResolver, pValidationSet, pFile );
}
else
{
getLog().debug( "Validating " + pFile.getPath() );
Validator validator = pSchema.newValidator();
if ( pResolver != null )
{
validator.setResourceResolver( pResolver );
}
validator.validate( new StreamSource( pFile ) );
}
}
catch ( SAXParseException e )
{
final String publicId = e.getPublicId();
final String systemId = e.getSystemId();
final int lineNum = e.getLineNumber();
final int colNum = e.getColumnNumber();
final String location;
if ( publicId == null && systemId == null && lineNum == -1 && colNum == -1 )
{
location = "";
}
else
{
final StringBuffer loc = new StringBuffer();
String sep = "";
if ( publicId != null )
{
loc.append( "Public ID " );
loc.append( publicId );
sep = ", ";
}
if ( systemId != null )
{
loc.append( sep );
loc.append( systemId );
sep = ", ";
}
if ( lineNum != -1 )
{
loc.append( sep );
loc.append( "line " );
loc.append( lineNum );
sep = ", ";
}
if ( colNum != -1 )
{
loc.append( sep );
loc.append( " column " );
loc.append( colNum );
sep = ", ";
}
location = loc.toString();
}
final String msg = "While parsing " + pFile.getPath() + ( "".equals( location ) ? "" : ", at " + location )
+ ": " + e.getMessage();
throw new MojoExecutionException( msg, e );
}
catch ( Exception e )
{
throw new MojoExecutionException( "While parsing " + pFile + ": " + e.getMessage(), e );
}
}
/**
* Creates a new instance of {@link SAXParserFactory}.
* @param pValidationSet The parser factories configuration.
* @return A new SAX parser factory.
*/
private SAXParserFactory newSAXParserFactory( ValidationSet pValidationSet )
{
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setValidating( pValidationSet.isValidating() );
if ( pValidationSet.isValidating() )
{
try
{
spf.setFeature( "http://apache.org/xml/features/validation/schema",
true );
}
catch ( SAXException e )
{
// Ignore this
}
catch ( ParserConfigurationException e )
{
// Ignore this
}
}
else
{
try
{
spf.setFeature( "http://apache.org/xml/features/nonvalidating/load-external-dtd", false );
}
catch ( SAXException e )
{
// Ignore this
}
catch ( ParserConfigurationException e )
{
// Ignore this
}
}
spf.setNamespaceAware( true );
return spf;
}
/**
* Called for validating a single file.
* @param pResolver The resolver to use for loading external entities.
* @param pValidationSet The validators configuration.
* @param pFile The file to validate.
* @throws IOException An I/O error occurred.
* @throws SAXException Parsing the file failed.
* @throws ParserConfigurationException Creating an XML parser failed.
*/
private void parse( Resolver pResolver, ValidationSet pValidationSet, File pFile )
throws IOException, SAXException, ParserConfigurationException
{
XMLReader xr = newSAXParserFactory( pValidationSet ).newSAXParser().getXMLReader();
if ( pResolver != null )
{
xr.setEntityResolver( pResolver );
}
xr.setErrorHandler( new ErrorHandler()
{
public void error( SAXParseException pException )
throws SAXException
{
throw pException;
}
public void fatalError( SAXParseException pException )
throws SAXException
{
throw pException;
}
public void warning( SAXParseException pException )
throws SAXException
{
throw pException;
}
} );
xr.parse( pFile.toURI().toURL().toExternalForm() );
}
/**
* Called for validating a set of XML files against a common schema.
* @param pResolver The resolver to use for loading external entities.
* @param pValidationSet The set of XML files to validate.
* @throws MojoExecutionException Validating the set of files failed.
* @throws MojoFailureException A configuration error was detected.
*/
private void validate( Resolver pResolver, ValidationSet pValidationSet )
throws MojoExecutionException, MojoFailureException
{
final Schema schema = getSchema( pResolver, pValidationSet );
final File[] files = getFiles( pValidationSet.getDir(),
pValidationSet.getIncludes(),
getExcludes( pValidationSet.getExcludes(),
pValidationSet.isSkipDefaultExcludes() ) );
if ( files.length == 0 )
{
getLog().info( "No matching files found for ValidationSet with public ID " + pValidationSet.getPublicId()
+ ", system ID " + pValidationSet.getSystemId() + "." );
}
for ( int i = 0; i < files.length; i++ )
{
validate( pResolver, pValidationSet, schema, files[i] );
}
}
/**
* Called by Maven for executing the Mojo.
* @throws MojoExecutionException Running the Mojo failed.
* @throws MojoFailureException A configuration error was detected.
*/
public void execute()
throws MojoExecutionException, MojoFailureException
{
if ( validationSets == null || validationSets.length == 0 )
{
throw new MojoFailureException( "No ValidationSets configured." );
}
Object oldProxySettings = activateProxy();
try
{
Resolver resolver = getResolver();
for ( int i = 0; i < validationSets.length; i++ )
{
ValidationSet validationSet = validationSets[i];
resolver.setValidating( validationSet.isValidating() );
validate( resolver, validationSet );
}
}
finally
{
passivateProxy( oldProxySettings );
}
}
}