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 */ 017package org.apache.commons.jxpath.ri.compiler; 018 019import org.apache.commons.jxpath.NodeSet; 020import org.apache.commons.jxpath.Pointer; 021import org.apache.commons.jxpath.ri.EvalContext; 022import org.apache.commons.jxpath.ri.model.NodePointer; 023import org.apache.commons.jxpath.ri.QName; 024import org.apache.commons.jxpath.util.ValueUtils; 025 026import java.util.Collections; 027import java.util.Iterator; 028import java.util.Locale; 029 030/** 031 * Common superclass for several types of nodes in the parse tree. Provides 032 * APIs for optimization of evaluation of expressions. Specifically, an 033 * expression only needs to executed once during the evaluation of an xpath 034 * if that expression is context-independent. Expression.isContextDependent() 035 * provides that hint. 036 * 037 * @author Dmitri Plotnikov 038 * @version $Revision: 652845 $ $Date: 2008-05-02 19:46:46 +0200 (Fr, 02 Mai 2008) $ 039 */ 040public abstract class Expression { 041 042 /** zero */ 043 protected static final Double ZERO = new Double(0); 044 045 /** one */ 046 protected static final Double ONE = new Double(1); 047 048 /** NaN */ 049 protected static final Double NOT_A_NUMBER = new Double(Double.NaN); 050 051 private boolean contextDependencyKnown = false; 052 private boolean contextDependent; 053 054 /** 055 * Returns true if this expression should be re-evaluated 056 * each time the current position in the context changes. 057 * @return boolean 058 */ 059 public synchronized boolean isContextDependent() { 060 if (!contextDependencyKnown) { 061 contextDependent = computeContextDependent(); 062 contextDependencyKnown = true; 063 } 064 return contextDependent; 065 } 066 067 /** 068 * Implemented by subclasses and result is cached by isContextDependent() 069 * @return calculated context-dependentness as boolean 070 */ 071 public abstract boolean computeContextDependent(); 072 073 /** 074 * Evaluates the expression. If the result is a node set, returns 075 * the first element of the node set. 076 * @param context evaluation context 077 * @return Object 078 */ 079 public abstract Object computeValue(EvalContext context); 080 081 /** 082 * Evaluates the expression. If the result is a node set, returns 083 * the first element of the node set. 084 * @param context evaluation context 085 * @return Object 086 */ 087 public abstract Object compute(EvalContext context); 088 089 /** 090 * Iterate over the values from the specified context. 091 * @param context evaluation context 092 * @return value Iterator 093 */ 094 public Iterator iterate(EvalContext context) { 095 Object result = compute(context); 096 if (result instanceof EvalContext) { 097 return new ValueIterator((EvalContext) result); 098 } 099 if (result instanceof NodeSet) { 100 return new ValueIterator(((NodeSet) result).getPointers().iterator()); 101 } 102 return ValueUtils.iterate(result); 103 } 104 105 /** 106 * Iterate over the pointers from the specified context. 107 * @param context evaluation context 108 * @return pointer Iterator 109 */ 110 public Iterator iteratePointers(EvalContext context) { 111 Object result = compute(context); 112 if (result == null) { 113 return Collections.EMPTY_LIST.iterator(); 114 } 115 if (result instanceof EvalContext) { 116 return (EvalContext) result; 117 } 118 if (result instanceof NodeSet) { 119 return new PointerIterator(((NodeSet) result).getPointers().iterator(), 120 new QName(null, "value"), 121 context.getRootContext().getCurrentNodePointer().getLocale()); 122 } 123 return new PointerIterator(ValueUtils.iterate(result), 124 new QName(null, "value"), 125 context.getRootContext().getCurrentNodePointer().getLocale()); 126 } 127 128 /** 129 * Pointer iterator 130 */ 131 public static class PointerIterator implements Iterator { 132 private Iterator iterator; 133 private QName qname; 134 private Locale locale; 135 136 //to what method does the following comment refer? 137 /** 138 * Create a new PointerIterator 139 * @param it underlying Iterator 140 * @param qname name 141 * @param locale Locale 142 * @deprecated Use the method that takes a NamespaceManager 143 */ 144 public PointerIterator(Iterator it, QName qname, Locale locale) { 145 this.iterator = it; 146 this.qname = qname; 147 this.locale = locale; 148 } 149 150 public boolean hasNext() { 151 return iterator.hasNext(); 152 } 153 154 public Object next() { 155 Object o = iterator.next(); 156 return o instanceof Pointer ? o : NodePointer.newNodePointer(qname, o, locale); 157 } 158 159 /** 160 * Unsupported. 161 */ 162 public void remove() { 163 throw new UnsupportedOperationException(); 164 } 165 } 166 167 /** 168 * Value Iterator 169 */ 170 public static class ValueIterator implements Iterator { 171 private Iterator iterator; 172 173 /** 174 * Create a new ValueIterator. 175 * @param it underlying Iterator, may contain pointers 176 */ 177 public ValueIterator(Iterator it) { 178 this.iterator = it; 179 } 180 181 public boolean hasNext() { 182 return iterator.hasNext(); 183 } 184 185 public Object next() { 186 Object o = iterator.next(); 187 return o instanceof Pointer ? ((Pointer) o).getValue() : o; 188 } 189 190 /** 191 * Unsupported. 192 */ 193 public void remove() { 194 throw new UnsupportedOperationException(); 195 } 196 } 197}