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