/*
* $Header: /home/cvspublic/xml-cocoon2/src/scratchpad/src/org/apache/cocoon/validation/schematron/Attic/SchematronValidator.java,v 1.1.2.2 2002/06/05 17:19:30 vgritsenko Exp $
* $Revision: 1.1.2.2 $
* $Date: 2002/06/05 17:19:30 $
*
* ====================================================================
* The Apache Software License, Version 1.1
*
*
*
* Copyright (c) 1999-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2001, Plotnix, Inc,
* <http://www.plotnix.com/>.
* For more information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.cocoon.validation.schematron;
import java.io.OutputStreamWriter;
import java.io.InputStream;
// java classes
import java.util.Properties;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.SortedSet;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.File;
import java.net.URL;
// XML classes
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
// logger
import org.apache.log.Hierarchy;
import org.apache.log.Logger;
import org.apache.log.Priority;
// JXPath classes
import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.jxpath.Pointer;
// Cocoon classes
import org.apache.cocoon.validation.Validator;
/**
* An object representing a single Schematron schema, used to validate
* multiple XML instances.
*
* This implementation can validate JavaBeans and DOM documents.
* It is based exclusively on the JXPath library from the Jakarta Commons project.
* See http://jakarta.apache.org/commons/
*
* @author Ivelin Ivanov, ivelin@acm.org, ivelin@iname.com
*/
public class SchematronValidator implements Validator
{
/** The schema instance for this Validator
* It is initialized once when a new Validator instance
* is created and used multiple times for validating
* different JavaBeans/DOM objects against the schema
*/
private SchematronSchema schema_;
/**
* lookup map, with phase id keys.
* Used for efficiency when validating by phase
*/
private Map phaseMap_ = new HashMap();
/**
* the schema name space prefix used in the schema document
*/
private String schemaPrefix_;
/**
* the default schema name space prefix
*/
private String defaultSchemaPrefix_ = "sch";
/*
* Schematron Phase property
*/
private String phaseProperty_ = null;
/*
* private logger
*/
private Logger logger = setupLogger();
//
// Constructors
//
/**
* Constructs a new Validator object for a given Schematron schema.
*
* @param schema
* The Schematron schema
*/
public SchematronValidator (SchematronSchema schema)
{
schema_ = schema;
preparePhaseMap();
}
//
// helper methods for the constructors
//
/**
* initialize logger
*/
protected Logger setupLogger()
{
Logger logger = Hierarchy.getDefaultHierarchy().getLoggerFor("XmlForm");
logger.setPriority( Priority.ERROR );
return logger;
}
protected void preparePhaseMap()
{
Map patternMap = new HashMap();
Iterator ptiter = schema_.getPattern().iterator();
while (ptiter.hasNext())
{
Pattern pattern = (Pattern) ptiter.next();
patternMap.put( pattern.getId(), pattern );
}
Iterator phiter = schema_.getPhase().iterator();
while (phiter.hasNext())
{
Phase phase = (Phase) phiter.next();
List activePatterns = new ArrayList();
phaseMap_.put( phase.getId(), activePatterns );
Iterator activeIter = phase.getActive().iterator();
while (activeIter.hasNext())
{
ActivePattern active = (ActivePattern) activeIter.next();
activePatterns.add( patternMap.get( active.getPattern() ) );
}
}
}
//
// public methods
//
/**
* Performs validation of the passed JavaBean or DOM object.
*
* This method tries to find the "phase" attribute
* and runs the active patterns for the phase.
* If phase not found, the method will try to match all patterns
*
*
* @param jbean The JavaBean or DOM object to be validated.
* @param props Properties which control different aspects of the
* validation process. This method only looks for the phase property.
* Another implementation may use other.
*
* @return A Result object which represents the result
* of the validation.
*/
public SortedSet validate( Object jbean )
{
List patterns = null;
if (phaseProperty_ != null)
{
patterns = getPatternsForPhase( phaseProperty_ );
logger.debug(" Validating for phase: " + phaseProperty_);
}
else
{
patterns = schema_.getPattern();
logger.debug(" Validating all patterns. No phase provided ");
}
ValidationResult vres = new ValidationResult();
if (patterns != null)
{
// create the JXPathContext
// which will be used to validate each rule
JXPathContext jxpContext = JXPathContext.newContext( jbean );
Iterator iter = patterns.iterator ();
while (iter.hasNext ())
{
Pattern resultPattern = evalPattern( jxpContext, (Pattern) iter.next());
// if the resultPattern is null,
// then it passed successfully
if ( resultPattern != null) vres.addPattern( resultPattern );
}
}
return vres.toSortedSet();
}
/**
* return the list of patterns listed
* as <active/> elements of <phase/>
*
* @param phase name of the phase
* @return List of patterns
*/
protected List getPatternsForPhase( String phase )
{
return (List) phaseMap_.get( phase );
}
/**
* Returns pattern with rules which failed during validation.
* The context attribute of each rule in the result pattern
* contains the exact location of the failed element
* unlike the context attribute of the original pattern which
* is an XSLT production pattern
*
* @param jxpContext The JXPathContext being validated
* @param pattern The production schema pattern to be evaluated
* @return pattern with rules wich failed during validation.
*/
protected Pattern evalPattern( JXPathContext jxpContext, Pattern pattern)
{
// copy attributes
Pattern resultPattern = new Pattern();
resultPattern.setName( pattern.getName() );
resultPattern.setId( pattern.getId() );
// evaluate rules
Iterator iter = pattern.getRule().iterator();
while (iter.hasNext())
{
List failedRules = evalRule(jxpContext, (Rule) iter.next () );
// if there were failed rules
// add them to the list of other failed rules
if (failedRules.size () > 0)
{
failedRules.addAll ( resultPattern.getRule() );
resultPattern.setRule ( failedRules );
}
}
// if there are no failed rules return null
if (resultPattern.getRule().size() == 0) return null;
else return resultPattern;
}
/**
* Returns rules with asserts or reports which failed during validation.
* The context attribute of each rule in the result pattern
* contains the exact location of the failed element
* unlike the context attribute of the original pattern which
* is an XSLT production pattern
*
* @param jxpContext The JXPath context being validated
* @param rule The original pattern rule to be evaluated
* @return pattern with rules wich failed during validation.
*/
protected List evalRule( JXPathContext jxpContext, Rule rule )
{
List elements = jxpContext.locate( rule.getContext() );
List failedRules = new ArrayList();
Iterator pointerIter = elements.iterator ();
while ( pointerIter.hasNext() )
{
Pointer ptr = (Pointer) pointerIter.next ();
// prepare result Rule
Rule nextFailedRule = new Rule();
nextFailedRule.setContext( ptr.asPath() );
// switch to the context of the rule
JXPathContext localJxpContext = JXPathContext.newContext( jxpContext, ptr.getValue() );
// evaluate asserts
Iterator assertIter = rule.getAssert().iterator();
while (assertIter.hasNext())
{
Assert anAssert = (Assert) assertIter.next();
// if an assert test fails, then it should be added
// to the result
boolean passed = evalTest( localJxpContext, anAssert.getTest() );
if (!passed)
{
nextFailedRule.addAssert ( anAssert );
}
}
// evaluate reports
Iterator reportIter = rule.getReport().iterator();
while (reportIter.hasNext())
{
Report report = (Report) reportIter.next();
// if a report test passes, then it should be added
// to the result
boolean passed = evalTest( localJxpContext, report.getTest() );
if (passed)
{
nextFailedRule.addReport ( report );
}
}
// if the nextFailedRule is non empty,
// then add it to the list of failed rules
if (nextFailedRule.getAssert().size() > 0 || nextFailedRule.getReport().size() > 0)
{
failedRules.add( nextFailedRule );
}
}
return failedRules;
}
/**
* Test an XPath expression in a context
*
* @param jxpContext The JXPath context being validated
* @param String The XPath expression
* @return boolean result of evaluation
*/
protected boolean evalTest( JXPathContext jxpContext, String test )
{
Boolean passed = (Boolean) jxpContext.getValue( test, Boolean.class);
return passed.booleanValue ();
}
/**
* @param property name
* @return the property value
* @throws IllegalArgumentException when the property is not supported
*/
public java.lang.Object getProperty (java.lang.String property) throws java.lang.IllegalArgumentException
{
if (property.equals ( Validator.PROPERTY_PHASE ) ) return phaseProperty_;
else throw new IllegalArgumentException(" Property " + property + " is not supported");
}
/**
* @param property name
* @param value property value
* @throws IllegalArgumentException when the property is not supported
*/
public void setProperty (java.lang.String property, java.lang.Object value) throws java.lang.IllegalArgumentException
{
if ( !property.equals ( Validator.PROPERTY_PHASE ) || ( !(value instanceof String) ) )
{
throw new IllegalArgumentException(" Property " + property + " is not supported or value is invalid");
}
else phaseProperty_ = (String) value;
}
}