1. /*
  2. * $Id: XmlDocument.java,v 1.10 2001/11/09 08:44:06 edwingo Exp $
  3. *
  4. * The Apache Software License, Version 1.1
  5. *
  6. *
  7. * Copyright (c) 2000 The Apache Software Foundation. All rights
  8. * reserved.
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions
  12. * are met:
  13. *
  14. * 1. Redistributions of source code must retain the above copyright
  15. * notice, this list of conditions and the following disclaimer.
  16. *
  17. * 2. Redistributions in binary form must reproduce the above copyright
  18. * notice, this list of conditions and the following disclaimer in
  19. * the documentation and/or other materials provided with the
  20. * distribution.
  21. *
  22. * 3. The end-user documentation included with the redistribution,
  23. * if any, must include the following acknowledgment:
  24. * "This product includes software developed by the
  25. * Apache Software Foundation (http://www.apache.org/)."
  26. * Alternately, this acknowledgment may appear in the software itself,
  27. * if and wherever such third-party acknowledgments normally appear.
  28. *
  29. * 4. The names "Crimson" and "Apache Software Foundation" must
  30. * not be used to endorse or promote products derived from this
  31. * software without prior written permission. For written
  32. * permission, please contact apache@apache.org.
  33. *
  34. * 5. Products derived from this software may not be called "Apache",
  35. * nor may "Apache" appear in their name, without prior written
  36. * permission of the Apache Software Foundation.
  37. *
  38. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  39. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  40. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  41. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  42. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  43. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  44. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  45. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  46. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  47. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  48. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  49. * SUCH DAMAGE.
  50. * ====================================================================
  51. *
  52. * This software consists of voluntary contributions made by many
  53. * individuals on behalf of the Apache Software Foundation and was
  54. * originally based on software copyright (c) 1999, Sun Microsystems, Inc.,
  55. * http://www.sun.com. For more information on the Apache Software
  56. * Foundation, please see <http://www.apache.org/>.
  57. */
  58. package org.apache.crimson.tree;
  59. import java.io.InputStream;
  60. import java.io.OutputStreamWriter;
  61. import java.io.OutputStream;
  62. import java.io.Writer;
  63. import java.io.IOException;
  64. import java.util.Dictionary;
  65. import java.util.Enumeration;
  66. import java.util.Hashtable;
  67. import java.util.Locale;
  68. import org.w3c.dom.*;
  69. import org.xml.sax.InputSource;
  70. import org.xml.sax.SAXException;
  71. import org.xml.sax.SAXParseException;
  72. import org.xml.sax.helpers.DefaultHandler;
  73. import org.xml.sax.helpers.XMLReaderFactory;
  74. import org.xml.sax.XMLReader;
  75. import org.apache.crimson.parser.Parser2;
  76. import org.apache.crimson.parser.Resolver;
  77. import org.apache.crimson.parser.ValidatingParser;
  78. import org.apache.crimson.util.MessageCatalog;
  79. import org.apache.crimson.util.XmlNames;
  80. /**
  81. * This class implements the DOM <em>Document</em> interface, and also
  82. * provides static factory methods to create document instances. Instances
  83. * represent the top level of an XML 1.0 document, typically consisting
  84. * of processing instructions followed by one tree of XML data. These
  85. * documents may be written out for transfer or storage using a variety
  86. * of text encodings.
  87. *
  88. * <P> The static factory methods do not offer any customization options.
  89. * in particular, they do not enforce XML Namespaces when parsing, do not
  90. * offer customizable element factories, and discard certain information
  91. * which is not intended to be significant to applications. If your
  92. * application requires more sophisticated use of DOM, you may need
  93. * to use SAX directly with an <em>XmlDocumentBuilder</em>.
  94. *
  95. * <P> <b>Note: element factories are deprecated</b> because they are
  96. * non-standard. They are only provided in this version for backwards
  97. * compatibility. Instances are factories for their subsidiary nodes, but
  98. * applications may provide their own element factory to bind element tags
  99. * to particular DOM implementation classes (which must subclass
  100. * ElementNode). For example, a factory may use a set of classes which
  101. * support the HTML DOM methods, or which support methods associated with
  102. * XML vocabularies for specialized problem domains as found within
  103. * Internet Commerce systems. For example, an element tag
  104. * <code><PurchaseOrder></code> could be mapped to a
  105. * <code>com.startup.commerce.PurchaseOrder</code> class. The factory can
  106. * also use XML Namespace information, if desired.
  107. *
  108. * <P> Since DOM requires nodes to be owned exclusively by one document,
  109. * they can't be moved from one document to another using DOM APIs. This
  110. * class provides an <em>changeNodeOwner</em> functionality which may be
  111. * used to change the document associated with a node, and with any of its
  112. * children.
  113. *
  114. * <P> <em> Only the core DOM model is supported here, not the HTML support.
  115. * Such support basically adds a set of convenience element types, and so
  116. * can be implemented through element factories and document subclasses.</em>
  117. *
  118. * @see XmlDocumentBuilder
  119. *
  120. * @author David Brownell
  121. * @author Rajiv Mordani
  122. * @version $Revision: 1.10 $
  123. */
  124. public class XmlDocument extends ParentNode implements DocumentEx
  125. {
  126. // package private (with jdk 1.1 'javac' bug workaround)
  127. static /* final */ String eol;
  128. static {
  129. String temp;
  130. try { temp = System.getProperty ("line.separator", "\n"); }
  131. catch (SecurityException e) { temp = "\n"; }
  132. eol = temp;
  133. }
  134. static final MessageCatalog catalog = new Catalog ();
  135. private Locale locale = Locale.getDefault ();
  136. private String systemId;
  137. private ElementFactory factory;
  138. // package private
  139. int mutationCount;
  140. boolean replaceRootElement;
  141. /**
  142. * Constructs an empty document object.
  143. */
  144. public XmlDocument() {
  145. // No-op
  146. }
  147. /**
  148. * Construct an XML document from the data at the specified URI,
  149. * optionally validating. This uses validating parser if
  150. * validation is requested, otherwise uses non-validating
  151. * parser. XML Namespace conformance is not tested when parsing.
  152. *
  153. * @param documentURI The URI (normally URL) of the document
  154. * @param doValidate If true, validity errors are treated as fatal
  155. *
  156. * @exception IOException as appropriate
  157. * @exception SAXException as appropriate
  158. * @exception SAXParseException (with line number information)
  159. * for parsing errors
  160. * @exception IllegalStateException at least when the parser
  161. * is configured incorrectly
  162. * @deprecated Use JAXP javax.xml.parsers package instead
  163. */
  164. public static XmlDocument createXmlDocument (
  165. String documentURI,
  166. boolean doValidate
  167. ) throws IOException, SAXException
  168. {
  169. return createXmlDocument (new InputSource (documentURI), doValidate);
  170. }
  171. /**
  172. * Construct an XML document from the data at the specified URI,
  173. * using the nonvalidating parser. XML Namespace conformance
  174. * is not tested when parsing.
  175. *
  176. * @param documentURI The URI (normally URL) of the document
  177. *
  178. * @exception IOException as appropriate
  179. * @exception SAXException as appropriate
  180. * @exception SAXParseException (with line number information)
  181. * for parsing errors
  182. * @exception IllegalStateException at least when the parser
  183. * is configured incorrectly
  184. * @deprecated Use JAXP javax.xml.parsers package instead
  185. */
  186. public static XmlDocument createXmlDocument (String documentURI)
  187. throws IOException, SAXException
  188. {
  189. return createXmlDocument (new InputSource (documentURI), false);
  190. }
  191. /**
  192. * Construct an XML document from input stream, optionally validating.
  193. * This document must not require interpretation of relative URLs,
  194. * since the base URL is not known. This uses the validating parser
  195. * if validation is requested, otherwise uses the non-validating
  196. * parser. XML Namespace conformance is not tested when parsing.
  197. *
  198. * @param in Holds xml document
  199. * @param doValidate If true, validity errors are treated as fatal
  200. *
  201. * @exception IOException as appropriate
  202. * @exception SAXException as appropriate
  203. * @exception SAXParseException (with line number information)
  204. * for parsing errors
  205. * @exception IllegalStateException at least when the parser
  206. * is configured incorrectly
  207. * @deprecated Use JAXP javax.xml.parsers package instead
  208. */
  209. public static XmlDocument createXmlDocument (
  210. InputStream in,
  211. boolean doValidate
  212. ) throws IOException, SAXException
  213. {
  214. return createXmlDocument (new InputSource (in), doValidate);
  215. }
  216. /**
  217. * Construct an XML document from the data in the specified input
  218. * source, optionally validating. This uses the validating parser
  219. * if validation is requested, otherwise uses the non-validating
  220. * parser. XML Namespace conformance is not tested when parsing.
  221. *
  222. * @param in The input source of the document
  223. * @param doValidate If true, validity errors are treated as fatal
  224. *
  225. * @exception IOException as appropriate
  226. * @exception SAXException as appropriate
  227. * @exception SAXParseException (with line number information)
  228. * for parsing errors
  229. * @exception IllegalStateException at least when the parser
  230. * is configured incorrectly
  231. * @deprecated Use JAXP javax.xml.parsers package instead
  232. */
  233. public static XmlDocument createXmlDocument(InputSource in,
  234. boolean doValidate)
  235. throws IOException, SAXException
  236. {
  237. // Create XMLReader allowing user to override using system property
  238. // String defaultReader = "org.apache.xerces.parsers.SAXParser";
  239. String defaultReader = "org.apache.crimson.parser.XMLReaderImpl";
  240. String prop;
  241. try {
  242. prop = System.getProperty("org.xml.sax.driver", defaultReader);
  243. } catch (SecurityException se) {
  244. // This can happen if we are running as an applet
  245. prop = defaultReader;
  246. }
  247. XMLReader xmlReader = XMLReaderFactory.createXMLReader(prop);
  248. //
  249. // Namespace related features needed for XmlDocumentBuilder
  250. //
  251. String namespaces = "http://xml.org/sax/features/namespaces";
  252. xmlReader.setFeature(namespaces, true);
  253. String nsPrefixes = "http://xml.org/sax/features/namespace-prefixes";
  254. xmlReader.setFeature(nsPrefixes, true);
  255. // Create XmlDocumentBuilder instance
  256. XmlDocumentBuilder builder = new XmlDocumentBuilder();
  257. // Use as the ContentHandler
  258. xmlReader.setContentHandler(builder);
  259. // org.xml.sax.ext.LexicalHandler
  260. String lexHandler = "http://xml.org/sax/properties/lexical-handler";
  261. xmlReader.setProperty(lexHandler, builder);
  262. // org.xml.sax.ext.DeclHandler
  263. String declHandler
  264. = "http://xml.org/sax/properties/declaration-handler";
  265. xmlReader.setProperty(declHandler, builder);
  266. // DTDHandler
  267. xmlReader.setDTDHandler(builder);
  268. // Validation
  269. String validation = "http://xml.org/sax/features/validation";
  270. xmlReader.setFeature(validation, doValidate);
  271. // If validating, use an error handler that throws an exception for
  272. // validation errors.
  273. if (doValidate) {
  274. xmlReader.setErrorHandler(new DefaultHandler() {
  275. public void error(SAXParseException e) throws SAXException {
  276. throw e;
  277. }
  278. });
  279. }
  280. builder.setDisableNamespaces(true);
  281. // Parse the input
  282. xmlReader.parse(in);
  283. return builder.getDocument();
  284. }
  285. /**
  286. * Returns the locale to be used for diagnostic messages.
  287. */
  288. public Locale getLocale ()
  289. { return locale; }
  290. /**
  291. * Assigns the locale to be used for diagnostic messages.
  292. * Multi-language applications, such as web servers dealing with
  293. * clients from different locales, need the ability to interact
  294. * with clients in languages other than the server's default.
  295. * When an XmlDocument is created, its locale is the default
  296. * locale for the virtual machine.
  297. *
  298. * @see #chooseLocale
  299. */
  300. public void setLocale (Locale locale)
  301. {
  302. if (locale == null)
  303. locale = Locale.getDefault ();
  304. this.locale = locale;
  305. }
  306. /**
  307. * Chooses a client locale to use for diagnostics, using the first
  308. * language specified in the list that is supported by this DOM
  309. * implementation. That locale is then automatically assigned using <a
  310. * href="#setLocale(java.util.Locale)">setLocale()</a>. Such a list
  311. * could be provided by a variety of user preference mechanisms,
  312. * including the HTTP <em>Accept-Language</em> header field.
  313. *
  314. * @see org.apache.crimson.util.MessageCatalog
  315. *
  316. * @param languages Array of language specifiers, ordered with the most
  317. * preferable one at the front. For example, "en-ca" then "fr-ca",
  318. * followed by "zh_CN". Both RFC 1766 and Java styles are supported.
  319. * @return The chosen locale, or null.
  320. */
  321. public Locale chooseLocale (String languages [])
  322. {
  323. Locale l = catalog.chooseLocale (languages);
  324. if (l != null)
  325. setLocale (l);
  326. return l;
  327. }
  328. /**
  329. * Writes the document in UTF-8 character encoding, as a well formed
  330. * XML construct.
  331. *
  332. * @param out stream on which the document will be written
  333. */
  334. public void write (OutputStream out) throws IOException
  335. {
  336. Writer writer = new OutputStreamWriter (out, "UTF8");
  337. write (writer, "UTF-8");
  338. }
  339. /**
  340. * Writes the document as a well formed XML construct. If the
  341. * encoding can be determined from the writer, that is used in
  342. * the document's XML declaration. The encoding name may first
  343. * be transformed from a Java-internal form to a standard one;
  344. * for example, Java's "UTF8" is the standard "UTF-8".
  345. *
  346. * <P> <em>Use of UTF-8 (or UTF-16) OutputStreamWriters is strongly
  347. * encouraged. </em> All other encodings may lose critical data,
  348. * since the standard Java output writers substitute characters
  349. * such as the question mark for data which they can't encode in
  350. * the current output encoding. The IETF and other organizations
  351. * strongly encourage the use of UTF-8; also, all XML processors
  352. * are guaranteed to support it.
  353. *
  354. * @see #write(java.io.Writer,java.lang.String)
  355. *
  356. * @param out stream on which the document will be written
  357. */
  358. public void write (Writer out) throws IOException
  359. {
  360. String encoding = null;
  361. if (out instanceof OutputStreamWriter)
  362. encoding = java2std (((OutputStreamWriter)out).getEncoding ());
  363. write (out, encoding);
  364. }
  365. //
  366. // Try some of the common conversions from Java's internal names
  367. // (which must fit in class names) to standard ones understood by
  368. // most other code. We use the IETF's preferred names; case is
  369. // supposed to be ignored, note.
  370. //
  371. // package private
  372. static String java2std (String encodingName)
  373. {
  374. if (encodingName == null)
  375. return null;
  376. //
  377. // ISO-8859-N is a common family of 8 bit encodings;
  378. // N=1 is the eight bit subset of UNICODE, and there
  379. // seem to be at least drafts for some N >10.
  380. //
  381. if (encodingName.startsWith ("ISO8859_")) // JDK 1.2
  382. return "ISO-8859-" + encodingName.substring (8);
  383. if (encodingName.startsWith ("8859_")) // JDK 1.1
  384. return "ISO-8859-" + encodingName.substring (5);
  385. // XXX seven bit encodings ISO-2022-* ...
  386. // XXX EBCDIC encodings ...
  387. if ("ASCII7".equalsIgnoreCase (encodingName)
  388. || "ASCII".equalsIgnoreCase (encodingName))
  389. return "US-ASCII";
  390. //
  391. // All XML parsers _must_ support UTF-8 and UTF-16.
  392. // (UTF-16 ~= ISO-10646-UCS-2 plus surrogate pairs)
  393. //
  394. if ("UTF8".equalsIgnoreCase (encodingName))
  395. return "UTF-8";
  396. if (encodingName.startsWith ("Unicode"))
  397. return "UTF-16";
  398. //
  399. // Some common Japanese character sets.
  400. //
  401. if ("SJIS".equalsIgnoreCase (encodingName))
  402. return "Shift_JIS";
  403. if ("JIS".equalsIgnoreCase (encodingName))
  404. return "ISO-2022-JP";
  405. if ("EUCJIS".equalsIgnoreCase (encodingName))
  406. return "EUC-JP";
  407. // else we can't really do anything
  408. return encodingName;
  409. }
  410. /**
  411. * Writes the document in the specified encoding, and listing
  412. * that encoding in the XML declaration. The document will be
  413. * well formed XML; at this time, it will not additionally be
  414. * valid XML or standalone, since it includes no document type
  415. * declaration.
  416. *
  417. * <P> Note that the document will by default be "pretty printed".
  418. * Extra whitespace is added to indent children of elements according
  419. * to their level of nesting, unless those elements have (or inherit)
  420. * the <em>xml:space='preserve'</em> attribute value. This space
  421. * will be removed if, when the document is read back with DOM, a
  422. * call to <em>ElementNode.normalize</em> is made. To avoid this
  423. * pretty printing, use a write context configured to disable it,
  424. * or explicitly assign an <em>xml:space='preserve'</em> attribute to
  425. * the root node of your document.
  426. *
  427. * <P> Also, if a SAX parser was used to construct this tree, data
  428. * will have been discarded. Most of that will be insignificant in
  429. * terms of a "logical" view of document data: comments, whitespace
  430. * outside of the top level element, the exact content of the XML
  431. * directive, and entity references were expanded. However, <em>if a
  432. * DOCTYPE declaration was provided, it was also discarded</em>.
  433. * Such declarations will often be logically significant, due to the
  434. * attribute value defaulting and normalization they can provide.
  435. *
  436. * <P> In general, DOM does not support "round tripping" data from
  437. * XML to DOM and back without losing data about physical structures
  438. * and DTD information. "Logical structure" will be preserved.
  439. *
  440. * @see #setDoctype
  441. * @see #writeXml
  442. *
  443. * @param out the writer to use when writing the document
  444. * @param encoding the encoding name to use; this should be a
  445. * standard encoding name registered with the IANA (like "UTF-8")
  446. * not a Java-internal name (like "UTF8").
  447. */
  448. public void write (Writer out, String encoding)
  449. throws IOException
  450. {
  451. //
  452. // We put a pretty minimal declaration here, which is the
  453. // best we can do given SAX input and DOM. For the moment
  454. // this precludes our generating "standalone" annotations.
  455. //
  456. out.write ("<?xml version=\"1.0\"");
  457. if (encoding != null) {
  458. out.write (" encoding=\"");
  459. out.write (encoding);
  460. out.write ('\"');
  461. }
  462. out.write ("?>");
  463. out.write (eol);
  464. out.write (eol);
  465. writeChildrenXml (createWriteContext (out, 0));
  466. out.write (eol);
  467. out.flush ();
  468. }
  469. /**
  470. * Returns an XML write context set up not to pretty-print,
  471. * and which knows about the entities defined for this document.
  472. *
  473. * @param out stream on which the document will be written
  474. */
  475. public XmlWriteContext createWriteContext (Writer out)
  476. {
  477. return new ExtWriteContext (out);
  478. }
  479. /**
  480. * Returns an XML write context which pretty-prints output starting
  481. * at a specified indent level, and which knows about the entities
  482. * defined for this document.
  483. *
  484. * @param out stream on which the document will be written
  485. * @param level initial indent level for pretty-printing
  486. */
  487. public XmlWriteContext createWriteContext (Writer out, int level)
  488. {
  489. return new ExtWriteContext (out, level);
  490. }
  491. /**
  492. * Writes the document out using the specified context, using
  493. * an encoding name derived from the stream in the context where
  494. * that is possible.
  495. *
  496. * @see #createWriteContext(java.io.Writer)
  497. * @see #createWriteContext(java.io.Writer,int)
  498. *
  499. * @param context describes how to write the document
  500. */
  501. public void writeXml (XmlWriteContext context) throws IOException
  502. {
  503. Writer out = context.getWriter ();
  504. String encoding = null;
  505. //
  506. // XXX as above, it should be possible to be "told" this name
  507. // in order to use more standard names. We can pretty print,
  508. // or we can use the right encoding name; not both!!
  509. //
  510. if (out instanceof OutputStreamWriter)
  511. encoding = java2std (((OutputStreamWriter)out).getEncoding ());
  512. //
  513. // We put a pretty minimal declaration here, which is the
  514. // best we can do given SAX input and DOM. For the moment
  515. // this precludes our generating "standalone" annotations.
  516. //
  517. out.write ("<?xml version=\"1.0\"");
  518. if (encoding != null) {
  519. out.write (" encoding=\"");
  520. out.write (encoding);
  521. out.write ('\"');
  522. }
  523. out.write ("?>");
  524. out.write (eol);
  525. out.write (eol);
  526. writeChildrenXml (context);
  527. }
  528. /**
  529. * Writes all the child nodes of the document, following each one
  530. * with the end-of-line string in use in this environment.
  531. */
  532. public void writeChildrenXml (XmlWriteContext context) throws IOException
  533. {
  534. int length = getLength ();
  535. Writer out = context.getWriter ();
  536. if (length == 0)
  537. return;
  538. for (int i = 0; i < length; i++) {
  539. ((NodeBase)item (i)).writeXml (context);
  540. out.write (eol);
  541. }
  542. }
  543. // package private -- overrides base class method
  544. void checkChildType (int type)
  545. throws DOMException
  546. {
  547. switch (type) {
  548. case ELEMENT_NODE:
  549. case PROCESSING_INSTRUCTION_NODE:
  550. case COMMENT_NODE:
  551. case DOCUMENT_TYPE_NODE:
  552. return;
  553. default:
  554. throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
  555. }
  556. }
  557. /**
  558. * Assigns the URI associated with the document, which is its
  559. * system ID.
  560. *
  561. * @param uri The document's system ID, as used when storing
  562. * the document.
  563. */
  564. final public void setSystemId (String uri)
  565. {
  566. systemId = uri;
  567. }
  568. /**
  569. * Returns system ID associated with the document, or null if
  570. * this is unknown.
  571. *
  572. * <P> This URI should not be used when interpreting relative URIs,
  573. * since the document may be partially stored in external parsed
  574. * entities with different base URIs. Instead, use methods in the
  575. * <em>XmlReadable</em> interface as the document is being parsed,
  576. * so that the correct base URI is available.
  577. */
  578. final public String getSystemId ()
  579. {
  580. return systemId;
  581. }
  582. // DOM support
  583. /**
  584. * DOM: Appends the specified child node to the document. Only one
  585. * element or document type node may be a child of a document.
  586. *
  587. * @param node the node to be appended.
  588. */
  589. public Node appendChild (Node n)
  590. throws DOMException
  591. {
  592. if (n instanceof Element && getDocumentElement () != null)
  593. throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
  594. if (n instanceof DocumentType && getDoctype () != null)
  595. throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
  596. return super.appendChild (n);
  597. }
  598. /**
  599. * DOM: Inserts the specified child node into the document. Only one
  600. * element or document type node may be a child of a document.
  601. *
  602. * @param n the node to be inserted.
  603. * @param refNode the node before which this is to be inserted
  604. */
  605. public Node insertBefore (Node n, Node refNode)
  606. throws DOMException
  607. {
  608. if (!replaceRootElement && n instanceof Element &&
  609. getDocumentElement () != null)
  610. throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
  611. if (!replaceRootElement && n instanceof DocumentType
  612. && getDoctype () != null)
  613. throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
  614. return super.insertBefore (n, refNode);
  615. }
  616. /**
  617. * <b>DOM:</b> Replaces the specified child with the new node,
  618. * returning the original child or throwing an exception.
  619. * The new child must belong to this particular document.
  620. *
  621. * @param newChild the new child to be inserted
  622. * @param refChild node which is to be replaced
  623. */
  624. public Node replaceChild (Node newChild, Node refChild)
  625. throws DOMException
  626. {
  627. if (newChild instanceof DocumentFragment ) {
  628. int elemCount = 0;
  629. int docCount = 0;
  630. replaceRootElement = false;
  631. ParentNode frag = (ParentNode) newChild;
  632. Node temp;
  633. int i = 0;
  634. while ((temp = frag.item (i)) != null) {
  635. if (temp instanceof Element)
  636. elemCount++;
  637. else if (temp instanceof DocumentType)
  638. docCount++;
  639. i++;
  640. }
  641. if (elemCount > 1 || docCount > 1)
  642. throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
  643. else
  644. replaceRootElement = true;
  645. }
  646. return super.replaceChild (newChild, refChild);
  647. }
  648. /** DOM: Returns the DOCUMENT_NODE node type constant. */
  649. final public short getNodeType () { return DOCUMENT_NODE; }
  650. /** DOM: returns the document type (DTD) */
  651. final public DocumentType getDoctype ()
  652. {
  653. // We ignore comments, PIs, whitespace, etc
  654. // and return the first (only!) doctype.
  655. for (int i = 0; true; i++) {
  656. Node n = item (i);
  657. if (n == null)
  658. return null;
  659. if (n instanceof DocumentType)
  660. return (DocumentType) n;
  661. }
  662. }
  663. /**
  664. * Establishes how the document prints its document type. If a system
  665. * ID (URI) is provided, that is used in a SYSTEM (or PUBLIC, if a public
  666. * ID is also provided) declaration. If an internal subset is provided,
  667. * that will be printed. The root element in the DTD will be what the
  668. * document itself provides.
  669. *
  670. * @param dtdPublicId Holds a "public identifier" used to identify the
  671. * last part of the external DTD subset that is read into the DTD.
  672. * This may be omitted, and in any case is ignored unless a system
  673. * ID is provided.
  674. * @param dtdSystemId Holds a "system identifier" (a URI) used to
  675. * identify the last part of the external DTD subset that is read
  676. * into the DTD. This may be omitted, in which case the document
  677. * type will contain at most an internal subset. This URI should
  678. * not be a relative URI unless the document will be accessed in a
  679. * context from which that relative URI makes sense.
  680. * @param internalSubset Optional; this holds XML text which will
  681. * be put into the internal subset. This must be legal syntax,
  682. * and it is not tested by this document.
  683. */
  684. public DocumentType setDoctype (
  685. String dtdPublicId,
  686. String dtdSystemId,
  687. String internalSubset
  688. ) {
  689. Doctype retval = (Doctype) getDoctype ();
  690. if (retval != null)
  691. retval.setPrintInfo (dtdPublicId, dtdSystemId,
  692. internalSubset);
  693. else {
  694. retval = new Doctype (dtdPublicId, dtdSystemId,
  695. internalSubset);
  696. retval.setOwnerDocument (this);
  697. insertBefore (retval, getFirstChild ());
  698. }
  699. return retval;
  700. }
  701. /**
  702. * DOM: Returns the content root element.
  703. */
  704. public Element getDocumentElement ()
  705. {
  706. // We ignore comments, PIs, whitespace, etc
  707. // and return the first (only!) element.
  708. for (int i = 0; true; i++) {
  709. Node n = item (i);
  710. if (n == null)
  711. return null;
  712. if (n instanceof Element) {
  713. return (Element)n;
  714. }
  715. }
  716. }
  717. /**
  718. * Assigns the element factory to be used by this document.
  719. *
  720. * @param factory the element factory to be used; if this is null,
  721. * all elements will be implemented by <em>ElementNode</em>.
  722. * @deprecated
  723. */
  724. final public void setElementFactory (ElementFactory factory)
  725. {
  726. this.factory = factory;
  727. }
  728. /**
  729. * Returns the element factory to be used by this document.
  730. * @deprecated
  731. */
  732. final public ElementFactory getElementFactory ()
  733. {
  734. return factory;
  735. }
  736. /**
  737. * DOM: Create a new element, associated with this document, with
  738. * no children, attributes, or parent, by calling createElementEx.
  739. *
  740. * @param tagName the tag of the element, used to determine what
  741. * type element to create as well as what tag to assign the new node.
  742. * @exception IllegalArgumentException if a mapping is defined,
  743. * but is invalid because the element can't be instantiated or
  744. * does not subclass <em>ElementNode</em>.
  745. */
  746. public Element createElement(String tagName)
  747. throws DOMException
  748. {
  749. return createElementEx (tagName);
  750. }
  751. /**
  752. * <b>DOM2:</b>
  753. * @since DOM Level 2
  754. * Warning: Does not work with the deprecated ElementFactory
  755. */
  756. public Element createElementNS(String namespaceURI, String qualifiedName)
  757. throws DOMException
  758. {
  759. // Check arguments and throw appropriate exceptions
  760. ElementNode2.checkArguments(namespaceURI, qualifiedName);
  761. ElementNode2 retval = new ElementNode2(namespaceURI, qualifiedName);
  762. retval.setOwnerDocument(this);
  763. return retval;
  764. }
  765. /**
  766. * Create a new element, associated with this document, with no
  767. * children, attributes, or parent. This uses the element factory,
  768. * or else directly constructs an ElementNode.
  769. *
  770. * @param tagName the tag of the element, used to determine what
  771. * type element to create as well as what tag to assign the new node.
  772. * @exception IllegalArgumentException if a mapping is defined,
  773. * but is invalid because the element can't be instantiated or
  774. * does not subclass <em>ElementNode</em>.
  775. * @deprecated Use the standard method createElement instead
  776. */
  777. final public ElementEx createElementEx(String tagName)
  778. throws DOMException
  779. {
  780. ElementNode retval;
  781. if (!XmlNames.isName (tagName)) {
  782. throw new DomEx (DomEx.INVALID_CHARACTER_ERR);
  783. }
  784. if (factory != null) {
  785. // Ask factory to create appropriate ElementNode subtype
  786. retval = (ElementNode) factory.createElementEx(tagName);
  787. // Set the name of the ElementNode
  788. retval.setTag(tagName);
  789. } else {
  790. retval = new ElementNode(tagName);
  791. }
  792. retval.setOwnerDocument(this);
  793. return retval;
  794. }
  795. /**
  796. * Create a new element, associated with this document, with no
  797. * children, attributes, or parent. This uses the element factory,
  798. * or else directly constructs an ElementNode.
  799. *
  800. * @param uri The namespace used to determine what type of element to
  801. * create. This is not stored with the element; the element must be
  802. * inserted into a DOM tree in a location where namespace declarations
  803. * cause its tag to be interpreted correctly.
  804. * @param tagName The tag of the element, which should not contain
  805. * any namespace prefix.
  806. * @exception IllegalArgumentException When a mapping is defined,
  807. * but is invalid because the element can't be instantiated or
  808. * does not subclass <em>ElementNode</em>.
  809. * @deprecated Use the standard method createElementNS instead
  810. */
  811. final public ElementEx createElementEx(String uri, String tagName)
  812. throws DOMException
  813. {
  814. ElementNode retval;
  815. if (!XmlNames.isName(tagName)) {
  816. throw new DomEx (DomEx.INVALID_CHARACTER_ERR);
  817. }
  818. if (factory != null) {
  819. // Ask factory to create appropriate ElementNode subtype
  820. retval = (ElementNode) factory.createElementEx(uri, tagName);
  821. // Set the name of the ElementNode
  822. retval.setTag(tagName);
  823. } else {
  824. retval = new ElementNode(tagName);
  825. }
  826. retval.setOwnerDocument(this);
  827. return retval;
  828. }
  829. /**
  830. * DOM: returns a Text node initialized with the given text.
  831. *
  832. * @param text The contents of the text node being created, which
  833. * should never contain "<em>]]></em>".
  834. */
  835. public Text createTextNode (String text)
  836. {
  837. TextNode retval;
  838. retval = new TextNode ();
  839. retval.setOwnerDocument (this);
  840. if (text != null)
  841. retval.setText (text.toCharArray ());
  842. return retval;
  843. }
  844. /**
  845. * DOM: Returns a CDATA section initialized with the given text.
  846. *
  847. * @param text the text which the CDATA section will hold, which
  848. * should never contain "<em>]]></em>".
  849. */
  850. public CDATASection createCDATASection (String text)
  851. {
  852. CDataNode retval = new CDataNode ();
  853. if (text != null)
  854. retval.setText (text.toCharArray ());
  855. retval.setOwnerDocument (this);
  856. return retval;
  857. }
  858. // package private ... convenience rtn, reduced mallocation
  859. TextNode newText (char buf [], int offset, int len)
  860. throws SAXException
  861. {
  862. TextNode retval = (TextNode) createTextNode (null);
  863. char data [] = new char [len];
  864. System.arraycopy (buf, offset, data, 0, len);
  865. retval.setText (data);
  866. return retval;
  867. }
  868. /**
  869. * DOM: Returns a Processing Instruction node for the specified
  870. * processing target, with the given instructions.
  871. *
  872. * @param target the target of the processing instruction
  873. * @param instructions the processing instruction, which should
  874. * never contain "<em>?></em>".
  875. */
  876. public ProcessingInstruction createProcessingInstruction (
  877. String target,
  878. String instructions
  879. ) throws DOMException
  880. {
  881. if (!XmlNames.isName (target))
  882. throw new DomEx (DomEx.INVALID_CHARACTER_ERR);
  883. PINode retval = new PINode (target, instructions);
  884. retval.setOwnerDocument (this);
  885. return retval;
  886. }
  887. /**
  888. * DOM: Returns a valueless attribute node with no default value.
  889. *
  890. * @param name the name of the attribute.
  891. */
  892. public Attr createAttribute(String name) throws DOMException {
  893. if (!XmlNames.isName(name)) {
  894. throw new DomEx(DOMException.INVALID_CHARACTER_ERR);
  895. }
  896. AttributeNode1 retval = new AttributeNode1(name, "", true, null);
  897. retval.setOwnerDocument(this);
  898. return retval;
  899. }
  900. /**
  901. * <b>DOM2:</b>
  902. * @since DOM Level 2
  903. */
  904. public Attr createAttributeNS(String namespaceURI, String qualifiedName)
  905. throws DOMException
  906. {
  907. AttributeNode.checkArguments(namespaceURI, qualifiedName);
  908. AttributeNode retval = new AttributeNode(namespaceURI, qualifiedName,
  909. "", true, null);
  910. retval.setOwnerDocument(this);
  911. return retval;
  912. }
  913. /**
  914. * DOM: creates a comment node.
  915. *
  916. * @param data The characters which will be in the comment.
  917. * This should not include the "<em>--</em>" characters.
  918. */
  919. public Comment createComment (String data)
  920. {
  921. CommentNode retval = new CommentNode (data);
  922. retval.setOwnerDocument (this);
  923. return retval;
  924. }
  925. /** DOM: returns null. */
  926. public Document getOwnerDoc ()
  927. {
  928. return null;
  929. }
  930. /**
  931. * The <code>DOMImplementation</code> object that handles this document.
  932. */
  933. public DOMImplementation getImplementation() {
  934. return DOMImplementationImpl.getDOMImplementation();
  935. }
  936. /**
  937. * DOM: Creates a new document fragment.
  938. */
  939. public DocumentFragment createDocumentFragment ()
  940. {
  941. DocFragNode retval = new DocFragNode ();
  942. retval.setOwnerDocument (this);
  943. return retval;
  944. }
  945. /**
  946. * DOM: Creates an entity reference to the named entity.
  947. * Note that the entity must already be defined in the document
  948. * type, and that the name must be a legal entity name.
  949. *
  950. * @param name the name of the the parsed entity
  951. */
  952. public EntityReference createEntityReference (String name)
  953. throws DOMException
  954. {
  955. if (!XmlNames.isName (name))
  956. throw new DomEx (DomEx.INVALID_CHARACTER_ERR);
  957. EntityRefNode retval = new EntityRefNode (name);
  958. retval.setOwnerDocument (this);
  959. return retval;
  960. }
  961. /** DOM: Returns the string "#document". */
  962. final public String getNodeName () { return "#document"; }
  963. /**
  964. * DOM: Returns a copy of this document.
  965. *
  966. * <P> <em>Note:</em> At this time, any element factory or document
  967. * type associated with this document will not be cloned.
  968. *
  969. * @param deep if true, child nodes are also cloned.
  970. */
  971. public Node cloneNode (boolean deep)
  972. {
  973. XmlDocument retval = new XmlDocument ();
  974. retval.systemId = systemId;
  975. // XXX clone the element factory ...
  976. if (deep) {
  977. Node node;
  978. for (int i = 0; (node = item (i)) != null; i++) {
  979. if (node instanceof DocumentType) {
  980. // XXX recreate
  981. continue;
  982. }
  983. node = node.cloneNode (true);
  984. retval.changeNodeOwner (node);
  985. retval.appendChild (node);
  986. }
  987. }
  988. return retval;
  989. }
  990. /**
  991. * Changes the "owner document" of the given node, and all child
  992. * and associated attribute nodes, to be this document. If the
  993. * node has a parent, it is first removed from that parent.
  994. * <b>Obsolete</b> Use importNode method instead. Still useful for
  995. * internal implementation.
  996. *
  997. * @param node
  998. * @exception DOMException WRONG_DOCUMENT_ERROR when attempting
  999. * to change the owner for some other DOM implementation<P>
  1000. * HIERARCHY_REQUEST_ERROR when the node is a document, document
  1001. * type, entity, or notation; or when it is an attribute associated
  1002. * with an element whose owner is not being (recursively) changed.
  1003. */
  1004. final public void changeNodeOwner (Node node)
  1005. throws DOMException
  1006. {
  1007. TreeWalker walker;
  1008. NodeBase n;
  1009. if (node.getOwnerDocument () == this)
  1010. return;
  1011. if (!(node instanceof NodeBase))
  1012. throw new DomEx (DomEx.WRONG_DOCUMENT_ERR);
  1013. switch (node.getNodeType ()) {
  1014. // Documents _are_ owners; can't switch identities
  1015. case Node.DOCUMENT_NODE:
  1016. // Entities, Notations only live in doctypes ... we
  1017. // don't support changing their ownership at this time
  1018. case Node.ENTITY_NODE:
  1019. case Node.NOTATION_NODE:
  1020. case Node.DOCUMENT_TYPE_NODE:
  1021. throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
  1022. }
  1023. //
  1024. // If node is an attribute, its "scoped" by one element...
  1025. // and if that scope hasn't been changed (i.e. if this isn't
  1026. // a recursive call) we can't really fix anything!
  1027. //
  1028. if (node instanceof AttributeNode) {
  1029. AttributeNode attr = (AttributeNode) node;
  1030. Element scope = attr.getOwnerElement();
  1031. if (scope != null && scope.getOwnerDocument () != this)
  1032. throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
  1033. }
  1034. // unparent node if needed
  1035. n = (NodeBase) node.getParentNode ();
  1036. if (n != null)
  1037. n.removeChild (node);
  1038. // change any children (including self)
  1039. for (walker = new TreeWalker (node),
  1040. n = (NodeBase) walker.getCurrent ();
  1041. n != null;
  1042. n = (NodeBase) walker.getNext ()) {
  1043. n.setOwnerDocument (this);
  1044. // Elements have associated attributes, which must
  1045. // also have owners changed.
  1046. if (n instanceof Element) {
  1047. NamedNodeMap list = n.getAttributes ();
  1048. int length = list.getLength ();
  1049. for (int i = 0; i < length; i++)
  1050. changeNodeOwner (list.item (i));
  1051. }
  1052. }
  1053. }
  1054. /**
  1055. * Returns the <code>Element</code> whose <code>ID</code> is given by
  1056. * <code>elementId</code>.
  1057. *
  1058. * @since DOM Level 2
  1059. */
  1060. public Element getElementById(String elementId) {
  1061. return getElementExById(elementId);
  1062. }
  1063. /**
  1064. * Returns the element whose ID is given by the parameter; or null
  1065. * if no such element exists. This relies on elements to know the
  1066. * name of their ID attribute, as will be currently be true only if
  1067. * the document has been parsed from XML text with a DTD using the
  1068. * <em>XmlDocumentBuilder</em> class, or if it has been constructed
  1069. * using specialized DOM implementation classes which know the name
  1070. * of their ID attribute. (XML allows only one ID attribute per
  1071. * element, and different elements may use different names for their
  1072. * ID attributes.)
  1073. *
  1074. * <P> This may be used to implement internal IDREF linkage, as well
  1075. * as some kinds of <em>XPointer</em> linkage as used in current
  1076. * drafts of <em>XLink</em>.
  1077. *
  1078. * @param id The value of the ID attribute which will be matched
  1079. * by any element which is returned.
  1080. * @deprecated As of DOM level 2, replaced by the method
  1081. * Document.getElementById
  1082. */
  1083. // Note: HTML DOM has getElementById() with "Element" return type
  1084. public ElementEx getElementExById (String id)
  1085. {
  1086. if (id == null)
  1087. throw new IllegalArgumentException (getMessage ("XD-000"));
  1088. TreeWalker w = new TreeWalker (this);
  1089. ElementEx element;
  1090. while ((element = (ElementEx) w.getNextElement (null)) != null) {
  1091. String idAttr = element.getIdAttributeName ();
  1092. String value;
  1093. if (idAttr == null)
  1094. continue;
  1095. value = element.getAttribute (idAttr);
  1096. if (value.equals (id))
  1097. return element;
  1098. }
  1099. return null;
  1100. }
  1101. /**
  1102. * @since DOM Level 2
  1103. */
  1104. public Node importNode(Node importedNode, boolean deep)
  1105. throws DOMException
  1106. {
  1107. // First make a copy of the subtree, then change the ownerDocument
  1108. // of the subtree.
  1109. Node node = null;
  1110. switch (importedNode.getNodeType()) {
  1111. case ATTRIBUTE_NODE:
  1112. node = importedNode.cloneNode(true);
  1113. break;
  1114. case DOCUMENT_FRAGMENT_NODE:
  1115. if (deep) {
  1116. node = importedNode.cloneNode(true);
  1117. } else {
  1118. node = new DocFragNode();
  1119. }
  1120. break;
  1121. case DOCUMENT_NODE:
  1122. case DOCUMENT_TYPE_NODE:
  1123. throw new DomEx(DomEx.NOT_SUPPORTED_ERR);
  1124. case ELEMENT_NODE:
  1125. node = ((ElementNode2) importedNode).createCopyForImportNode(deep);
  1126. break;
  1127. case ENTITY_NODE:
  1128. node = importedNode.cloneNode(deep);
  1129. break;
  1130. case ENTITY_REFERENCE_NODE:
  1131. node = importedNode.cloneNode(false);
  1132. break;
  1133. case NOTATION_NODE:
  1134. case PROCESSING_INSTRUCTION_NODE:
  1135. case TEXT_NODE:
  1136. case CDATA_SECTION_NODE:
  1137. case COMMENT_NODE:
  1138. default:
  1139. node = importedNode.cloneNode(false);
  1140. break;
  1141. }
  1142. // Change the ownerDocument of subtree root and any children
  1143. TreeWalker walker;
  1144. NodeBase n;
  1145. for (walker = new TreeWalker (node),
  1146. n = (NodeBase) walker.getCurrent ();
  1147. n != null;
  1148. n = (NodeBase) walker.getNext ()) {
  1149. n.setOwnerDocument (this);
  1150. // Elements have associated attributes, which must
  1151. // also have owners changed.
  1152. if (n instanceof Element) {
  1153. NamedNodeMap list = n.getAttributes ();
  1154. int length = list.getLength ();
  1155. for (int i = 0; i < length; i++)
  1156. changeNodeOwner (list.item (i));
  1157. }
  1158. }
  1159. return node;
  1160. }
  1161. //
  1162. // Represent document fragments other than the document itself.
  1163. // (This class is primarily motivated for use with editors.)
  1164. //
  1165. static final class DocFragNode extends ParentNode
  1166. implements DocumentFragment
  1167. {
  1168. // package private -- overrides base class method
  1169. void checkChildType (int type)
  1170. throws DOMException
  1171. {
  1172. switch (type) {
  1173. case ELEMENT_NODE:
  1174. case PROCESSING_INSTRUCTION_NODE:
  1175. case COMMENT_NODE:
  1176. case TEXT_NODE:
  1177. case CDATA_SECTION_NODE:
  1178. case ENTITY_REFERENCE_NODE:
  1179. return;
  1180. default:
  1181. throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
  1182. }
  1183. }
  1184. public void writeXml (XmlWriteContext context) throws IOException
  1185. {
  1186. this.writeChildrenXml (context);
  1187. }
  1188. public Node getParentNode ()
  1189. { return null; }
  1190. public void setParentNode (Node p)
  1191. { if (p != null) throw new IllegalArgumentException (); }
  1192. public short getNodeType ()
  1193. { return DOCUMENT_FRAGMENT_NODE; }
  1194. public String getNodeName () {
  1195. return ("#document-fragment");
  1196. }
  1197. public Node cloneNode (boolean deep)
  1198. {
  1199. DocFragNode retval = new DocFragNode ();
  1200. ((NodeBase)retval).setOwnerDocument
  1201. ((XmlDocument)this.getOwnerDocument ());
  1202. if (deep) {
  1203. Node node;
  1204. for (int i = 0; (node = item (i)) != null; i++) {
  1205. node = node.cloneNode (true);
  1206. retval.appendChild (node);
  1207. }
  1208. }
  1209. return retval;
  1210. }
  1211. }
  1212. //
  1213. // Represent entity references.
  1214. //
  1215. final static class EntityRefNode extends ParentNode
  1216. implements EntityReference
  1217. {
  1218. private String entity;
  1219. EntityRefNode (String name)
  1220. {
  1221. if (name == null)
  1222. throw new IllegalArgumentException (getMessage ("XD-002"));
  1223. entity = name;
  1224. }
  1225. // package private -- overrides base class method
  1226. void checkChildType (int type)
  1227. throws DOMException
  1228. {
  1229. switch (type) {
  1230. case ELEMENT_NODE:
  1231. case PROCESSING_INSTRUCTION_NODE:
  1232. case COMMENT_NODE:
  1233. case TEXT_NODE:
  1234. case CDATA_SECTION_NODE:
  1235. case ENTITY_REFERENCE_NODE:
  1236. return;
  1237. default:
  1238. throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
  1239. }
  1240. }
  1241. public void writeXml (XmlWriteContext context)
  1242. throws IOException
  1243. {
  1244. if (!context.isEntityDeclared (entity))
  1245. throw new IOException (getMessage ("XD-003", new Object[]
  1246. { entity }));
  1247. Writer out = context.getWriter ();
  1248. out.write ('&');
  1249. out.write (entity);
  1250. out.write (';');
  1251. }
  1252. public short getNodeType ()
  1253. { return ENTITY_REFERENCE_NODE; }
  1254. public String getNodeName ()
  1255. { return entity; }
  1256. public Node cloneNode (boolean deep) {
  1257. EntityRefNode retval = new EntityRefNode (entity);
  1258. ((NodeBase)retval).setOwnerDocument((
  1259. XmlDocument)this.getOwnerDocument ());
  1260. if (deep) {
  1261. Node node;
  1262. for (int i = 0; (node = item (i)) != null; i++) {
  1263. node = node.cloneNode (true);
  1264. retval.appendChild (node);
  1265. }
  1266. // XXX
  1267. //throw new RuntimeException (getMessage ("XD-001"));
  1268. }
  1269. return retval;
  1270. }
  1271. }
  1272. class ExtWriteContext extends XmlWriteContext
  1273. {
  1274. ExtWriteContext (Writer out) { super (out); }
  1275. ExtWriteContext (Writer out, int level) { super (out, level); }
  1276. public boolean isEntityDeclared (String name)
  1277. {
  1278. if (super.isEntityDeclared (name))
  1279. return true;
  1280. DocumentType doctype = getDoctype ();
  1281. if (doctype == null)
  1282. return false;
  1283. else
  1284. return doctype.getEntities ().getNamedItem (name) != null;
  1285. }
  1286. }
  1287. static class Catalog extends MessageCatalog
  1288. {
  1289. Catalog () { super (Catalog.class); }
  1290. }
  1291. }