001: /*
002: * Copyright 1999-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: OpMap.java,v 1.16 2004/02/17 04:32:49 minchau Exp $
018: */
019: package org.apache.xpath.compiler;
020:
021: import org.apache.xalan.res.XSLMessages;
022: import org.apache.xml.utils.ObjectVector;
023: import org.apache.xpath.patterns.NodeTest;
024: import org.apache.xpath.res.XPATHErrorResources;
025:
026: /**
027: * This class represents the data structure basics of the XPath
028: * object.
029: */
030: public class OpMap {
031:
032: /**
033: * The current pattern string, for diagnostics purposes
034: */
035: protected String m_currentPattern;
036:
037: /**
038: * Return the expression as a string for diagnostics.
039: *
040: * @return The expression string.
041: */
042: public String toString() {
043: return m_currentPattern;
044: }
045:
046: /**
047: * Return the expression as a string for diagnostics.
048: *
049: * @return The expression string.
050: */
051: public String getPatternString() {
052: return m_currentPattern;
053: }
054:
055: /**
056: * The starting size of the token queue.
057: */
058: static final int MAXTOKENQUEUESIZE = 500;
059:
060: /*
061: * Amount to grow token queue when it becomes full
062: */
063: static final int BLOCKTOKENQUEUESIZE = 500;
064:
065: /**
066: * TokenStack is the queue of used tokens. The current token is the token at the
067: * end of the m_tokenQueue. The idea is that the queue can be marked and a sequence
068: * of tokens can be reused.
069: */
070: ObjectVector m_tokenQueue = new ObjectVector(MAXTOKENQUEUESIZE,
071: BLOCKTOKENQUEUESIZE);
072:
073: /**
074: * Get the XPath as a list of tokens.
075: *
076: * @return ObjectVector of tokens.
077: */
078: public ObjectVector getTokenQueue() {
079: return m_tokenQueue;
080: }
081:
082: /**
083: * Get the XPath as a list of tokens.
084: *
085: * @param pos index into token queue.
086: *
087: * @return The token, normally a string.
088: */
089: public Object getToken(int pos) {
090: return m_tokenQueue.elementAt(pos);
091: }
092:
093: /**
094: * The current size of the token queue.
095: */
096: // public int m_tokenQueueSize = 0;
097: /**
098: * Get size of the token queue.
099: *
100: * @return The size of the token queue.
101: */
102: public int getTokenQueueSize() {
103: return m_tokenQueue.size();
104:
105: }
106:
107: /**
108: * An operations map is used instead of a proper parse tree. It contains
109: * operations codes and indexes into the m_tokenQueue.
110: * I use an array instead of a full parse tree in order to cut down
111: * on the number of objects created.
112: */
113: OpMapVector m_opMap = null;
114:
115: /**
116: * Get the opcode list that describes the XPath operations. 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: * @return An IntVector that is the opcode list that describes the XPath operations.
122: */
123: public OpMapVector getOpMap() {
124: return m_opMap;
125: }
126:
127: // Position indexes
128:
129: /**
130: * The length is always the opcode position + 1.
131: * Length is always expressed as the opcode+length bytes,
132: * so it is always 2 or greater.
133: */
134: public static final int MAPINDEX_LENGTH = 1;
135:
136: /**
137: * Replace the large arrays
138: * with a small array.
139: */
140: void shrink() {
141:
142: int n = m_opMap.elementAt(MAPINDEX_LENGTH);
143: m_opMap.setToSize(n + 4);
144:
145: m_opMap.setElementAt(0, n);
146: m_opMap.setElementAt(0, n + 1);
147: m_opMap.setElementAt(0, n + 2);
148:
149: n = m_tokenQueue.size();
150: m_tokenQueue.setToSize(n + 4);
151:
152: m_tokenQueue.setElementAt(null, n);
153: m_tokenQueue.setElementAt(null, n + 1);
154: m_tokenQueue.setElementAt(null, n + 2);
155: }
156:
157: /**
158: * Given an operation position, return the current op.
159: *
160: * @param opPos index into op map.
161: * @return the op that corresponds to the opPos argument.
162: */
163: public int getOp(int opPos) {
164: return m_opMap.elementAt(opPos);
165: }
166:
167: /**
168: * Set the op at index to the given int.
169: *
170: * @param opPos index into op map.
171: * @param value Value to set
172: */
173: public void setOp(int opPos, int value) {
174: m_opMap.setElementAt(value, opPos);
175: }
176:
177: /**
178: * Given an operation position, return the end position, i.e. the
179: * beginning of the next operation.
180: *
181: * @param opPos An op position of an operation for which there is a size
182: * entry following.
183: * @return position of next operation in m_opMap.
184: */
185: public int getNextOpPos(int opPos) {
186: return opPos + m_opMap.elementAt(opPos + 1);
187: }
188:
189: /**
190: * Given a location step position, return the end position, i.e. the
191: * beginning of the next step.
192: *
193: * @param opPos the position of a location step.
194: * @return the position of the next location step.
195: */
196: public int getNextStepPos(int opPos) {
197:
198: int stepType = getOp(opPos);
199:
200: if ((stepType >= OpCodes.AXES_START_TYPES)
201: && (stepType <= OpCodes.AXES_END_TYPES)) {
202: return getNextOpPos(opPos);
203: } else if ((stepType >= OpCodes.FIRST_NODESET_OP)
204: && (stepType <= OpCodes.LAST_NODESET_OP)) {
205: int newOpPos = getNextOpPos(opPos);
206:
207: while (OpCodes.OP_PREDICATE == getOp(newOpPos)) {
208: newOpPos = getNextOpPos(newOpPos);
209: }
210:
211: stepType = getOp(newOpPos);
212:
213: if (!((stepType >= OpCodes.AXES_START_TYPES) && (stepType <= OpCodes.AXES_END_TYPES))) {
214: return OpCodes.ENDOP;
215: }
216:
217: return newOpPos;
218: } else {
219: throw new RuntimeException(XSLMessages.createXPATHMessage(
220: XPATHErrorResources.ER_UNKNOWN_STEP,
221: new Object[] { new Integer(stepType).toString() }));
222: //"Programmer's assertion in getNextStepPos: unknown stepType: " + stepType);
223: }
224: }
225:
226: /**
227: * Given an operation position, return the end position, i.e. the
228: * beginning of the next operation.
229: *
230: * @param opMap The operations map.
231: * @param opPos index to operation, for which there is a size entry following.
232: * @return position of next operation in m_opMap.
233: */
234: public static int getNextOpPos(int[] opMap, int opPos) {
235: return opPos + opMap[opPos + 1];
236: }
237:
238: /**
239: * Given an FROM_stepType position, return the position of the
240: * first predicate, if there is one, or else this will point
241: * to the end of the FROM_stepType.
242: * Example:
243: * int posOfPredicate = xpath.getNextOpPos(stepPos);
244: * boolean hasPredicates =
245: * OpCodes.OP_PREDICATE == xpath.getOp(posOfPredicate);
246: *
247: * @param opPos position of FROM_stepType op.
248: * @return position of predicate in FROM_stepType structure.
249: */
250: public int getFirstPredicateOpPos(int opPos)
251: throws javax.xml.transform.TransformerException {
252:
253: int stepType = m_opMap.elementAt(opPos);
254:
255: if ((stepType >= OpCodes.AXES_START_TYPES)
256: && (stepType <= OpCodes.AXES_END_TYPES)) {
257: return opPos + m_opMap.elementAt(opPos + 2);
258: } else if ((stepType >= OpCodes.FIRST_NODESET_OP)
259: && (stepType <= OpCodes.LAST_NODESET_OP)) {
260: return opPos + m_opMap.elementAt(opPos + 1);
261: } else if (-2 == stepType) {
262: return -2;
263: } else {
264: error(
265: org.apache.xpath.res.XPATHErrorResources.ER_UNKNOWN_OPCODE,
266: new Object[] { String.valueOf(stepType) }); //"ERROR! Unknown op code: "+m_opMap[opPos]);
267: return -1;
268: }
269: }
270:
271: /**
272: * Tell the user of an error, and probably throw an
273: * exception.
274: *
275: * @param msg An error msgkey that corresponds to one of the constants found
276: * in {@link org.apache.xpath.res.XPATHErrorResources}, which is
277: * a key for a format string.
278: * @param args An array of arguments represented in the format string, which
279: * may be null.
280: *
281: * @throws TransformerException if the current ErrorListoner determines to
282: * throw an exception.
283: */
284: public void error(String msg, Object[] args)
285: throws javax.xml.transform.TransformerException {
286:
287: java.lang.String fmsg = org.apache.xalan.res.XSLMessages
288: .createXPATHMessage(msg, args);
289:
290: throw new javax.xml.transform.TransformerException(fmsg);
291: }
292:
293: /**
294: * Go to the first child of a given operation.
295: *
296: * @param opPos position of operation.
297: *
298: * @return The position of the first child of the operation.
299: */
300: public static int getFirstChildPos(int opPos) {
301: return opPos + 2;
302: }
303:
304: /**
305: * Get the length of an operation.
306: *
307: * @param opPos The position of the operation in the op map.
308: *
309: * @return The size of the operation.
310: */
311: public int getArgLength(int opPos) {
312: return m_opMap.elementAt(opPos + MAPINDEX_LENGTH);
313: }
314:
315: /**
316: * Given a location step, get the length of that step.
317: *
318: * @param opPos Position of location step in op map.
319: *
320: * @return The length of the step.
321: */
322: public int getArgLengthOfStep(int opPos) {
323: return m_opMap.elementAt(opPos + MAPINDEX_LENGTH + 1) - 3;
324: }
325:
326: /**
327: * Get the first child position of a given location step.
328: *
329: * @param opPos Position of location step in the location map.
330: *
331: * @return The first child position of the step.
332: */
333: public static int getFirstChildPosOfStep(int opPos) {
334: return opPos + 3;
335: }
336:
337: /**
338: * Get the test type of the step, i.e. NODETYPE_XXX value.
339: *
340: * @param opPosOfStep The position of the FROM_XXX step.
341: *
342: * @return NODETYPE_XXX value.
343: */
344: public int getStepTestType(int opPosOfStep) {
345: return m_opMap.elementAt(opPosOfStep + 3); // skip past op, len, len without predicates
346: }
347:
348: /**
349: * Get the namespace of the step.
350: *
351: * @param opPosOfStep The position of the FROM_XXX step.
352: *
353: * @return The step's namespace, NodeTest.WILD, or null for null namespace.
354: */
355: public String getStepNS(int opPosOfStep) {
356:
357: int argLenOfStep = getArgLengthOfStep(opPosOfStep);
358:
359: // System.out.println("getStepNS.argLenOfStep: "+argLenOfStep);
360: if (argLenOfStep == 3) {
361: int index = m_opMap.elementAt(opPosOfStep + 4);
362:
363: if (index >= 0)
364: return (String) m_tokenQueue.elementAt(index);
365: else if (OpCodes.ELEMWILDCARD == index)
366: return NodeTest.WILD;
367: else
368: return null;
369: } else
370: return null;
371: }
372:
373: /**
374: * Get the local name of the step.
375: * @param opPosOfStep The position of the FROM_XXX step.
376: *
377: * @return OpCodes.EMPTY, OpCodes.ELEMWILDCARD, or the local name.
378: */
379: public String getStepLocalName(int opPosOfStep) {
380:
381: int argLenOfStep = getArgLengthOfStep(opPosOfStep);
382:
383: // System.out.println("getStepLocalName.argLenOfStep: "+argLenOfStep);
384: int index;
385:
386: switch (argLenOfStep) {
387: case 0:
388: index = OpCodes.EMPTY;
389: break;
390: case 1:
391: index = OpCodes.ELEMWILDCARD;
392: break;
393: case 2:
394: index = m_opMap.elementAt(opPosOfStep + 4);
395: break;
396: case 3:
397: index = m_opMap.elementAt(opPosOfStep + 5);
398: break;
399: default:
400: index = OpCodes.EMPTY;
401: break; // Should assert error
402: }
403:
404: // int index = (argLenOfStep == 3) ? m_opMap[opPosOfStep+5]
405: // : ((argLenOfStep == 1) ? -3 : -2);
406: if (index >= 0)
407: return (String) m_tokenQueue.elementAt(index).toString();
408: else if (OpCodes.ELEMWILDCARD == index)
409: return NodeTest.WILD;
410: else
411: return null;
412: }
413:
414: }
|