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