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    }