/* * Copyright 2001-2004 The Apache Software Foundation. * * Licensed 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. */ /* * $Id: ParentLocationPath.java,v 1.23 2004/02/16 22:24:29 minchau Exp $ */ package com.sun.org.apache.xalan.internal.xsltc.compiler; import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen; import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE; import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL; import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL; import com.sun.org.apache.bcel.internal.generic.InstructionList; import com.sun.org.apache.bcel.internal.generic.NEW; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError; import com.sun.org.apache.xalan.internal.xsltc.dom.Axis; import com.sun.org.apache.xml.internal.dtm.DTM; /** * @author Jacek Ambroziak * @author Santiago Pericas-Geertsen */ final class ParentLocationPath extends RelativeLocationPath { private Expression _step; private final RelativeLocationPath _path; private Type stype; private boolean _orderNodes = false; private boolean _axisMismatch = false; public ParentLocationPath(RelativeLocationPath path, Expression step) { _path = path; _step = step; _path.setParent(this); _step.setParent(this); if (_step instanceof Step) { _axisMismatch = checkAxisMismatch(); } } public void setAxis(int axis) { _path.setAxis(axis); } public int getAxis() { return _path.getAxis(); } public RelativeLocationPath getPath() { return(_path); } public Expression getStep() { return(_step); } public void setParser(Parser parser) { super.setParser(parser); _step.setParser(parser); _path.setParser(parser); } public String toString() { return "ParentLocationPath(" + _path + ", " + _step + ')'; } public Type typeCheck(SymbolTable stable) throws TypeCheckError { stype = _step.typeCheck(stable); _path.typeCheck(stable); if (_axisMismatch) enableNodeOrdering(); return _type = Type.NodeSet; } public void enableNodeOrdering() { SyntaxTreeNode parent = getParent(); if (parent instanceof ParentLocationPath) ((ParentLocationPath)parent).enableNodeOrdering(); else { _orderNodes = true; } } /** * This method is used to determine if this parent location path is a * combination of two step's with axes that will create duplicate or * unordered nodes. */ public boolean checkAxisMismatch() { int left = _path.getAxis(); int right = ((Step)_step).getAxis(); if (((left == Axis.ANCESTOR) || (left == Axis.ANCESTORORSELF)) && ((right == Axis.CHILD) || (right == Axis.DESCENDANT) || (right == Axis.DESCENDANTORSELF) || (right == Axis.PARENT) || (right == Axis.PRECEDING) || (right == Axis.PRECEDINGSIBLING))) return true; if ((left == Axis.CHILD) && (right == Axis.ANCESTOR) || (right == Axis.ANCESTORORSELF) || (right == Axis.PARENT) || (right == Axis.PRECEDING)) return true; if ((left == Axis.DESCENDANT) || (left == Axis.DESCENDANTORSELF)) return true; if (((left == Axis.FOLLOWING) || (left == Axis.FOLLOWINGSIBLING)) && ((right == Axis.FOLLOWING) || (right == Axis.PARENT) || (right == Axis.PRECEDING) || (right == Axis.PRECEDINGSIBLING))) return true; if (((left == Axis.PRECEDING) || (left == Axis.PRECEDINGSIBLING)) && ((right == Axis.DESCENDANT) || (right == Axis.DESCENDANTORSELF) || (right == Axis.FOLLOWING) || (right == Axis.FOLLOWINGSIBLING) || (right == Axis.PARENT) || (right == Axis.PRECEDING) || (right == Axis.PRECEDINGSIBLING))) return true; if ((right == Axis.FOLLOWING) && (left == Axis.CHILD)) { // Special case for '@*/following::*' expressions. The resulting // iterator is initialised with the parent's first child, and this // can cause duplicates in the output if the parent has more than // one attribute that matches the left step. if (_path instanceof Step) { int type = ((Step)_path).getNodeType(); if (type == DTM.ATTRIBUTE_NODE) return true; } } return false; } public void translate(ClassGenerator classGen, MethodGenerator methodGen) { final ConstantPoolGen cpg = classGen.getConstantPool(); final InstructionList il = methodGen.getInstructionList(); // Create new StepIterator final int initSI = cpg.addMethodref(STEP_ITERATOR_CLASS, "", "(" +NODE_ITERATOR_SIG +NODE_ITERATOR_SIG +")V"); il.append(new NEW(cpg.addClass(STEP_ITERATOR_CLASS))); il.append(DUP); // Compile path iterator _path.translate(classGen, methodGen); // iterator on stack.... _step.translate(classGen, methodGen); // Initialize StepIterator with iterators from the stack il.append(new INVOKESPECIAL(initSI)); // This is a special case for the //* path with or without predicates Expression stp = _step; if (stp instanceof ParentLocationPath) stp = ((ParentLocationPath)stp).getStep(); if ((_path instanceof Step) && (stp instanceof Step)) { final int path = ((Step)_path).getAxis(); final int step = ((Step)stp).getAxis(); if ((path == Axis.DESCENDANTORSELF && step == Axis.CHILD) || (path == Axis.PRECEDING && step == Axis.PARENT)) { final int incl = cpg.addMethodref(NODE_ITERATOR_BASE, "includeSelf", "()" + NODE_ITERATOR_SIG); il.append(new INVOKEVIRTUAL(incl)); } } /* * If this pattern contains a sequence of descendant iterators we * run the risk of returning the same node several times. We put * a new iterator on top of the existing one to assure node order * and prevent returning a single node multiple times. */ if (_orderNodes) { final int order = cpg.addInterfaceMethodref(DOM_INTF, ORDER_ITERATOR, ORDER_ITERATOR_SIG); il.append(methodGen.loadDOM()); il.append(SWAP); il.append(methodGen.loadContextNode()); il.append(new INVOKEINTERFACE(order, 3)); } } }