001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.expression.jxpath;
018:
019: import java.util.Iterator;
020: import java.util.LinkedList;
021: import java.util.List;
022:
023: import org.apache.cocoon.components.expression.Expression;
024: import org.apache.cocoon.components.expression.ExpressionContext;
025: import org.apache.cocoon.components.expression.ExpressionException;
026: import org.apache.cocoon.components.expression.jexl.JSIntrospector;
027: import org.apache.cocoon.util.jxpath.NamespacesTablePointer;
028: import org.apache.commons.jxpath.CompiledExpression;
029: import org.apache.commons.jxpath.JXPathContext;
030: import org.apache.commons.jxpath.Pointer;
031: import org.apache.commons.jxpath.Variables;
032: import org.mozilla.javascript.NativeArray;
033: import org.w3c.dom.Node;
034:
035: /**
036: * @version SVN $Id: JXPathExpression.java 449189 2006-09-23 06:52:29Z crossley $
037: */
038: public class JXPathExpression implements Expression {
039: private final String language;
040: private final String expression;
041: private final CompiledExpression compiledExpression;
042: private boolean lenient = false;
043:
044: public static final String LENIENT = "lenient";
045:
046: public JXPathExpression(String language, String expression)
047: throws ExpressionException {
048: this .language = language;
049: this .expression = expression;
050: this .compiledExpression = JXPathContext.compile(expression);
051: }
052:
053: public Object evaluate(ExpressionContext context)
054: throws ExpressionException {
055: return this .compiledExpression.getValue(getContext(context));
056: }
057:
058: public Iterator iterate(ExpressionContext context)
059: throws ExpressionException {
060: final JXPathContext jxpathContext = getContext(context);
061: Object val = this .compiledExpression.getPointer(jxpathContext,
062: this .expression).getNode();
063: // FIXME: workaround for JXPath bug
064: if (val instanceof NativeArray)
065: return new JSIntrospector.NativeArrayIterator(
066: (NativeArray) val);
067: else
068: return new Iterator() {
069: Iterator iter = compiledExpression
070: .iteratePointers(jxpathContext);
071:
072: public boolean hasNext() {
073: return iter.hasNext();
074: }
075:
076: public Object next() {
077: return ((Pointer) iter.next()).getNode();
078: }
079:
080: public void remove() {
081: iter.remove();
082: }
083: };
084: }
085:
086: public void assign(ExpressionContext context, Object value)
087: throws ExpressionException {
088: this .compiledExpression.setValue(getContext(context), value);
089: }
090:
091: public String getExpression() {
092: return this .expression;
093: }
094:
095: public String getLanguage() {
096: return this .language;
097: }
098:
099: public void setProperty(String property, Object value) {
100: if (LENIENT.equals(property))
101: this .lenient = ((Boolean) value).booleanValue();
102: }
103:
104: // Hack: try to prevent JXPath from converting result to a String
105: public Object getNode(ExpressionContext context)
106: throws ExpressionException {
107: Iterator iter = this .compiledExpression
108: .iteratePointers(getContext(context));
109: if (iter.hasNext()) {
110: Pointer first = (Pointer) iter.next();
111: if (iter.hasNext()) {
112: List result = new LinkedList();
113: result.add(first.getNode());
114: boolean dom = (first.getNode() instanceof Node);
115: while (iter.hasNext()) {
116: Object obj = ((Pointer) iter.next()).getNode();
117: dom = dom && (obj instanceof Node);
118: result.add(obj);
119: }
120: Object[] arr;
121: if (dom) {
122: arr = new Node[result.size()];
123: } else {
124: arr = new Object[result.size()];
125: }
126: result.toArray(arr);
127: return arr;
128: }
129: return first.getNode();
130: }
131: return null;
132: }
133:
134: private JXPathContext getContext(ExpressionContext context) {
135: // This could be made more efficient by caching the
136: // JXPathContext within the Context object.
137: JXPathContext jxcontext = JXPathContext.newContext(context
138: .getContextBean());
139: jxcontext.setVariables(new VariableAdapter(context));
140: jxcontext.setLenient(this .lenient);
141: jxcontext
142: .setNamespaceContextPointer(new NamespacesTablePointer(
143: context.getNamespaces()));
144: return jxcontext;
145: }
146:
147: private static class VariableAdapter implements Variables {
148: private ExpressionContext context;
149:
150: public VariableAdapter(ExpressionContext context) {
151: this .context = context;
152: }
153:
154: public void declareVariable(String name, Object value) {
155: this .context.put(name, value);
156: }
157:
158: public Object getVariable(String name) {
159: return this .context.get(name);
160: }
161:
162: public boolean isDeclaredVariable(String name) {
163: return this .context.containsKey(name);
164: }
165:
166: public void undeclareVariable(String name) {
167: throw new UnsupportedOperationException(
168: "Operation undeclareVariable is not supported");
169: }
170: }
171: }
|