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 }