/* * © Copyright IBM Corp. 2012 * * 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. */ /* * Created on May 29, 2005 * */ package com.ibm.commons.xml.xpath; import java.util.ArrayList; import java.util.Iterator; import com.ibm.commons.util.StringUtil; import com.ibm.commons.xml.NamespaceContext; import com.ibm.commons.xml.XResult; import com.ibm.commons.xml.xpath.part.Part; import com.ibm.commons.xml.xpath.xml.Utils; /** * @author Mark Wallace * @author Eugene Konstantinov * @author Philippe Riand */ abstract public class AbstractSimpleExpression extends AbstractExpression { protected boolean _isFromRoot; protected Part[] _parts; /** * Construct a SimpleXPathExpression with the specified properties * * @param expression * @param fromRoot * @param parts */ public AbstractSimpleExpression(String expression, boolean isFromRoot, Part[] parts) { super(expression); _isFromRoot = isFromRoot; _parts = parts; } /** * Return true if this expression is relative to the root, otherwise return * false. * * @return true if this expression is relative to the root */ public boolean isFromRoot() { return _isFromRoot; } /** * Return the count of Part objects associated with this * SimpleXPathExpression. * * @return the count of Part objects */ public int getPartCount() { return (_parts != null) ? _parts.length : 0; } /** * Return the nth Part object associated with this SimpleXPathExpression. * * @param index * @return the nth Part object */ public Part getPart(int index) { return (_parts != null) ? _parts[index] : null; } // // Implementation of XPathExpression // /** * * @see com.ibm.xfaces.xpath.XPathExpression#isSimple() */ public boolean isSimple() { return true; } /** * Evaluates this simple xpath expression. * <p> * The algorithm for evaluation iteratively step through each part of the * xpath calling evaluatePart() * * @param node * the node evaluation start from * @see com.ibm.xfaces.xpath.XPathExpression#isSimple() * @see #evaluatePart(Object, Part) */ protected XResult doEval(Object node, NamespaceContext namespaceContext) throws XPathException { try { if(isFromRoot()) { node = getRootNode(node); } namespaceContext = resolveNamespaceContext(node,namespaceContext); int count = (_parts != null) ? _parts.length : 0; if (count== 0 && StringUtil.isEmpty(getExpression())) { throw new XPathException(null, "Incorrect XPATH: Empty text was provided"); // $NLS-AbstractSimpleExpression.IncorrectXPATHEmptytextwasprovide-1$ } for(int pos = 0; pos < count && node != null; pos++) { node = evaluatePart(node, _parts[pos], namespaceContext); } return wrapUp(node); } catch (Exception e) { // System.err.println("Evaluation error for: " + getExpression() // + " Root cause:" + e.getMessage()); throw new XPathException(e); } } /** * Sets up the new value of the node * * @param node * the node the new value is being set * @param value * the new value for the node * @param autoCreate * set to <code>true</code> to indicate that node must be * created if it doesnot exist * @see com.ibm.xfaces.xpath.XPathExpression#setValue(Object, Object, * boolean) */ protected void doSetValue(Object node, Object value, NamespaceContext namespaceContext, boolean autoCreate) throws XPathException { if(isFromRoot()) { node = getRootNode(node); } Object curObject = node; Object prevObject = null; try { if (!(isValid(node))) { throw new XPathException(null,"Incorrect node type for evaluation:" // $NLS-AbstractSimpleExpression.Incorrectnodetypeforevaluation-1$ + node.getClass()); } if (getPartCount() == 0 && StringUtil.isEmpty(getExpression())) { throw new XPathException(null, "Incorrect XPATH: Empty text was provided"); // $NLS-AbstractSimpleExpression.IncorrectXPATHEmptytextwasprovide.1-1$ } namespaceContext = resolveNamespaceContext(node,namespaceContext); // evaluate all parts and create any that are missing if // auto create has been set to true Part currentPart = null; int currentPos = 0; while (getPartCount() > currentPos) { currentPart = getPart(currentPos); currentPos++; prevObject = curObject; curObject = evaluatePart(curObject, currentPart, namespaceContext); if (curObject == null) { if (autoCreate) { curObject = createPart(prevObject, currentPart, namespaceContext); } else { throw new XPathException(null,"Evaluation error for " // $NLS-AbstractSimpleExpression.Evaluationerrorfor.1-1$ + currentPart.toString() + " of the XPATH:" // $NLS-AbstractSimpleExpression.oftheXPATH-1$ + getExpression()); } } if (!isValid(curObject)) { throw new XPathException(null, "Incorrect node type for evaluation:" // $NLS-AbstractSimpleExpression.Incorrectnodetypeforevaluation.1-1$ + node.getClass()); } } String strValue = Utils.getAsString(value); setNodeValue(curObject, strValue); } catch (Exception e) { // System.err.println("Evaluation error for : " + getExpression() // + " Root cause:" + e.getMessage()); throw new XPathException(e); } } /** * Creates node or nodes * * @param node * the node the evaluation starts from * @return the inner most node created (corresponds to the very last part of * xpath expression) or null * @see com.ibm.xfaces.xpath.XPathExpression#doCreateNodes(Object, Object) */ protected Object doCreateNodes(Object node, NamespaceContext namespaceContext) throws XPathException { if(isFromRoot()) { node = getRootNode(node); } ArrayList<Object> created = new ArrayList<Object>(); Object curObject = node; try { if (!(isValid(node))) { throw new XPathException(null,"Incorrect node type for evaluation:" // $NLS-AbstractSimpleExpression.Incorrectnodetypeforevaluation.2-1$ + node.getClass()); } if (getPartCount() == 0 && StringUtil.isEmpty(getExpression())) { throw new XPathException(null, "Incorrect XPATH: Empty text was provided"); // $NLS-AbstractSimpleExpression.IncorrectXPATHEmptytextwasprovide.2-1$ } namespaceContext = resolveNamespaceContext(node,namespaceContext); Part currentPart = null; int currentPos = 0; while (getPartCount() > currentPos) { currentPart = getPart(currentPos); currentPos++; Object evalObject = evaluatePart(curObject, currentPart, namespaceContext); if (evalObject == null) { evalObject = createPart(curObject, currentPart, namespaceContext); // keep record created nodes if (evalObject != null) { created.add(evalObject); } } curObject = evalObject; continue; } return curObject; } catch (Exception e) { // release any created DataObjects in an event of an error for (Iterator<Object> iter = created.iterator(); iter.hasNext();) { deletePart(iter.next(), null); } throw new XPathException(e); } } /** * Get the root element for a particular node. * This method is called by absolute XPath evaluation. At leat, this method should * return the node itself if a root node doesn't make sense. * @param node * @return */ public abstract Object getRootNode(Object node); /** * Creates node for the respective xpath part * * @param node * the node that is parent for the newly created node * @param part * the part of the xpath that the current node is being created * @return the newly create node or <code>null</code> */ abstract protected Object createPart(Object node, Part part, NamespaceContext namespaceContext) throws XPathException; /** * Sets a new value to a node * * @param node * the node whisch value is being set * @param part * the part that corresponds to the node which value is being set * @param value * the new value for the node */ abstract protected void setNodeValue(Object node, Object value) throws XPathException; /** * Deletes node * * @param node * the node that is being deleted * @param part * the part that corresponds to the node */ abstract protected void deletePart(Object node, Part part); /** * Evaluates the part of xpath expression * * @param node * the parent node * @param part * the part of the xpath to be evaluated * @return the node that corresponds with the part of the xpath * @throws XPathException */ abstract protected Object evaluatePart(Object node, Part part, NamespaceContext namespaceContext) throws XPathException; /** * Returns wrapper over the node to extract value of the given node. * <p> * This wrapper object must override <code>toString()</code>to return * <code>String</code> representation of the node's value * * @param node * the context node which value we trying to resolve * @return the object that wrapps up the node */ abstract public XResult wrapUp(Object node) throws XPathException; }