1 /* 2 * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 /* 21 * $Id: XSLTC.java,v 1.2.4.1 2005/09/05 09:51:38 pvedula Exp $ 22 */ 23 24 package com.sun.org.apache.xalan.internal.xsltc.compiler; 25 26 import com.sun.org.apache.bcel.internal.classfile.JavaClass; 27 import com.sun.org.apache.xalan.internal.XalanConstants; 28 import com.sun.org.apache.xalan.internal.utils.SecuritySupport; 29 import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager; 30 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg; 31 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util; 32 import com.sun.org.apache.xml.internal.dtm.DTM; 33 import java.io.BufferedOutputStream; 34 import java.io.ByteArrayOutputStream; 35 import java.io.File; 36 import java.io.FileOutputStream; 37 import java.io.IOException; 38 import java.io.InputStream; 39 import java.net.URL; 40 import java.util.ArrayList; 41 import java.util.Collections; 42 import java.util.Date; 43 import java.util.Enumeration; 44 import java.util.HashMap; 45 import java.util.Map; 46 import java.util.Objects; 47 import java.util.Properties; 48 import java.util.Vector; 49 import java.util.jar.JarEntry; 50 import java.util.jar.JarOutputStream; 51 import java.util.jar.Manifest; 52 import javax.xml.XMLConstants; 53 import javax.xml.catalog.CatalogFeatures; 54 import jdk.xml.internal.JdkXmlFeatures; 55 import jdk.xml.internal.JdkXmlUtils; 56 import org.xml.sax.InputSource; 57 import org.xml.sax.XMLReader; 58 59 /** 60 * @author Jacek Ambroziak 61 * @author Santiago Pericas-Geertsen 62 * @author G. Todd Miller 63 * @author Morten Jorgensen 64 * @author John Howard (johnh@schemasoft.com) 65 */ 66 public final class XSLTC { 67 68 // A reference to the main stylesheet parser object. 69 private Parser _parser; 70 71 // A reference to an external XMLReader (SAX parser) passed to us 72 private XMLReader _reader = null; 73 74 // A reference to an external SourceLoader (for use with include/import) 75 private SourceLoader _loader = null; 76 77 // A reference to the stylesheet being compiled. 78 private Stylesheet _stylesheet; 79 80 // Counters used by various classes to generate unique names. 81 // private int _variableSerial = 1; 82 private int _modeSerial = 1; 83 private int _stylesheetSerial = 1; 84 private int _stepPatternSerial = 1; 85 private int _helperClassSerial = 0; 86 private int _attributeSetSerial = 0; 87 88 private int[] _numberFieldIndexes; 89 90 // Name index tables 91 private int _nextGType; // Next available element type 92 private Vector _namesIndex; // Index of all registered QNames 93 private Map<String, Integer> _elements; // Map of all registered elements 94 private Map<String, Integer> _attributes; // Map of all registered attributes 95 96 // Namespace index tables 97 private int _nextNSType; // Next available namespace type 98 private Vector _namespaceIndex; // Index of all registered namespaces 99 private Map<String, Integer> _namespaces; // Map of all registered namespaces 100 private Map<String, Integer> _namespacePrefixes;// Map of all registered namespace prefixes 101 102 103 // All literal text in the stylesheet 104 private ArrayList<StringBuilder> m_characterData; 105 106 // These define the various methods for outputting the translet 107 public static final int FILE_OUTPUT = 0; 108 public static final int JAR_OUTPUT = 1; 109 public static final int BYTEARRAY_OUTPUT = 2; 110 public static final int CLASSLOADER_OUTPUT = 3; 111 public static final int BYTEARRAY_AND_FILE_OUTPUT = 4; 112 public static final int BYTEARRAY_AND_JAR_OUTPUT = 5; 113 114 115 // Compiler options (passed from command line or XSLTC client) 116 private boolean _debug = false; // -x 117 private String _jarFileName = null; // -j <jar-file-name> 118 private String _className = null; // -o <class-name> 119 private String _packageName = "die.verwandlung"; // override with -p <package-name> 120 private File _destDir = null; // -d <directory-name> 121 private int _outputType = FILE_OUTPUT; // by default 122 123 private Vector _classes; 124 private Vector _bcelClasses; 125 private boolean _callsNodeset = false; 126 private boolean _multiDocument = false; 127 private boolean _hasIdCall = false; 128 129 /** 130 * Set to true if template inlining is requested. Template 131 * inlining used to be the default, but we have found that 132 * Hotspots does a better job with shorter methods, so the 133 * default is *not* to inline now. 134 */ 135 private boolean _templateInlining = false; 136 137 /** 138 * State of the secure processing feature. 139 */ 140 private boolean _isSecureProcessing = false; 141 142 private boolean _useServicesMechanism = true; 143 144 /** 145 * protocols allowed for external references set by the stylesheet processing instruction, Import and Include element. 146 */ 147 private String _accessExternalStylesheet = XalanConstants.EXTERNAL_ACCESS_DEFAULT; 148 /** 149 * protocols allowed for external DTD references in source file and/or stylesheet. 150 */ 151 private String _accessExternalDTD = XalanConstants.EXTERNAL_ACCESS_DEFAULT; 152 153 private XMLSecurityManager _xmlSecurityManager; 154 155 private final JdkXmlFeatures _xmlFeatures; 156 157 /** 158 * Extension function class loader variables 159 */ 160 161 /* Class loader reference that will be used to external extension functions loading */ 162 private ClassLoader _extensionClassLoader; 163 164 /** 165 * HashSet with the loaded classes 166 */ 167 private final Map<String, Class> _externalExtensionFunctions; 168 169 /** 170 * Catalog features 171 */ 172 CatalogFeatures _catalogFeatures; 173 174 /** 175 * XSLTC compiler constructor 176 */ 177 public XSLTC(boolean useServicesMechanism, JdkXmlFeatures featureManager) { 178 _parser = new Parser(this, useServicesMechanism); 179 _xmlFeatures = featureManager; 180 _extensionClassLoader = null; 181 _externalExtensionFunctions = new HashMap<>(); 182 } 183 184 /** 185 * Set the state of the secure processing feature. 186 */ 187 public void setSecureProcessing(boolean flag) { 188 _isSecureProcessing = flag; 189 } 190 191 /** 192 * Return the state of the secure processing feature. 193 */ 194 public boolean isSecureProcessing() { 195 return _isSecureProcessing; 196 } 197 /** 198 * Return the state of the services mechanism feature. 199 */ 200 public boolean useServicesMechnism() { 201 return _useServicesMechanism; 202 } 203 204 /** 205 * Set the state of the services mechanism feature. 206 */ 207 public void setServicesMechnism(boolean flag) { 208 _useServicesMechanism = flag; 209 } 210 211 /** 212 * Return the value of the specified feature 213 * @param name name of the feature 214 * @return true if the feature is enabled, false otherwise 215 */ 216 public boolean getFeature(JdkXmlFeatures.XmlFeature name) { 217 return _xmlFeatures.getFeature(name); 218 } 219 220 /** 221 * Return allowed protocols for accessing external stylesheet. 222 * @param name the name of the property 223 * @return the value of the property 224 */ 225 public Object getProperty(String name) { 226 if (name.equals(XMLConstants.ACCESS_EXTERNAL_STYLESHEET)) { 227 return _accessExternalStylesheet; 228 } 229 else if (name.equals(XMLConstants.ACCESS_EXTERNAL_DTD)) { 230 return _accessExternalDTD; 231 } else if (name.equals(XalanConstants.SECURITY_MANAGER)) { 232 return _xmlSecurityManager; 233 } else if (name.equals(XalanConstants.JDK_EXTENSION_CLASSLOADER)) { 234 return _extensionClassLoader; 235 } else if (JdkXmlFeatures.CATALOG_FEATURES.equals(name)) { 236 return _catalogFeatures; 237 } 238 return null; 239 } 240 241 /** 242 * Set allowed protocols for accessing external stylesheet. 243 * @param name the name of the property 244 * @param value the value of the property 245 */ 246 public void setProperty(String name, Object value) { 247 if (name.equals(XMLConstants.ACCESS_EXTERNAL_STYLESHEET)) { 248 _accessExternalStylesheet = (String)value; 249 } 250 else if (name.equals(XMLConstants.ACCESS_EXTERNAL_DTD)) { 251 _accessExternalDTD = (String)value; 252 } else if (name.equals(XalanConstants.SECURITY_MANAGER)) { 253 _xmlSecurityManager = (XMLSecurityManager)value; 254 } else if (name.equals(XalanConstants.JDK_EXTENSION_CLASSLOADER)) { 255 _extensionClassLoader = (ClassLoader) value; 256 /* Clear the external extension functions HashMap if extension class 257 loader was changed */ 258 _externalExtensionFunctions.clear(); 259 } else if (JdkXmlFeatures.CATALOG_FEATURES.equals(name)) { 260 _catalogFeatures = (CatalogFeatures)value; 261 } 262 } 263 264 /** 265 * Only for user by the internal TrAX implementation. 266 */ 267 public Parser getParser() { 268 return _parser; 269 } 270 271 /** 272 * Only for user by the internal TrAX implementation. 273 */ 274 public void setOutputType(int type) { 275 _outputType = type; 276 } 277 278 /** 279 * Only for user by the internal TrAX implementation. 280 */ 281 public Properties getOutputProperties() { 282 return _parser.getOutputProperties(); 283 } 284 285 /** 286 * Initializes the compiler to compile a new stylesheet 287 */ 288 public void init() { 289 reset(); 290 _reader = null; 291 _classes = new Vector(); 292 _bcelClasses = new Vector(); 293 } 294 295 private void setExternalExtensionFunctions(String name, Class clazz) { 296 if (_isSecureProcessing && clazz != null && !_externalExtensionFunctions.containsKey(name)) { 297 _externalExtensionFunctions.put(name, clazz); 298 } 299 } 300 301 /* 302 * Function loads an external external extension functions. 303 * The filtering of function types (external,internal) takes place in FunctionCall class 304 * 305 */ 306 Class loadExternalFunction(String name) throws ClassNotFoundException { 307 Class loaded = null; 308 //Check if the function is not loaded already 309 if (_externalExtensionFunctions.containsKey(name)) { 310 loaded = _externalExtensionFunctions.get(name); 311 } else if (_extensionClassLoader != null) { 312 loaded = Class.forName(name, true, _extensionClassLoader); 313 setExternalExtensionFunctions(name, loaded); 314 } 315 if (loaded == null) { 316 throw new ClassNotFoundException(name); 317 } 318 //Return loaded class 319 return (Class) loaded; 320 } 321 322 /* 323 * Returns unmodifiable view of HashMap with loaded external extension 324 * functions - will be needed for the TransformerImpl 325 */ 326 public Map<String, Class> getExternalExtensionFunctions() { 327 return Collections.unmodifiableMap(_externalExtensionFunctions); 328 } 329 330 /** 331 * Initializes the compiler to produce a new translet 332 */ 333 private void reset() { 334 _nextGType = DTM.NTYPES; 335 _elements = new HashMap<>(); 336 _attributes = new HashMap<>(); 337 _namespaces = new HashMap<>(); 338 _namespaces.put("",new Integer(_nextNSType)); 339 _namesIndex = new Vector(128); 340 _namespaceIndex = new Vector(32); 341 _namespacePrefixes = new HashMap<>(); 342 _stylesheet = null; 343 _parser.init(); 344 //_variableSerial = 1; 345 _modeSerial = 1; 346 _stylesheetSerial = 1; 347 _stepPatternSerial = 1; 348 _helperClassSerial = 0; 349 _attributeSetSerial = 0; 350 _multiDocument = false; 351 _hasIdCall = false; 352 _numberFieldIndexes = new int[] { 353 -1, // LEVEL_SINGLE 354 -1, // LEVEL_MULTIPLE 355 -1 // LEVEL_ANY 356 }; 357 _externalExtensionFunctions.clear(); 358 } 359 360 /** 361 * Defines an external SourceLoader to provide the compiler with documents 362 * referenced in xsl:include/import 363 * @param loader The SourceLoader to use for include/import 364 */ 365 public void setSourceLoader(SourceLoader loader) { 366 _loader = loader; 367 } 368 369 /** 370 * Set a flag indicating if templates are to be inlined or not. The 371 * default is to do inlining, but this causes problems when the 372 * stylesheets have a large number of templates (e.g. branch targets 373 * exceeding 64K or a length of a method exceeding 64K). 374 */ 375 public void setTemplateInlining(boolean templateInlining) { 376 _templateInlining = templateInlining; 377 } 378 /** 379 * Return the state of the template inlining feature. 380 */ 381 public boolean getTemplateInlining() { 382 return _templateInlining; 383 } 384 385 /** 386 * Set the parameters to use to locate the correct <?xml-stylesheet ...?> 387 * processing instruction in the case where the input document to the 388 * compiler (and parser) is an XML document. 389 * @param media The media attribute to be matched. May be null, in which 390 * case the prefered templates will be used (i.e. alternate = no). 391 * @param title The value of the title attribute to match. May be null. 392 * @param charset The value of the charset attribute to match. May be null. 393 */ 394 public void setPIParameters(String media, String title, String charset) { 395 _parser.setPIParameters(media, title, charset); 396 } 397 398 /** 399 * Compiles an XSL stylesheet pointed to by a URL 400 * @param url An URL containing the input XSL stylesheet 401 */ 402 public boolean compile(URL url) { 403 try { 404 // Open input stream from URL and wrap inside InputSource 405 final InputStream stream = url.openStream(); 406 final InputSource input = new InputSource(stream); 407 input.setSystemId(url.toString()); 408 return compile(input, _className); 409 } 410 catch (IOException e) { 411 _parser.reportError(Constants.FATAL, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e)); 412 return false; 413 } 414 } 415 416 /** 417 * Compiles an XSL stylesheet pointed to by a URL 418 * @param url An URL containing the input XSL stylesheet 419 * @param name The name to assign to the translet class 420 */ 421 public boolean compile(URL url, String name) { 422 try { 423 // Open input stream from URL and wrap inside InputSource 424 final InputStream stream = url.openStream(); 425 final InputSource input = new InputSource(stream); 426 input.setSystemId(url.toString()); 427 return compile(input, name); 428 } 429 catch (IOException e) { 430 _parser.reportError(Constants.FATAL, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e)); 431 return false; 432 } 433 } 434 435 /** 436 * Compiles an XSL stylesheet passed in through an InputStream 437 * @param stream An InputStream that will pass in the stylesheet contents 438 * @param name The name of the translet class to generate 439 * @return 'true' if the compilation was successful 440 */ 441 public boolean compile(InputStream stream, String name) { 442 final InputSource input = new InputSource(stream); 443 input.setSystemId(name); // We have nothing else!!! 444 return compile(input, name); 445 } 446 447 /** 448 * Compiles an XSL stylesheet passed in through an InputStream 449 * @param input An InputSource that will pass in the stylesheet contents 450 * @param name The name of the translet class to generate - can be null 451 * @return 'true' if the compilation was successful 452 */ 453 public boolean compile(InputSource input, String name) { 454 try { 455 // Reset globals in case we're called by compile(Vector v); 456 reset(); 457 458 // The systemId may not be set, so we'll have to check the URL 459 String systemId = null; 460 if (input != null) { 461 systemId = input.getSystemId(); 462 } 463 464 // Set the translet class name if not already set 465 if (_className == null) { 466 if (name != null) { 467 setClassName(name); 468 } 469 else if (systemId != null && !systemId.equals("")) { 470 setClassName(Util.baseName(systemId)); 471 } 472 473 // Ensure we have a non-empty class name at this point 474 if (_className == null || _className.length() == 0) { 475 setClassName("GregorSamsa"); // default translet name 476 } 477 } 478 479 // Get the root node of the abstract syntax tree 480 SyntaxTreeNode element = null; 481 if (_reader == null) { 482 element = _parser.parse(input); 483 } 484 else { 485 element = _parser.parse(_reader, input); 486 } 487 488 // Compile the translet - this is where the work is done! 489 if ((!_parser.errorsFound()) && (element != null)) { 490 // Create a Stylesheet element from the root node 491 _stylesheet = _parser.makeStylesheet(element); 492 _stylesheet.setSourceLoader(_loader); 493 _stylesheet.setSystemId(systemId); 494 _stylesheet.setParentStylesheet(null); 495 _stylesheet.setTemplateInlining(_templateInlining); 496 _parser.setCurrentStylesheet(_stylesheet); 497 498 // Create AST under the Stylesheet element (parse & type-check) 499 _parser.createAST(_stylesheet); 500 } 501 // Generate the bytecodes and output the translet class(es) 502 if ((!_parser.errorsFound()) && (_stylesheet != null)) { 503 _stylesheet.setCallsNodeset(_callsNodeset); 504 _stylesheet.setMultiDocument(_multiDocument); 505 _stylesheet.setHasIdCall(_hasIdCall); 506 507 // Class synchronization is needed for BCEL 508 synchronized (getClass()) { 509 _stylesheet.translate(); 510 } 511 } 512 } 513 catch (Exception e) { 514 /*if (_debug)*/ e.printStackTrace(); 515 _parser.reportError(Constants.FATAL, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e)); 516 } 517 catch (Error e) { 518 if (_debug) e.printStackTrace(); 519 _parser.reportError(Constants.FATAL, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e)); 520 } 521 finally { 522 _reader = null; // reset this here to be sure it is not re-used 523 } 524 return !_parser.errorsFound(); 525 } 526 527 /** 528 * Compiles a set of stylesheets pointed to by a Vector of URLs 529 * @param stylesheets A Vector containing URLs pointing to the stylesheets 530 * @return 'true' if the compilation was successful 531 */ 532 public boolean compile(Vector stylesheets) { 533 // Get the number of stylesheets (ie. URLs) in the vector 534 final int count = stylesheets.size(); 535 536 // Return straight away if the vector is empty 537 if (count == 0) return true; 538 539 // Special handling needed if the URL count is one, becuase the 540 // _className global must not be reset if it was set explicitly 541 if (count == 1) { 542 final Object url = stylesheets.firstElement(); 543 if (url instanceof URL) 544 return compile((URL)url); 545 else 546 return false; 547 } 548 else { 549 // Traverse all elements in the vector and compile 550 final Enumeration urls = stylesheets.elements(); 551 while (urls.hasMoreElements()) { 552 _className = null; // reset, so that new name will be computed 553 final Object url = urls.nextElement(); 554 if (url instanceof URL) { 555 if (!compile((URL)url)) return false; 556 } 557 } 558 } 559 return true; 560 } 561 562 /** 563 * Returns an array of bytecode arrays generated by a compilation. 564 * @return JVM bytecodes that represent translet class definition 565 */ 566 public byte[][] getBytecodes() { 567 final int count = _classes.size(); 568 final byte[][] result = new byte[count][1]; 569 for (int i = 0; i < count; i++) 570 result[i] = (byte[])_classes.elementAt(i); 571 return result; 572 } 573 574 /** 575 * Compiles a stylesheet pointed to by a URL. The result is put in a 576 * set of byte arrays. One byte array for each generated class. 577 * @param name The name of the translet class to generate 578 * @param input An InputSource that will pass in the stylesheet contents 579 * @param outputType The output type 580 * @return JVM bytecodes that represent translet class definition 581 */ 582 public byte[][] compile(String name, InputSource input, int outputType) { 583 _outputType = outputType; 584 if (compile(input, name)) 585 return getBytecodes(); 586 else 587 return null; 588 } 589 590 /** 591 * Compiles a stylesheet pointed to by a URL. The result is put in a 592 * set of byte arrays. One byte array for each generated class. 593 * @param name The name of the translet class to generate 594 * @param input An InputSource that will pass in the stylesheet contents 595 * @return JVM bytecodes that represent translet class definition 596 */ 597 public byte[][] compile(String name, InputSource input) { 598 return compile(name, input, BYTEARRAY_OUTPUT); 599 } 600 601 /** 602 * Set the XMLReader to use for parsing the next input stylesheet 603 * @param reader XMLReader (SAX2 parser) to use 604 */ 605 public void setXMLReader(XMLReader reader) { 606 _reader = reader; 607 } 608 609 /** 610 * Get the XMLReader to use for parsing the next input stylesheet 611 */ 612 public XMLReader getXMLReader() { 613 return _reader ; 614 } 615 616 /** 617 * Get a Vector containing all compile error messages 618 * @return A Vector containing all compile error messages 619 */ 620 public ArrayList<ErrorMsg> getErrors() { 621 return _parser.getErrors(); 622 } 623 624 /** 625 * Get a Vector containing all compile warning messages 626 * @return A Vector containing all compile error messages 627 */ 628 public ArrayList<ErrorMsg> getWarnings() { 629 return _parser.getWarnings(); 630 } 631 632 /** 633 * Print all compile error messages to standard output 634 */ 635 public void printErrors() { 636 _parser.printErrors(); 637 } 638 639 /** 640 * Print all compile warning messages to standard output 641 */ 642 public void printWarnings() { 643 _parser.printWarnings(); 644 } 645 646 /** 647 * This method is called by the XPathParser when it encounters a call 648 * to the document() function. Affects the DOM used by the translet. 649 */ 650 protected void setMultiDocument(boolean flag) { 651 _multiDocument = flag; 652 } 653 654 public boolean isMultiDocument() { 655 return _multiDocument; 656 } 657 658 /** 659 * This method is called by the XPathParser when it encounters a call 660 * to the nodeset() extension function. Implies multi document. 661 */ 662 protected void setCallsNodeset(boolean flag) { 663 if (flag) setMultiDocument(flag); 664 _callsNodeset = flag; 665 } 666 667 public boolean callsNodeset() { 668 return _callsNodeset; 669 } 670 671 protected void setHasIdCall(boolean flag) { 672 _hasIdCall = flag; 673 } 674 675 public boolean hasIdCall() { 676 return _hasIdCall; 677 } 678 679 /** 680 * Set the class name for the generated translet. This class name is 681 * overridden if multiple stylesheets are compiled in one go using the 682 * compile(Vector urls) method. 683 * @param className The name to assign to the translet class 684 */ 685 public void setClassName(String className) { 686 final String base = Util.baseName(className); 687 final String noext = Util.noExtName(base); 688 String name = Util.toJavaName(noext); 689 690 if (_packageName == null) 691 _className = name; 692 else 693 _className = _packageName + '.' + name; 694 } 695 696 /** 697 * Get the class name for the generated translet. 698 */ 699 public String getClassName() { 700 return _className; 701 } 702 703 /** 704 * Convert for Java class name of local system file name. 705 * (Replace '.' with '/' on UNIX and replace '.' by '\' on Windows/DOS.) 706 */ 707 private String classFileName(final String className) { 708 return className.replace('.', File.separatorChar) + ".class"; 709 } 710 711 /** 712 * Generate an output File object to send the translet to 713 */ 714 private File getOutputFile(String className) { 715 if (_destDir != null) 716 return new File(_destDir, classFileName(className)); 717 else 718 return new File(classFileName(className)); 719 } 720 721 /** 722 * Set the destination directory for the translet. 723 * The current working directory will be used by default. 724 */ 725 public boolean setDestDirectory(String dstDirName) { 726 final File dir = new File(dstDirName); 727 if (SecuritySupport.getFileExists(dir) || dir.mkdirs()) { 728 _destDir = dir; 729 return true; 730 } 731 else { 732 _destDir = null; 733 return false; 734 } 735 } 736 737 /** 738 * Set an optional package name for the translet and auxiliary classes 739 */ 740 public void setPackageName(String packageName) { 741 _packageName = Objects.requireNonNull(packageName); 742 if (_className != null) setClassName(_className); 743 } 744 745 /** 746 * Set the name of an optional JAR-file to dump the translet and 747 * auxiliary classes to 748 */ 749 public void setJarFileName(String jarFileName) { 750 final String JAR_EXT = ".jar"; 751 if (jarFileName.endsWith(JAR_EXT)) 752 _jarFileName = jarFileName; 753 else 754 _jarFileName = jarFileName + JAR_EXT; 755 _outputType = JAR_OUTPUT; 756 } 757 758 public String getJarFileName() { 759 return _jarFileName; 760 } 761 762 /** 763 * Set the top-level stylesheet 764 */ 765 public void setStylesheet(Stylesheet stylesheet) { 766 if (_stylesheet == null) _stylesheet = stylesheet; 767 } 768 769 /** 770 * Returns the top-level stylesheet 771 */ 772 public Stylesheet getStylesheet() { 773 return _stylesheet; 774 } 775 776 /** 777 * Registers an attribute and gives it a type so that it can be mapped to 778 * DOM attribute types at run-time. 779 */ 780 public int registerAttribute(QName name) { 781 Integer code = _attributes.get(name.toString()); 782 if (code == null) { 783 code = _nextGType++; 784 _attributes.put(name.toString(), code); 785 final String uri = name.getNamespace(); 786 final String local = "@"+name.getLocalPart(); 787 if ((uri != null) && (!uri.equals(""))) 788 _namesIndex.addElement(uri+":"+local); 789 else 790 _namesIndex.addElement(local); 791 if (name.getLocalPart().equals("*")) { 792 registerNamespace(name.getNamespace()); 793 } 794 } 795 return code.intValue(); 796 } 797 798 /** 799 * Registers an element and gives it a type so that it can be mapped to 800 * DOM element types at run-time. 801 */ 802 public int registerElement(QName name) { 803 // Register element (full QName) 804 Integer code = _elements.get(name.toString()); 805 if (code == null) { 806 _elements.put(name.toString(), code = _nextGType++); 807 _namesIndex.addElement(name.toString()); 808 } 809 if (name.getLocalPart().equals("*")) { 810 registerNamespace(name.getNamespace()); 811 } 812 return code.intValue(); 813 } 814 815 /** 816 * Registers a namespace prefix and gives it a type so that it can be mapped to 817 * DOM namespace types at run-time. 818 */ 819 820 public int registerNamespacePrefix(QName name) { 821 822 Integer code = _namespacePrefixes.get(name.toString()); 823 if (code == null) { 824 code = _nextGType++; 825 _namespacePrefixes.put(name.toString(), code); 826 final String uri = name.getNamespace(); 827 if ((uri != null) && (!uri.equals(""))){ 828 // namespace::ext2:ped2 will be made empty in TypedNamespaceIterator 829 _namesIndex.addElement("?"); 830 } else{ 831 _namesIndex.addElement("?"+name.getLocalPart()); 832 } 833 } 834 return code.intValue(); 835 } 836 837 /** 838 * Registers a namespace and gives it a type so that it can be mapped to 839 * DOM namespace types at run-time. 840 */ 841 public int registerNamespace(String namespaceURI) { 842 Integer code = _namespaces.get(namespaceURI); 843 if (code == null) { 844 code = _nextNSType++; 845 _namespaces.put(namespaceURI,code); 846 _namespaceIndex.addElement(namespaceURI); 847 } 848 return code.intValue(); 849 } 850 851 public int nextModeSerial() { 852 return _modeSerial++; 853 } 854 855 public int nextStylesheetSerial() { 856 return _stylesheetSerial++; 857 } 858 859 public int nextStepPatternSerial() { 860 return _stepPatternSerial++; 861 } 862 863 public int[] getNumberFieldIndexes() { 864 return _numberFieldIndexes; 865 } 866 867 public int nextHelperClassSerial() { 868 return _helperClassSerial++; 869 } 870 871 public int nextAttributeSetSerial() { 872 return _attributeSetSerial++; 873 } 874 875 public Vector getNamesIndex() { 876 return _namesIndex; 877 } 878 879 public Vector getNamespaceIndex() { 880 return _namespaceIndex; 881 } 882 883 /** 884 * Returns a unique name for every helper class needed to 885 * execute a translet. 886 */ 887 public String getHelperClassName() { 888 return getClassName() + '$' + _helperClassSerial++; 889 } 890 891 public void dumpClass(JavaClass clazz) { 892 893 if (_outputType == FILE_OUTPUT || 894 _outputType == BYTEARRAY_AND_FILE_OUTPUT) 895 { 896 File outFile = getOutputFile(clazz.getClassName()); 897 String parentDir = outFile.getParent(); 898 if (parentDir != null) { 899 File parentFile = new File(parentDir); 900 if (!SecuritySupport.getFileExists(parentFile)) 901 parentFile.mkdirs(); 902 } 903 } 904 905 try { 906 switch (_outputType) { 907 case FILE_OUTPUT: 908 clazz.dump( 909 new BufferedOutputStream( 910 new FileOutputStream( 911 getOutputFile(clazz.getClassName())))); 912 break; 913 case JAR_OUTPUT: 914 _bcelClasses.addElement(clazz); 915 break; 916 case BYTEARRAY_OUTPUT: 917 case BYTEARRAY_AND_FILE_OUTPUT: 918 case BYTEARRAY_AND_JAR_OUTPUT: 919 case CLASSLOADER_OUTPUT: 920 ByteArrayOutputStream out = new ByteArrayOutputStream(2048); 921 clazz.dump(out); 922 _classes.addElement(out.toByteArray()); 923 924 if (_outputType == BYTEARRAY_AND_FILE_OUTPUT) 925 clazz.dump(new BufferedOutputStream( 926 new FileOutputStream(getOutputFile(clazz.getClassName())))); 927 else if (_outputType == BYTEARRAY_AND_JAR_OUTPUT) 928 _bcelClasses.addElement(clazz); 929 930 break; 931 } 932 } 933 catch (Exception e) { 934 e.printStackTrace(); 935 } 936 } 937 938 /** 939 * File separators are converted to forward slashes for ZIP files. 940 */ 941 private String entryName(File f) throws IOException { 942 return f.getName().replace(File.separatorChar, '/'); 943 } 944 945 /** 946 * Generate output JAR-file and packages 947 */ 948 public void outputToJar() throws IOException { 949 // create the manifest 950 final Manifest manifest = new Manifest(); 951 final java.util.jar.Attributes atrs = manifest.getMainAttributes(); 952 atrs.put(java.util.jar.Attributes.Name.MANIFEST_VERSION,"1.2"); 953 954 final Map map = manifest.getEntries(); 955 // create manifest 956 Enumeration classes = _bcelClasses.elements(); 957 final String now = (new Date()).toString(); 958 final java.util.jar.Attributes.Name dateAttr = 959 new java.util.jar.Attributes.Name("Date"); 960 while (classes.hasMoreElements()) { 961 final JavaClass clazz = (JavaClass)classes.nextElement(); 962 final String className = clazz.getClassName().replace('.','/'); 963 final java.util.jar.Attributes attr = new java.util.jar.Attributes(); 964 attr.put(dateAttr, now); 965 map.put(className+".class", attr); 966 } 967 968 final File jarFile = new File(_destDir, _jarFileName); 969 final JarOutputStream jos = 970 new JarOutputStream(new FileOutputStream(jarFile), manifest); 971 classes = _bcelClasses.elements(); 972 while (classes.hasMoreElements()) { 973 final JavaClass clazz = (JavaClass)classes.nextElement(); 974 final String className = clazz.getClassName().replace('.','/'); 975 jos.putNextEntry(new JarEntry(className+".class")); 976 final ByteArrayOutputStream out = new ByteArrayOutputStream(2048); 977 clazz.dump(out); // dump() closes it's output stream 978 out.writeTo(jos); 979 } 980 jos.close(); 981 } 982 983 /** 984 * Turn debugging messages on/off 985 */ 986 public void setDebug(boolean debug) { 987 _debug = debug; 988 } 989 990 /** 991 * Get current debugging message setting 992 */ 993 public boolean debug() { 994 return _debug; 995 } 996 997 998 /** 999 * Retrieve a string representation of the character data to be stored 1000 * in the translet as a <code>char[]</code>. There may be more than 1001 * one such array required. 1002 * @param index The index of the <code>char[]</code>. Zero-based. 1003 * @return String The character data to be stored in the corresponding 1004 * <code>char[]</code>. 1005 */ 1006 public String getCharacterData(int index) { 1007 return (m_characterData.get(index)).toString(); 1008 } 1009 1010 /** 1011 * Get the number of char[] arrays, thus far, that will be created to 1012 * store literal text in the stylesheet. 1013 */ 1014 public int getCharacterDataCount() { 1015 return (m_characterData != null) ? m_characterData.size() : 0; 1016 } 1017 1018 /** 1019 * Add literal text to char arrays that will be used to store character 1020 * data in the stylesheet. 1021 * @param newData String data to be added to char arrays. 1022 * Pre-condition: <code>newData.length() ≤ 21845</code> 1023 * @return int offset at which character data will be stored 1024 */ 1025 public int addCharacterData(String newData) { 1026 StringBuilder currData; 1027 if (m_characterData == null) { 1028 m_characterData = new ArrayList<>(); 1029 currData = new StringBuilder(); 1030 m_characterData.add(currData); 1031 } else { 1032 currData = m_characterData.get(m_characterData.size()-1); 1033 } 1034 1035 // Character data could take up to three-times as much space when 1036 // written to the class file as UTF-8. The maximum size for a 1037 // constant is 65535/3. If we exceed that, 1038 // (We really should use some "bin packing".) 1039 if (newData.length() + currData.length() > 21845) { 1040 currData = new StringBuilder(); 1041 m_characterData.add(currData); 1042 } 1043 1044 int newDataOffset = currData.length(); 1045 currData.append(newData); 1046 1047 return newDataOffset; 1048 } 1049 }