View Javadoc
1   /*
2    * Copyright (c) 2002-2026 Gargoyle Software Inc.
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    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.xml;
16  
17  import static java.nio.charset.StandardCharsets.UTF_8;
18  
19  import java.io.IOException;
20  import java.nio.charset.Charset;
21  
22  import javax.xml.parsers.ParserConfigurationException;
23  
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  import org.htmlunit.SgmlPage;
27  import org.htmlunit.WebResponse;
28  import org.htmlunit.WebWindow;
29  import org.htmlunit.html.DomElement;
30  import org.htmlunit.html.DomProcessingInstruction;
31  import org.htmlunit.util.MimeType;
32  import org.htmlunit.util.XmlUtils;
33  import org.w3c.dom.Attr;
34  import org.w3c.dom.DOMConfiguration;
35  import org.w3c.dom.DOMImplementation;
36  import org.w3c.dom.Document;
37  import org.w3c.dom.DocumentType;
38  import org.w3c.dom.Element;
39  import org.w3c.dom.EntityReference;
40  import org.w3c.dom.Node;
41  import org.xml.sax.SAXException;
42  
43  /**
44   * A page that will be returned for response with content type "text/xml".
45   *
46   * @author Marc Guillemot
47   * @author David K. Taylor
48   * @author Ahmed Ashour
49   * @author Frank Danek
50   * @author Ronald Brill
51   */
52  public class XmlPage extends SgmlPage {
53  
54      private static final Log LOG = LogFactory.getLog(XmlPage.class);
55  
56      private Node node_;
57  
58      /**
59       * Creates an instance.
60       * A warning is logged if an exception is thrown while parsing the XML content
61       * (for instance when the content is not a valid XML and can't be parsed).
62       *
63       * @param webResponse the response from the server
64       * @param enclosingWindow the window that holds the page
65       * @throws IOException if the page could not be created
66       */
67      public XmlPage(final WebResponse webResponse, final WebWindow enclosingWindow) throws IOException {
68          this(webResponse, enclosingWindow, true);
69      }
70  
71      /**
72       * Creates an instance.
73       * A warning is logged if an exception is thrown while parsing the XML content
74       * (for instance when the content is not a valid XML and can't be parsed).
75       *
76       * @param node the node to initialize this page with
77       * @param enclosingWindow the window that holds the page
78       */
79      public XmlPage(final Node node, final WebWindow enclosingWindow) {
80          super(null, enclosingWindow);
81          node_ = node;
82          if (node_ != null) {
83              XmlUtils.appendChild(this, this, node_, true);
84          }
85      }
86  
87      /**
88       * Creates an instance.
89       * A warning is logged if an exception is thrown while parsing the XML content
90       * (for instance when the content is not a valid XML and can't be parsed).
91       *
92       * @param webResponse the response from the server
93       * @param enclosingWindow the window that holds the page
94       * @param ignoreSAXException Whether to ignore {@link SAXException} or throw it as {@link IOException}
95       * @throws IOException if the page could not be created
96       */
97      public XmlPage(final WebResponse webResponse, final WebWindow enclosingWindow, final boolean ignoreSAXException)
98          throws IOException {
99          this(webResponse, enclosingWindow, ignoreSAXException, true);
100     }
101 
102     /**
103      * Creates an instance.
104      * A warning is logged if an exception is thrown while parsing the XML content
105      * (for instance when the content is not a valid XML and can't be parsed).
106      *
107      * @param webResponse the response from the server
108      * @param enclosingWindow the window that holds the page
109      * @param ignoreSAXException Whether to ignore {@link SAXException} or throw it as {@link IOException}
110      * @param handleXHTMLAsHTML if true elements from the XHTML namespace are handled as HTML elements instead of
111      *     DOM elements
112      * @throws IOException if the page could not be created
113      */
114     public XmlPage(final WebResponse webResponse, final WebWindow enclosingWindow, final boolean ignoreSAXException,
115         final boolean handleXHTMLAsHTML) throws IOException {
116         super(webResponse, enclosingWindow);
117 
118         try {
119             final Document document = XmlUtils.buildDocument(webResponse);
120             node_ = document.getFirstChild();
121         }
122         catch (final SAXException e) {
123             if (LOG.isWarnEnabled()) {
124                 LOG.warn("Failed parsing XML document '" + webResponse.getWebRequest().getUrl() + "'", e);
125             }
126             if (!ignoreSAXException) {
127                 throw new IOException(
128                         "Failed parsing XML document '" + webResponse.getWebRequest().getUrl() + "'", e);
129             }
130         }
131         catch (final ParserConfigurationException e) {
132             if (LOG.isWarnEnabled()) {
133                 if (webResponse == null) {
134                     LOG.warn("Failed parsing XML empty document: " + e.getMessage(), e);
135                 }
136                 else {
137                     LOG.warn("Failed parsing XML empty document '" + webResponse.getWebRequest().getUrl() + "'", e);
138                 }
139             }
140         }
141 
142         for (Node node = node_; node != null; node = node.getNextSibling()) {
143             XmlUtils.appendChild(this, this, node, handleXHTMLAsHTML);
144         }
145     }
146 
147     /**
148      * {@inheritDoc}
149      */
150     @Override
151     public void initialize() throws IOException {
152         // nothing to do here
153     }
154 
155     /**
156      * {@inheritDoc}
157      */
158     @Override
159     public boolean hasCaseSensitiveTagNames() {
160         return true;
161     }
162 
163     /**
164      * Returns the DOM representation of the XML content.
165      * @return {@code null} if the content couldn't be parsed
166      */
167     public Document getXmlDocument() {
168         if (node_ != null) {
169             return node_.getOwnerDocument();
170         }
171         return null;
172     }
173 
174     /**
175      * {@inheritDoc}
176      * Not yet implemented.
177      */
178     @Override
179     public Node adoptNode(final Node source) {
180         throw new UnsupportedOperationException("XmlPage.adoptNode is not yet implemented.");
181     }
182 
183     /**
184      * {@inheritDoc}
185      * Not yet implemented.
186      */
187     @Override
188     public Attr createAttributeNS(final String namespaceURI, final String qualifiedName) {
189         throw new UnsupportedOperationException("XmlPage.createAttributeNS is not yet implemented.");
190     }
191 
192     /**
193      * {@inheritDoc}
194      */
195     @Override
196     public DomElement createElement(final String tagName) {
197         return createElementNS(null, tagName);
198     }
199 
200     /**
201      * {@inheritDoc}
202      */
203     @Override
204     public DomElement createElementNS(final String namespaceURI, final String qualifiedName) {
205         return new DomElement(namespaceURI, qualifiedName, this, null);
206     }
207 
208     /**
209      * {@inheritDoc}
210      * Not yet implemented.
211      */
212     @Override
213     public EntityReference createEntityReference(final String name) {
214         throw new UnsupportedOperationException("XmlPage.createEntityReference is not yet implemented.");
215     }
216 
217     /**
218      * {@inheritDoc}
219      */
220     @Override
221     public DomProcessingInstruction createProcessingInstruction(final String target, final String data) {
222         return new DomProcessingInstruction(this, target, data);
223     }
224 
225     /**
226      * {@inheritDoc}
227      * Not yet implemented.
228      */
229     @Override
230     public String getDocumentURI() {
231         throw new UnsupportedOperationException("XmlPage.getDocumentURI is not yet implemented.");
232     }
233 
234     /**
235      * {@inheritDoc}
236      * Not yet implemented.
237      */
238     @Override
239     public DOMConfiguration getDomConfig() {
240         throw new UnsupportedOperationException("XmlPage.getDomConfig is not yet implemented.");
241     }
242 
243     /**
244      * {@inheritDoc}
245      * Not yet implemented.
246      */
247     @Override
248     public Element getElementById(final String elementId) {
249         throw new UnsupportedOperationException("XmlPage.getElementById is not yet implemented.");
250     }
251 
252     /**
253      * {@inheritDoc}
254      * Not yet implemented.
255      */
256     @Override
257     public DOMImplementation getImplementation() {
258         throw new UnsupportedOperationException("XmlPage.getImplementation is not yet implemented.");
259     }
260 
261     /**
262      * {@inheritDoc}
263      * Not yet implemented.
264      */
265     @Override
266     public String getInputEncoding() {
267         throw new UnsupportedOperationException("XmlPage.getInputEncoding is not yet implemented.");
268     }
269 
270     /**
271      * {@inheritDoc}
272      * Not yet implemented.
273      */
274     @Override
275     public boolean getStrictErrorChecking() {
276         throw new UnsupportedOperationException("XmlPage.getStrictErrorChecking is not yet implemented.");
277     }
278 
279     /**
280      * {@inheritDoc}
281      */
282     @Override
283     public String getXmlEncoding() {
284         return null;
285     }
286 
287     /**
288      * {@inheritDoc}
289      */
290     @Override
291     public boolean getXmlStandalone() {
292         return false;
293     }
294 
295     /**
296      * {@inheritDoc}
297      */
298     @Override
299     public String getXmlVersion() {
300         return "1.0";
301     }
302 
303     /**
304      * {@inheritDoc}
305      * Not yet implemented.
306      */
307     @Override
308     public Node importNode(final Node importedNode, final boolean deep) {
309         throw new UnsupportedOperationException("XmlPage.importNode is not yet implemented.");
310     }
311 
312     /**
313      * {@inheritDoc}
314      * Not yet implemented.
315      */
316     @Override
317     public Node renameNode(final Node n, final String namespaceURI, final String qualifiedName) {
318         throw new UnsupportedOperationException("XmlPage.renameNode is not yet implemented.");
319     }
320 
321     /**
322      * {@inheritDoc}
323      * Not yet implemented.
324      */
325     @Override
326     public void setDocumentURI(final String documentURI) {
327         throw new UnsupportedOperationException("XmlPage.setDocumentURI is not yet implemented.");
328     }
329 
330     /**
331      * {@inheritDoc}
332      * Not yet implemented.
333      */
334     @Override
335     public void setStrictErrorChecking(final boolean strictErrorChecking) {
336         throw new UnsupportedOperationException("XmlPage.setStrictErrorChecking is not yet implemented.");
337     }
338 
339     /**
340      * {@inheritDoc}
341      * Not yet implemented.
342      */
343     @Override
344     public void setXmlStandalone(final boolean xmlStandalone) {
345         throw new UnsupportedOperationException("XmlPage.setXmlStandalone is not yet implemented.");
346     }
347 
348     /**
349      * {@inheritDoc}
350      * Not yet implemented.
351      */
352     @Override
353     public void setXmlVersion(final String xmlVersion) {
354         throw new UnsupportedOperationException("XmlPage.setXmlVersion is not yet implemented.");
355     }
356 
357     /**
358      * {@inheritDoc}
359      */
360     @Override
361     public Charset getCharset() {
362         return UTF_8;
363     }
364 
365     /**
366      * {@inheritDoc}
367      */
368     @Override
369     public String getContentType() {
370         return MimeType.APPLICATION_XML;
371     }
372 
373     /**
374      * {@inheritDoc}
375      */
376     @Override
377     public void setDocumentType(final DocumentType type) {
378         super.setDocumentType(type);
379     }
380 
381     /**
382      * {@inheritDoc}
383      */
384     @Override
385     public void setNodeValue(final String value) {
386         // Default behavior is to do nothing, overridden in some subclasses
387     }
388 
389     /**
390      * {@inheritDoc}
391      */
392     @Override
393     public void setPrefix(final String prefix) {
394         // Empty.
395     }
396 }