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: XSLTC.java,v 1.57 2004/02/16 22:25:33 minchau Exp $
  18. */
  19. package com.sun.org.apache.xalan.internal.xsltc.compiler;
  20. import java.io.BufferedOutputStream;
  21. import java.io.ByteArrayOutputStream;
  22. import java.io.File;
  23. import java.io.FileOutputStream;
  24. import java.io.IOException;
  25. import java.io.InputStream;
  26. import java.net.URL;
  27. import java.util.Date;
  28. import java.util.Enumeration;
  29. import java.util.Hashtable;
  30. import java.util.Map;
  31. import java.util.Properties;
  32. import java.util.Vector;
  33. import java.util.jar.JarEntry;
  34. import java.util.jar.JarOutputStream;
  35. import java.util.jar.Manifest;
  36. import com.sun.org.apache.bcel.internal.classfile.JavaClass;
  37. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
  38. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  39. import com.sun.org.apache.xml.internal.dtm.DTM;
  40. import org.xml.sax.InputSource;
  41. import org.xml.sax.XMLReader;
  42. /**
  43. * @author Jacek Ambroziak
  44. * @author Santiago Pericas-Geertsen
  45. * @author G. Todd Miller
  46. * @author Morten Jorgensen
  47. * @author John Howard (johnh@schemasoft.com)
  48. */
  49. public final class XSLTC {
  50. // A reference to the main stylesheet parser object.
  51. private Parser _parser;
  52. // A reference to an external XMLReader (SAX parser) passed to us
  53. private XMLReader _reader = null;
  54. // A reference to an external SourceLoader (for use with include/import)
  55. private SourceLoader _loader = null;
  56. // A reference to the stylesheet being compiled.
  57. private Stylesheet _stylesheet;
  58. // Counters used by various classes to generate unique names.
  59. // private int _variableSerial = 1;
  60. private int _modeSerial = 1;
  61. private int _stylesheetSerial = 1;
  62. private int _stepPatternSerial = 1;
  63. private int _helperClassSerial = 0;
  64. private int _attributeSetSerial = 0;
  65. private int[] _numberFieldIndexes;
  66. // Name index tables
  67. private int _nextGType; // Next available element type
  68. private Vector _namesIndex; // Index of all registered QNames
  69. private Hashtable _elements; // Hashtable of all registered elements
  70. private Hashtable _attributes; // Hashtable of all registered attributes
  71. // Namespace index tables
  72. private int _nextNSType; // Next available namespace type
  73. private Vector _namespaceIndex; // Index of all registered namespaces
  74. private Hashtable _namespaces; // Hashtable of all registered namespaces
  75. private Hashtable _namespacePrefixes;// Hashtable of all registered namespace prefixes
  76. // All literal text in the stylesheet
  77. private Vector m_characterData;
  78. // These define the various methods for outputting the translet
  79. public static final int FILE_OUTPUT = 0;
  80. public static final int JAR_OUTPUT = 1;
  81. public static final int BYTEARRAY_OUTPUT = 2;
  82. public static final int CLASSLOADER_OUTPUT = 3;
  83. public static final int BYTEARRAY_AND_FILE_OUTPUT = 4;
  84. public static final int BYTEARRAY_AND_JAR_OUTPUT = 5;
  85. // Compiler options (passed from command line or XSLTC client)
  86. private boolean _debug = false; // -x
  87. private String _jarFileName = null; // -j <jar-file-name>
  88. private String _className = null; // -o <class-name>
  89. private String _packageName = null; // -p <package-name>
  90. private File _destDir = null; // -d <directory-name>
  91. private int _outputType = FILE_OUTPUT; // by default
  92. private Vector _classes;
  93. private Vector _bcelClasses;
  94. private boolean _callsNodeset = false;
  95. private boolean _multiDocument = false;
  96. private boolean _hasIdCall = false;
  97. /**
  98. * Set to true if template inlining is requested. Template
  99. * inlining used to be the default, but we have found that
  100. * Hotspots does a better job with shorter methods, so the
  101. * default is *not* to inline now.
  102. */
  103. private boolean _templateInlining = false;
  104. /**
  105. * XSLTC compiler constructor
  106. */
  107. public XSLTC() {
  108. _parser = new Parser(this);
  109. }
  110. /**
  111. * Only for user by the internal TrAX implementation.
  112. */
  113. public Parser getParser() {
  114. return _parser;
  115. }
  116. /**
  117. * Only for user by the internal TrAX implementation.
  118. */
  119. public void setOutputType(int type) {
  120. _outputType = type;
  121. }
  122. /**
  123. * Only for user by the internal TrAX implementation.
  124. */
  125. public Properties getOutputProperties() {
  126. return _parser.getOutputProperties();
  127. }
  128. /**
  129. * Initializes the compiler to compile a new stylesheet
  130. */
  131. public void init() {
  132. reset();
  133. _reader = null;
  134. _classes = new Vector();
  135. _bcelClasses = new Vector();
  136. }
  137. /**
  138. * Initializes the compiler to produce a new translet
  139. */
  140. private void reset() {
  141. _nextGType = DTM.NTYPES;
  142. _elements = new Hashtable();
  143. _attributes = new Hashtable();
  144. _namespaces = new Hashtable();
  145. _namespaces.put("",new Integer(_nextNSType));
  146. _namesIndex = new Vector(128);
  147. _namespaceIndex = new Vector(32);
  148. _namespacePrefixes = new Hashtable();
  149. _stylesheet = null;
  150. _parser.init();
  151. //_variableSerial = 1;
  152. _modeSerial = 1;
  153. _stylesheetSerial = 1;
  154. _stepPatternSerial = 1;
  155. _helperClassSerial = 0;
  156. _attributeSetSerial = 0;
  157. _multiDocument = false;
  158. _hasIdCall = false;
  159. _numberFieldIndexes = new int[] {
  160. -1, // LEVEL_SINGLE
  161. -1, // LEVEL_MULTIPLE
  162. -1 // LEVEL_ANY
  163. };
  164. }
  165. /**
  166. * Defines an external SourceLoader to provide the compiler with documents
  167. * referenced in xsl:include/import
  168. * @param loader The SourceLoader to use for include/import
  169. */
  170. public void setSourceLoader(SourceLoader loader) {
  171. _loader = loader;
  172. }
  173. /**
  174. * Set a flag indicating if templates are to be inlined or not. The
  175. * default is to do inlining, but this causes problems when the
  176. * stylesheets have a large number of templates (e.g. branch targets
  177. * exceeding 64K or a length of a method exceeding 64K).
  178. */
  179. public void setTemplateInlining(boolean templateInlining) {
  180. _templateInlining = templateInlining;
  181. }
  182. /**
  183. * Set the parameters to use to locate the correct <?xml-stylesheet ...?>
  184. * processing instruction in the case where the input document to the
  185. * compiler (and parser) is an XML document.
  186. * @param media The media attribute to be matched. May be null, in which
  187. * case the prefered templates will be used (i.e. alternate = no).
  188. * @param title The value of the title attribute to match. May be null.
  189. * @param charset The value of the charset attribute to match. May be null.
  190. */
  191. public void setPIParameters(String media, String title, String charset) {
  192. _parser.setPIParameters(media, title, charset);
  193. }
  194. /**
  195. * Compiles an XSL stylesheet pointed to by a URL
  196. * @param url An URL containing the input XSL stylesheet
  197. */
  198. public boolean compile(URL url) {
  199. try {
  200. // Open input stream from URL and wrap inside InputSource
  201. final InputStream stream = url.openStream();
  202. final InputSource input = new InputSource(stream);
  203. input.setSystemId(url.toString());
  204. return compile(input, _className);
  205. }
  206. catch (IOException e) {
  207. _parser.reportError(Constants.FATAL, new ErrorMsg(e));
  208. return false;
  209. }
  210. }
  211. /**
  212. * Compiles an XSL stylesheet pointed to by a URL
  213. * @param url An URL containing the input XSL stylesheet
  214. * @param name The name to assign to the translet class
  215. */
  216. public boolean compile(URL url, String name) {
  217. try {
  218. // Open input stream from URL and wrap inside InputSource
  219. final InputStream stream = url.openStream();
  220. final InputSource input = new InputSource(stream);
  221. input.setSystemId(url.toString());
  222. return compile(input, name);
  223. }
  224. catch (IOException e) {
  225. _parser.reportError(Constants.FATAL, new ErrorMsg(e));
  226. return false;
  227. }
  228. }
  229. /**
  230. * Compiles an XSL stylesheet passed in through an InputStream
  231. * @param input An InputStream that will pass in the stylesheet contents
  232. * @param name The name of the translet class to generate
  233. * @return 'true' if the compilation was successful
  234. */
  235. public boolean compile(InputStream stream, String name) {
  236. final InputSource input = new InputSource(stream);
  237. input.setSystemId(name); // We have nothing else!!!
  238. return compile(input, name);
  239. }
  240. /**
  241. * Compiles an XSL stylesheet passed in through an InputStream
  242. * @param input An InputSource that will pass in the stylesheet contents
  243. * @param name The name of the translet class to generate - can be null
  244. * @return 'true' if the compilation was successful
  245. */
  246. public boolean compile(InputSource input, String name) {
  247. try {
  248. // Reset globals in case we're called by compile(Vector v);
  249. reset();
  250. // The systemId may not be set, so we'll have to check the URL
  251. String systemId = null;
  252. if (input != null) {
  253. systemId = input.getSystemId();
  254. }
  255. // Set the translet class name if not already set
  256. if (_className == null) {
  257. if (name != null) {
  258. setClassName(name);
  259. }
  260. else if (systemId != null && !systemId.equals("")) {
  261. setClassName(Util.baseName(systemId));
  262. }
  263. // Ensure we have a non-empty class name at this point
  264. if (_className == null || _className.length() == 0) {
  265. setClassName("GregorSamsa"); // default translet name
  266. }
  267. }
  268. // Get the root node of the abstract syntax tree
  269. SyntaxTreeNode element = null;
  270. if (_reader == null) {
  271. element = _parser.parse(input);
  272. }
  273. else {
  274. element = _parser.parse(_reader, input);
  275. }
  276. // Compile the translet - this is where the work is done!
  277. if ((!_parser.errorsFound()) && (element != null)) {
  278. // Create a Stylesheet element from the root node
  279. _stylesheet = _parser.makeStylesheet(element);
  280. _stylesheet.setSourceLoader(_loader);
  281. _stylesheet.setSystemId(systemId);
  282. _stylesheet.setParentStylesheet(null);
  283. _stylesheet.setTemplateInlining(_templateInlining);
  284. _parser.setCurrentStylesheet(_stylesheet);
  285. // Create AST under the Stylesheet element (parse & type-check)
  286. _parser.createAST(_stylesheet);
  287. }
  288. // Generate the bytecodes and output the translet class(es)
  289. if ((!_parser.errorsFound()) && (_stylesheet != null)) {
  290. _stylesheet.setCallsNodeset(_callsNodeset);
  291. _stylesheet.setMultiDocument(_multiDocument);
  292. _stylesheet.setHasIdCall(_hasIdCall);
  293. // Class synchronization is needed for BCEL
  294. synchronized (getClass()) {
  295. _stylesheet.translate();
  296. }
  297. }
  298. }
  299. catch (Exception e) {
  300. /*if (_debug)*/ e.printStackTrace();
  301. _parser.reportError(Constants.FATAL, new ErrorMsg(e));
  302. }
  303. catch (Error e) {
  304. if (_debug) e.printStackTrace();
  305. _parser.reportError(Constants.FATAL, new ErrorMsg(e));
  306. }
  307. finally {
  308. _reader = null; // reset this here to be sure it is not re-used
  309. }
  310. return !_parser.errorsFound();
  311. }
  312. /**
  313. * Compiles a set of stylesheets pointed to by a Vector of URLs
  314. * @param stylesheets A Vector containing URLs pointing to the stylesheets
  315. * @return 'true' if the compilation was successful
  316. */
  317. public boolean compile(Vector stylesheets) {
  318. // Get the number of stylesheets (ie. URLs) in the vector
  319. final int count = stylesheets.size();
  320. // Return straight away if the vector is empty
  321. if (count == 0) return true;
  322. // Special handling needed if the URL count is one, becuase the
  323. // _className global must not be reset if it was set explicitly
  324. if (count == 1) {
  325. final Object url = stylesheets.firstElement();
  326. if (url instanceof URL)
  327. return compile((URL)url);
  328. else
  329. return false;
  330. }
  331. else {
  332. // Traverse all elements in the vector and compile
  333. final Enumeration urls = stylesheets.elements();
  334. while (urls.hasMoreElements()) {
  335. _className = null; // reset, so that new name will be computed
  336. final Object url = urls.nextElement();
  337. if (url instanceof URL) {
  338. if (!compile((URL)url)) return false;
  339. }
  340. }
  341. }
  342. return true;
  343. }
  344. /**
  345. * Returns an array of bytecode arrays generated by a compilation.
  346. * @return JVM bytecodes that represent translet class definition
  347. */
  348. public byte[][] getBytecodes() {
  349. final int count = _classes.size();
  350. final byte[][] result = new byte[count][1];
  351. for (int i = 0; i < count; i++)
  352. result[i] = (byte[])_classes.elementAt(i);
  353. return result;
  354. }
  355. /**
  356. * Compiles a stylesheet pointed to by a URL. The result is put in a
  357. * set of byte arrays. One byte array for each generated class.
  358. * @param name The name of the translet class to generate
  359. * @param input An InputSource that will pass in the stylesheet contents
  360. * @param outputType The output type
  361. * @return JVM bytecodes that represent translet class definition
  362. */
  363. public byte[][] compile(String name, InputSource input, int outputType) {
  364. _outputType = outputType;
  365. if (compile(input, name))
  366. return getBytecodes();
  367. else
  368. return null;
  369. }
  370. /**
  371. * Compiles a stylesheet pointed to by a URL. The result is put in a
  372. * set of byte arrays. One byte array for each generated class.
  373. * @param name The name of the translet class to generate
  374. * @param input An InputSource that will pass in the stylesheet contents
  375. * @return JVM bytecodes that represent translet class definition
  376. */
  377. public byte[][] compile(String name, InputSource input) {
  378. return compile(name, input, BYTEARRAY_OUTPUT);
  379. }
  380. /**
  381. * Set the XMLReader to use for parsing the next input stylesheet
  382. * @param reader XMLReader (SAX2 parser) to use
  383. */
  384. public void setXMLReader(XMLReader reader) {
  385. _reader = reader;
  386. }
  387. /**
  388. * Get the XMLReader to use for parsing the next input stylesheet
  389. */
  390. public XMLReader getXMLReader() {
  391. return _reader ;
  392. }
  393. /**
  394. * Get a Vector containing all compile error messages
  395. * @return A Vector containing all compile error messages
  396. */
  397. public Vector getErrors() {
  398. return _parser.getErrors();
  399. }
  400. /**
  401. * Get a Vector containing all compile warning messages
  402. * @return A Vector containing all compile error messages
  403. */
  404. public Vector getWarnings() {
  405. return _parser.getWarnings();
  406. }
  407. /**
  408. * Print all compile error messages to standard output
  409. */
  410. public void printErrors() {
  411. _parser.printErrors();
  412. }
  413. /**
  414. * Print all compile warning messages to standard output
  415. */
  416. public void printWarnings() {
  417. _parser.printWarnings();
  418. }
  419. /**
  420. * This method is called by the XPathParser when it encounters a call
  421. * to the document() function. Affects the DOM used by the translet.
  422. */
  423. protected void setMultiDocument(boolean flag) {
  424. _multiDocument = flag;
  425. }
  426. public boolean isMultiDocument() {
  427. return _multiDocument;
  428. }
  429. /**
  430. * This method is called by the XPathParser when it encounters a call
  431. * to the nodeset() extension function. Implies multi document.
  432. */
  433. protected void setCallsNodeset(boolean flag) {
  434. if (flag) setMultiDocument(flag);
  435. _callsNodeset = flag;
  436. }
  437. public boolean callsNodeset() {
  438. return _callsNodeset;
  439. }
  440. protected void setHasIdCall(boolean flag) {
  441. _hasIdCall = flag;
  442. }
  443. public boolean hasIdCall() {
  444. return _hasIdCall;
  445. }
  446. /**
  447. * Set the class name for the generated translet. This class name is
  448. * overridden if multiple stylesheets are compiled in one go using the
  449. * compile(Vector urls) method.
  450. * @param className The name to assign to the translet class
  451. */
  452. public void setClassName(String className) {
  453. final String base = Util.baseName(className);
  454. final String noext = Util.noExtName(base);
  455. String name = Util.toJavaName(noext);
  456. if (_packageName == null)
  457. _className = name;
  458. else
  459. _className = _packageName + '.' + name;
  460. }
  461. /**
  462. * Get the class name for the generated translet.
  463. */
  464. public String getClassName() {
  465. return _className;
  466. }
  467. /**
  468. * Convert for Java class name of local system file name.
  469. * (Replace '.' with '/' on UNIX and replace '.' by '\' on Windows/DOS.)
  470. */
  471. private String classFileName(final String className) {
  472. return className.replace('.', File.separatorChar) + ".class";
  473. }
  474. /**
  475. * Generate an output File object to send the translet to
  476. */
  477. private File getOutputFile(String className) {
  478. if (_destDir != null)
  479. return new File(_destDir, classFileName(className));
  480. else
  481. return new File(classFileName(className));
  482. }
  483. /**
  484. * Set the destination directory for the translet.
  485. * The current working directory will be used by default.
  486. */
  487. public boolean setDestDirectory(String dstDirName) {
  488. final File dir = new File(dstDirName);
  489. if (dir.exists() || dir.mkdirs()) {
  490. _destDir = dir;
  491. return true;
  492. }
  493. else {
  494. _destDir = null;
  495. return false;
  496. }
  497. }
  498. /**
  499. * Set an optional package name for the translet and auxiliary classes
  500. */
  501. public void setPackageName(String packageName) {
  502. _packageName = packageName;
  503. if (_className != null) setClassName(_className);
  504. }
  505. /**
  506. * Set the name of an optional JAR-file to dump the translet and
  507. * auxiliary classes to
  508. */
  509. public void setJarFileName(String jarFileName) {
  510. final String JAR_EXT = ".jar";
  511. if (jarFileName.endsWith(JAR_EXT))
  512. _jarFileName = jarFileName;
  513. else
  514. _jarFileName = jarFileName + JAR_EXT;
  515. _outputType = JAR_OUTPUT;
  516. }
  517. public String getJarFileName() {
  518. return _jarFileName;
  519. }
  520. /**
  521. * Set the top-level stylesheet
  522. */
  523. public void setStylesheet(Stylesheet stylesheet) {
  524. if (_stylesheet == null) _stylesheet = stylesheet;
  525. }
  526. /**
  527. * Returns the top-level stylesheet
  528. */
  529. public Stylesheet getStylesheet() {
  530. return _stylesheet;
  531. }
  532. /**
  533. * Registers an attribute and gives it a type so that it can be mapped to
  534. * DOM attribute types at run-time.
  535. */
  536. public int registerAttribute(QName name) {
  537. Integer code = (Integer)_attributes.get(name.toString());
  538. if (code == null) {
  539. code = new Integer(_nextGType++);
  540. _attributes.put(name.toString(), code);
  541. final String uri = name.getNamespace();
  542. final String local = "@"+name.getLocalPart();
  543. if ((uri != null) && (!uri.equals("")))
  544. _namesIndex.addElement(uri+":"+local);
  545. else
  546. _namesIndex.addElement(local);
  547. if (name.getLocalPart().equals("*")) {
  548. registerNamespace(name.getNamespace());
  549. }
  550. }
  551. return code.intValue();
  552. }
  553. /**
  554. * Registers an element and gives it a type so that it can be mapped to
  555. * DOM element types at run-time.
  556. */
  557. public int registerElement(QName name) {
  558. // Register element (full QName)
  559. Integer code = (Integer)_elements.get(name.toString());
  560. if (code == null) {
  561. _elements.put(name.toString(), code = new Integer(_nextGType++));
  562. _namesIndex.addElement(name.toString());
  563. }
  564. if (name.getLocalPart().equals("*")) {
  565. registerNamespace(name.getNamespace());
  566. }
  567. return code.intValue();
  568. }
  569. /**
  570. * Registers a namespace prefix and gives it a type so that it can be mapped to
  571. * DOM namespace types at run-time.
  572. */
  573. public int registerNamespacePrefix(QName name) {
  574. Integer code = (Integer)_namespacePrefixes.get(name.toString());
  575. if (code == null) {
  576. code = new Integer(_nextGType++);
  577. _namespacePrefixes.put(name.toString(), code);
  578. final String uri = name.getNamespace();
  579. if ((uri != null) && (!uri.equals(""))){
  580. // namespace::ext2:ped2 will be made empty in TypedNamespaceIterator
  581. _namesIndex.addElement("?");
  582. } else{
  583. _namesIndex.addElement("?"+name.getLocalPart());
  584. }
  585. }
  586. return code.intValue();
  587. }
  588. /**
  589. * Registers a namespace and gives it a type so that it can be mapped to
  590. * DOM namespace types at run-time.
  591. */
  592. public int registerNamespace(String namespaceURI) {
  593. Integer code = (Integer)_namespaces.get(namespaceURI);
  594. if (code == null) {
  595. code = new Integer(_nextNSType++);
  596. _namespaces.put(namespaceURI,code);
  597. _namespaceIndex.addElement(namespaceURI);
  598. }
  599. return code.intValue();
  600. }
  601. public int nextModeSerial() {
  602. return _modeSerial++;
  603. }
  604. public int nextStylesheetSerial() {
  605. return _stylesheetSerial++;
  606. }
  607. public int nextStepPatternSerial() {
  608. return _stepPatternSerial++;
  609. }
  610. public int[] getNumberFieldIndexes() {
  611. return _numberFieldIndexes;
  612. }
  613. public int nextHelperClassSerial() {
  614. return _helperClassSerial++;
  615. }
  616. public int nextAttributeSetSerial() {
  617. return _attributeSetSerial++;
  618. }
  619. public Vector getNamesIndex() {
  620. return _namesIndex;
  621. }
  622. public Vector getNamespaceIndex() {
  623. return _namespaceIndex;
  624. }
  625. /**
  626. * Returns a unique name for every helper class needed to
  627. * execute a translet.
  628. */
  629. public String getHelperClassName() {
  630. return getClassName() + '$' + _helperClassSerial++;
  631. }
  632. public void dumpClass(JavaClass clazz) {
  633. if (_outputType == FILE_OUTPUT ||
  634. _outputType == BYTEARRAY_AND_FILE_OUTPUT)
  635. {
  636. File outFile = getOutputFile(clazz.getClassName());
  637. String parentDir = outFile.getParent();
  638. if (parentDir != null) {
  639. File parentFile = new File(parentDir);
  640. if (!parentFile.exists())
  641. parentFile.mkdirs();
  642. }
  643. }
  644. try {
  645. switch (_outputType) {
  646. case FILE_OUTPUT:
  647. clazz.dump(
  648. new BufferedOutputStream(
  649. new FileOutputStream(
  650. getOutputFile(clazz.getClassName()))));
  651. break;
  652. case JAR_OUTPUT:
  653. _bcelClasses.addElement(clazz);
  654. break;
  655. case BYTEARRAY_OUTPUT:
  656. case BYTEARRAY_AND_FILE_OUTPUT:
  657. case BYTEARRAY_AND_JAR_OUTPUT:
  658. case CLASSLOADER_OUTPUT:
  659. ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
  660. clazz.dump(out);
  661. _classes.addElement(out.toByteArray());
  662. if (_outputType == BYTEARRAY_AND_FILE_OUTPUT)
  663. clazz.dump(new BufferedOutputStream(
  664. new FileOutputStream(getOutputFile(clazz.getClassName()))));
  665. else if (_outputType == BYTEARRAY_AND_JAR_OUTPUT)
  666. _bcelClasses.addElement(clazz);
  667. break;
  668. }
  669. }
  670. catch (Exception e) {
  671. e.printStackTrace();
  672. }
  673. }
  674. /**
  675. * File separators are converted to forward slashes for ZIP files.
  676. */
  677. private String entryName(File f) throws IOException {
  678. return f.getName().replace(File.separatorChar, '/');
  679. }
  680. /**
  681. * Generate output JAR-file and packages
  682. */
  683. public void outputToJar() throws IOException {
  684. // create the manifest
  685. final Manifest manifest = new Manifest();
  686. final java.util.jar.Attributes atrs = manifest.getMainAttributes();
  687. atrs.put(java.util.jar.Attributes.Name.MANIFEST_VERSION,"1.2");
  688. final Map map = manifest.getEntries();
  689. // create manifest
  690. Enumeration classes = _bcelClasses.elements();
  691. final String now = (new Date()).toString();
  692. final java.util.jar.Attributes.Name dateAttr =
  693. new java.util.jar.Attributes.Name("Date");
  694. while (classes.hasMoreElements()) {
  695. final JavaClass clazz = (JavaClass)classes.nextElement();
  696. final String className = clazz.getClassName().replace('.','/');
  697. final java.util.jar.Attributes attr = new java.util.jar.Attributes();
  698. attr.put(dateAttr, now);
  699. map.put(className+".class", attr);
  700. }
  701. final File jarFile = new File(_destDir, _jarFileName);
  702. final JarOutputStream jos =
  703. new JarOutputStream(new FileOutputStream(jarFile), manifest);
  704. classes = _bcelClasses.elements();
  705. while (classes.hasMoreElements()) {
  706. final JavaClass clazz = (JavaClass)classes.nextElement();
  707. final String className = clazz.getClassName().replace('.','/');
  708. jos.putNextEntry(new JarEntry(className+".class"));
  709. final ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
  710. clazz.dump(out); // dump() closes it's output stream
  711. out.writeTo(jos);
  712. }
  713. jos.close();
  714. }
  715. /**
  716. * Turn debugging messages on/off
  717. */
  718. public void setDebug(boolean debug) {
  719. _debug = debug;
  720. }
  721. /**
  722. * Get current debugging message setting
  723. */
  724. public boolean debug() {
  725. return _debug;
  726. }
  727. /**
  728. * Retrieve a string representation of the character data to be stored
  729. * in the translet as a <code>char[]</code>. There may be more than
  730. * one such array required.
  731. * @param index The index of the <code>char[]</code>. Zero-based.
  732. * @return String The character data to be stored in the corresponding
  733. * <code>char[]</code>.
  734. */
  735. public String getCharacterData(int index) {
  736. return ((StringBuffer) m_characterData.elementAt(index)).toString();
  737. }
  738. /**
  739. * Get the number of char[] arrays, thus far, that will be created to
  740. * store literal text in the stylesheet.
  741. */
  742. public int getCharacterDataCount() {
  743. return (m_characterData != null) ? m_characterData.size() : 0;
  744. }
  745. /**
  746. * Add literal text to char arrays that will be used to store character
  747. * data in the stylesheet.
  748. * @param newData String data to be added to char arrays.
  749. * Pre-condition: <code>newData.length() ≤ 21845</code>
  750. * @return int offset at which character data will be stored
  751. */
  752. public int addCharacterData(String newData) {
  753. StringBuffer currData;
  754. if (m_characterData == null) {
  755. m_characterData = new Vector();
  756. currData = new StringBuffer();
  757. m_characterData.addElement(currData);
  758. } else {
  759. currData = (StringBuffer) m_characterData
  760. .elementAt(m_characterData.size()-1);
  761. }
  762. // Character data could take up to three-times as much space when
  763. // written to the class file as UTF-8. The maximum size for a
  764. // constant is 65535/3. If we exceed that,
  765. // (We really should use some "bin packing".)
  766. if (newData.length() + currData.length() > 21845) {
  767. currData = new StringBuffer();
  768. m_characterData.addElement(currData);
  769. }
  770. int newDataOffset = currData.length();
  771. currData.append(newData);
  772. return newDataOffset;
  773. }
  774. }