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