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 */ 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