1 /*
   2  * Copyright (c) 2006, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * @LastModified: Oct 2017
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *     http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 
  22 package com.sun.org.apache.xalan.internal.xsltc.runtime;
  23 
  24 import com.sun.org.apache.xalan.internal.XalanConstants;
  25 import com.sun.org.apache.xalan.internal.utils.FactoryImpl;
  26 import com.sun.org.apache.xalan.internal.xsltc.DOM;
  27 import com.sun.org.apache.xalan.internal.xsltc.DOMCache;
  28 import com.sun.org.apache.xalan.internal.xsltc.DOMEnhancedForDTM;
  29 import com.sun.org.apache.xalan.internal.xsltc.Translet;
  30 import com.sun.org.apache.xalan.internal.xsltc.TransletException;
  31 import com.sun.org.apache.xalan.internal.xsltc.dom.DOMAdapter;
  32 import com.sun.org.apache.xalan.internal.xsltc.dom.KeyIndex;
  33 import com.sun.org.apache.xalan.internal.xsltc.runtime.output.TransletOutputHandlerFactory;
  34 import com.sun.org.apache.xml.internal.dtm.DTM;
  35 import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
  36 import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
  37 import java.io.BufferedOutputStream;
  38 import java.io.File;
  39 import java.io.FileOutputStream;
  40 import java.text.DecimalFormat;
  41 import java.text.DecimalFormatSymbols;
  42 import java.util.ArrayList;
  43 import java.util.HashMap;
  44 import java.util.List;
  45 import java.util.Map;
  46 import javax.xml.parsers.DocumentBuilderFactory;
  47 import javax.xml.parsers.ParserConfigurationException;
  48 import javax.xml.transform.Templates;
  49 import org.w3c.dom.DOMImplementation;
  50 import org.w3c.dom.Document;
  51 
  52 /**
  53  * @author Jacek Ambroziak
  54  * @author Santiago Pericas-Geertsen
  55  * @author Morten Jorgensen
  56  * @author G. Todd Miller
  57  * @author John Howard, JohnH@schemasoft.com
  58  */
  59 public abstract class AbstractTranslet implements Translet {
  60 
  61     // These attributes are extracted from the xsl:output element. They also
  62     // appear as fields (with the same type, only public) in Output.java
  63     public String  _version = "1.0";
  64     public String  _method = null;
  65     public String  _encoding = "UTF-8";
  66     public boolean _omitHeader = false;
  67     public String  _standalone = null;
  68     //see OutputPropertiesFactory.ORACLE_IS_STANDALONE
  69     public boolean  _isStandalone = false;
  70     public String  _doctypePublic = null;
  71     public String  _doctypeSystem = null;
  72     public boolean _indent = false;
  73     public String  _mediaType = null;
  74     public List<String> _cdata = null;
  75     public int _indentamount = -1;
  76 
  77     public static final int FIRST_TRANSLET_VERSION = 100;
  78     public static final int VER_SPLIT_NAMES_ARRAY = 101;
  79     public static final int CURRENT_TRANSLET_VERSION = VER_SPLIT_NAMES_ARRAY;
  80 
  81     // Initialize Translet version field to base value.  A class that extends
  82     // AbstractTranslet may override this value to a more recent translet
  83     // version; if it doesn't override the value (because it was compiled
  84     // before the notion of a translet version was introduced, it will get
  85     // this default value).
  86     protected int transletVersion = FIRST_TRANSLET_VERSION;
  87 
  88     // DOM/translet handshaking - the arrays are set by the compiled translet
  89     protected String[] namesArray;
  90     protected String[] urisArray;
  91     protected int[]    typesArray;
  92     protected String[] namespaceArray;
  93 
  94     // The Templates object that is used to create this Translet instance
  95     protected Templates _templates = null;
  96 
  97     // Boolean flag to indicate whether this translet has id functions.
  98     protected boolean _hasIdCall = false;
  99 
 100     // TODO - these should only be instanciated when needed
 101     protected StringValueHandler stringValueHandler = new StringValueHandler();
 102 
 103     // Use one empty string instead of constantly instanciating String("");
 104     private final static String EMPTYSTRING = "";
 105 
 106     // This is the name of the index used for ID attributes
 107     private final static String ID_INDEX_NAME = "##id";
 108 
 109     private boolean _useServicesMechanism;
 110 
 111     // The OutputStream for redirect function
 112     private FileOutputStream output = null;
 113 
 114     /**
 115      * protocols allowed for external references set by the stylesheet processing instruction, Document() function, Import and Include element.
 116      */
 117     private String _accessExternalStylesheet = XalanConstants.EXTERNAL_ACCESS_DEFAULT;
 118 
 119     /************************************************************************
 120      * Debugging
 121      ************************************************************************/
 122     public void printInternalState() {
 123         System.out.println("-------------------------------------");
 124         System.out.println("AbstractTranslet this = " + this);
 125         System.out.println("pbase = " + pbase);
 126         System.out.println("vframe = " + pframe);
 127         System.out.println("paramsStack.size() = " + paramsStack.size());
 128         System.out.println("namesArray.size = " + namesArray.length);
 129         System.out.println("namespaceArray.size = " + namespaceArray.length);
 130         System.out.println("");
 131         System.out.println("Total memory = " + Runtime.getRuntime().totalMemory());
 132     }
 133 
 134     /**
 135      * Wrap the initial input DOM in a dom adapter. This adapter is wrapped in
 136      * a DOM multiplexer if the document() function is used (handled by compiled
 137      * code in the translet - see compiler/Stylesheet.compileTransform()).
 138      */
 139     public final DOMAdapter makeDOMAdapter(DOM dom)
 140         throws TransletException {
 141         setRootForKeys(dom.getDocument());
 142         return new DOMAdapter(dom, namesArray, urisArray, typesArray, namespaceArray);
 143     }
 144 
 145     /************************************************************************
 146      * Parameter handling
 147      ************************************************************************/
 148 
 149     // Parameter's stack: <tt>pbase</tt> and <tt>pframe</tt> are used
 150     // to denote the current parameter frame.
 151     protected int pbase = 0, pframe = 0;
 152     protected List<Object> paramsStack = new ArrayList<>();
 153 
 154     /**
 155      * Push a new parameter frame.
 156      */
 157     public final void pushParamFrame() {
 158         paramsStack.add(pframe, pbase);
 159         pbase = ++pframe;
 160     }
 161 
 162     /**
 163      * Pop the topmost parameter frame.
 164      */
 165     public final void popParamFrame() {
 166         if (pbase > 0) {
 167             final int oldpbase = ((Integer)paramsStack.get(--pbase));
 168             for (int i = pframe - 1; i >= pbase; i--) {
 169                 paramsStack.remove(i);
 170             }
 171             pframe = pbase; pbase = oldpbase;
 172         }
 173     }
 174 
 175     /**
 176      * Add a new global parameter if not already in the current frame.
 177      * To setParameters of the form {http://foo.bar}xyz
 178      * This needs to get mapped to an instance variable in the class
 179      * The mapping  created so that
 180      * the global variables in the generated class become
 181      * http$colon$$flash$$flash$foo$dot$bar$colon$xyz
 182      */
 183     public final Object addParameter(String name, Object value) {
 184         name = BasisLibrary.mapQNameToJavaName (name);
 185         return addParameter(name, value, false);
 186     }
 187 
 188     /**
 189      * Add a new global or local parameter if not already in the current frame.
 190      * The 'isDefault' parameter is set to true if the value passed is the
 191      * default value from the <xsl:parameter> element's select attribute or
 192      * element body.
 193      */
 194     public final Object addParameter(String name, Object value,
 195         boolean isDefault)
 196     {
 197         // Local parameters need to be re-evaluated for each iteration
 198         for (int i = pframe - 1; i >= pbase; i--) {
 199             final Parameter param = (Parameter) paramsStack.get(i);
 200 
 201             if (param._name.equals(name)) {
 202                 // Only overwrite if current value is the default value and
 203                 // the new value is _NOT_ the default value.
 204                 if (param._isDefault || !isDefault) {
 205                     param._value = value;
 206                     param._isDefault = isDefault;
 207                     return value;
 208                 }
 209                 return param._value;
 210             }
 211         }
 212 
 213         // Add new parameter to parameter stack
 214         paramsStack.add(pframe++, new Parameter(name, value, isDefault));
 215         return value;
 216     }
 217 
 218     /**
 219      * Clears the parameter stack.
 220      */
 221     public void clearParameters() {
 222         pbase = pframe = 0;
 223         paramsStack.clear();
 224     }
 225 
 226     /**
 227      * Get the value of a parameter from the current frame or
 228      * <tt>null</tt> if undefined.
 229      */
 230     public final Object getParameter(String name) {
 231 
 232         name = BasisLibrary.mapQNameToJavaName (name);
 233 
 234         for (int i = pframe - 1; i >= pbase; i--) {
 235             final Parameter param = (Parameter)paramsStack.get(i);
 236             if (param._name.equals(name)) return param._value;
 237         }
 238         return null;
 239     }
 240 
 241     /************************************************************************
 242      * Message handling - implementation of <xsl:message>
 243      ************************************************************************/
 244 
 245     // Holds the translet's message handler - used for <xsl:message>.
 246     // The deault message handler dumps a string stdout, but anything can be
 247     // used, such as a dialog box for applets, etc.
 248     private MessageHandler _msgHandler = null;
 249 
 250     /**
 251      * Set the translet's message handler - must implement MessageHandler
 252      */
 253     public final void setMessageHandler(MessageHandler handler) {
 254         _msgHandler = handler;
 255     }
 256 
 257     /**
 258      * Pass a message to the message handler - used by Message class.
 259      */
 260     public final void displayMessage(String msg) {
 261         if (_msgHandler == null) {
 262             System.err.println(msg);
 263         }
 264         else {
 265             _msgHandler.displayMessage(msg);
 266         }
 267     }
 268 
 269     /************************************************************************
 270      * Decimal number format symbol handling
 271      ************************************************************************/
 272 
 273     // Contains decimal number formatting symbols used by FormatNumberCall
 274     public Map<String, DecimalFormat> _formatSymbols = null;
 275 
 276     /**
 277      * Adds a DecimalFormat object to the _formatSymbols map.
 278      * The entry is created with the input DecimalFormatSymbols.
 279      */
 280     public void addDecimalFormat(String name, DecimalFormatSymbols symbols) {
 281         // Instanciate map for formatting symbols if needed
 282         if (_formatSymbols == null) _formatSymbols = new HashMap<>();
 283 
 284         // The name cannot be null - use empty string instead
 285         if (name == null) name = EMPTYSTRING;
 286 
 287         // Construct a DecimalFormat object containing the symbols we got
 288         final DecimalFormat df = new DecimalFormat();
 289         if (symbols != null) {
 290             df.setDecimalFormatSymbols(symbols);
 291         }
 292         _formatSymbols.put(name, df);
 293     }
 294 
 295     /**
 296      * Retrieves a named DecimalFormat object from the _formatSymbols map.
 297      */
 298     public final DecimalFormat getDecimalFormat(String name) {
 299 
 300         if (_formatSymbols != null) {
 301             // The name cannot be null - use empty string instead
 302             if (name == null) name = EMPTYSTRING;
 303 
 304             DecimalFormat df = _formatSymbols.get(name);
 305             if (df == null) df = _formatSymbols.get(EMPTYSTRING);
 306             return df;
 307         }
 308         return(null);
 309     }
 310 
 311     /**
 312      * Give the translet an opportunity to perform a prepass on the document
 313      * to extract any information that it can store in an optimized form.
 314      *
 315      * Currently, it only extracts information about attributes of type ID.
 316      */
 317     public final void prepassDocument(DOM document) {
 318         setIndexSize(document.getSize());
 319         buildIDIndex(document);
 320     }
 321 
 322     /**
 323      * Leverages the Key Class to implement the XSLT id() function.
 324      * buildIdIndex creates the index (##id) that Key Class uses.
 325      * The index contains the element node index (int) and Id value (String).
 326      */
 327     private final void buildIDIndex(DOM document) {
 328         setRootForKeys(document.getDocument());
 329 
 330         if (document instanceof DOMEnhancedForDTM) {
 331             DOMEnhancedForDTM enhancedDOM = (DOMEnhancedForDTM)document;
 332 
 333             // If the input source is DOMSource, the KeyIndex table is not
 334             // built at this time. It will be built later by the lookupId()
 335             // and containsId() methods of the KeyIndex class.
 336             if (enhancedDOM.hasDOMSource()) {
 337                 buildKeyIndex(ID_INDEX_NAME, document);
 338                 return;
 339             }
 340             else {
 341                 final Map<String, Integer> elementsByID = enhancedDOM.getElementsWithIDs();
 342 
 343                 if (elementsByID == null) {
 344                     return;
 345                 }
 346 
 347                 // Given a Map of DTM nodes indexed by ID attribute values,
 348                 // loop through the table copying information to a KeyIndex
 349                 // for the mapping from ID attribute value to DTM node
 350                 boolean hasIDValues = false;
 351                 for (Map.Entry<String, Integer> entry : elementsByID.entrySet()) {
 352                     final int element = document.getNodeHandle(entry.getValue());
 353                     buildKeyIndex(ID_INDEX_NAME, element, entry.getKey());
 354                     hasIDValues = true;
 355                 }
 356 
 357                 if (hasIDValues) {
 358                     setKeyIndexDom(ID_INDEX_NAME, document);
 359                 }
 360             }
 361         }
 362     }
 363 
 364     /**
 365      * After constructing the translet object, this method must be called to
 366      * perform any version-specific post-initialization that's required.
 367      */
 368     public final void postInitialization() {
 369         // If the version of the translet had just one namesArray, split
 370         // it into multiple fields.
 371         if (transletVersion < VER_SPLIT_NAMES_ARRAY) {
 372             int arraySize = namesArray.length;
 373             String[] newURIsArray = new String[arraySize];
 374             String[] newNamesArray = new String[arraySize];
 375             int[] newTypesArray = new int[arraySize];
 376 
 377             for (int i = 0; i < arraySize; i++) {
 378                 String name = namesArray[i];
 379                 int colonIndex = name.lastIndexOf(':');
 380                 int lNameStartIdx = colonIndex+1;
 381 
 382                 if (colonIndex > -1) {
 383                     newURIsArray[i] = name.substring(0, colonIndex);
 384                 }
 385 
 386                // Distinguish attribute and element names.  Attribute has
 387                // @ before local part of name.
 388                if (name.charAt(lNameStartIdx) == '@') {
 389                    lNameStartIdx++;
 390                    newTypesArray[i] = DTM.ATTRIBUTE_NODE;
 391                } else if (name.charAt(lNameStartIdx) == '?') {
 392                    lNameStartIdx++;
 393                    newTypesArray[i] = DTM.NAMESPACE_NODE;
 394                } else {
 395                    newTypesArray[i] = DTM.ELEMENT_NODE;
 396                }
 397                newNamesArray[i] =
 398                           (lNameStartIdx == 0) ? name
 399                                                : name.substring(lNameStartIdx);
 400             }
 401 
 402             namesArray = newNamesArray;
 403             urisArray  = newURIsArray;
 404             typesArray = newTypesArray;
 405         }
 406 
 407         // Was translet compiled using a more recent version of the XSLTC
 408         // compiler than is known by the AbstractTranslet class?  If, so
 409         // and we've made it this far (which is doubtful), we should give up.
 410         if (transletVersion > CURRENT_TRANSLET_VERSION) {
 411             BasisLibrary.runTimeError(BasisLibrary.UNKNOWN_TRANSLET_VERSION_ERR,
 412                                       this.getClass().getName());
 413         }
 414     }
 415 
 416     /************************************************************************
 417      * Index(es) for <xsl:key> / key() / id()
 418      ************************************************************************/
 419 
 420     // Container for all indexes for xsl:key elements
 421     private Map<String, KeyIndex> _keyIndexes = null;
 422     private KeyIndex  _emptyKeyIndex = null;
 423     private int       _indexSize = 0;
 424     private int       _currentRootForKeys = 0;
 425 
 426     /**
 427      * This method is used to pass the largest DOM size to the translet.
 428      * Needed to make sure that the translet can index the whole DOM.
 429      */
 430     public void setIndexSize(int size) {
 431         if (size > _indexSize) _indexSize = size;
 432     }
 433 
 434     /**
 435      * Creates a KeyIndex object of the desired size - don't want to resize!!!
 436      */
 437     public KeyIndex createKeyIndex() {
 438         return(new KeyIndex(_indexSize));
 439     }
 440 
 441     /**
 442      * Adds a value to a key/id index
 443      *   @param name is the name of the index (the key or ##id)
 444      *   @param node is the node handle of the node to insert
 445      *   @param value is the value that will look up the node in the given index
 446      */
 447     public void buildKeyIndex(String name, int node, String value) {
 448         KeyIndex index = buildKeyIndexHelper(name);
 449         index.add(value, node, _currentRootForKeys);
 450     }
 451 
 452     /**
 453      * Create an empty KeyIndex in the DOM case
 454      *   @param name is the name of the index (the key or ##id)
 455      *   @param dom is the DOM
 456      */
 457     public void buildKeyIndex(String name, DOM dom) {
 458         KeyIndex index = buildKeyIndexHelper(name);
 459         index.setDom(dom, dom.getDocument());
 460     }
 461 
 462     /**
 463      * Return KeyIndex for the buildKeyIndex methods. Note the difference from the
 464      * public getKeyIndex method, this method creates a new Map if keyIndexes does
 465      * not exist.
 466      *
 467      * @param name the name of the index (the key or ##id)
 468      * @return a KeyIndex.
 469      */
 470     private KeyIndex buildKeyIndexHelper(String name) {
 471         if (_keyIndexes == null) _keyIndexes = new HashMap<>();
 472 
 473         KeyIndex index = _keyIndexes.get(name);
 474         if (index == null) {
 475             _keyIndexes.put(name, index = new KeyIndex(_indexSize));
 476         }
 477         return index;
 478     }
 479 
 480     /**
 481      * Returns the index for a given key (or id).
 482      * The index implements our internal iterator interface
 483      * @param name the name of the index (the key or ##id)
 484      * @return a KeyIndex.
 485      */
 486     public KeyIndex getKeyIndex(String name) {
 487         // Return an empty key index iterator if none are defined
 488         if (_keyIndexes == null) {
 489             return (_emptyKeyIndex != null)
 490                 ? _emptyKeyIndex
 491                 : (_emptyKeyIndex = new KeyIndex(1));
 492         }
 493 
 494         // Look up the requested key index
 495         final KeyIndex index = _keyIndexes.get(name);
 496 
 497         // Return an empty key index iterator if the requested index not found
 498         if (index == null) {
 499             return (_emptyKeyIndex != null)
 500                 ? _emptyKeyIndex
 501                 : (_emptyKeyIndex = new KeyIndex(1));
 502         }
 503 
 504         return(index);
 505     }
 506 
 507     private void setRootForKeys(int root) {
 508         _currentRootForKeys = root;
 509     }
 510 
 511     /**
 512      * This method builds key indexes - it is overridden in the compiled
 513      * translet in cases where the <xsl:key> element is used
 514      */
 515     public void buildKeys(DOM document, DTMAxisIterator iterator,
 516                           SerializationHandler handler,
 517                           int root) throws TransletException {
 518 
 519     }
 520 
 521     /**
 522      * This method builds key indexes - it is overridden in the compiled
 523      * translet in cases where the <xsl:key> element is used
 524      */
 525     public void setKeyIndexDom(String name, DOM document) {
 526         getKeyIndex(name).setDom(document, document.getDocument());
 527     }
 528 
 529     /************************************************************************
 530      * DOM cache handling
 531      ************************************************************************/
 532 
 533     // Hold the DOM cache (if any) used with this translet
 534     private DOMCache _domCache = null;
 535 
 536     /**
 537      * Sets the DOM cache used for additional documents loaded using the
 538      * document() function.
 539      */
 540     public void setDOMCache(DOMCache cache) {
 541         _domCache = cache;
 542     }
 543 
 544     /**
 545      * Returns the DOM cache used for this translet. Used by the LoadDocument
 546      * class (if present) when the document() function is used.
 547      */
 548     public DOMCache getDOMCache() {
 549         return(_domCache);
 550     }
 551 
 552     /************************************************************************
 553      * Multiple output document extension.
 554      * See compiler/TransletOutput for actual implementation.
 555      ************************************************************************/
 556 
 557     public SerializationHandler openOutputHandler(String filename, boolean append)
 558         throws TransletException
 559     {
 560         try {
 561             final TransletOutputHandlerFactory factory
 562                 = TransletOutputHandlerFactory.newInstance();
 563 
 564             String dirStr = new File(filename).getParent();
 565             if ((null != dirStr) && (dirStr.length() > 0)) {
 566                File dir = new File(dirStr);
 567                dir.mkdirs();
 568             }
 569 
 570             output = new FileOutputStream(filename, append);
 571             factory.setEncoding(_encoding);
 572             factory.setOutputMethod(_method);
 573             factory.setOutputStream(new BufferedOutputStream(output));
 574             factory.setOutputType(TransletOutputHandlerFactory.STREAM);
 575 
 576             final SerializationHandler handler
 577                 = factory.getSerializationHandler();
 578 
 579             transferOutputSettings(handler);
 580             handler.startDocument();
 581             return handler;
 582         }
 583         catch (Exception e) {
 584             throw new TransletException(e);
 585         }
 586     }
 587 
 588     public SerializationHandler openOutputHandler(String filename)
 589        throws TransletException
 590     {
 591        return openOutputHandler(filename, false);
 592     }
 593 
 594     public void closeOutputHandler(SerializationHandler handler) {
 595         try {
 596             handler.endDocument();
 597             handler.close();
 598             if (output != null) {
 599                 output.close();
 600             }
 601         }
 602         catch (Exception e) {
 603             // what can you do?
 604         }
 605     }
 606 
 607     /************************************************************************
 608      * Native API transformation methods - _NOT_ JAXP/TrAX
 609      ************************************************************************/
 610 
 611     /**
 612      * Main transform() method - this is overridden by the compiled translet
 613      */
 614     public abstract void transform(DOM document, DTMAxisIterator iterator,
 615                                    SerializationHandler handler)
 616         throws TransletException;
 617 
 618     /**
 619      * Calls transform() with a given output handler
 620      */
 621     public final void transform(DOM document, SerializationHandler handler)
 622         throws TransletException {
 623         try {
 624             transform(document, document.getIterator(), handler);
 625         } finally {
 626             _keyIndexes = null;
 627         }
 628     }
 629 
 630     /**
 631      * Used by some compiled code as a shortcut for passing strings to the
 632      * output handler
 633      */
 634     public final void characters(final String string,
 635                                  SerializationHandler handler)
 636         throws TransletException {
 637         if (string != null) {
 638            //final int length = string.length();
 639            try {
 640                handler.characters(string);
 641            } catch (Exception e) {
 642                throw new TransletException(e);
 643            }
 644         }
 645     }
 646 
 647     /**
 648      * Add's a name of an element whose text contents should be output as CDATA
 649      */
 650     public void addCdataElement(String name) {
 651         if (_cdata == null) {
 652             _cdata = new ArrayList<>();
 653         }
 654 
 655         int lastColon = name.lastIndexOf(':');
 656 
 657         if (lastColon > 0) {
 658             String uri = name.substring(0, lastColon);
 659             String localName = name.substring(lastColon+1);
 660             _cdata.add(uri);
 661             _cdata.add(localName);
 662         } else {
 663             _cdata.add(null);
 664             _cdata.add(name);
 665         }
 666     }
 667 
 668     /**
 669      * Transfer the output settings to the output post-processor
 670      */
 671     protected void transferOutputSettings(SerializationHandler handler) {
 672         if (_method != null) {
 673             if (_method.equals("xml")) {
 674                 if (_standalone != null) {
 675                     handler.setStandalone(_standalone);
 676                 }
 677                 if (_omitHeader) {
 678                     handler.setOmitXMLDeclaration(true);
 679                 }
 680                 handler.setCdataSectionElements(_cdata);
 681                 if (_version != null) {
 682                     handler.setVersion(_version);
 683                 }
 684                 handler.setIndent(_indent);
 685                 if (_indentamount >= 0)
 686                     handler.setIndentAmount(_indentamount);
 687                 if (_doctypeSystem != null) {
 688                     handler.setDoctype(_doctypeSystem, _doctypePublic);
 689                 }
 690                 handler.setIsStandalone(_isStandalone);
 691             }
 692             else if (_method.equals("html")) {
 693                 handler.setIndent(_indent);
 694                 handler.setDoctype(_doctypeSystem, _doctypePublic);
 695                 if (_mediaType != null) {
 696                     handler.setMediaType(_mediaType);
 697                 }
 698             }
 699         }
 700         else {
 701             handler.setCdataSectionElements(_cdata);
 702             if (_version != null) {
 703                 handler.setVersion(_version);
 704             }
 705             if (_standalone != null) {
 706                 handler.setStandalone(_standalone);
 707             }
 708             if (_omitHeader) {
 709                 handler.setOmitXMLDeclaration(true);
 710             }
 711             handler.setIndent(_indent);
 712             handler.setDoctype(_doctypeSystem, _doctypePublic);
 713             handler.setIsStandalone(_isStandalone);
 714         }
 715     }
 716 
 717     private Map<String, Class<?>> _auxClasses = null;
 718 
 719     public void addAuxiliaryClass(Class<?> auxClass) {
 720         if (_auxClasses == null) _auxClasses = new HashMap<>();
 721         _auxClasses.put(auxClass.getName(), auxClass);
 722     }
 723 
 724     public void setAuxiliaryClasses(Map<String, Class<?>> auxClasses) {
 725         _auxClasses = auxClasses;
 726     }
 727 
 728     public Class<?> getAuxiliaryClass(String className) {
 729         if (_auxClasses == null) return null;
 730         return((Class)_auxClasses.get(className));
 731     }
 732 
 733     // GTM added (see pg 110)
 734     public String[] getNamesArray() {
 735         return namesArray;
 736     }
 737 
 738     public String[] getUrisArray() {
 739         return urisArray;
 740     }
 741 
 742     public int[] getTypesArray() {
 743         return typesArray;
 744     }
 745 
 746     public String[] getNamespaceArray() {
 747         return namespaceArray;
 748     }
 749 
 750     public boolean hasIdCall() {
 751         return _hasIdCall;
 752     }
 753 
 754     public Templates getTemplates() {
 755         return _templates;
 756     }
 757 
 758     public void setTemplates(Templates templates) {
 759         _templates = templates;
 760     }
 761     /**
 762      * Return the state of the services mechanism feature.
 763      */
 764     public boolean useServicesMechnism() {
 765         return _useServicesMechanism;
 766     }
 767 
 768     /**
 769      * Set the state of the services mechanism feature.
 770      */
 771     public void setServicesMechnism(boolean flag) {
 772         _useServicesMechanism = flag;
 773     }
 774 
 775     /**
 776      * Return allowed protocols for accessing external stylesheet.
 777      */
 778     public String getAllowedProtocols() {
 779         return _accessExternalStylesheet;
 780     }
 781 
 782     /**
 783      * Set allowed protocols for accessing external stylesheet.
 784      */
 785     public void setAllowedProtocols(String protocols) {
 786         _accessExternalStylesheet = protocols;
 787     }
 788 
 789     /************************************************************************
 790      * DOMImplementation caching for basis library
 791      ************************************************************************/
 792     protected DOMImplementation _domImplementation = null;
 793 
 794     public Document newDocument(String uri, String qname)
 795         throws ParserConfigurationException
 796     {
 797         if (_domImplementation == null) {
 798             DocumentBuilderFactory dbf = FactoryImpl.getDOMFactory(_useServicesMechanism);
 799             _domImplementation = dbf.newDocumentBuilder().getDOMImplementation();
 800         }
 801         return _domImplementation.createDocument(uri, qname, null);
 802     }
 803 }