/* * 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. */ package org.apache.cocoon.components.expression.jxpath; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.apache.cocoon.components.expression.Expression; import org.apache.cocoon.components.expression.ExpressionContext; import org.apache.cocoon.components.expression.ExpressionException; import org.apache.cocoon.components.expression.jexl.JSIntrospector; import org.apache.cocoon.util.jxpath.NamespacesTablePointer; import org.apache.commons.jxpath.CompiledExpression; import org.apache.commons.jxpath.JXPathContext; import org.apache.commons.jxpath.Pointer; import org.apache.commons.jxpath.Variables; import org.mozilla.javascript.NativeArray; import org.w3c.dom.Node; /** * @version SVN $Id$ */ public class JXPathExpression implements Expression { private final String language; private final String expression; private final CompiledExpression compiledExpression; private boolean lenient = false; public static final String LENIENT = "lenient"; public JXPathExpression(String language, String expression) throws ExpressionException { this.language = language; this.expression = expression; this.compiledExpression = JXPathContext.compile(expression); } public Object evaluate(ExpressionContext context) throws ExpressionException{ return this.compiledExpression.getValue(getContext(context)); } public Iterator iterate(ExpressionContext context) throws ExpressionException { final JXPathContext jxpathContext = getContext(context); Object val = this.compiledExpression.getPointer(jxpathContext, this.expression).getNode(); // FIXME: workaround for JXPath bug if (val instanceof NativeArray) return new JSIntrospector.NativeArrayIterator((NativeArray) val); else return new Iterator() { Iterator iter = compiledExpression.iteratePointers(jxpathContext); public boolean hasNext() { return iter.hasNext(); } public Object next() { return ((Pointer)iter.next()).getNode(); } public void remove() { iter.remove(); } }; } public void assign(ExpressionContext context, Object value) throws ExpressionException { this.compiledExpression.setValue(getContext(context), value); } public String getExpression() { return this.expression; } public String getLanguage() { return this.language; } public void setProperty(String property, Object value) { if (LENIENT.equals(property)) this.lenient = ((Boolean)value).booleanValue(); } // Hack: try to prevent JXPath from converting result to a String public Object getNode(ExpressionContext context) throws ExpressionException { Iterator iter = this.compiledExpression.iteratePointers(getContext(context)); if (iter.hasNext()) { Pointer first = (Pointer)iter.next(); if (iter.hasNext()) { List result = new LinkedList(); result.add(first.getNode()); boolean dom = (first.getNode() instanceof Node); while (iter.hasNext()) { Object obj = ((Pointer)iter.next()).getNode(); dom = dom && (obj instanceof Node); result.add(obj); } Object[] arr; if (dom) { arr = new Node[result.size()]; } else { arr = new Object[result.size()]; } result.toArray(arr); return arr; } return first.getNode(); } return null; } private JXPathContext getContext(ExpressionContext context) { // This could be made more efficient by caching the // JXPathContext within the Context object. JXPathContext jxcontext = JXPathContext.newContext(context.getContextBean()); jxcontext.setVariables(new VariableAdapter(context)); jxcontext.setLenient(this.lenient); jxcontext.setNamespaceContextPointer(new NamespacesTablePointer(context.getNamespaces())); return jxcontext; } private static class VariableAdapter implements Variables { private ExpressionContext context; public VariableAdapter(ExpressionContext context) { this.context = context; } public void declareVariable(String name, Object value) { this.context.put(name, value); } public Object getVariable(String name) { return this.context.get(name); } public boolean isDeclaredVariable(String name) { return this.context.containsKey(name); } public void undeclareVariable(String name) { throw new UnsupportedOperationException("Operation undeclareVariable is not supported"); } } }