001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019package org.apache.isis.core.commons.debug;
020
021import java.io.PrintWriter;
022import java.io.StringWriter;
023
024public abstract class DebugHtmlStringAbstract implements DebugBuilder {
025
026    private final boolean createPage;
027    private int tableLevel;
028    private boolean isOdd;
029    private boolean titleShown;
030    private boolean endLine;
031
032    public DebugHtmlStringAbstract(final boolean createPage) {
033        this.createPage = createPage;
034    }
035
036    @Override
037    public void concat(final DebugBuilder debug) {
038        appendHtml(debug.toString());
039    }
040
041    @Override
042    public void append(final int number, final int width) {
043        appendHtml(number + "");
044    }
045
046    @Override
047    public void append(final Object object) {
048        if (object instanceof DebuggableWithTitle) {
049            DebuggableWithTitle d = (DebuggableWithTitle) object;
050            appendTitle(d.debugTitle());
051            d.debugData(this);
052        } else {
053            appendHtml(object.toString());
054        }
055    }
056
057    @Override
058    public void append(final Object object, final int width) {
059        appendHtml(object.toString());
060    }
061
062    @Override
063    public void appendln() {
064        if (tableLevel > 0) {
065            endLine = true;
066        } else {
067            appendHtml("<p></p>");
068        }
069    }
070
071    @Override
072    public void blankLine() {
073        if (tableLevel > 0) {
074            appendHtml(row() + "<td class=\"error\" colspan=\"2\" >blank line</td></tr>");
075        } else {
076            appendHtml("<p>blank line</p>");
077        }
078    }
079
080    @Override
081    public void appendln(final String label, final boolean value) {
082        appendln(label, String.valueOf(value));
083    }
084
085    @Override
086    public void appendln(final String label, final double value) {
087        appendln(label, String.valueOf(value));
088    }
089
090    @Override
091    public void appendln(final String label, final long value) {
092        appendln(label, String.valueOf(value));
093    }
094
095    @Override
096    public void appendAsHexln(final String label, final long value) {
097        appendln(label, Long.toHexString(value));
098    }
099
100    @Override
101    public void appendPreformatted(final String label, final String object) {
102        final String value = object == null ? "null" : object.toString();
103        appendln(label, "<pre>" + value + "</pre>");
104    };
105
106    @Override
107    public void appendln(final String label, final Object object) {
108        final String value = object == null ? "null" : object.toString();
109        appendln(label, value);
110    }
111
112    @Override
113    public void appendln(final String label, final Object[] object) {
114        if (object.length == 0) {
115            appendln(label, "empty array");
116        } else {
117            appendln(label, object[0]);
118            for (int i = 1; i < object.length; i++) {
119                appendHtml(row() + "<td></td><td>" + object[i] + "</td></tr>");
120            }
121        }
122    }
123
124    @Override
125    public void startSection(final String title) {
126        startTableIfNeeded(true);
127        appendTitle(title);
128    }
129
130    @Override
131    public void endSection() {
132        endTableIfStarted();
133    }
134
135    @Override
136    public void indent() {
137        if (tableLevel > 0) {
138            appendHtml(row() + "<td>indented</td><td>");
139            startTableIfNeeded(true);
140        }
141    }
142
143    @Override
144    public void unindent() {
145        if (tableLevel > 0) {
146            endTableIfStarted();
147            appendHtml("</td>");
148        }
149    }
150
151    protected void header() {
152        if (createPage) {
153            appendHtml("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">");
154            appendHtml("<html>");
155            appendHtml("<head>");
156            appendHtml("<title>Debug Details</title>");
157            appendHtml("<style type=\"text/css\">");
158            appendHtml("body { margin: 15px; }\n" + ".links { background: #ddd; font-size: 80%; padding-bottom:5px; }\n" + ".links > p { display: inline-block; }\n" + "td {vertical-align: top; margin-left: 15px;}\n" + "td.error {color: red; font-style: italic; }\n" + "td.code {white-space: pre; font-family: monospace;}\n"
159                    + "th.title {text-align: left; padding: 0.3em 1em; font-style: italic; background: #AED1FF; }\n" + "td.label {width: 16em; text-align: right; padding-right: 1.5em; padding-top: 0.2em; font-size: 80%; font-weight: bold; }\n"
160                    + "span.facet-type { font-weight: bold; padding-right: 10px; }\n");
161            appendHtml("</style>");
162            appendHtml("</head>");
163            appendHtml("<body>");
164        }
165    }
166
167    protected abstract void appendHtml(String html);
168
169    protected void footer() {
170        endTableIfStarted();
171        if (createPage) {
172            appendHtml("</body>");
173            appendHtml("</html>");
174        }
175    }
176
177    @Override
178    public void appendException(final Throwable e) {
179        appendTitle("Exception");
180        final String message = e.getMessage();
181        if (message != null) {
182            appendHtml(row() + "<td class=\"error\" colspan=\"2\" >" + message + "</td></tr>");
183        }
184        causingException(e);
185        appendHtml(row() + "<td class=\"code\" colspan=\"2\" ><pre>");
186        final StringWriter stringWriter = new StringWriter();
187        final PrintWriter printWriter = new PrintWriter(stringWriter);
188        e.printStackTrace(printWriter);
189        appendHtml(stringWriter.toString());
190        appendHtml("</pre></td></tr>");
191
192    }
193
194    private void causingException(final Throwable throwable) {
195        final Throwable cause = throwable.getCause();
196        if (cause != null && cause != throwable) {
197            appendHtml(row() + "<td colspan=\"2\" >" + cause.getMessage() + "</td></tr>");
198            causingException(cause);
199        }
200    }
201
202    @Override
203    public void appendTitle(final String title) {
204        if (tableLevel > 0) {
205            String className = titleShown ? "subtitle" : "title";
206            appendHtml(row() + "<th class=\""+ className + "\" colspan=\"2\" >" + title + "</th></tr>");
207            titleShown = true;
208        } else {
209            appendHtml("<h2>" + title + "</h2>");
210        }
211    }
212
213    private void appendln(final String name, final String value) {
214        startTableIfNeeded(false);
215        appendHtml(row() + "<td class=\"label\">" + name + "</td><td>" + value + "</td></tr>");
216    }
217
218    private String row() {
219        final String line = (isOdd ? "odd" : "even") + (endLine ? " end-line" : ""); 
220        isOdd = !isOdd;
221        endLine = false;
222        return "<tr class=\"" + line + "\">";
223    }
224
225    private void startTableIfNeeded(final boolean b) {
226        if (tableLevel == 0 || b) {
227            appendHtml("<table class=\"debug\" summary=\"Debug details\" >");
228            tableLevel++;
229            titleShown = false;
230        }
231    }
232
233    private void endTableIfStarted() {
234        if (tableLevel > 0) {
235            appendHtml("</table>");
236            tableLevel--;
237        }
238    }
239
240    @Override
241    public void appendPreformatted(final String text) {
242        appendln("<pre>" + text + "</pre>");
243    }
244
245    @Override
246    public void appendln(final String text) {
247        if (tableLevel > 0) {
248            appendHtml(row() + "<td colspan=\"2\">" + text + "</td></tr>");
249        } else {
250            appendHtml("<p>" + text + "</p>");
251        }
252    }
253
254    @Override
255    public void close() {
256        endTableIfStarted();
257        doClose();
258    }
259
260    protected abstract void doClose();
261
262}