001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: ParentLocationPath.java 468650 2006-10-28 07:03:30Z minchau $
020 */
021
022 package org.apache.xalan.xsltc.compiler;
023
024 import org.apache.bcel.generic.ALOAD;
025 import org.apache.bcel.generic.ASTORE;
026 import org.apache.bcel.generic.ConstantPoolGen;
027 import org.apache.bcel.generic.INVOKEINTERFACE;
028 import org.apache.bcel.generic.INVOKESPECIAL;
029 import org.apache.bcel.generic.INVOKEVIRTUAL;
030 import org.apache.bcel.generic.InstructionList;
031 import org.apache.bcel.generic.LocalVariableGen;
032 import org.apache.bcel.generic.NEW;
033 import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
034 import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
035 import org.apache.xalan.xsltc.compiler.util.Type;
036 import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
037 import org.apache.xalan.xsltc.compiler.util.Util;
038 import org.apache.xml.dtm.Axis;
039 import org.apache.xml.dtm.DTM;
040
041 /**
042 * @author Jacek Ambroziak
043 * @author Santiago Pericas-Geertsen
044 */
045 final class ParentLocationPath extends RelativeLocationPath {
046 private Expression _step;
047 private final RelativeLocationPath _path;
048 private Type stype;
049 private boolean _orderNodes = false;
050 private boolean _axisMismatch = false;
051
052 public ParentLocationPath(RelativeLocationPath path, Expression step) {
053 _path = path;
054 _step = step;
055 _path.setParent(this);
056 _step.setParent(this);
057
058 if (_step instanceof Step) {
059 _axisMismatch = checkAxisMismatch();
060 }
061 }
062
063 public void setAxis(int axis) {
064 _path.setAxis(axis);
065 }
066
067 public int getAxis() {
068 return _path.getAxis();
069 }
070
071 public RelativeLocationPath getPath() {
072 return(_path);
073 }
074
075 public Expression getStep() {
076 return(_step);
077 }
078
079 public void setParser(Parser parser) {
080 super.setParser(parser);
081 _step.setParser(parser);
082 _path.setParser(parser);
083 }
084
085 public String toString() {
086 return "ParentLocationPath(" + _path + ", " + _step + ')';
087 }
088
089 public Type typeCheck(SymbolTable stable) throws TypeCheckError {
090 stype = _step.typeCheck(stable);
091 _path.typeCheck(stable);
092
093 if (_axisMismatch) enableNodeOrdering();
094
095 return _type = Type.NodeSet;
096 }
097
098 public void enableNodeOrdering() {
099 SyntaxTreeNode parent = getParent();
100 if (parent instanceof ParentLocationPath)
101 ((ParentLocationPath)parent).enableNodeOrdering();
102 else {
103 _orderNodes = true;
104 }
105 }
106
107 /**
108 * This method is used to determine if this parent location path is a
109 * combination of two step's with axes that will create duplicate or
110 * unordered nodes.
111 */
112 public boolean checkAxisMismatch() {
113
114 int left = _path.getAxis();
115 int right = ((Step)_step).getAxis();
116
117 if (((left == Axis.ANCESTOR) || (left == Axis.ANCESTORORSELF)) &&
118 ((right == Axis.CHILD) ||
119 (right == Axis.DESCENDANT) ||
120 (right == Axis.DESCENDANTORSELF) ||
121 (right == Axis.PARENT) ||
122 (right == Axis.PRECEDING) ||
123 (right == Axis.PRECEDINGSIBLING)))
124 return true;
125
126 if ((left == Axis.CHILD) &&
127 (right == Axis.ANCESTOR) ||
128 (right == Axis.ANCESTORORSELF) ||
129 (right == Axis.PARENT) ||
130 (right == Axis.PRECEDING))
131 return true;
132
133 if ((left == Axis.DESCENDANT) || (left == Axis.DESCENDANTORSELF))
134 return true;
135
136 if (((left == Axis.FOLLOWING) || (left == Axis.FOLLOWINGSIBLING)) &&
137 ((right == Axis.FOLLOWING) ||
138 (right == Axis.PARENT) ||
139 (right == Axis.PRECEDING) ||
140 (right == Axis.PRECEDINGSIBLING)))
141 return true;
142
143 if (((left == Axis.PRECEDING) || (left == Axis.PRECEDINGSIBLING)) &&
144 ((right == Axis.DESCENDANT) ||
145 (right == Axis.DESCENDANTORSELF) ||
146 (right == Axis.FOLLOWING) ||
147 (right == Axis.FOLLOWINGSIBLING) ||
148 (right == Axis.PARENT) ||
149 (right == Axis.PRECEDING) ||
150 (right == Axis.PRECEDINGSIBLING)))
151 return true;
152
153 if ((right == Axis.FOLLOWING) && (left == Axis.CHILD)) {
154 // Special case for '@*/following::*' expressions. The resulting
155 // iterator is initialised with the parent's first child, and this
156 // can cause duplicates in the output if the parent has more than
157 // one attribute that matches the left step.
158 if (_path instanceof Step) {
159 int type = ((Step)_path).getNodeType();
160 if (type == DTM.ATTRIBUTE_NODE) return true;
161 }
162 }
163
164 return false;
165 }
166
167 public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
168 final ConstantPoolGen cpg = classGen.getConstantPool();
169 final InstructionList il = methodGen.getInstructionList();
170
171 // Backwards branches are prohibited if an uninitialized object is
172 // on the stack by section 4.9.4 of the JVM Specification, 2nd Ed.
173 // We don't know whether this code might contain backwards branches
174 // so we mustn't create the new object until after we've created
175 // the suspect arguments to its constructor. Instead we calculate
176 // the values of the arguments to the constructor first, store them
177 // in temporary variables, create the object and reload the
178 // arguments from the temporaries to avoid the problem.
179
180 // Compile path iterator
181 _path.translate(classGen, methodGen); // iterator on stack....
182 LocalVariableGen pathTemp
183 = methodGen.addLocalVariable("parent_location_path_tmp1",
184 Util.getJCRefType(NODE_ITERATOR_SIG),
185 null, null);
186 pathTemp.setStart(il.append(new ASTORE(pathTemp.getIndex())));
187
188 _step.translate(classGen, methodGen);
189 LocalVariableGen stepTemp
190 = methodGen.addLocalVariable("parent_location_path_tmp2",
191 Util.getJCRefType(NODE_ITERATOR_SIG),
192 null, null);
193 stepTemp.setStart(il.append(new ASTORE(stepTemp.getIndex())));
194
195 // Create new StepIterator
196 final int initSI = cpg.addMethodref(STEP_ITERATOR_CLASS,
197 "<init>",
198 "("
199 +NODE_ITERATOR_SIG
200 +NODE_ITERATOR_SIG
201 +")V");
202 il.append(new NEW(cpg.addClass(STEP_ITERATOR_CLASS)));
203 il.append(DUP);
204
205 pathTemp.setEnd(il.append(new ALOAD(pathTemp.getIndex())));
206 stepTemp.setEnd(il.append(new ALOAD(stepTemp.getIndex())));
207
208 // Initialize StepIterator with iterators from the stack
209 il.append(new INVOKESPECIAL(initSI));
210
211 // This is a special case for the //* path with or without predicates
212 Expression stp = _step;
213 if (stp instanceof ParentLocationPath)
214 stp = ((ParentLocationPath)stp).getStep();
215
216 if ((_path instanceof Step) && (stp instanceof Step)) {
217 final int path = ((Step)_path).getAxis();
218 final int step = ((Step)stp).getAxis();
219 if ((path == Axis.DESCENDANTORSELF && step == Axis.CHILD) ||
220 (path == Axis.PRECEDING && step == Axis.PARENT)) {
221 final int incl = cpg.addMethodref(NODE_ITERATOR_BASE,
222 "includeSelf",
223 "()" + NODE_ITERATOR_SIG);
224 il.append(new INVOKEVIRTUAL(incl));
225 }
226 }
227
228 /*
229 * If this pattern contains a sequence of descendant iterators we
230 * run the risk of returning the same node several times. We put
231 * a new iterator on top of the existing one to assure node order
232 * and prevent returning a single node multiple times.
233 */
234 if (_orderNodes) {
235 final int order = cpg.addInterfaceMethodref(DOM_INTF,
236 ORDER_ITERATOR,
237 ORDER_ITERATOR_SIG);
238 il.append(methodGen.loadDOM());
239 il.append(SWAP);
240 il.append(methodGen.loadContextNode());
241 il.append(new INVOKEINTERFACE(order, 3));
242 }
243 }
244 }