1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 2001-2004 The Apache Software Foundation.
   7  *
   8  * Licensed under the Apache License, Version 2.0 (the "License");
   9  * you may not use this file except in compliance with the License.
  10  * 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: AbstractTranslet.java,v 1.6 2006/06/19 19:49:03 spericas Exp $
  22  */
  23 
  24 package com.sun.org.apache.xalan.internal.xsltc.runtime;
  25 
  26 import com.sun.org.apache.xalan.internal.XalanConstants;
  27 import com.sun.org.apache.xalan.internal.utils.FactoryImpl;
  28 import java.io.File;
  29 import java.io.FileOutputStream;
  30 import java.io.BufferedOutputStream;
  31 import java.text.DecimalFormat;
  32 import java.text.DecimalFormatSymbols;
  33 import java.util.ArrayList;
  34 import java.util.Enumeration;
  35 import java.util.Vector;
  36 import javax.xml.transform.Templates;
  37 import javax.xml.parsers.DocumentBuilderFactory;
  38 import org.w3c.dom.Document;
  39 import org.w3c.dom.DOMImplementation;
  40 import javax.xml.parsers.ParserConfigurationException;
  41 
  42 import com.sun.org.apache.xml.internal.dtm.DTM;
  43 
  44 import com.sun.org.apache.xalan.internal.xsltc.DOM;
  45 import com.sun.org.apache.xalan.internal.xsltc.DOMCache;
  46 import com.sun.org.apache.xalan.internal.xsltc.DOMEnhancedForDTM;
  47 import com.sun.org.apache.xalan.internal.xsltc.Translet;
  48 import com.sun.org.apache.xalan.internal.xsltc.TransletException;
  49 import com.sun.org.apache.xalan.internal.xsltc.dom.DOMAdapter;
  50 import com.sun.org.apache.xalan.internal.xsltc.dom.KeyIndex;
  51 import com.sun.org.apache.xalan.internal.xsltc.runtime.output.TransletOutputHandlerFactory;
  52 import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
  53 import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
  54 
  55 /**
  56  * @author Jacek Ambroziak
  57  * @author Santiago Pericas-Geertsen
  58  * @author Morten Jorgensen
  59  * @author G. Todd Miller
  60  * @author John Howard, JohnH@schemasoft.com
  61  */
  62 public abstract class AbstractTranslet implements Translet {
  63 
  64     // These attributes are extracted from the xsl:output element. They also
  65     // appear as fields (with the same type, only public) in Output.java
  66     public String  _version = "1.0";
  67     public String  _method = null;
  68     public String  _encoding = "UTF-8";
  69     public boolean _omitHeader = false;
  70     public String  _standalone = null;
  71     //see OutputPropertiesFactory.ORACLE_IS_STANDALONE
  72     public boolean  _isStandalone = false;
  73     public String  _doctypePublic = null;
  74     public String  _doctypeSystem = null;
  75     public boolean _indent = false;
  76     public String  _mediaType = null;
  77     public Vector _cdata = null;
  78     public int _indentamount = -1;
  79 
  80     public static final int FIRST_TRANSLET_VERSION = 100;
  81     public static final int VER_SPLIT_NAMES_ARRAY = 101;
  82     public static final int CURRENT_TRANSLET_VERSION = VER_SPLIT_NAMES_ARRAY;
  83 
  84     // Initialize Translet version field to base value.  A class that extends
  85     // AbstractTranslet may override this value to a more recent translet
  86     // version; if it doesn't override the value (because it was compiled
  87     // before the notion of a translet version was introduced, it will get
  88     // this default value).
  89     protected int transletVersion = FIRST_TRANSLET_VERSION;
  90 
  91     // DOM/translet handshaking - the arrays are set by the compiled translet
  92     protected String[] namesArray;
  93     protected String[] urisArray;
  94     protected int[]    typesArray;
  95     protected String[] namespaceArray;
  96 
  97     // The Templates object that is used to create this Translet instance
  98     protected Templates _templates = null;
  99 
 100     // Boolean flag to indicate whether this translet has id functions.
 101     protected boolean _hasIdCall = false;
 102 
 103     // TODO - these should only be instanciated when needed
 104     protected StringValueHandler stringValueHandler = new StringValueHandler();
 105 
 106     // Use one empty string instead of constantly instanciating String("");
 107     private final static String EMPTYSTRING = "";
 108 
 109     // This is the name of the index used for ID attributes
 110     private final static String ID_INDEX_NAME = "##id";
 111 
 112     private boolean _useServicesMechanism;
 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 ArrayList paramsStack = new ArrayList();
 153 
 154     /**
 155      * Push a new parameter frame.
 156      */
 157     public final void pushParamFrame() {
 158         paramsStack.add(pframe, new Integer(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)).intValue();
 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 Hashtable _formatSymbols = null;
 275 
 276     /**
 277      * Adds a DecimalFormat object to the _formatSymbols hashtable.
 278      * The entry is created with the input DecimalFormatSymbols.
 279      */
 280     public void addDecimalFormat(String name, DecimalFormatSymbols symbols) {
 281         // Instanciate hashtable for formatting symbols if needed
 282         if (_formatSymbols == null) _formatSymbols = new Hashtable();
 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 _formatSymbols hashtable.
 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 = (DecimalFormat)_formatSymbols.get(name);
 305             if (df == null) df = (DecimalFormat)_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 Hashtable elementsByID = enhancedDOM.getElementsWithIDs();
 342 
 343                 if (elementsByID == null) {
 344                     return;
 345                 }
 346 
 347                 // Given a Hashtable 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                 final Enumeration idValues = elementsByID.keys();
 351                 boolean hasIDValues = false;
 352 
 353                 while (idValues.hasMoreElements()) {
 354                     final Object idValue = idValues.nextElement();
 355                     final int element =
 356                             document.getNodeHandle(
 357                                         ((Integer)elementsByID.get(idValue))
 358                                                 .intValue());
 359 
 360                     buildKeyIndex(ID_INDEX_NAME, element, idValue);
 361                     hasIDValues = true;
 362                 }
 363 
 364                 if (hasIDValues) {
 365                     setKeyIndexDom(ID_INDEX_NAME, document);
 366                 }
 367             }
 368         }
 369     }
 370 
 371     /**
 372      * After constructing the translet object, this method must be called to
 373      * perform any version-specific post-initialization that's required.
 374      */
 375     public final void postInitialization() {
 376         // If the version of the translet had just one namesArray, split
 377         // it into multiple fields.
 378         if (transletVersion < VER_SPLIT_NAMES_ARRAY) {
 379             int arraySize = namesArray.length;
 380             String[] newURIsArray = new String[arraySize];
 381             String[] newNamesArray = new String[arraySize];
 382             int[] newTypesArray = new int[arraySize];
 383 
 384             for (int i = 0; i < arraySize; i++) {
 385                 String name = namesArray[i];
 386                 int colonIndex = name.lastIndexOf(':');
 387                 int lNameStartIdx = colonIndex+1;
 388 
 389                 if (colonIndex > -1) {
 390                     newURIsArray[i] = name.substring(0, colonIndex);
 391                 }
 392 
 393                // Distinguish attribute and element names.  Attribute has
 394                // @ before local part of name.
 395                if (name.charAt(lNameStartIdx) == '@') {
 396                    lNameStartIdx++;
 397                    newTypesArray[i] = DTM.ATTRIBUTE_NODE;
 398                } else if (name.charAt(lNameStartIdx) == '?') {
 399                    lNameStartIdx++;
 400                    newTypesArray[i] = DTM.NAMESPACE_NODE;
 401                } else {
 402                    newTypesArray[i] = DTM.ELEMENT_NODE;
 403                }
 404                newNamesArray[i] =
 405                           (lNameStartIdx == 0) ? name
 406                                                : name.substring(lNameStartIdx);
 407             }
 408 
 409             namesArray = newNamesArray;
 410             urisArray  = newURIsArray;
 411             typesArray = newTypesArray;
 412         }
 413 
 414         // Was translet compiled using a more recent version of the XSLTC
 415         // compiler than is known by the AbstractTranslet class?  If, so
 416         // and we've made it this far (which is doubtful), we should give up.
 417         if (transletVersion > CURRENT_TRANSLET_VERSION) {
 418             BasisLibrary.runTimeError(BasisLibrary.UNKNOWN_TRANSLET_VERSION_ERR,
 419                                       this.getClass().getName());
 420         }
 421     }
 422 
 423     /************************************************************************
 424      * Index(es) for <xsl:key> / key() / id()
 425      ************************************************************************/
 426 
 427     // Container for all indexes for xsl:key elements
 428     private Hashtable _keyIndexes = null;
 429     private KeyIndex  _emptyKeyIndex = null;
 430     private int       _indexSize = 0;
 431     private int       _currentRootForKeys = 0;
 432 
 433     /**
 434      * This method is used to pass the largest DOM size to the translet.
 435      * Needed to make sure that the translet can index the whole DOM.
 436      */
 437     public void setIndexSize(int size) {
 438         if (size > _indexSize) _indexSize = size;
 439     }
 440 
 441     /**
 442      * Creates a KeyIndex object of the desired size - don't want to resize!!!
 443      */
 444     public KeyIndex createKeyIndex() {
 445         return(new KeyIndex(_indexSize));
 446     }
 447 
 448     /**
 449      * Adds a value to a key/id index
 450      *   @param name is the name of the index (the key or ##id)
 451      *   @param node is the node handle of the node to insert
 452      *   @param value is the value that will look up the node in the given index
 453      */
 454     public void buildKeyIndex(String name, int node, Object value) {
 455         if (_keyIndexes == null) _keyIndexes = new Hashtable();
 456 
 457         KeyIndex index = (KeyIndex)_keyIndexes.get(name);
 458         if (index == null) {
 459             _keyIndexes.put(name, index = new KeyIndex(_indexSize));
 460         }
 461         index.add(value, node, _currentRootForKeys);
 462     }
 463 
 464     /**
 465      * Create an empty KeyIndex in the DOM case
 466      *   @param name is the name of the index (the key or ##id)
 467      *   @param dom is the DOM
 468      */
 469     public void buildKeyIndex(String name, DOM dom) {
 470         if (_keyIndexes == null) _keyIndexes = new Hashtable();
 471 
 472         KeyIndex index = (KeyIndex)_keyIndexes.get(name);
 473         if (index == null) {
 474             _keyIndexes.put(name, index = new KeyIndex(_indexSize));
 475         }
 476         index.setDom(dom, dom.getDocument());
 477     }
 478 
 479     /**
 480      * Returns the index for a given key (or id).
 481      * The index implements our internal iterator interface
 482      */
 483     public KeyIndex getKeyIndex(String name) {
 484         // Return an empty key index iterator if none are defined
 485         if (_keyIndexes == null) {
 486             return (_emptyKeyIndex != null)
 487                 ? _emptyKeyIndex
 488                 : (_emptyKeyIndex = new KeyIndex(1));
 489         }
 490 
 491         // Look up the requested key index
 492         final KeyIndex index = (KeyIndex)_keyIndexes.get(name);
 493 
 494         // Return an empty key index iterator if the requested index not found
 495         if (index == null) {
 496             return (_emptyKeyIndex != null)
 497                 ? _emptyKeyIndex
 498                 : (_emptyKeyIndex = new KeyIndex(1));
 499         }
 500 
 501         return(index);
 502     }
 503 
 504     private void setRootForKeys(int root) {
 505         _currentRootForKeys = root;
 506     }
 507 
 508     /**
 509      * This method builds key indexes - it is overridden in the compiled
 510      * translet in cases where the <xsl:key> element is used
 511      */
 512     public void buildKeys(DOM document, DTMAxisIterator iterator,
 513                           SerializationHandler handler,
 514                           int root) throws TransletException {
 515 
 516     }
 517 
 518     /**
 519      * This method builds key indexes - it is overridden in the compiled
 520      * translet in cases where the <xsl:key> element is used
 521      */
 522     public void setKeyIndexDom(String name, DOM document) {
 523         getKeyIndex(name).setDom(document, document.getDocument());
 524     }
 525 
 526     /************************************************************************
 527      * DOM cache handling
 528      ************************************************************************/
 529 
 530     // Hold the DOM cache (if any) used with this translet
 531     private DOMCache _domCache = null;
 532 
 533     /**
 534      * Sets the DOM cache used for additional documents loaded using the
 535      * document() function.
 536      */
 537     public void setDOMCache(DOMCache cache) {
 538         _domCache = cache;
 539     }
 540 
 541     /**
 542      * Returns the DOM cache used for this translet. Used by the LoadDocument
 543      * class (if present) when the document() function is used.
 544      */
 545     public DOMCache getDOMCache() {
 546         return(_domCache);
 547     }
 548 
 549     /************************************************************************
 550      * Multiple output document extension.
 551      * See compiler/TransletOutput for actual implementation.
 552      ************************************************************************/
 553 
 554     public SerializationHandler openOutputHandler(String filename, boolean append)
 555         throws TransletException
 556     {
 557         try {
 558             final TransletOutputHandlerFactory factory
 559                 = TransletOutputHandlerFactory.newInstance();
 560 
 561             String dirStr = new File(filename).getParent();
 562             if ((null != dirStr) && (dirStr.length() > 0)) {
 563                File dir = new File(dirStr);
 564                dir.mkdirs();
 565             }
 566 
 567             factory.setEncoding(_encoding);
 568             factory.setOutputMethod(_method);
 569             factory.setOutputStream(new BufferedOutputStream(new FileOutputStream(filename, append)));
 570             factory.setOutputType(TransletOutputHandlerFactory.STREAM);
 571 
 572             final SerializationHandler handler
 573                 = factory.getSerializationHandler();
 574 
 575             transferOutputSettings(handler);
 576             handler.startDocument();
 577             return handler;
 578         }
 579         catch (Exception e) {
 580             throw new TransletException(e);
 581         }
 582     }
 583 
 584     public SerializationHandler openOutputHandler(String filename)
 585        throws TransletException
 586     {
 587        return openOutputHandler(filename, false);
 588     }
 589 
 590     public void closeOutputHandler(SerializationHandler handler) {
 591         try {
 592             handler.endDocument();
 593             handler.close();
 594         }
 595         catch (Exception e) {
 596             // what can you do?
 597         }
 598     }
 599 
 600     /************************************************************************
 601      * Native API transformation methods - _NOT_ JAXP/TrAX
 602      ************************************************************************/
 603 
 604     /**
 605      * Main transform() method - this is overridden by the compiled translet
 606      */
 607     public abstract void transform(DOM document, DTMAxisIterator iterator,
 608                                    SerializationHandler handler)
 609         throws TransletException;
 610 
 611     /**
 612      * Calls transform() with a given output handler
 613      */
 614     public final void transform(DOM document, SerializationHandler handler)
 615         throws TransletException {
 616         try {
 617             transform(document, document.getIterator(), handler);
 618         } finally {
 619             _keyIndexes = null;
 620         }
 621     }
 622 
 623     /**
 624      * Used by some compiled code as a shortcut for passing strings to the
 625      * output handler
 626      */
 627     public final void characters(final String string,
 628                                  SerializationHandler handler)
 629         throws TransletException {
 630         if (string != null) {
 631            //final int length = string.length();
 632            try {
 633                handler.characters(string);
 634            } catch (Exception e) {
 635                throw new TransletException(e);
 636            }
 637         }
 638     }
 639 
 640     /**
 641      * Add's a name of an element whose text contents should be output as CDATA
 642      */
 643     public void addCdataElement(String name) {
 644         if (_cdata == null) {
 645             _cdata = new Vector();
 646         }
 647 
 648         int lastColon = name.lastIndexOf(':');
 649 
 650         if (lastColon > 0) {
 651             String uri = name.substring(0, lastColon);
 652             String localName = name.substring(lastColon+1);
 653             _cdata.addElement(uri);
 654             _cdata.addElement(localName);
 655         } else {
 656             _cdata.addElement(null);
 657             _cdata.addElement(name);
 658         }
 659     }
 660 
 661     /**
 662      * Transfer the output settings to the output post-processor
 663      */
 664     protected void transferOutputSettings(SerializationHandler handler) {
 665         if (_method != null) {
 666             if (_method.equals("xml")) {
 667                 if (_standalone != null) {
 668                     handler.setStandalone(_standalone);
 669                 }
 670                 if (_omitHeader) {
 671                     handler.setOmitXMLDeclaration(true);
 672                 }
 673                 handler.setCdataSectionElements(_cdata);
 674                 if (_version != null) {
 675                     handler.setVersion(_version);
 676                 }
 677                 handler.setIndent(_indent);
 678                 handler.setIndentAmount(_indentamount);
 679                 if (_doctypeSystem != null) {
 680                     handler.setDoctype(_doctypeSystem, _doctypePublic);
 681                 }
 682                 handler.setIsStandalone(_isStandalone);
 683             }
 684             else if (_method.equals("html")) {
 685                 handler.setIndent(_indent);
 686                 handler.setDoctype(_doctypeSystem, _doctypePublic);
 687                 if (_mediaType != null) {
 688                     handler.setMediaType(_mediaType);
 689                 }
 690             }
 691         }
 692         else {
 693             handler.setCdataSectionElements(_cdata);
 694             if (_version != null) {
 695                 handler.setVersion(_version);
 696             }
 697             if (_standalone != null) {
 698                 handler.setStandalone(_standalone);
 699             }
 700             if (_omitHeader) {
 701                 handler.setOmitXMLDeclaration(true);
 702             }
 703             handler.setIndent(_indent);
 704             handler.setDoctype(_doctypeSystem, _doctypePublic);
 705             handler.setIsStandalone(_isStandalone);
 706         }
 707     }
 708 
 709     private Hashtable _auxClasses = null;
 710 
 711     public void addAuxiliaryClass(Class auxClass) {
 712         if (_auxClasses == null) _auxClasses = new Hashtable();
 713         _auxClasses.put(auxClass.getName(), auxClass);
 714     }
 715 
 716     public void setAuxiliaryClasses(Hashtable auxClasses) {
 717         _auxClasses = auxClasses;
 718     }
 719 
 720     public Class getAuxiliaryClass(String className) {
 721         if (_auxClasses == null) return null;
 722         return((Class)_auxClasses.get(className));
 723     }
 724 
 725     // GTM added (see pg 110)
 726     public String[] getNamesArray() {
 727         return namesArray;
 728     }
 729 
 730     public String[] getUrisArray() {
 731         return urisArray;
 732     }
 733 
 734     public int[] getTypesArray() {
 735         return typesArray;
 736     }
 737 
 738     public String[] getNamespaceArray() {
 739         return namespaceArray;
 740     }
 741 
 742     public boolean hasIdCall() {
 743         return _hasIdCall;
 744     }
 745 
 746     public Templates getTemplates() {
 747         return _templates;
 748     }
 749 
 750     public void setTemplates(Templates templates) {
 751         _templates = templates;
 752     }
 753     /**
 754      * Return the state of the services mechanism feature.
 755      */
 756     public boolean useServicesMechnism() {
 757         return _useServicesMechanism;
 758     }
 759 
 760     /**
 761      * Set the state of the services mechanism feature.
 762      */
 763     public void setServicesMechnism(boolean flag) {
 764         _useServicesMechanism = flag;
 765     }
 766 
 767     /**
 768      * Return allowed protocols for accessing external stylesheet.
 769      */
 770     public String getAllowedProtocols() {
 771         return _accessExternalStylesheet;
 772     }
 773 
 774     /**
 775      * Set allowed protocols for accessing external stylesheet.
 776      */
 777     public void setAllowedProtocols(String protocols) {
 778         _accessExternalStylesheet = protocols;
 779     }
 780 
 781     /************************************************************************
 782      * DOMImplementation caching for basis library
 783      ************************************************************************/
 784     protected DOMImplementation _domImplementation = null;
 785 
 786     public Document newDocument(String uri, String qname)
 787         throws ParserConfigurationException
 788     {
 789         if (_domImplementation == null) {
 790             DocumentBuilderFactory dbf = FactoryImpl.getDOMFactory(_useServicesMechanism);
 791             _domImplementation = dbf.newDocumentBuilder().getDOMImplementation();
 792         }
 793         return _domImplementation.createDocument(uri, qname, null);
 794     }
 795 }