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