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