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 }