All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.taglibs.standard.tag.common.xml.JSTLXPathImpl Maven / Gradle / Ivy

There is a newer version: 3.0.1
Show newest version
/*
 * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
 * Copyright 2004 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.
 */

package org.apache.taglibs.standard.tag.common.xml;


import javax.xml.namespace.QName;
import javax.xml.namespace.NamespaceContext;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFunctionResolver;
import javax.xml.xpath.XPathVariableResolver;
import javax.xml.xpath.XPathExpression;

import org.apache.xml.dtm.DTM;
import org.apache.xpath.*;
import org.apache.xpath.objects.XObject;
import org.apache.xpath.res.XPATHErrorResources;
import org.apache.xalan.res.XSLMessages;

import org.w3c.dom.Node;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.traversal.NodeIterator;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.*;

import java.io.IOException;

/**
 * The JSTLXPathImpl class provides implementation for the methods defined  in
 * javax.xml.xpath.XPath interface. This provide simple access to the results
 * of an XPath expression. 
 * 
 * This class provides our own implementation of XPath, so that we can support 
 * a generic Object type in returnType arguement for XPath's evaluate instance 
 * method.
 *
 * Most of the implementation is exactly similar to what is already provided in 
 * com.sun.org.apache.xpath.internal.jaxp.XPathImpl.java
 */
public class JSTLXPathImpl implements javax.xml.xpath.XPath {

    // Private variables
    private XPathVariableResolver variableResolver;
    private XPathFunctionResolver functionResolver;
    private XPathVariableResolver origVariableResolver;
    private XPathFunctionResolver origFunctionResolver;
    private NamespaceContext namespaceContext=null;
    private org.apache.xpath.jaxp.JAXPPrefixResolver prefixResolver;
    // By default Extension Functions are allowed in XPath Expressions. If 
    // Secure Processing Feature is set on XPathFactory then the invocation of
    // extensions function need to throw XPathFunctionException
    private boolean featureSecureProcessing = false; 

    JSTLXPathImpl( XPathVariableResolver vr, XPathFunctionResolver fr ) {
        this.origVariableResolver = this.variableResolver = vr;
        this.origFunctionResolver = this.functionResolver = fr;
    }

    JSTLXPathImpl( XPathVariableResolver vr, XPathFunctionResolver fr, 
            boolean featureSecureProcessing ) {
        this.origVariableResolver = this.variableResolver = vr;
        this.origFunctionResolver = this.functionResolver = fr;
        this.featureSecureProcessing = featureSecureProcessing;
    }

