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: TestSeq.java 468650 2006-10-28 07:03:30Z minchau $
020     */
021    
022    package org.apache.xalan.xsltc.compiler;
023    
024    import java.util.Dictionary;
025    import java.util.Vector;
026    
027    import org.apache.bcel.generic.GOTO_W;
028    import org.apache.bcel.generic.InstructionHandle;
029    import org.apache.bcel.generic.InstructionList;
030    import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
031    import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
032    
033    /**
034     * A test sequence is a sequence of patterns that
035     *
036     *  (1) occured in templates in the same mode
037     *  (2) share the same kernel node type (e.g. A/B and C/C/B)
038     *  (3) may also contain patterns matching "*" and "node()"
039     *      (element sequence only) or matching "@*" (attribute
040     *      sequence only).
041     *
042     * A test sequence may have a default template, which will be 
043     * instantiated if none of the other patterns match. 
044     * @author Jacek Ambroziak
045     * @author Santiago Pericas-Geertsen
046     * @author Erwin Bolwidt <ejb@klomp.org>
047     * @author Morten Jorgensen <morten.jorgensen@sun.com>
048     */
049    final class TestSeq {
050    
051        /**
052         * Integer code for the kernel type of this test sequence
053         */
054        private int _kernelType;
055    
056        /**
057         * Vector of all patterns in the test sequence. May include
058         * patterns with "*", "@*" or "node()" kernel.
059         */
060        private Vector _patterns = null;
061    
062        /**
063         * A reference to the Mode object.
064         */
065        private Mode _mode = null;
066    
067        /**
068         * Default template for this test sequence
069         */
070        private Template _default = null;
071    
072        /**
073         * Instruction list representing this test sequence.
074         */
075        private InstructionList _instructionList;
076    
077        /**
078         * Cached handle to avoid compiling more than once.
079         */
080        private InstructionHandle _start = null;
081    
082        /**
083         * Creates a new test sequence given a set of patterns and a mode.
084         */
085        public TestSeq(Vector patterns, Mode mode) {
086            this(patterns, -2, mode);
087        }
088    
089        public TestSeq(Vector patterns, int kernelType, Mode mode) {
090            _patterns = patterns;
091            _kernelType = kernelType;
092            _mode = mode;
093        }
094    
095        /**
096         * Returns a string representation of this test sequence. Notice
097         * that test sequences are mutable, so the value returned by this
098         * method is different before and after calling reduce().
099         */
100        public String toString() {
101            final int count = _patterns.size();
102            final StringBuffer result = new StringBuffer();
103    
104            for (int i = 0; i < count; i++) {
105                final LocationPathPattern pattern =
106                    (LocationPathPattern) _patterns.elementAt(i);
107    
108                if (i == 0) {
109                    result.append("Testseq for kernel " + _kernelType)
110                          .append('\n');
111                }
112                result.append("   pattern " + i + ": ")
113                      .append(pattern.toString())
114                      .append('\n');
115            }
116            return result.toString();
117        }
118    
119        /**
120         * Returns the instruction list for this test sequence
121         */
122        public InstructionList getInstructionList() {
123            return _instructionList;
124        }
125    
126        /**
127         * Return the highest priority for a pattern in this test
128         * sequence. This is either the priority of the first or
129         * of the default pattern.
130         */
131        public double getPriority() {
132            final Template template = (_patterns.size() == 0) ? _default 
133                : ((Pattern) _patterns.elementAt(0)).getTemplate();
134            return template.getPriority();
135        }
136    
137        /**
138         * Returns the position of the highest priority pattern in 
139         * this test sequence.
140         */
141        public int getPosition() {
142            final Template template = (_patterns.size() == 0) ? _default 
143                : ((Pattern) _patterns.elementAt(0)).getTemplate();
144            return template.getPosition();
145        }
146    
147        /**
148         * Reduce the patterns in this test sequence. Creates a new
149         * vector of patterns and sets the default pattern if it
150         * finds a patterns that is fully reduced.
151         */
152        public void reduce() {
153            final Vector newPatterns = new Vector();
154    
155            final int count = _patterns.size();
156            for (int i = 0; i < count; i++) {
157                final LocationPathPattern pattern =
158                    (LocationPathPattern)_patterns.elementAt(i);
159                    
160                // Reduce this pattern
161                pattern.reduceKernelPattern();
162                            
163                // Is this pattern fully reduced?
164                if (pattern.isWildcard()) {
165                    _default = pattern.getTemplate();
166                    break;          // Ignore following patterns 
167                }
168                else {
169                    newPatterns.addElement(pattern);
170                }
171            }
172            _patterns = newPatterns;
173        }
174    
175        /**
176         * Returns, by reference, the templates that are included in 
177         * this test sequence. Note that a single template can occur 
178         * in several test sequences if its pattern is a union.
179         */
180        public void findTemplates(Dictionary templates) {
181            if (_default != null) {
182                templates.put(_default, this);
183            }
184            for (int i = 0; i < _patterns.size(); i++) {
185                final LocationPathPattern pattern =
186                    (LocationPathPattern)_patterns.elementAt(i);
187                templates.put(pattern.getTemplate(), this);
188            }
189        }
190    
191        /**
192         * Get the instruction handle to a template's code. This is 
193         * used when a single template occurs in several test 
194         * sequences; that is, if its pattern is a union of patterns 
195         * (e.g. match="A/B | A/C").
196         */
197        private InstructionHandle getTemplateHandle(Template template) {
198            return (InstructionHandle)_mode.getTemplateInstructionHandle(template);
199        }
200    
201        /**
202         * Returns pattern n in this test sequence
203         */
204        private LocationPathPattern getPattern(int n) {
205            return (LocationPathPattern)_patterns.elementAt(n);
206        }
207    
208        /**
209         * Compile the code for this test sequence. Compile patterns 
210         * from highest to lowest priority. Note that since patterns 
211         * can be share by multiple test sequences, instruction lists 
212         * must be copied before backpatching.
213         */
214        public InstructionHandle compile(ClassGenerator classGen,
215                                         MethodGenerator methodGen,
216                                         InstructionHandle continuation) 
217        {
218            // Returned cached value if already compiled
219            if (_start != null) {
220                return _start;
221            }
222    
223            // If not patterns, then return handle for default template
224            final int count = _patterns.size();
225            if (count == 0) {
226                return (_start = getTemplateHandle(_default));
227            }
228    
229            // Init handle to jump when all patterns failed
230            InstructionHandle fail = (_default == null) ? continuation
231                : getTemplateHandle(_default);
232            
233            // Compile all patterns in reverse order
234            for (int n = count - 1; n >= 0; n--) {
235                final LocationPathPattern pattern = getPattern(n);
236                final Template template = pattern.getTemplate();
237                final InstructionList il = new InstructionList();
238    
239                // Patterns expect current node on top of stack
240                il.append(methodGen.loadCurrentNode());
241    
242                // Apply the test-code compiled for the pattern
243                InstructionList ilist = methodGen.getInstructionList(pattern);
244                if (ilist == null) {
245                    ilist = pattern.compile(classGen, methodGen);
246                    methodGen.addInstructionList(pattern, ilist);
247                }
248    
249                // Make a copy of the instruction list for backpatching
250                InstructionList copyOfilist = ilist.copy();
251    
252                FlowList trueList = pattern.getTrueList();
253                if (trueList != null) {
254                    trueList = trueList.copyAndRedirect(ilist, copyOfilist);
255                }
256                FlowList falseList = pattern.getFalseList();
257                if (falseList != null) {
258                    falseList = falseList.copyAndRedirect(ilist, copyOfilist);
259                }
260    
261                il.append(copyOfilist);
262    
263                // On success branch to the template code
264                final InstructionHandle gtmpl = getTemplateHandle(template);
265                final InstructionHandle success = il.append(new GOTO_W(gtmpl));
266    
267                if (trueList != null) {
268                    trueList.backPatch(success);
269                }
270                if (falseList != null) {
271                    falseList.backPatch(fail);
272                } 
273    
274                // Next pattern's 'fail' target is this pattern's first instruction
275                fail = il.getStart();
276    
277                // Append existing instruction list to the end of this one
278                if (_instructionList != null) {
279                    il.append(_instructionList);
280                }
281    
282                // Set current instruction list to be this one
283                _instructionList = il;
284            }
285            return (_start = fail);
286        }
287    }