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