    /**
     * 

Establishes a variable resolver.

* * @param resolver Variable Resolver */ public void setXPathVariableResolver(XPathVariableResolver resolver) { if ( resolver == null ) { String fmsg = XSLMessages.createXPATHMessage( XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, new Object[] {"XPathVariableResolver"} ); throw new NullPointerException( fmsg ); } this.variableResolver = resolver; } /** *

Returns the current variable resolver.

* * @return Current variable resolver */ public XPathVariableResolver getXPathVariableResolver() { return variableResolver; } /** *

Establishes a function resolver.

* * @param resolver XPath function resolver */ public void setXPathFunctionResolver(XPathFunctionResolver resolver) { if ( resolver == null ) { String fmsg = XSLMessages.createXPATHMessage( XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, new Object[] {"XPathFunctionResolver"} ); throw new NullPointerException( fmsg ); } this.functionResolver = resolver; } /** *

Returns the current function resolver.

* * @return Current function resolver */ public XPathFunctionResolver getXPathFunctionResolver() { return functionResolver; } /** *

Establishes a namespace context.

* * @param nsContext Namespace context to use */ public void setNamespaceContext(NamespaceContext nsContext) { if ( nsContext == null ) { String fmsg = XSLMessages.createXPATHMessage( XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, new Object[] {"NamespaceContext"} ); throw new NullPointerException( fmsg ); } this.namespaceContext = nsContext; this.prefixResolver = new org.apache.xpath.jaxp.JAXPPrefixResolver ( nsContext ); } /** *

Returns the current namespace context.

* * @return Current Namespace context */ public NamespaceContext getNamespaceContext() { return namespaceContext; } private static Document d = null; private static DocumentBuilder getParser() { try { // we'd really like to cache those DocumentBuilders, but we can't because: // 1. thread safety. parsers are not thread-safe, so at least // we need one instance per a thread. // 2. parsers are non-reentrant, so now we are looking at having a // pool of parsers. // 3. then the class loading issue. The look-up procedure of // DocumentBuilderFactory.newInstance() depends on context class loader // and system properties, which may change during the execution of JVM. // // so we really have to create a fresh DocumentBuilder every time we need one // - KK DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware( true ); dbf.setValidating( false ); return dbf.newDocumentBuilder(); } catch (ParserConfigurationException e) { // this should never happen with a well-behaving JAXP implementation. throw new Error(e); } } private static Document getDummyDocument( ) { // we don't need synchronization here; even if two threads // enter this code at the same time, we just waste a little time if(d==null) { DOMImplementation dim = getParser().getDOMImplementation(); d = dim.createDocument("http://java.sun.com/jaxp/xpath", "dummyroot", null); } return d; } private XObject eval(String expression, Object contextItem) throws javax.xml.transform.TransformerException { org.apache.xpath.XPath xpath = new org.apache.xpath.XPath( expression, null, prefixResolver, org.apache.xpath.XPath.SELECT ); org.apache.xpath.XPathContext xpathSupport = null; if ( functionResolver != null ) { org.apache.xpath.jaxp.JAXPExtensionsProvider jep = new org.apache.xpath.jaxp.JAXPExtensionsProvider( functionResolver, featureSecureProcessing ); xpathSupport = new org.apache.xpath.XPathContext( jep ); } else { xpathSupport = new org.apache.xpath.XPathContext(); } XObject xobj = null; xpathSupport.setVarStack(new org.apache.xpath.jaxp.JAXPVariableStack(variableResolver)); // If item is null, then we will create a a Dummy contextNode if ( contextItem instanceof Node ) { xobj = xpath.execute (xpathSupport, (Node)contextItem, prefixResolver ); } else { xobj = xpath.execute ( xpathSupport, DTM.NULL, prefixResolver ); } return xobj; } /** *

Evaluate an XPath expression in the specified context and return the result as the specified type.

* *

See "Evaluation of XPath Expressions" section of JAXP 1.3 spec * for context item evaluation, * variable, function and QName resolution and return type conversion.

* *

If returnType is not one of the types defined in {@link XPathConstants} ( * {@link XPathConstants#NUMBER NUMBER}, * {@link XPathConstants#STRING STRING}, * {@link XPathConstants#BOOLEAN BOOLEAN}, * {@link XPathConstants#NODE NODE} or * {@link XPathConstants#NODESET NODESET}) * then an IllegalArgumentException is thrown.

* *

If a null value is provided for * item, an empty document will be used for the * context. * If expression or returnType is null, then a * NullPointerException is thrown.

* * @param expression The XPath expression. * @param item The starting context (node or node list, for example). * @param returnType The desired return type. * * @return Result of evaluating an XPath expression as an Object of returnType. * * @throws XPathExpressionException If expression cannot be evaluated. * @throws IllegalArgumentException If returnType is not one of the types defined in {@link XPathConstants}. * @throws NullPointerException If expression or returnType is null. */ public Object evaluate(String expression, Object item, QName returnType) throws XPathExpressionException { if ( expression == null ) { String fmsg = XSLMessages.createXPATHMessage( XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, new Object[] {"XPath expression"} ); throw new NullPointerException ( fmsg ); } if ( returnType == null ) { String fmsg = XSLMessages.createXPATHMessage( XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, new Object[] {"returnType"} ); throw new NullPointerException ( fmsg ); } // Checking if requested returnType is supported. returnType need to // be defined in XPathConstants or JSTLXPathConstants if ( !isSupported ( returnType ) ) { String fmsg = XSLMessages.createXPATHMessage( XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, new Object[] { returnType.toString() } ); throw new IllegalArgumentException ( fmsg ); } try { XObject resultObject = eval( expression, item ); return getResultAsType( resultObject, returnType ); } catch ( java.lang.NullPointerException npe ) { // If VariableResolver returns null Or if we get // NullPointerException at this stage for some other reason // then we have to reurn XPathException throw new XPathExpressionException ( npe ); } catch ( javax.xml.transform.TransformerException te ) { Throwable nestedException = te.getException(); if ( nestedException instanceof javax.xml.xpath.XPathFunctionException ) { throw (javax.xml.xpath.XPathFunctionException)nestedException; } else { // For any other exceptions we need to throw // XPathExpressionException ( as per spec ) throw new XPathExpressionException ( te ); } } } private boolean isSupported( QName returnType ) { if ( ( returnType.equals( XPathConstants.STRING ) ) || ( returnType.equals( XPathConstants.NUMBER ) ) || ( returnType.equals( XPathConstants.BOOLEAN ) ) || ( returnType.equals( XPathConstants.NODE ) ) || ( returnType.equals( XPathConstants.NODESET ) ) || ( returnType.equals( JSTLXPathConstants.OBJECT ) ) ) { return true; } return false; } private Object getResultAsType( XObject resultObject, QName returnType ) throws javax.xml.transform.TransformerException { // XPathConstants.STRING if ( returnType.equals( XPathConstants.STRING ) ) { return resultObject.str(); } // XPathConstants.NUMBER if ( returnType.equals( XPathConstants.NUMBER ) ) { return new Double ( resultObject.num()); } // XPathConstants.BOOLEAN if ( returnType.equals( XPathConstants.BOOLEAN ) ) { return Boolean.valueOf( resultObject.bool()); } // XPathConstants.NODESET ---ORdered, UNOrdered??? if ( returnType.equals( XPathConstants.NODESET ) ) { return resultObject.nodelist(); } // XPathConstants.NODE if ( returnType.equals( XPathConstants.NODE ) ) { NodeIterator ni = resultObject.nodeset(); //Return the first node, or null return ni.nextNode(); } // JSTLXPathConstants.OBJECT if ( returnType.equals( JSTLXPathConstants.OBJECT ) ) { if (resultObject instanceof org.apache.xpath.objects.XNodeSet) return resultObject.nodelist(); else return resultObject.object(); } String fmsg = XSLMessages.createXPATHMessage( XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, new Object[] { returnType.toString()}); throw new IllegalArgumentException( fmsg ); } /** *

Evaluate an XPath expression in the specified context and return the result as a String.

* *

This method calls {@link #evaluate(String expression, Object item, QName returnType)} with a returnType of * {@link XPathConstants#STRING}.

* *

See "Evaluation of XPath Expressions" of JAXP 1.3 spec * for context item evaluation, * variable, function and QName resolution and return type conversion.

* *

If a null value is provided for * item, an empty document will be used for the * context. * If expression is null, then a NullPointerException is thrown.

* * @param expression The XPath expression. * @param item The starting context (node or node list, for example). * * @return The String that is the result of evaluating the expression and * converting the result to a String. * * @throws XPathExpressionException If expression cannot be evaluated. * @throws NullPointerException If expression is null. */ public String evaluate(String expression, Object item) throws XPathExpressionException { return (String)this.evaluate( expression, item, XPathConstants.STRING ); } /** *

Compile an XPath expression for later evaluation.

* *

If expression contains any {@link javax.xml.xpath.XPathFunction}s, * they must be available via the {@link XPathFunctionResolver}. * An {@link XPathExpressionException} will be thrown if the XPathFunction * cannot be resovled with the XPathFunctionResolver.

* *

If expression is null, a NullPointerException is thrown.

* * @param expression The XPath expression. * * @return Compiled XPath expression. * @throws XPathExpressionException If expression cannot be compiled. * @throws NullPointerException If expression is null. */ public XPathExpression compile(String expression) throws XPathExpressionException { // This is never used in JSTL if ( expression == null ) { String fmsg = XSLMessages.createXPATHMessage( XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, new Object[] {"XPath expression"} ); throw new NullPointerException ( fmsg ); } return null; /* try { com.sun.org.apache.xpath.internal.XPath xpath = new XPath (expression, null, prefixResolver, com.sun.org.apache.xpath.internal.XPath.SELECT ); // Can have errorListener com.sun.org.apache.xpath.internal.jaxp.XPathExpressionImpl ximpl = new com.sun.org.apache.xpath.internal.jaxp.XPathExpressionImpl (xpath, prefixResolver, functionResolver, variableResolver, featureSecureProcessing ); return ximpl; } catch ( javax.xml.transform.TransformerException te ) { throw new XPathExpressionException ( te ) ; } **/ } /** *

Evaluate an XPath expression in the context of the specified InputSource * and return the result as the specified type.

* *

This method builds a data model for the {@link InputSource} and calls * {@link #evaluate(String expression, Object item, QName returnType)} on the resulting document object.

* *

See "Evaluation of XPath Expressions" section of JAXP 1.3 spec * for context item evaluation, * variable, function and QName resolution and return type conversion.

* *

If returnType is not one of the types defined in {@link XPathConstants}, * then an IllegalArgumentException is thrown.

* *

If expression, source or returnType is null, * then a NullPointerException is thrown.

* * @param expression The XPath expression. * @param source The input source of the document to evaluate over. * @param returnType The desired return type. * * @return The Object that encapsulates the result of evaluating the expression. * * @throws XPathExpressionException If expression cannot be evaluated. * @throws IllegalArgumentException If returnType is not one of the types defined in {@link XPathConstants}. * @throws NullPointerException If expression, source or returnType * is null. */ public Object evaluate(String expression, InputSource source, QName returnType) throws XPathExpressionException { // Checking validity of different parameters if( source== null ) { String fmsg = XSLMessages.createXPATHMessage( XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, new Object[] {"source"} ); throw new NullPointerException ( fmsg ); } if ( expression == null ) { String fmsg = XSLMessages.createXPATHMessage( XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, new Object[] {"XPath expression"} ); throw new NullPointerException ( fmsg ); } if ( returnType == null ) { String fmsg = XSLMessages.createXPATHMessage( XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, new Object[] {"returnType"} ); throw new NullPointerException ( fmsg ); } //Checking if requested returnType is supported. //returnType need to be defined in XPathConstants if ( !isSupported ( returnType ) ) { String fmsg = XSLMessages.createXPATHMessage( XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, new Object[] { returnType.toString() } ); throw new IllegalArgumentException ( fmsg ); } try { Document document = getParser().parse( source ); XObject resultObject = eval( expression, document ); return getResultAsType( resultObject, returnType ); } catch ( SAXException e ) { throw new XPathExpressionException ( e ); } catch( IOException e ) { throw new XPathExpressionException ( e ); } catch ( javax.xml.transform.TransformerException te ) { Throwable nestedException = te.getException(); if ( nestedException instanceof javax.xml.xpath.XPathFunctionException ) { throw (javax.xml.xpath.XPathFunctionException)nestedException; } else { throw new XPathExpressionException ( te ); } } } /** *

Evaluate an XPath expression in the context of the specified InputSource * and return the result as a String.

* *

This method calls {@link #evaluate(String expression, InputSource source, QName returnType)} with a * returnType of {@link XPathConstants#STRING}.

* *

See "Evaluation of XPath Expressions" section of JAXP 1.3 spec * for context item evaluation, * variable, function and QName resolution and return type conversion.

* *

If expression or source is null, * then a NullPointerException is thrown.

* * @param expression The XPath expression. * @param source The InputSource of the document to evaluate over. * * @return The String that is the result of evaluating the expression and * converting the result to a String. * * @throws XPathExpressionException If expression cannot be evaluated. * @throws NullPointerException If expression or source is null. */ public String evaluate(String expression, InputSource source) throws XPathExpressionException { return (String)this.evaluate( expression, source, XPathConstants.STRING ); } /** *

Reset this XPath to its original configuration.

* *

XPath is reset to the same state as when it was created with * {@link javax.xml.xpath.XPathFactory#newXPath()}. * reset() is designed to allow the reuse of existing XPaths * thus saving resources associated with the creation of new XPaths.

* *

The reset XPath is not guaranteed to have the same * {@link XPathFunctionResolver}, {@link XPathVariableResolver} * or {@link NamespaceContext} Objects, e.g. {@link Object#equals(Object obj)}. * It is guaranteed to have a functionally equal XPathFunctionResolver, * XPathVariableResolver * and NamespaceContext.

*/ public void reset() { this.variableResolver = this.origVariableResolver; this.functionResolver = this.origFunctionResolver; this.namespaceContext = null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy