View Javadoc
1   /**
2    * This file Copyright (c) 2017-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
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   * Implementation of {@link Outputter} that wraps output into HTML tags.
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 }