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