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