1. /*
  2. * Copyright 2001-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: Number.java,v 1.14 2004/02/24 02:58:42 zongaro Exp $
  18. */
  19. package com.sun.org.apache.xalan.internal.xsltc.compiler;
  20. import java.util.ArrayList;
  21. import com.sun.org.apache.bcel.internal.classfile.Field;
  22. import com.sun.org.apache.bcel.internal.generic.ALOAD;
  23. import com.sun.org.apache.bcel.internal.generic.ASTORE;
  24. import com.sun.org.apache.bcel.internal.generic.BranchHandle;
  25. import com.sun.org.apache.bcel.internal.generic.CHECKCAST;
  26. import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  27. import com.sun.org.apache.bcel.internal.generic.GETFIELD;
  28. import com.sun.org.apache.bcel.internal.generic.GOTO;
  29. import com.sun.org.apache.bcel.internal.generic.IFNONNULL;
  30. import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
  31. import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
  32. import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
  33. import com.sun.org.apache.bcel.internal.generic.InstructionList;
  34. import com.sun.org.apache.bcel.internal.generic.L2I;
  35. import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  36. import com.sun.org.apache.bcel.internal.generic.NEW;
  37. import com.sun.org.apache.bcel.internal.generic.PUSH;
  38. import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
  39. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  40. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MatchGenerator;
  41. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  42. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeCounterGenerator;
  43. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.RealType;
  44. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  45. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  46. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  47. /**
  48. * @author Jacek Ambroziak
  49. * @author Santiago Pericas-Geertsen
  50. */
  51. final class Number extends Instruction implements Closure {
  52. private static final int LEVEL_SINGLE = 0;
  53. private static final int LEVEL_MULTIPLE = 1;
  54. private static final int LEVEL_ANY = 2;
  55. static final private String[] ClassNames = {
  56. "com.sun.org.apache.xalan.internal.xsltc.dom.SingleNodeCounter", // LEVEL_SINGLE
  57. "com.sun.org.apache.xalan.internal.xsltc.dom.MultipleNodeCounter", // LEVEL_MULTIPLE
  58. "com.sun.org.apache.xalan.internal.xsltc.dom.AnyNodeCounter" // LEVEL_ANY
  59. };
  60. static final private String[] FieldNames = {
  61. "___single_node_counter", // LEVEL_SINGLE
  62. "___multiple_node_counter", // LEVEL_MULTIPLE
  63. "___any_node_counter" // LEVEL_ANY
  64. };
  65. private Pattern _from = null;
  66. private Pattern _count = null;
  67. private Expression _value = null;
  68. private AttributeValueTemplate _lang = null;
  69. private AttributeValueTemplate _format = null;
  70. private AttributeValueTemplate _letterValue = null;
  71. private AttributeValueTemplate _groupingSeparator = null;
  72. private AttributeValueTemplate _groupingSize = null;
  73. private int _level = LEVEL_SINGLE;
  74. private boolean _formatNeeded = false;
  75. private String _className = null;
  76. private ArrayList _closureVars = null;
  77. // -- Begin Closure interface --------------------
  78. /**
  79. * Returns true if this closure is compiled in an inner class (i.e.
  80. * if this is a real closure).
  81. */
  82. public boolean inInnerClass() {
  83. return (_className != null);
  84. }
  85. /**
  86. * Returns a reference to its parent closure or null if outermost.
  87. */
  88. public Closure getParentClosure() {
  89. return null;
  90. }
  91. /**
  92. * Returns the name of the auxiliary class or null if this predicate
  93. * is compiled inside the Translet.
  94. */
  95. public String getInnerClassName() {
  96. return _className;
  97. }
  98. /**
  99. * Add new variable to the closure.
  100. */
  101. public void addVariable(VariableRefBase variableRef) {
  102. if (_closureVars == null) {
  103. _closureVars = new ArrayList();
  104. }
  105. // Only one reference per variable
  106. if (!_closureVars.contains(variableRef)) {
  107. _closureVars.add(variableRef);
  108. }
  109. }
  110. // -- End Closure interface ----------------------
  111. public void parseContents(Parser parser) {
  112. final int count = _attributes.getLength();
  113. for (int i = 0; i < count; i++) {
  114. final String name = _attributes.getQName(i);
  115. final String value = _attributes.getValue(i);
  116. if (name.equals("value")) {
  117. _value = parser.parseExpression(this, name, null);
  118. }
  119. else if (name.equals("count")) {
  120. _count = parser.parsePattern(this, name, null);
  121. }
  122. else if (name.equals("from")) {
  123. _from = parser.parsePattern(this, name, null);
  124. }
  125. else if (name.equals("level")) {
  126. if (value.equals("single")) {
  127. _level = LEVEL_SINGLE;
  128. }
  129. else if (value.equals("multiple")) {
  130. _level = LEVEL_MULTIPLE;
  131. }
  132. else if (value.equals("any")) {
  133. _level = LEVEL_ANY;
  134. }
  135. }
  136. else if (name.equals("format")) {
  137. _format = new AttributeValueTemplate(value, parser, this);
  138. _formatNeeded = true;
  139. }
  140. else if (name.equals("lang")) {
  141. _lang = new AttributeValueTemplate(value, parser, this);
  142. _formatNeeded = true;
  143. }
  144. else if (name.equals("letter-value")) {
  145. _letterValue = new AttributeValueTemplate(value, parser, this);
  146. _formatNeeded = true;
  147. }
  148. else if (name.equals("grouping-separator")) {
  149. _groupingSeparator = new AttributeValueTemplate(value, parser, this);
  150. _formatNeeded = true;
  151. }
  152. else if (name.equals("grouping-size")) {
  153. _groupingSize = new AttributeValueTemplate(value, parser, this);
  154. _formatNeeded = true;
  155. }
  156. }
  157. }
  158. public Type typeCheck(SymbolTable stable) throws TypeCheckError {
  159. if (_value != null) {
  160. Type tvalue = _value.typeCheck(stable);
  161. if (tvalue instanceof RealType == false) {
  162. _value = new CastExpr(_value, Type.Real);
  163. }
  164. }
  165. if (_count != null) {
  166. _count.typeCheck(stable);
  167. }
  168. if (_from != null) {
  169. _from.typeCheck(stable);
  170. }
  171. if (_format != null) {
  172. _format.typeCheck(stable);
  173. }
  174. if (_lang != null) {
  175. _lang.typeCheck(stable);
  176. }
  177. if (_letterValue != null) {
  178. _letterValue.typeCheck(stable);
  179. }
  180. if (_groupingSeparator != null) {
  181. _groupingSeparator.typeCheck(stable);
  182. }
  183. if (_groupingSize != null) {
  184. _groupingSize.typeCheck(stable);
  185. }
  186. return Type.Void;
  187. }
  188. /**
  189. * True if the has specified a value for this instance of number.
  190. */
  191. public boolean hasValue() {
  192. return _value != null;
  193. }
  194. /**
  195. * Returns <tt>true</tt> if this instance of number has neither
  196. * a from nor a count pattern.
  197. */
  198. public boolean isDefault() {
  199. return _from == null && _count == null;
  200. }
  201. private void compileDefault(ClassGenerator classGen,
  202. MethodGenerator methodGen) {
  203. int index;
  204. ConstantPoolGen cpg = classGen.getConstantPool();
  205. InstructionList il = methodGen.getInstructionList();
  206. int[] fieldIndexes = getXSLTC().getNumberFieldIndexes();
  207. if (fieldIndexes[_level] == -1) {
  208. Field defaultNode = new Field(ACC_PRIVATE,
  209. cpg.addUtf8(FieldNames[_level]),
  210. cpg.addUtf8(NODE_COUNTER_SIG),
  211. null,
  212. cpg.getConstantPool());
  213. // Add a new private field to this class
  214. classGen.addField(defaultNode);
  215. // Get a reference to the newly added field
  216. fieldIndexes[_level] = cpg.addFieldref(classGen.getClassName(),
  217. FieldNames[_level],
  218. NODE_COUNTER_SIG);
  219. }
  220. // Check if field is initialized (runtime)
  221. il.append(classGen.loadTranslet());
  222. il.append(new GETFIELD(fieldIndexes[_level]));
  223. final BranchHandle ifBlock1 = il.append(new IFNONNULL(null));
  224. // Create an instance of DefaultNodeCounter
  225. index = cpg.addMethodref(ClassNames[_level],
  226. "getDefaultNodeCounter",
  227. "(" + TRANSLET_INTF_SIG
  228. + DOM_INTF_SIG
  229. + NODE_ITERATOR_SIG
  230. + ")" + NODE_COUNTER_SIG);
  231. il.append(classGen.loadTranslet());
  232. il.append(methodGen.loadDOM());
  233. il.append(methodGen.loadIterator());
  234. il.append(new INVOKESTATIC(index));
  235. il.append(DUP);
  236. // Store the node counter in the field
  237. il.append(classGen.loadTranslet());
  238. il.append(SWAP);
  239. il.append(new PUTFIELD(fieldIndexes[_level]));
  240. final BranchHandle ifBlock2 = il.append(new GOTO(null));
  241. // Backpatch conditionals
  242. ifBlock1.setTarget(il.append(classGen.loadTranslet()));
  243. il.append(new GETFIELD(fieldIndexes[_level]));
  244. ifBlock2.setTarget(il.append(NOP));
  245. }
  246. /**
  247. * Compiles a constructor for the class <tt>_className</tt> that
  248. * inherits from {Any,Single,Multiple}NodeCounter. This constructor
  249. * simply calls the same constructor in the super class.
  250. */
  251. private void compileConstructor(ClassGenerator classGen) {
  252. MethodGenerator cons;
  253. final InstructionList il = new InstructionList();
  254. final ConstantPoolGen cpg = classGen.getConstantPool();
  255. cons = new MethodGenerator(ACC_PUBLIC,
  256. com.sun.org.apache.bcel.internal.generic.Type.VOID,
  257. new com.sun.org.apache.bcel.internal.generic.Type[] {
  258. Util.getJCRefType(TRANSLET_INTF_SIG),
  259. Util.getJCRefType(DOM_INTF_SIG),
  260. Util.getJCRefType(NODE_ITERATOR_SIG)
  261. },
  262. new String[] {
  263. "dom",
  264. "translet",
  265. "iterator"
  266. },
  267. "<init>", _className, il, cpg);
  268. il.append(ALOAD_0); // this
  269. il.append(ALOAD_1); // translet
  270. il.append(ALOAD_2); // DOM
  271. il.append(new ALOAD(3));// iterator
  272. int index = cpg.addMethodref(ClassNames[_level],
  273. "<init>",
  274. "(" + TRANSLET_INTF_SIG
  275. + DOM_INTF_SIG
  276. + NODE_ITERATOR_SIG
  277. + ")V");
  278. il.append(new INVOKESPECIAL(index));
  279. il.append(RETURN);
  280. cons.stripAttributes(true);
  281. cons.setMaxLocals();
  282. cons.setMaxStack();
  283. classGen.addMethod(cons.getMethod());
  284. }
  285. /**
  286. * This method compiles code that is common to matchesFrom() and
  287. * matchesCount() in the auxillary class.
  288. */
  289. private void compileLocals(NodeCounterGenerator nodeCounterGen,
  290. MatchGenerator matchGen,
  291. InstructionList il)
  292. {
  293. int field;
  294. LocalVariableGen local;
  295. ConstantPoolGen cpg = nodeCounterGen.getConstantPool();
  296. // Get NodeCounter._iterator and store locally
  297. local = matchGen.addLocalVariable("iterator",
  298. Util.getJCRefType(NODE_ITERATOR_SIG),
  299. null, null);
  300. field = cpg.addFieldref(NODE_COUNTER, "_iterator",
  301. ITERATOR_FIELD_SIG);
  302. il.append(ALOAD_0); // 'this' pointer on stack
  303. il.append(new GETFIELD(field));
  304. il.append(new ASTORE(local.getIndex()));
  305. matchGen.setIteratorIndex(local.getIndex());
  306. // Get NodeCounter._translet and store locally
  307. local = matchGen.addLocalVariable("translet",
  308. Util.getJCRefType(TRANSLET_SIG),
  309. null, null);
  310. field = cpg.addFieldref(NODE_COUNTER, "_translet",
  311. "Lcom/sun/org/apache/xalan/internal/xsltc/Translet;");
  312. il.append(ALOAD_0); // 'this' pointer on stack
  313. il.append(new GETFIELD(field));
  314. il.append(new CHECKCAST(cpg.addClass(TRANSLET_CLASS)));
  315. il.append(new ASTORE(local.getIndex()));
  316. nodeCounterGen.setTransletIndex(local.getIndex());
  317. // Get NodeCounter._document and store locally
  318. local = matchGen.addLocalVariable("document",
  319. Util.getJCRefType(DOM_INTF_SIG),
  320. null, null);
  321. field = cpg.addFieldref(_className, "_document", DOM_INTF_SIG);
  322. il.append(ALOAD_0); // 'this' pointer on stack
  323. il.append(new GETFIELD(field));
  324. // Make sure we have the correct DOM type on the stack!!!
  325. il.append(new ASTORE(local.getIndex()));
  326. matchGen.setDomIndex(local.getIndex());
  327. }
  328. private void compilePatterns(ClassGenerator classGen,
  329. MethodGenerator methodGen)
  330. {
  331. int current;
  332. int field;
  333. LocalVariableGen local;
  334. MatchGenerator matchGen;
  335. NodeCounterGenerator nodeCounterGen;
  336. _className = getXSLTC().getHelperClassName();
  337. nodeCounterGen = new NodeCounterGenerator(_className,
  338. ClassNames[_level],
  339. toString(),
  340. ACC_PUBLIC | ACC_SUPER,
  341. null,
  342. classGen.getStylesheet());
  343. InstructionList il = null;
  344. ConstantPoolGen cpg = nodeCounterGen.getConstantPool();
  345. // Add a new instance variable for each var in closure
  346. final int closureLen = (_closureVars == null) ? 0 :
  347. _closureVars.size();
  348. for (int i = 0; i < closureLen; i++) {
  349. VariableBase var =
  350. ((VariableRefBase) _closureVars.get(i)).getVariable();
  351. nodeCounterGen.addField(new Field(ACC_PUBLIC,
  352. cpg.addUtf8(var.getEscapedName()),
  353. cpg.addUtf8(var.getType().toSignature()),
  354. null, cpg.getConstantPool()));
  355. }
  356. // Add a single constructor to the class
  357. compileConstructor(nodeCounterGen);
  358. /*
  359. * Compile method matchesFrom()
  360. */
  361. if (_from != null) {
  362. il = new InstructionList();
  363. matchGen =
  364. new MatchGenerator(ACC_PUBLIC | ACC_FINAL,
  365. com.sun.org.apache.bcel.internal.generic.Type.BOOLEAN,
  366. new com.sun.org.apache.bcel.internal.generic.Type[] {
  367. com.sun.org.apache.bcel.internal.generic.Type.INT,
  368. },
  369. new String[] {
  370. "node",
  371. },
  372. "matchesFrom", _className, il, cpg);
  373. compileLocals(nodeCounterGen,matchGen,il);
  374. // Translate Pattern
  375. il.append(matchGen.loadContextNode());
  376. _from.translate(nodeCounterGen, matchGen);
  377. _from.synthesize(nodeCounterGen, matchGen);
  378. il.append(IRETURN);
  379. matchGen.stripAttributes(true);
  380. matchGen.setMaxLocals();
  381. matchGen.setMaxStack();
  382. matchGen.removeNOPs();
  383. nodeCounterGen.addMethod(matchGen.getMethod());
  384. }
  385. /*
  386. * Compile method matchesCount()
  387. */
  388. if (_count != null) {
  389. il = new InstructionList();
  390. matchGen = new MatchGenerator(ACC_PUBLIC | ACC_FINAL,
  391. com.sun.org.apache.bcel.internal.generic.Type.BOOLEAN,
  392. new com.sun.org.apache.bcel.internal.generic.Type[] {
  393. com.sun.org.apache.bcel.internal.generic.Type.INT,
  394. },
  395. new String[] {
  396. "node",
  397. },
  398. "matchesCount", _className, il, cpg);
  399. compileLocals(nodeCounterGen,matchGen,il);
  400. // Translate Pattern
  401. il.append(matchGen.loadContextNode());
  402. _count.translate(nodeCounterGen, matchGen);
  403. _count.synthesize(nodeCounterGen, matchGen);
  404. il.append(IRETURN);
  405. matchGen.stripAttributes(true);
  406. matchGen.setMaxLocals();
  407. matchGen.setMaxStack();
  408. matchGen.removeNOPs();
  409. nodeCounterGen.addMethod(matchGen.getMethod());
  410. }
  411. getXSLTC().dumpClass(nodeCounterGen.getJavaClass());
  412. // Push an instance of the newly created class
  413. cpg = classGen.getConstantPool();
  414. il = methodGen.getInstructionList();
  415. final int index = cpg.addMethodref(_className, "<init>",
  416. "(" + TRANSLET_INTF_SIG
  417. + DOM_INTF_SIG
  418. + NODE_ITERATOR_SIG
  419. + ")V");
  420. il.append(new NEW(cpg.addClass(_className)));
  421. il.append(DUP);
  422. il.append(classGen.loadTranslet());
  423. il.append(methodGen.loadDOM());
  424. il.append(methodGen.loadIterator());
  425. il.append(new INVOKESPECIAL(index));
  426. // Initialize closure variables
  427. for (int i = 0; i < closureLen; i++) {
  428. final VariableRefBase varRef = (VariableRefBase) _closureVars.get(i);
  429. final VariableBase var = varRef.getVariable();
  430. final Type varType = var.getType();
  431. // Store variable in new closure
  432. il.append(DUP);
  433. il.append(var.loadInstruction());
  434. il.append(new PUTFIELD(
  435. cpg.addFieldref(_className, var.getEscapedName(),
  436. varType.toSignature())));
  437. }
  438. }
  439. public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
  440. int index;
  441. final ConstantPoolGen cpg = classGen.getConstantPool();
  442. final InstructionList il = methodGen.getInstructionList();
  443. // Push "this" for the call to characters()
  444. il.append(classGen.loadTranslet());
  445. if (hasValue()) {
  446. compileDefault(classGen, methodGen);
  447. _value.translate(classGen, methodGen);
  448. // Round the number to the nearest integer
  449. index = cpg.addMethodref(MATH_CLASS, "round", "(D)J");
  450. il.append(new INVOKESTATIC(index));
  451. il.append(new L2I());
  452. // Call setValue on the node counter
  453. index = cpg.addMethodref(NODE_COUNTER,
  454. "setValue",
  455. "(I)" + NODE_COUNTER_SIG);
  456. il.append(new INVOKEVIRTUAL(index));
  457. }
  458. else if (isDefault()) {
  459. compileDefault(classGen, methodGen);
  460. }
  461. else {
  462. compilePatterns(classGen, methodGen);
  463. }
  464. // Call setStartNode()
  465. if (!hasValue()) {
  466. il.append(methodGen.loadContextNode());
  467. index = cpg.addMethodref(NODE_COUNTER,
  468. SET_START_NODE,
  469. "(I)" + NODE_COUNTER_SIG);
  470. il.append(new INVOKEVIRTUAL(index));
  471. }
  472. // Call getCounter() with or without args
  473. if (_formatNeeded) {
  474. if (_format != null) {
  475. _format.translate(classGen, methodGen);
  476. }
  477. else {
  478. il.append(new PUSH(cpg, "1"));
  479. }
  480. if (_lang != null) {
  481. _lang.translate(classGen, methodGen);
  482. }
  483. else {
  484. il.append(new PUSH(cpg, "en")); // TODO ??
  485. }
  486. if (_letterValue != null) {
  487. _letterValue.translate(classGen, methodGen);
  488. }
  489. else {
  490. il.append(new PUSH(cpg, Constants.EMPTYSTRING));
  491. }
  492. if (_groupingSeparator != null) {
  493. _groupingSeparator.translate(classGen, methodGen);
  494. }
  495. else {
  496. il.append(new PUSH(cpg, Constants.EMPTYSTRING));
  497. }
  498. if (_groupingSize != null) {
  499. _groupingSize.translate(classGen, methodGen);
  500. }
  501. else {
  502. il.append(new PUSH(cpg, "0"));
  503. }
  504. index = cpg.addMethodref(NODE_COUNTER, "getCounter",
  505. "(" + STRING_SIG + STRING_SIG
  506. + STRING_SIG + STRING_SIG
  507. + STRING_SIG + ")" + STRING_SIG);
  508. il.append(new INVOKEVIRTUAL(index));
  509. }
  510. else {
  511. index = cpg.addMethodref(NODE_COUNTER, "setDefaultFormatting",
  512. "()" + NODE_COUNTER_SIG);
  513. il.append(new INVOKEVIRTUAL(index));
  514. index = cpg.addMethodref(NODE_COUNTER, "getCounter",
  515. "()" + STRING_SIG);
  516. il.append(new INVOKEVIRTUAL(index));
  517. }
  518. // Output the resulting string to the handler
  519. il.append(methodGen.loadHandler());
  520. index = cpg.addMethodref(TRANSLET_CLASS,
  521. CHARACTERSW,
  522. CHARACTERSW_SIG);
  523. il.append(new INVOKEVIRTUAL(index));
  524. }
  525. }