The Document Interface as a Node Type


In addition to the factory methods and the methods common to all nodes, the Document interface has unique methods that perform operations relevant only to document nodes. These include

  • Getter methods

  • Methods to find elements

  • A method to copy nodes from other documents

Getter Methods

The Document interface has three methods that simply return particular parts of the document:

 public Element  getDocumentElement  ()  public DocumentType  getDoctype  () public DOMImplementation  getImplementation  () 

These are fairly self-explanatory. You've already seen the getDocumentElement() method used several times. It returns the Element object that represents the root element of the document. Similarly, the getDoctype() method returns the document's DocumentType object, or null if the document does not have a document type declaration. The getImplementation() method returns the DOMImplementation object that created this document.

Several pieces are missing. In particular, no part of the XML declaration is available: not version, not encoding, not standalone status. Other useful information that's missing from DOM2 includes the actual encoding of the document (which is usually but not always the same as the encoding declared in the XML declaration) and the base URI of the document against which relative URIs in the document should be resolved. DOM3 will add several more getter and setter methods to the Document interface to make these available:

 public String  getActualEncoding  ()  public void  setActualEncoding  (String  actualEncoding  ) public String  getEncoding  () public void  setEncoding  (String  encoding  ) public boolean  getStandalone  () public void  setStandalone  (boolean  standalone  ) public String  getVersion  () public void  setVersion  (String  version  ) public void  setBaseURI  (String  baseURI  ) throws DOMException 

The obvious getBaseURI() method is not really missing. It's just included in the Node super-interface rather than directly in the Document interface. Thus you can find out the base URI for any kind of node. This is important because XML documents can be built from multiple entities, and different nodes may come from different files.

 public String  getBaseURI  () 

Finally, DOM3 adds one more setter/getter pair that, strictly speaking, doesn't describe the document so much as the implementation. These two methods determine how draconian DOM is about checking for errors as the document is built in memory:

 public boolean  getStrictErrorChecking  ()  public void  setStrictErrorChecking  (boolean  strictErrorChecking  ) 

If the strict error-checking property is false, then the implementation may not make every test it could possibly make. For example, it might allow namespace prefixes that are not mapped to namespace URIs, or make a text node a child of the document element. This can be faster, but it is also dangerous, because other code may fail when presented with a Document object that does not satisfy all of the usual constraints. Strict error checking is enabled by default. Even if strict error checking is false, however, some error checking may still be done. The purpose of these methods is to allow implementations to skip some of the tedious checking they normally do and thus improve performance. The purpose is not to allow malformed documents, although that may be the effect in some cases.

These properties are experimentally supported in Xerces 2.0.2. No other parsers support them at the time of this writing. The detailed signatures are still subject to change, however, and you should not rely on them.

Example 10.10 is a simple program that parses a document from a URL passed through the command line and prints the values of these various properties. Because Xerces is currently the only parser to support the DOM3 properties, I used its implementation classes explicitly rather than the more generic JAXP.

