1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 /*
19  * $Id: OpMap.java 468655 2006-10-28 07:12:06Z minchau $
20  */
21 package org.apache.xpath.compiler;
22 
23 import org.apache.xalan.res.XSLMessages;
24 import org.apache.xml.utils.ObjectVector;
25 import org.apache.xpath.patterns.NodeTest;
26 import org.apache.xpath.res.XPATHErrorResources;
27 
28 /**
29  * This class represents the data structure basics of the XPath
30  * object.
31  */
32 public class OpMap
33 {
34 
35   /**
36    * The current pattern string, for diagnostics purposes
37    */
38   protected String m_currentPattern;
39 
40   /**
41    * Return the expression as a string for diagnostics.
42    *
43    * @return The expression string.
44    */
toString()45   public String toString()
46   {
47     return m_currentPattern;
48   }
49 
50   /**
51    * Return the expression as a string for diagnostics.
52    *
53    * @return The expression string.
54    */
getPatternString()55   public String getPatternString()
56   {
57     return m_currentPattern;
58   }
59 
60   /**
61    * The starting size of the token queue.
62    */
63   static final int MAXTOKENQUEUESIZE = 500;
64 
65   /*
66    * Amount to grow token queue when it becomes full
67    */
68   static final int BLOCKTOKENQUEUESIZE = 500;
69 
70   /**
71    *  TokenStack is the queue of used tokens. The current token is the token at the
72    * end of the m_tokenQueue. The idea is that the queue can be marked and a sequence
73    * of tokens can be reused.
74    */
75   ObjectVector m_tokenQueue = new ObjectVector(MAXTOKENQUEUESIZE, BLOCKTOKENQUEUESIZE);
76 
77   /**
78    * Get the XPath as a list of tokens.
79    *
80    * @return ObjectVector of tokens.
81    */
getTokenQueue()82   public ObjectVector getTokenQueue()
83   {
84     return m_tokenQueue;
85   }
86 
87   /**
88    * Get the XPath as a list of tokens.
89    *
90    * @param pos index into token queue.
91    *
92    * @return The token, normally a string.
93    */
getToken(int pos)94   public Object getToken(int pos)
95   {
96     return m_tokenQueue.elementAt(pos);
97   }
98 
99   /**
100    * The current size of the token queue.
101    */
102 //  public int m_tokenQueueSize = 0;
103 
104   /**
105     * Get size of the token queue.
106    *
107    * @return The size of the token queue.
108    */
getTokenQueueSize()109   public int getTokenQueueSize()
110   {
111     return m_tokenQueue.size();
112 
113   }
114 
115   /**
116    * An operations map is used instead of a proper parse tree.  It contains
117    * operations codes and indexes into the m_tokenQueue.
118    * I use an array instead of a full parse tree in order to cut down
119    * on the number of objects created.
120    */
121   OpMapVector m_opMap = null;
122 
123   /**
124     * Get the opcode list that describes the XPath operations.  It contains
125    * operations codes and indexes into the m_tokenQueue.
126    * I use an array instead of a full parse tree in order to cut down
127    * on the number of objects created.
128    *
129    * @return An IntVector that is the opcode list that describes the XPath operations.
130    */
getOpMap()131   public OpMapVector getOpMap()
132   {
133     return m_opMap;
134   }
135 
136   // Position indexes
137 
138   /**
139    * The length is always the opcode position + 1.
140    * Length is always expressed as the opcode+length bytes,
141    * so it is always 2 or greater.
142    */
143   public static final int MAPINDEX_LENGTH = 1;
144 
145   /**
146    * Replace the large arrays
147    * with a small array.
148    */
shrink()149   void shrink()
150   {
151 
152     int n = m_opMap.elementAt(MAPINDEX_LENGTH);
153     m_opMap.setToSize(n + 4);
154 
155     m_opMap.setElementAt(0,n);
156     m_opMap.setElementAt(0,n+1);
157     m_opMap.setElementAt(0,n+2);
158 
159 
160     n = m_tokenQueue.size();
161     m_tokenQueue.setToSize(n + 4);
162 
163     m_tokenQueue.setElementAt(null,n);
164     m_tokenQueue.setElementAt(null,n + 1);
165     m_tokenQueue.setElementAt(null,n + 2);
166   }
167 
168   /**
169   * Given an operation position, return the current op.
170    *
171    * @param opPos index into op map.
172    * @return the op that corresponds to the opPos argument.
173    */
getOp(int opPos)174   public int getOp(int opPos)
175   {
176     return m_opMap.elementAt(opPos);
177   }
178 
179   /**
180   * Set the op at index to the given int.
181    *
182    * @param opPos index into op map.
183    * @param value Value to set
184    */
setOp(int opPos, int value)185   public void setOp(int opPos, int value)
186   {
187      m_opMap.setElementAt(value,opPos);
188   }
189 
190   /**
191    * Given an operation position, return the end position, i.e. the
192    * beginning of the next operation.
193    *
194    * @param opPos An op position of an operation for which there is a size
195    *              entry following.
196    * @return position of next operation in m_opMap.
197    */
getNextOpPos(int opPos)198   public int getNextOpPos(int opPos)
199   {
200     return opPos + m_opMap.elementAt(opPos + 1);
201   }
202 
203   /**
204    * Given a location step position, return the end position, i.e. the
205    * beginning of the next step.
206    *
207    * @param opPos the position of a location step.
208    * @return the position of the next location step.
209    */
getNextStepPos(int opPos)210   public int getNextStepPos(int opPos)
211   {
212 
213     int stepType = getOp(opPos);
214 
215     if ((stepType >= OpCodes.AXES_START_TYPES)
216             && (stepType <= OpCodes.AXES_END_TYPES))
217     {
218       return getNextOpPos(opPos);
219     }
220     else if ((stepType >= OpCodes.FIRST_NODESET_OP)
221              && (stepType <= OpCodes.LAST_NODESET_OP))
222     {
223       int newOpPos = getNextOpPos(opPos);
224 
225       while (OpCodes.OP_PREDICATE == getOp(newOpPos))
226       {
227         newOpPos = getNextOpPos(newOpPos);
228       }
229 
230       stepType = getOp(newOpPos);
231 
232       if (!((stepType >= OpCodes.AXES_START_TYPES)
233             && (stepType <= OpCodes.AXES_END_TYPES)))
234       {
235         return OpCodes.ENDOP;
236       }
237 
238       return newOpPos;
239     }
240     else
241     {
242       throw new RuntimeException(
243         XSLMessages.createXPATHMessage(XPATHErrorResources.ER_UNKNOWN_STEP, new Object[]{String.valueOf(stepType)}));
244       //"Programmer's assertion in getNextStepPos: unknown stepType: " + stepType);
245     }
246   }
247 
248   /**
249    * Given an operation position, return the end position, i.e. the
250    * beginning of the next operation.
251    *
252    * @param opMap The operations map.
253    * @param opPos index to operation, for which there is a size entry following.
254    * @return position of next operation in m_opMap.
255    */
getNextOpPos(int[] opMap, int opPos)256   public static int getNextOpPos(int[] opMap, int opPos)
257   {
258     return opPos + opMap[opPos + 1];
259   }
260 
261   /**
262    * Given an FROM_stepType position, return the position of the
263    * first predicate, if there is one, or else this will point
264    * to the end of the FROM_stepType.
265    * Example:
266    *  int posOfPredicate = xpath.getNextOpPos(stepPos);
267    *  boolean hasPredicates =
268    *            OpCodes.OP_PREDICATE == xpath.getOp(posOfPredicate);
269    *
270    * @param opPos position of FROM_stepType op.
271    * @return position of predicate in FROM_stepType structure.
272    */
getFirstPredicateOpPos(int opPos)273   public int getFirstPredicateOpPos(int opPos)
274      throws javax.xml.transform.TransformerException
275   {
276 
277     int stepType = m_opMap.elementAt(opPos);
278 
279     if ((stepType >= OpCodes.AXES_START_TYPES)
280             && (stepType <= OpCodes.AXES_END_TYPES))
281     {
282       return opPos + m_opMap.elementAt(opPos + 2);
283     }
284     else if ((stepType >= OpCodes.FIRST_NODESET_OP)
285              && (stepType <= OpCodes.LAST_NODESET_OP))
286     {
287       return opPos + m_opMap.elementAt(opPos + 1);
288     }
289     else if(-2 == stepType)
290     {
291       return -2;
292     }
293     else
294     {
295       error(org.apache.xpath.res.XPATHErrorResources.ER_UNKNOWN_OPCODE,
296             new Object[]{ String.valueOf(stepType) });  //"ERROR! Unknown op code: "+m_opMap[opPos]);
297       return -1;
298     }
299   }
300 
301   /**
302    * Tell the user of an error, and probably throw an
303    * exception.
304    *
305    * @param msg An error msgkey that corresponds to one of the constants found
306    *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is
307    *            a key for a format string.
308    * @param args An array of arguments represented in the format string, which
309    *             may be null.
310    *
311    * @throws TransformerException if the current ErrorListoner determines to
312    *                              throw an exception.
313    */
error(String msg, Object[] args)314   public void error(String msg, Object[] args) throws javax.xml.transform.TransformerException
315   {
316 
317     java.lang.String fmsg = org.apache.xalan.res.XSLMessages.createXPATHMessage(msg, args);
318 
319 
320     throw new javax.xml.transform.TransformerException(fmsg);
321   }
322 
323 
324   /**
325    * Go to the first child of a given operation.
326    *
327    * @param opPos position of operation.
328    *
329    * @return The position of the first child of the operation.
330    */
getFirstChildPos(int opPos)331   public static int getFirstChildPos(int opPos)
332   {
333     return opPos + 2;
334   }
335 
336   /**
337    * Get the length of an operation.
338    *
339    * @param opPos The position of the operation in the op map.
340    *
341    * @return The size of the operation.
342    */
getArgLength(int opPos)343   public int getArgLength(int opPos)
344   {
345     return m_opMap.elementAt(opPos + MAPINDEX_LENGTH);
346   }
347 
348   /**
349    * Given a location step, get the length of that step.
350    *
351    * @param opPos Position of location step in op map.
352    *
353    * @return The length of the step.
354    */
getArgLengthOfStep(int opPos)355   public int getArgLengthOfStep(int opPos)
356   {
357     return m_opMap.elementAt(opPos + MAPINDEX_LENGTH + 1) - 3;
358   }
359 
360   /**
361    * Get the first child position of a given location step.
362    *
363    * @param opPos Position of location step in the location map.
364    *
365    * @return The first child position of the step.
366    */
getFirstChildPosOfStep(int opPos)367   public static int getFirstChildPosOfStep(int opPos)
368   {
369     return opPos + 3;
370   }
371 
372   /**
373    * Get the test type of the step, i.e. NODETYPE_XXX value.
374    *
375    * @param opPosOfStep The position of the FROM_XXX step.
376    *
377    * @return NODETYPE_XXX value.
378    */
getStepTestType(int opPosOfStep)379   public int getStepTestType(int opPosOfStep)
380   {
381     return m_opMap.elementAt(opPosOfStep + 3);  // skip past op, len, len without predicates
382   }
383 
384   /**
385    * Get the namespace of the step.
386    *
387    * @param opPosOfStep The position of the FROM_XXX step.
388    *
389    * @return The step's namespace, NodeTest.WILD, or null for null namespace.
390    */
getStepNS(int opPosOfStep)391   public String getStepNS(int opPosOfStep)
392   {
393 
394     int argLenOfStep = getArgLengthOfStep(opPosOfStep);
395 
396     // System.out.println("getStepNS.argLenOfStep: "+argLenOfStep);
397     if (argLenOfStep == 3)
398     {
399       int index = m_opMap.elementAt(opPosOfStep + 4);
400 
401       if (index >= 0)
402         return (String) m_tokenQueue.elementAt(index);
403       else if (OpCodes.ELEMWILDCARD == index)
404         return NodeTest.WILD;
405       else
406         return null;
407     }
408     else
409       return null;
410   }
411 
412   /**
413    * Get the local name of the step.
414    * @param opPosOfStep The position of the FROM_XXX step.
415    *
416    * @return OpCodes.EMPTY, OpCodes.ELEMWILDCARD, or the local name.
417    */
getStepLocalName(int opPosOfStep)418   public String getStepLocalName(int opPosOfStep)
419   {
420 
421     int argLenOfStep = getArgLengthOfStep(opPosOfStep);
422 
423     // System.out.println("getStepLocalName.argLenOfStep: "+argLenOfStep);
424     int index;
425 
426     switch (argLenOfStep)
427     {
428     case 0 :
429       index = OpCodes.EMPTY;
430       break;
431     case 1 :
432       index = OpCodes.ELEMWILDCARD;
433       break;
434     case 2 :
435       index = m_opMap.elementAt(opPosOfStep + 4);
436       break;
437     case 3 :
438       index = m_opMap.elementAt(opPosOfStep + 5);
439       break;
440     default :
441       index = OpCodes.EMPTY;
442       break;  // Should assert error
443     }
444 
445     // int index = (argLenOfStep == 3) ? m_opMap[opPosOfStep+5]
446     //                                  : ((argLenOfStep == 1) ? -3 : -2);
447     if (index >= 0)
448       return (String) m_tokenQueue.elementAt(index).toString();
449     else if (OpCodes.ELEMWILDCARD == index)
450       return NodeTest.WILD;
451     else
452       return null;
453   }
454 
455 }
456