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