1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.templating.inspector.formatter;
35
36 import info.magnolia.templating.inspector.spi.ValueFormatter;
37
38 import java.io.ByteArrayOutputStream;
39
40 import javax.xml.parsers.DocumentBuilder;
41 import javax.xml.parsers.DocumentBuilderFactory;
42 import javax.xml.parsers.ParserConfigurationException;
43 import javax.xml.transform.OutputKeys;
44 import javax.xml.transform.Transformer;
45 import javax.xml.transform.TransformerException;
46 import javax.xml.transform.TransformerFactory;
47 import javax.xml.transform.dom.DOMSource;
48 import javax.xml.transform.stream.StreamResult;
49
50 import org.apache.commons.lang3.StringUtils;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53 import org.w3c.dom.Document;
54 import org.w3c.dom.Element;
55
56
57
58
59 public class HtmlOutputter implements Outputter {
60
61 private static final Logger log = LoggerFactory.getLogger(HtmlOutputter.class);
62
63 private static final String CLASS = "mgnl-inspector-%s";
64 private static final String PRE_TAG = "pre";
65 private static final String SPAN_TAG = "span";
66 private static final String DIV_TAG = "div";
67 private static final String CLASS_ATTRIBUTE = "class";
68
69 private final Document document;
70 private final Element root;
71 private Element div;
72
73 private boolean isFormattingChildren = false;
74
75 public HtmlOutputter() {
76 DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
77 DocumentBuilder docBuilder;
78 try {
79 docBuilder = docFactory.newDocumentBuilder();
80 } catch (ParserConfigurationException e) {
81 log.warn("Unable to create document builder for Outputter", e);
82 throw new RuntimeException(e);
83 }
84 document = docBuilder.newDocument();
85 root = document.createElement(PRE_TAG);
86 }
87
88 @Override
89 public void formatSingleValue(ValueFormatter<?> valueFormatter) {
90 Element row = createRowElement(formatClassName(valueFormatter), valueFormatter.getValueAsString(), valueFormatter.getDescription());
91 root.appendChild(row);
92 root.appendChild(document.createTextNode("\n"));
93 }
94
95 @Override
96 public void formatMultiValue(ValueFormatter<?> valueFormatter) {
97 Element wrapper = createMultiValueWrapper();
98 Element row = createRowElement(formatClassName(valueFormatter), valueFormatter.getValueAsString(), valueFormatter.getDescription());
99 wrapper.appendChild(row);
100 root.appendChild(wrapper);
101 root.appendChild(document.createTextNode("\n"));
102 }
103
104 @Override
105 public void formatMaxDepthReached(ValueFormatter<?> valueFormatter) {
106 Element maxDepth = createMaxDepthReachedElement(formatClassName(valueFormatter), valueFormatter.getValueAsString(), valueFormatter.getDescription());
107 root.appendChild(maxDepth);
108 root.appendChild(document.createTextNode("\n"));
109 }
110
111 @Override
112 public void formatKey(String key) {
113 root.appendChild(createKeyElement(key));
114 root.appendChild(document.createTextNode(" = "));
115 }
116
117 @Override
118 public void indent(int level) {
119 root.appendChild(createIndentElement(level));
120 }
121
122 @Override
123 public String asString() {
124 try {
125 TransformerFactory transformerFactory = TransformerFactory.newInstance();
126 Transformer transformer = transformerFactory.newTransformer();
127 configureTransformer(transformer);
128 DOMSource source = new DOMSource(root);
129 ByteArrayOutputStream baos = new ByteArrayOutputStream();
130 StreamResult result = new StreamResult(baos);
131 transformer.transform(source, result);
132 return new String(baos.toByteArray());
133 } catch (TransformerException e) {
134 log.warn("Unable to transform DOM into String output", e);
135 }
136 return "";
137 }
138
139 protected void configureTransformer(Transformer transformer) {
140 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
141 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
142 transformer.setOutputProperty(OutputKeys.INDENT, "no");
143 transformer.setOutputProperty(OutputKeys.METHOD, "html");
144 }
145
146 private String formatClassName(ValueFormatter<?> valueFormatter) {
147 return valueFormatter.getName().toLowerCase();
148 }
149
150 private Element createRowElement(String className, String value, String description) {
151 Element row = document.createElement(SPAN_TAG);
152 row.setAttribute("class", String.format(CLASS, className));
153 row.appendChild(document.createTextNode(String.format(ROW, value, description)));
154 return row;
155 }
156
157 private Element createMultiValueWrapper() {
158 Element wrapper = document.createElement(SPAN_TAG);
159 wrapper.setAttribute(CLASS_ATTRIBUTE, String.format(CLASS, "expandable"));
160 return wrapper;
161 }
162
163 private Element createKeyElement(String value) {
164 Element key = document.createElement(SPAN_TAG);
165 key.setAttribute("class", String.format(CLASS, "key"));
166 key.appendChild(document.createTextNode(value));
167 return key;
168 }
169
170 private Element createIndentElement(int level) {
171 Element indent = document.createElement(SPAN_TAG);
172 indent.setAttribute("class", String.format(CLASS, "indent"));
173 indent.appendChild(document.createTextNode(" " + StringUtils.repeat(" ", level)));
174 return indent;
175 }
176
177 private Element createMaxDepthReachedElement(String className, String value, String description) {
178 Element row = document.createElement(SPAN_TAG);
179 row.setAttribute("class", String.format(CLASS, className));
180 row.appendChild(document.createTextNode(String.format(MAX_DEPTH, value, description)));
181 return row;
182 }
183 }