View Javadoc
1   /*
2    * Copyright (c) 2002-2017 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    * http://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 com.gargoylesoftware.htmlunit.activex.javascript.msxml;
16  
17  import org.w3c.dom.NamedNodeMap;
18  
19  import com.gargoylesoftware.htmlunit.html.DomAttr;
20  import com.gargoylesoftware.htmlunit.html.DomElement;
21  import com.gargoylesoftware.htmlunit.html.DomNode;
22  import com.gargoylesoftware.htmlunit.javascript.host.dom.Node;
23  import com.gargoylesoftware.htmlunit.util.StringUtils;
24  
25  /**
26   * A JavaScript object for XMLSerializer.
27   *
28   * @author Ahmed Ashour
29   * @author Darrell DeBoer
30   * @author Ronald Brill
31   * @author Frank Danek
32   */
33  public class XMLSerializer {
34  
35      private boolean preserveWhiteSpace_;
36  
37      /**
38       * @param preserveWhiteSpace whether to preserve whitespaces or not
39       */
40      public XMLSerializer(final boolean preserveWhiteSpace) {
41          preserveWhiteSpace_ = preserveWhiteSpace;
42      }
43  
44      /**
45       * The subtree rooted by the specified element is serialized to a string.
46       * @param root the root of the subtree to be serialized (this may be any node, even a document)
47       * @return the serialized string
48       */
49      public String serializeToString(XMLDOMNode root) {
50          if (root == null) {
51              return "";
52          }
53          if (root instanceof XMLDOMDocument) {
54              root = ((XMLDOMDocument) root).getDocumentElement();
55          }
56          else if (root instanceof XMLDOMDocumentFragment) {
57              root = root.getFirstChild();
58          }
59          if (root instanceof XMLDOMElement) {
60              final StringBuilder builder = new StringBuilder();
61              final DomNode node = root.getDomNodeOrDie();
62  
63              toXml(1, node, builder);
64  
65              builder.append("\r\n");
66              return builder.toString();
67          }
68          return root.getDomNodeOrDie().asXml();
69      }
70  
71      private void toXml(final int indent,
72              final DomNode node, final StringBuilder builder) {
73          final String nodeName = node.getNodeName();
74          builder.append('<').append(nodeName);
75  
76          final String optionalPrefix = "";
77          final String namespaceURI = node.getNamespaceURI();
78          final String prefix = node.getPrefix();
79          if (namespaceURI != null && prefix != null) {
80              boolean sameNamespace = false;
81              for (DomNode parentNode = node.getParentNode(); parentNode instanceof DomElement;
82                      parentNode = parentNode.getParentNode()) {
83                  if (namespaceURI.equals(parentNode.getNamespaceURI())) {
84                      sameNamespace = true;
85                  }
86              }
87              if (node.getParentNode() == null || !sameNamespace) {
88                  ((DomElement) node).setAttribute("xmlns:" + prefix, namespaceURI);
89              }
90          }
91  
92          final NamedNodeMap attributesMap = node.getAttributes();
93          for (int i = 0; i < attributesMap.getLength(); i++) {
94              final DomAttr attrib = (DomAttr) attributesMap.item(i);
95              builder.append(' ').append(attrib.getQualifiedName()).append('=')
96                  .append('"').append(attrib.getValue()).append('"');
97          }
98          boolean startTagClosed = false;
99          for (final DomNode child : node.getChildren()) {
100             if (!startTagClosed) {
101                 builder.append(optionalPrefix).append('>');
102                 startTagClosed = true;
103             }
104             switch (child.getNodeType()) {
105                 case Node.ELEMENT_NODE:
106                     toXml(indent + 1, child, builder);
107                     break;
108 
109                 case Node.TEXT_NODE:
110                     String value = child.getNodeValue();
111                     value = StringUtils.escapeXmlChars(value);
112                     if (preserveWhiteSpace_) {
113                         builder.append(value.replace("\n", "\r\n"));
114                     }
115                     else if (org.apache.commons.lang3.StringUtils.isBlank(value)) {
116                         builder.append("\r\n");
117                         final DomNode sibling = child.getNextSibling();
118                         if (sibling != null && sibling.getNodeType() == Node.ELEMENT_NODE) {
119                             for (int i = 0; i < indent; i++) {
120                                 builder.append('\t');
121                             }
122                         }
123                     }
124                     else {
125                         builder.append(value.replace("\n", "\r\n"));
126                     }
127                     break;
128 
129                 case Node.CDATA_SECTION_NODE:
130                 case Node.COMMENT_NODE:
131                     if (!preserveWhiteSpace_ && builder.charAt(builder.length() - 1) == '\n') {
132                         for (int i = 0; i < indent; i++) {
133                             builder.append('\t');
134                         }
135                     }
136                     builder.append(child.asXml());
137                     break;
138 
139                 default:
140 
141             }
142         }
143         if (!startTagClosed) {
144             builder.append(optionalPrefix).append("/>");
145         }
146         else {
147             if (!preserveWhiteSpace_ && builder.charAt(builder.length() - 1) == '\n') {
148                 for (int i = 0; i < indent - 1; i++) {
149                     builder.append('\t');
150                 }
151             }
152             builder.append('<').append('/').append(nodeName).append('>');
153         }
154     }
155 
156 }