blob: 9ffc0e2aeeb9cb65a243a2bcb5f0b5b9c4c4a0d3 [file] [log] [blame]
Yuta HIGUCHIb34078e2017-08-17 12:06:02 -07001/*
2 * Copyright 2017-present Open Networking Foundation
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 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onlab.util;
17
18import java.io.IOException;
19import java.io.StringWriter;
20
21import javax.xml.parsers.DocumentBuilderFactory;
22import javax.xml.transform.OutputKeys;
23import javax.xml.transform.Transformer;
24import javax.xml.transform.TransformerFactory;
25import javax.xml.transform.dom.DOMSource;
26import javax.xml.transform.stream.StreamResult;
27import javax.xml.xpath.XPath;
28import javax.xml.xpath.XPathConstants;
29import javax.xml.xpath.XPathFactory;
30
31import org.slf4j.Logger;
32import org.slf4j.LoggerFactory;
33import org.w3c.dom.Document;
34import org.w3c.dom.Node;
35import org.w3c.dom.NodeList;
36import org.xml.sax.InputSource;
37import com.google.common.base.Supplier;
38import com.google.common.base.Suppliers;
39import com.google.common.io.CharSource;
40
41/**
42 * PrettyPrinted XML String.
43 */
44public class XmlString implements CharSequence {
45
46 private static final Logger log = LoggerFactory.getLogger(XmlString.class);
47
48 private final Supplier<String> prettyString;
49
50
51 /**
52 * Prettifies given XML String.
53 *
54 * @param xml input XML
55 * @return prettified input or input itself is input is not well-formed
56 */
57 public static CharSequence prettifyXml(CharSequence xml) {
58 return new XmlString(CharSource.wrap(xml));
59 }
60
61 XmlString(CharSource inputXml) {
62 prettyString = Suppliers.memoize(() -> prettyPrintXml(inputXml));
63 }
64
65 private String prettyPrintXml(CharSource inputXml) {
66 try {
67 Document document = DocumentBuilderFactory.newInstance()
68 .newDocumentBuilder()
69 .parse(new InputSource(inputXml.openStream()));
70
71 document.normalize();
72
73 XPath xPath = XPathFactory.newInstance().newXPath();
74 NodeList nodeList = (NodeList) xPath.evaluate("//text()[normalize-space()='']",
75 document,
76 XPathConstants.NODESET);
77
78 for (int i = 0; i < nodeList.getLength(); ++i) {
79 Node node = nodeList.item(i);
80 node.getParentNode().removeChild(node);
81 }
82
83 // Setup pretty print options
84 Transformer t = TransformerFactory.newInstance().newTransformer();
85 t.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
86 t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
87 t.setOutputProperty(OutputKeys.INDENT, "yes");
88 t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
89
90 // Return pretty print xml string
91 StringWriter strWriter = new StringWriter();
92 t.transform(new DOMSource(document), new StreamResult(strWriter));
93 return strWriter.toString();
94 } catch (Exception e) {
95 log.warn("Pretty printing failed", e);
96 try {
97 String rawInput = inputXml.read();
98 log.debug(" failed input: \n{}", rawInput);
99 return rawInput;
100 } catch (IOException e1) {
101 log.error("Failed to read from input", e1);
102 return inputXml.toString();
103 }
104 }
105 }
106
107 @Override
108 public int length() {
109 return toString().length();
110 }
111
112 @Override
113 public char charAt(int index) {
114 return toString().charAt(index);
115 }
116
117 @Override
118 public CharSequence subSequence(int start, int end) {
119 return toString().subSequence(start, end);
120 }
121
122 @Override
123 public String toString() {
124 return prettyString.get();
125 }
126
127}