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