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