Example 10.10 The Properties of a Document Object
 import org.apache.xerces.parsers.DOMParser; import org.apache.xerces.dom.DocumentImpl; import org.w3c.dom.*; import org.xml.sax.SAXException; import java.io.IOException; public class DocumentProperties {   public static void main(String[] args) {     if (args.length <= 0) {       System.out.println("Usage: java DocumentProperties URL");       return;     }     String url = args[0];     DOMParser parser = new DOMParser();     try {       parser.parse(url);       DocumentImpl document = (DocumentImpl) parser.getDocument();       // DOM2 properties       System.out.println("Implementation: " +        document.getImplementation());       System.out.println("Root element: " +        document.getDocumentElement());       System.out.println("DOCTYPE: " + document.getDoctype());       // DOM3 Properties       System.out.println("Version: " + document.getVersion());       System.out.println("Standalone: " +        document.getStandalone());       System.out.println("Declared encoding: " +        document.getEncoding());       System.out.println("Strict error checking: " +        document.getStrictErrorChecking());       System.out.println("Actual encoding: " +        document.getActualEncoding());       System.out.println("Base URI: " + document.getBaseURI());     }     catch (SAXException e) {       System.out.println(url + " is not well-formed.");     }     catch (IOException e) {       System.out.println(        "Due to an IOException, the parser could not read " + url       );     }   } } 

Running this program against the DocBook source code for this chapter produced the following ouput:

  $java DocumentProperties ch10.xml  Implementation: org.apache.xerces.dom.DOMImplementationImpl@ef9f1d Root element: [chapter: null] DOCTYPE: [chapter: null] Version: 1.0 Standalone: false Declared encoding: UTF-8 Strict error checking: true Actual encoding: UTF-8 Base URI: file: ///home/elharo/books/xmljava/ch10.xml 

In this case, the detailed output for the DOM2 properties depends on what the toString() method for each of the implementation classes does. A more serious application would use the methods of each interface ( Document , Doctype , and Element ) to provide more complete output.

Tip

If a non-Xerces DOM implementation precedes Xerces in your class path , then this program won't compile. You need to make sure Xerces is the first DOM the compiler and runtime find. This is particularly problematic in Java 1.4, which includes a DOM implementation that does not support the DOM3 properties used here. However, in Java 1.4 you can use the java interpreter's -Xbootclasspath/p: option to prepend JAR archives to the boot class path, so that they will be preferred to the ones bundled with Java.


Finding Elements

Some of the most useful methods in the Document interface are those that retrieve all of the elements with certain names or IDs in the document, irrespective of where in the document they may actually be. When you're only really interested in certain elements, this can avoid a lot of tedious and complex tree-walking . These three methods are

 public NodeList  getElementsByTagName  (String  tagName  )  public NodeList  getElementsByTagNameNS  (String  namespaceURI,  String  localName  ) public Element  getElementByID  () 

The first two methods return a NodeList of the elements with the specified name or local name /namespace URI pair. This list is in document order. You can use the asterisk ( * ) to match all names or all namespace URIs. The third method returns the single element with the specified ID value, or null if no such element is present in the document. The ID is given by an ID-type attribute on that element.

Caution

It is possible though invalid for multiple elements in one document to share the same ID. In this case, this method's behavior is undefined. For maximum safety, you may want to limit this method to provably valid documents.


As a demonstration, let's develop an XML-RPC servlet that generates Fibonacci numbers . (This actually was on the other side of the clients in Chapter 3 and Chapter 5.) Recall that the request document looks like Example 10.11. The server needs to find the integer value of the single param . Because we know there's exactly one int element in the request, it's easy to use getElementsByTagName() to find it.

Example 10.11 An XML-RPC Request Document
 <?xml version="1.0"?> <methodCall>   <methodName>calculateFibonacci</methodName>   <params>     <param>       <value><int>23</int></value>     </param>   </params> </methodCall> 

The server needs to calculate the result based on the input transmitted by the client, wrap that up in a response document like the one shown in Example 10.12, and transmit that document back to the client.

Example 10.12 An XML-RPC Response Document
 <?xml version="1.0"?> <methodResponse>   <params>     <param>       <value><double>28657</double></value>     </param>   </params> </methodResponse> 

Example 10.13 demonstrates the complete servlet. It extends HttpServlet and implements SingleThreadModel . This interface notifies the servlet container that this servlet is not thread safe, and it should use a different instance of this class for each concurrent thread. However, one instance may be used for successive threads. This was necessary here because the JAXP DocumentBuilder and Transformer classes and possibly the class that implements DOMImplementation are not thread safe. You could make the servlet thread safe by loading new instances of these interfaces inside doPost() rather than sharing instances created in init() . However, in a potentially high-volume server environment, the resource cost for that feels disturbingly large. A better alternative would be to manually synchronize access to these objects inside doPost() . But because proper synchronization is notoriously difficult, I prefer to leave the work to the server.

Example 10.13 A DOM-Based XML-RPC Servlet
 import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.math.BigInteger; import org.w3c.dom.*; import org.xml.sax.SAXException; import javax.xml.parsers.*; import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; public class FibonacciXMLRPCDOMServlet extends HttpServlet  implements SingleThreadModel {   // Fault codes   public final static int MALFORMED_REQUEST_DOCUMENT = 1;   public final static int INVALID_REQUEST_DOCUMENT   = 2;   public final static int INDEX_MISSING              = 3;   public final static int NON_POSITIVE_INDEX         = 4;   public final static int BAD_INTEGER_FORMAT         = 5;   public final static int UNEXPECTED_PROBLEM         = 255;   private DocumentBuilder   parser;   private DOMImplementation impl;   private Transformer       idTransform;   // Load a parser, transformer, and implementation   public void init() throws ServletException {     try {       DocumentBuilderFactory factory        = DocumentBuilderFactory.newInstance();       this.parser = factory.newDocumentBuilder();       this.impl   = parser.getDOMImplementation();     }     catch (Throwable t) {       // It's unusual to catch a generic Throwable instead of an       // exception. Here I'm specifically worried about       // FactoryConfigurationErrors and       // ParserConfigurationExceptions, both of which are real       // possibilities in a servlet environment because of the       // weird ways servlet containers arrange classpaths.       throw new ServletException(        "Could not locate a JAXP parser", t);     }     try {       TransformerFactory xformFactory        = TransformerFactory.newInstance();       this.idTransform = xformFactory.newTransformer();     }     catch (Throwable t) {       throw new ServletException(        "Could not locate a JAXP transformer", t);     }   }   // Respond to an XML-RPC request   public void doPost(HttpServletRequest servletRequest,    HttpServletResponse servletResponse)    throws ServletException, IOException {     servletResponse.setContentType("text/xml; charset=UTF-8");       PrintWriter out = servletResponse.getWriter();       InputStream in  = servletRequest.getInputStream();       Document request;       Document response;       try {         request = parser.parse(in);       NodeList ints = request.getElementsByTagName("int");       if (ints.getLength() == 0) {         // XML-RPC allows i4 as an alias for int.         ints = request.getElementsByTagName("i4");       }       Node input = ints.item(0); // throws NullPointerException       String generations = getFullText(input);       int numberOfGenerations = Integer.parseInt(generations);       BigInteger result = calculateFibonacci(numberOfGenerations);       response = makeResponseDocument(result);     }     catch (SAXException e) {       response = makeFaultDocument(MALFORMED_REQUEST_DOCUMENT,        e.getMessage());     }     catch (NullPointerException e) {       response = makeFaultDocument(INDEX_MISSING, e.getMessage());     }     catch (NumberFormatException e) {       response = makeFaultDocument(BAD_INTEGER_FORMAT,        e.getMessage());     }     catch (IndexOutOfBoundsException e) {       response = makeFaultDocument(NON_POSITIVE_INDEX,        e.getMessage());     }     catch (Exception e) {       response = makeFaultDocument(UNEXPECTED_PROBLEM,        e.getMessage());     }     // Transform onto the OutputStream     try {       Source input = new DOMSource(response);       Result output = new StreamResult(out);       idTransform.transform(input, output);       servletResponse.flushBuffer();       out.flush();       out.println();     }     catch (TransformerException e) {       // If we get an exception at this point, it's too late to       // switch over to an XML-RPC fault.       throw new ServletException(e);     }   }   // Given a node that does not contain any Element children,   // accumulate all its text content from both text nodes and   // CDATA sections (but not comments or processing instructions)   // and return it as a single string.   private static String getFullText(Node node) {     StringBuffer result = new StringBuffer();     NodeList children = node.getChildNodes();     for (int i = 0; i < children.getLength(); i++) {       Node child = children.item(i);       int type = child.getNodeType();       if (type == Node.TEXT_NODE         type == Node.CDATA_SECTION_NODE) {         result.append(child.getNodeValue());       }       else if (type == Node.ENTITY_REFERENCE_NODE) {         // The JAXP spec is unclear about whether or not it's         // possible for entity reference nodes to appear in the         // tree. Just in case, let's expand them recursively:         result.append(getFullText(child));         // Validity does require that if they do appear their         // replacement text is pure text, no elements.       }     }     return result.toString();   }   // If performance is an issue, this could be pre-built in the   // init() method and then cached. You'd just change one text   // node each time.  This would only work in a SingleThreadModel   // servlet.   public Document makeResponseDocument(BigInteger result) {     Document response      = impl.createDocument(null, "methodResponse", null);     Element methodResponse = response.getDocumentElement();     Element params         = response.createElement("params");     Element param          = response.createElement("param");     Element value          = response.createElement("value");     Element doubleElement  = response.createElement("double");     Text    text = response.createTextNode(result.toString());     methodResponse.appendChild(params);     params.appendChild(param);     param.appendChild(value);     value.appendChild(doubleElement);     doubleElement.appendChild(text);     return response;   }   public Document makeFaultDocument(int faultCode,    String faultString) {     Document faultDoc      = impl.createDocument(null, "methodResponse", null);     Element methodResponse = faultDoc.getDocumentElement();     Element fault         = faultDoc.createElement("fault");     Element value         = faultDoc.createElement("value");     Element struct        = faultDoc.createElement("struct");     Element memberCode    = faultDoc.createElement("member");     Element nameCode      = faultDoc.createElement("name");     Text    nameCodeText  = faultDoc.createTextNode("faultCode");     Element valueCode     = faultDoc.createElement("value");     Element intCode       = faultDoc.createElement("int");     String  codeString    = String.valueOf(faultCode);     Text    textCode      = faultDoc.createTextNode(codeString);     Element doubleElement = faultDoc.createElement("double");     Element memberString  = faultDoc.createElement("member");     Element nameString    = faultDoc.createElement("name");     Text    nameText      = faultDoc.createTextNode("faultString");     Element valueString   = faultDoc.createElement("value");     Element stringString  = faultDoc.createElement("string");     Text    textString    = faultDoc.createTextNode(faultString);     methodResponse.appendChild(fault);     fault.appendChild(value);     value.appendChild(struct);     struct.appendChild(memberCode);     struct.appendChild(memberString);     memberCode.appendChild(nameCode);     memberCode.appendChild(valueCode);     memberString.appendChild(nameString);     memberString.appendChild(valueString);     nameCode.appendChild(nameCodeText);     nameString.appendChild(nameText);     valueCode.appendChild(intCode);     valueString.appendChild(stringString);     intCode.appendChild(textCode);     stringString.appendChild(textString);     return faultDoc;   }   public static BigInteger calculateFibonacci(int generations)    throws IndexOutOfBoundsException {     if (generations < 1) {       throw new IndexOutOfBoundsException(        "Fibonacci numbers are not defined for " + generations        + "or any other number less than one.");     }     BigInteger low  = BigInteger.ONE;     BigInteger high = BigInteger.ONE;     for (int i = 2; i <= generations; i++) {       BigInteger temp = high;       high = high.add(low);       low = temp;     }     return low;   } } 

To compile FibonacciXMLRPCDOMServlet , you will need to install the Java Servlet API somewhere in your class path. This is not included in the base distribution of the JDK. To get it to run in most servlet containers, therefore, you'll need to add the JAR files for the DOM and JAXP implementations to the servlet's library. In Tomcat, that directory is $TOMCAT_HOME/lib . Note that it is not enough to have these files in the virtual machine's default ext directory because most servlet engines do not load classes from there (details are in Chapter 3).

The servlet is divided into six methods: init() doPost() , getFullText() , makeResponseDocument() , makeFaultDocument() , and calculateFibonacci() . The init() method, the servlet substitute for a constructor, is responsible for finding a DOMImplementation class and loading a parser and a transformer engine. There's no reason to waste time creating new DOMImplementations for each request.

doPost() is the standard servlet method for responding to HTTP POST. Each POST to this servlet represents a separate XML-RPC request. This method first uses getElementsByTagName() to find the single int element in this request. Then it extracts the text content of this element and converts it to a Java int . This is more involved than you might expect because of the possibility that this text might not be part of a single node. For example, any of these three legal elements would give the int element multiple children:

 <int>12<!-- Why is this comment here? -->34</int>  <int><?target data?>1234</int <int>12<![CDATA[34]]></int> 

Admittedly these are unusual cases, but they must be handled because they are legal. Comments and CDATA sections could be eliminated at parse time with the DocumentBuilderFactory 's setCoalescing(true) and setIgnoringComments(true) methods, but that still leaves the possibility of a processing instruction.

There are also a lot of illegal things we aren't handling. For example, nothing notices if the methodName element is missing or if the root element isn't methodCall . The proper way to handle this is to write a schema and validate the document against it before processing. Example 2.14 demonstrated an XML-RPC schema, but actually validating against this requires resorting to parser-specific classes. If you're using Xerces, the following code would do the trick:

 DOMParser parser = new DOMParser();  parser.setErrorHandler(  YourErrorHandler  ); parser.setFeature(  "http://apache.org/xml/features/validation/schema", true); parser.setProperty(  "http://apache.org/xml/properties/schema/"  + "external-noNamespaceSchemaLocation",  "http://example.com/schemas/xmlrpc.xsd"); parser.parse(in); Document doc = parser.getDocument(); // Work with the parser as before 

YourErrorHandler is an instance of some org.xml.sax.ErrorHandler implementation that throws a SAXException on detecting a validity error. Other schema-validating parsers such as the Oracle XML Parser for Java have slightly different APIs for checking a document against a known schema. Neither JAXP nor DOM2 provides a standard way to do this. When finished and implemented, DOM3 should allow you to perform schema validation in a parser-independent fashion.

Assuming that the request document is valid, the next step is to calculate the requested Fibonacci number with the calculateFibonacci() method. There's nothing new here, just math. This method doesn't have to know anything about XML, DOM, or XML-RPC. If the original XML-RPC request contained a nonpositive integer, then this method detects it and throws an IndexOutOfBoundsException . This will be caught in the doPost() method and converted into an XML-RPC fault response.

Once the result has been calculated, the makeResponseDocument() method wraps it up in properly formatted XML-RPC. If at any point something goes wrongfor example, the request document is missing the required int elementthen an exception is thrown. This is caught, and instead of the normal response, makeFaultDocument() is called to produce a proper XML-RPC fault response document.

Finally, a JAXP identity transform copies the finished result document onto the servlet's output Writer . There's not a lot we can do about an exception at this point, so any TransformerException s caught during the transform are converted into a ServletException , with the original exception kept available as the root cause of the ServletException . We can't just let the TransformerException exception bubble up because doPost() in the superclass is not declared to throw it.

The SOAP servlet is similar in structure. But because namespaces are significant in SOAP, getElementsByTagNameNS() and other namespace-aware methods should be used. Example 10.14 demonstrates.

Example 10.14 A DOM-Based SOAP Servlet
 import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.math.BigInteger; import org.w3c.dom.*; import org.xml.sax.SAXException; import javax.xml.parsers.*; import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; public class FibonacciSOAPDOMServlet extends HttpServlet   implements SingleThreadModel {   // Fault codes   public final static String MALFORMED_REQUEST_DOCUMENT    = "MalformedRequest";   public final static String INVALID_REQUEST_DOCUMENT    = "InvalidRequest";   public final static String INDEX_MISSING    = "IndexMissing";   public final static String NON_POSITIVE_INDEX    = "NonPositiveIndex";   public final static String BAD_INTEGER_FORMAT   = "BadIntegerFormat";   public final static String UNEXPECTED_PROBLEM   = "UnexpectedProblem";   private DocumentBuilder   parser;   private DOMImplementation impl;   private Transformer       idTransform;   // Load a parser, transformer, and implementation   public void init() throws ServletException {     try {       DocumentBuilderFactory factory        = DocumentBuilderFactory.newInstance();       // Always turn on namespace awareness       factory.setNamespaceAware(true);       this.parser = factory.newDocumentBuilder();       this.impl   = parser.getDOMImplementation();     }     catch (Throwable t) {       throw new ServletException(        "Could not locate a JAXP parser", t);     }     try {       TransformerFactory xformFactory =        TransformerFactory.newInstance();       this.idTransform = xformFactory.newTransformer();     }     catch (Throwable t) {       throw new ServletException(        "Could not locate a JAXP transformer", t);     }   }   public void doPost(HttpServletRequest servletRequest,    HttpServletResponse servletResponse)    throws ServletException, IOException {     servletResponse.setContentType("text/xml; charset=UTF-8");     PrintWriter out = servletResponse.getWriter();     InputStream in  = servletRequest.getInputStream();     Document request;     Document response;     String generations ="here";     try {       request = parser.parse(in);       NodeList ints = request.getElementsByTagNameNS(        "http://namespaces.cafeconleche.org/xmljava/ch3/",        "calculateFibonacci");       Node input = ints.item(0);       generations = getFullText(input);       int numberOfGenerations = Integer.parseInt(generations);       BigInteger result = calculateFibonacci(numberOfGenerations);       response = makeResponseDocument(result);     }     catch (SAXException e) {       response = makeFaultDocument(MALFORMED_REQUEST_DOCUMENT,        e.getMessage());     }     catch (NullPointerException e) {       response = makeFaultDocument(INDEX_MISSING,        e.getMessage());     }     catch (NumberFormatException e) {       response = makeFaultDocument(BAD_INTEGER_FORMAT,        generations + e.getMessage());     }     catch (IndexOutOfBoundsException e) {       response = makeFaultDocument(NON_POSITIVE_INDEX,        e.getMessage());     }     catch (Exception e) {       response = makeFaultDocument(UNEXPECTED_PROBLEM,        e.getMessage());     }     // Transform onto the OutputStream     try {       Source input = new DOMSource(response);       Result output = new StreamResult(out);       idTransform.transform(input, output);       servletResponse.flushBuffer();       out.flush();       out.println();     }     catch (TransformerException e) {       // If we get an exception at this point, it's too late to       // switch over to a SOAP fault       throw new ServletException(e);     }   }   private static String getFullText(Node node) {     StringBuffer result = new StringBuffer();     NodeList children = node.getChildNodes();     for (int i = 0; i < children.getLength(); i++) {       Node child = children.item(i);       int type = child.getNodeType();       if (type == Node.TEXT_NODE         type == Node.CDATA_SECTION_NODE) {         result.append(child.getNodeValue());       }     }     return result.toString();   }   // The details of the formats and namespace URIs are likely to   // change when SOAP 1.2 is released.   public Document makeResponseDocument(BigInteger result) {     Document response      = impl.createDocument(       "http://schemas.xmlsoap.org/soap/envelope/",       "SOAP-ENV:Envelope", null);     Element envelope = response.getDocumentElement();     Element body = response.createElementNS(      "http://schemas.xmlsoap.org/soap/envelope/",      "SOAP-ENV:Body");     envelope.appendChild(body);     Element Fibonacci_Numbers = response.createElementNS(      "http://namespaces.cafeconleche.org/xmljava/ch3/",      "Fibonacci_Numbers");     body.appendChild(Fibonacci_Numbers);     Element fibonacci = response.createElementNS(      "http://namespaces.cafeconleche.org/xmljava/ch3/",      "fibonacci");     Fibonacci_Numbers.appendChild(fibonacci);     Text text = response.createTextNode(result.toString());     fibonacci.appendChild(text);     return response;   }   public Document makeFaultDocument(String code, String message){     Document faultDoc = impl.createDocument(       "http://schemas.xmlsoap.org/soap/envelope/",       "SOAP-ENV:Envelope", null);     Element envelope = faultDoc.getDocumentElement();     Element body = faultDoc.createElementNS(      "http://schemas.xmlsoap.org/soap/envelope/",      "SOAP-ENV:Body");     envelope.appendChild(body);     Element fault = faultDoc.createElementNS(      "http://schemas.xmlsoap.org/soap/envelope/", "Fault");     body.appendChild(fault);     Element faultCode = faultDoc.createElement("faultcode");     fault.appendChild(faultCode);     Element faultString = faultDoc.createElement("faultstring");     fault.appendChild(faultString);     Text textCode = faultDoc.createTextNode(code);     faultCode.appendChild(textCode);     Text textString = faultDoc.createTextNode(message);     faultString.appendChild(textString);     return faultDoc;   }   public static BigInteger calculateFibonacci(int generations)    throws IndexOutOfBoundsException {     if (generations < 1) {       throw new IndexOutOfBoundsException(        "Fibonacci numbers are not defined for " + generations        + "or any other number less than one.");     }     BigInteger low  = BigInteger.ONE;     BigInteger high = BigInteger.ONE;     for (int i = 2; i <= generations; i++) {       BigInteger temp = high;       high = high.add(low);       low = temp;     }     return low;   } } 

This example has the same basic structure as the XML-RPC version (Example 10.13). That is, the init() method loads the parser, DOM implementation, and identity transform. The doPost() method reads the request data and delegates building the request to the makeResponseDocument() method. If anything goes wrong, makeFaultDocument() is called to produce a properly formatted SOAP fault response. Finally, a JAXP ID transform serializes the response onto the network stream. The formats of the request and response are a little different, but the program flow is the same.

I did structure the building of the two response documents (success and fault) a little differently. FibonacciXMLRPCServlet built all of the nodes first and then connected them. Here I added them to the tree as soon as they were created. There's not a lot of reason to choose one way over the otherjust use whichever seems more natural to you.

The details can be a little opaque . In a real-world program, I'd definitely add some comments containing an example of the documents that each method builds. If you're building a lot of XML-RPC or SOAP documents with varying parameters, then it wouldn't hurt to either develop a more generic library, or buy or borrow a third-party library such as Apache SOAP [http://xml.apache.org/soap/docs/index.html]. Behind the scenes, they're doing something very similar to what I've done in Example 10.13 and Example 10.14.

Transferring Nodes between Documents

In DOM, every node belongs to exactly one document at all times. It cannot exist independently of a Document , and it cannot be part of more than one Document at the same time. This is why the Document interface serves as the factory for creating node objects of various kinds. Furthermore, in DOM2, a node cannot be detached from its original document and placed in a new document, although this restriction is loosened somewhat in DOM3.

Copying Nodes

The importNode() method makes a copy of a node found in another document. The copy can then be inserted in the importing document's tree using the usual methods, such as insertBefore() and appendChild() . The document from which the node is imported is not changed in any way.

 public Node  importNode  (Node  toBeImported,  boolean  deep  )  throws DOMException 

The deep argument determines whether or not all of the node's descendants are copied with it. If true, they are; if false, they aren't.

Document and document type nodes cannot be imported. Trying to import one will throw a DOMException . Entity and notation nodes can be imported but cannot be added as children of the importing document's document type node. Thus there's little reason to import them.

Most of the other kinds of nodes can be imported with pretty much the results you'd expect. (Just remember to pass true for the second argument. This is almost always what you want.) The only really tricky ones are entity reference nodes. Even if deep is true, the children of the entity reference node are not copied. Instead the replacement text (node list) of the imported entity reference depends on what the importing document's DTD says it is, not the original replacement text.

Moving Nodes

DOM3 adds an adoptNode() method that moves a node from one document to another. That is, it deletes the node from the original document and inserts it into the new document:

 public Node  adoptNode  (Node  adoptee  ) throws DOMException 

Adopted nodes are always moved with all of their descendants intact. Otherwise, this method behaves pretty much like importNode . That is, document nodes, document type nodes, notation nodes, and entity nodes cannot be adopted. All other kinds can be. Descendants of entity reference nodes are deleted from the original document but not copied into the new document.

As usual with DOM3 methods, this is beyond the bleeding edge. The latest versions of Xerces support it, but no other parsers do. There also remain a lot of unresolved issues concerning the behavior of this method, so I wouldn't rely on it. For the time being, it's better to use a two-step procedure in which a node is first copied into the new document with importNode() and then deleted from the original document with removeChild() .



Processing XML with Java. A Guide to SAX, DOM, JDOM, JAXP, and TrAX
Processing XML with Javaв„ў: A Guide to SAX, DOM, JDOM, JAXP, and TrAX
ISBN: 0201771861
EAN: 2147483647
Year: 2001
Pages: 191

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net