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