/** * Copyright 2011 meltmedia * * 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. */ package org.xchain.framework.jxpath; import java.util.Map; import javax.xml.namespace.NamespaceContext; import javax.xml.XMLConstants; import org.apache.commons.jxpath.ri.QName; import org.apache.commons.jxpath.ri.compiler.*; import org.apache.commons.jxpath.ri.Compiler; import org.apache.commons.jxpath.ri.Parser; import org.apache.commons.jxpath.JXPathException; /** * A static utility for validating the syntax of a JXPath Expression. * * @author Christian Trimble */ public class JXPathValidator { public static Compiler compiler = new TreeCompiler(); /** * Parses the specified xpath and throws a JXPathException if there is a syntax exception. */ public static void validate( String xpath ) { Parser.parseExpression(xpath, compiler); } public static void validate( String xpath, NamespaceContext xmlns ) { Expression expression = (Expression)Parser.parseExpression(xpath, compiler); validateExpression(expression, xmlns); } static void validateExpression( Expression expression, NamespaceContext xmlns ) { if( expression instanceof Path ) { validatePath( (Path)expression, xmlns ); } else if( expression instanceof VariableReference ) { validateVariableReference( (VariableReference)expression, xmlns ); } else if( expression instanceof Operation ) { validateOperation((Operation)expression, xmlns); } } static void validateExtensionFunction( ExtensionFunction ef, NamespaceContext xmlns ) { QName name = ef.getFunctionName(); String prefix = name.getPrefix(); // validate that the prefix is mapped. validatePrefix( prefix, xmlns ); // if the prefix is mapped to an xchain namespace uri, then validate that a function is bound that might match the function. // TODO: add hooks for lifecycle based validation. } static void validatePath( Path path, NamespaceContext xmlns ) { for( Step step : path.getSteps() ) { validateStep( step, xmlns ); } if( path instanceof ExpressionPath ) { validateExpressionPath( (ExpressionPath)path, xmlns ); } } static void validateVariableReference( VariableReference variableReference, NamespaceContext xmlns ) { QName name = variableReference.getVariableName(); validatePrefix(name.getPrefix(), xmlns); } static void validateOperation( Operation operation, NamespaceContext xmlns ) { if ( operation.getArguments() != null ) for ( Expression argument : operation.getArguments() ) { validateExpression(argument, xmlns); } if( operation instanceof ExtensionFunction ) { validateExtensionFunction((ExtensionFunction)operation, xmlns); } // ASSERT: all other types of operations do not need validation. } static void validateStep( Step step, NamespaceContext xmlns ) { validateNodeTest(step.getNodeTest(), xmlns); for( Expression predicate : step.getPredicates() ) { validateExpression( predicate, xmlns ); } } static void validateNodeTest( NodeTest nodeTest, NamespaceContext xmlns ) { if( nodeTest instanceof NodeNameTest ) { validateNodeNameTest( (NodeNameTest)nodeTest, xmlns); } // ASSERT: all other node test types are valid if they parsed. } static void validateNodeNameTest( NodeNameTest nodeNameTest, NamespaceContext xmlns ) { if( nodeNameTest.isWildcard() ) { // TODO: we need to investigate wildcards with namespaces. } else { QName name = nodeNameTest.getNodeName(); validatePrefix(name.getPrefix(), xmlns); } } static void validateExpressionPath( ExpressionPath ep, NamespaceContext xmlns ) { validateExpression(ep.getExpression(), xmlns); for( Expression predicate : ep.getPredicates() ) { validateExpression(predicate, xmlns); } } static void validatePrefix( String prefix, NamespaceContext xmlns ) { if( (prefix != null) && (!XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) && XMLConstants.NULL_NS_URI.equals(xmlns.getNamespaceURI(prefix)) ) { throw new JXPathException("The prefix '"+prefix+"' is not defined in the current context."); } } }