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: OpMap.java 468655 2006-10-28 07:12:06Z minchau $
020     */
021    package org.apache.xpath.compiler;
022    
023    import org.apache.xalan.res.XSLMessages;
024    import org.apache.xml.utils.ObjectVector;
025    import org.apache.xpath.patterns.NodeTest;
026    import org.apache.xpath.res.XPATHErrorResources;
027    
028    /**
029     * This class represents the data structure basics of the XPath
030     * object.
031     */
032    public class OpMap
033    {
034    
035      /**
036       * The current pattern string, for diagnostics purposes
037       */
038      protected String m_currentPattern;
039    
040      /**
041       * Return the expression as a string for diagnostics.
042       *
043       * @return The expression string.
044       */
045      public String toString()
046      {
047        return m_currentPattern;
048      }
049    
050      /**
051       * Return the expression as a string for diagnostics.
052       *
053       * @return The expression string.
054       */
055      public String getPatternString()
056      {
057        return m_currentPattern;
058      }
059    
060      /**
061       * The starting size of the token queue.
062       */
063      static final int MAXTOKENQUEUESIZE = 500;
064    
065      /*
066       * Amount to grow token queue when it becomes full
067       */
068      static final int BLOCKTOKENQUEUESIZE = 500;
069      
070      /**
071       *  TokenStack is the queue of used tokens. The current token is the token at the
072       * end of the m_tokenQueue. The idea is that the queue can be marked and a sequence
073       * of tokens can be reused.
074       */
075      ObjectVector m_tokenQueue = new ObjectVector(MAXTOKENQUEUESIZE, BLOCKTOKENQUEUESIZE);
076    
077      /**
078       * Get the XPath as a list of tokens.
079       *
080       * @return ObjectVector of tokens.
081       */
082      public ObjectVector getTokenQueue()
083      {
084        return m_tokenQueue;
085      }
086    
087      /**
088       * Get the XPath as a list of tokens.
089       *
090       * @param pos index into token queue.
091       *
092       * @return The token, normally a string.
093       */
094      public Object getToken(int pos)
095      {
096        return m_tokenQueue.elementAt(pos);
097      }
098    
099      /**
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    }