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 */
21package org.apache.xpath.compiler;
22
23import org.apache.xalan.res.XSLMessages;
24import org.apache.xml.utils.ObjectVector;
25import org.apache.xpath.patterns.NodeTest;
26import org.apache.xpath.res.XPATHErrorResources;
27
28/**
29 * This class represents the data structure basics of the XPath
30 * object.
31 */
32public 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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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