001package org.javasimon.console.html;
002
003import org.javasimon.Simon;
004import org.javasimon.console.SimonType;
005import org.javasimon.console.reflect.Getter;
006import org.javasimon.console.reflect.GetterFactory;
007import org.javasimon.console.text.StringifierFactory;
008
009import java.io.IOException;
010import java.io.Writer;
011
012/**
013 * Class to ease HTML generation.
014 *
015 * @author gquintana
016 */
017public class HtmlBuilder<T extends HtmlBuilder> {
018
019        /** Delegate write. */
020        private final Writer writer;
021        /** Stringifier factory. */
022        private final StringifierFactory stringifierFactory;
023
024        /** Constructor without stringifier factory. */
025        public HtmlBuilder(Writer writer) {
026                this.writer = writer;
027                this.stringifierFactory = null;
028        }
029
030        /** Constructor with stringifier factory. */
031        public HtmlBuilder(Writer writer, StringifierFactory stringifierFactory) {
032                this.writer = writer;
033                this.stringifierFactory = stringifierFactory;
034        }
035
036        /**
037         * Begin an element without closing (no ending &gt;).
038         *
039         * @param element tag name
040         * @param id element ID
041         * @param cssClass CSS class name
042         */
043        private void doBegin(String element, String id, String cssClass) throws IOException {
044                writer.write('<');
045                writer.write(element);
046                attr("id", id);
047                attr("class", cssClass);
048        }
049
050        /**
051         * Add attribute.
052         *
053         * @param name Attribute name
054         * @param value Attribute value
055         */
056        public T attr(String name, String value) throws IOException {
057                if (value != null) {
058                        writer.write(' ');
059                        writer.write(name);
060                        writer.write("=\"");
061                        writer.write(value);
062                        writer.write('\"');
063                }
064                return (T) this;
065        }
066
067        /**
068         * Begin an element.
069         *
070         * @param element Tag name
071         * @param id Element Id
072         * @param cssClass CSS class name
073         */
074        public T begin(String element, String id, String cssClass) throws IOException {
075                doBegin(element, id, cssClass);
076                writer.write('>');
077                return (T) this;
078        }
079
080        /**
081         * Begin an element.
082         */
083        public T begin(String element) throws IOException {
084                return begin(element, null, null);
085        }
086
087        /**
088         * Simple element.
089         *
090         * @param element Tag name
091         * @param id Element Id
092         * @param cssClass CSS class name
093         */
094        public T element(String element, String id, String cssClass) throws IOException {
095                doBegin(element, id, cssClass);
096                writer.write(" />");
097                return (T) this;
098        }
099
100        /**
101         * End an element.
102         *
103         * @param element Tag name
104         */
105        public T end(String element) throws IOException {
106                writer.write("</");
107                writer.write(element);
108                writer.write('>');
109                return (T) this;
110        }
111
112        /**
113         * Writes text.
114         *
115         * @param text Text (can be null)
116         */
117        public T text(String text) throws IOException {
118                if (text != null) {
119                        writer.write(text);
120                }
121                return (T) this;
122        }
123
124        /**
125         * Writes verbatim.
126         *
127         * @param text Text
128         */
129        public T write(String text) throws IOException {
130                writer.write(text);
131                return (T) this;
132        }
133
134        /**
135         * Adds a CSS StyleSheet link.
136         */
137        private T cssResource(String path) throws IOException {
138                return (T) write("<link href=\"../resource/").write(path).write("\" rel=\"stylesheet\" type=\"text/css\" />");
139        }
140
141        private T resources(Iterable<HtmlResource> resources) throws IOException {
142                // Write CSS Resources
143                cssResource("css/javasimon.css");
144                for (HtmlResource resource : resources) {
145                        if (resource.getType() == HtmlResourceType.CSS) {
146                                cssResource(resource.getPath());
147                        }
148                }
149                // Write JavaScript Resources
150                for (HtmlResource resource : resources) {
151                        if (resource.getType() == HtmlResourceType.JS) {
152                                write("<script src=\"../resource/").write(resource.getPath()).write("\" type=\"text/javascript\"></script>");
153                        }
154                }
155                return (T) this;
156        }
157
158        /**
159         * Page header.
160         *
161         * @param title Page title
162         */
163        public T header(String title, Iterable<HtmlResource> resources) throws IOException {
164                return (T) begin("html")
165                        .begin("head")
166                        .begin("title").text("Simon Console: ").text(title).end("title")
167                        .resources(resources)
168                        .end("head")
169                        .begin("body")
170                        .begin("h1")
171                        .write("<img id=\"logo\" src=\"../resource/images/logo.png\" alt=\"Logo\" />")
172                        .text("Simon Console: ").text(title)
173                        .end("h1");
174        }
175
176        /**
177         * Page footer.
178         */
179        public T footer() throws IOException {
180                return (T) end("body").end("html");
181        }
182
183        /**
184         * Write value using appropriate stringifier.
185         *
186         * @param value Typed value
187         * @param subType Sub type
188         */
189        public <X> T value(X value, String subType) throws IOException {
190                return text(stringifierFactory.toString(value, subType));
191        }
192
193        /**
194         * Write Simon property (using Java Bean convention).
195         *
196         * @param simon Simon
197         * @param propertyName Property name
198         */
199        public T simonProperty(Simon simon, String propertyName) throws IOException {
200                Getter getter = GetterFactory.getGetter(simon.getClass(), propertyName);
201                if (getter != null) {
202                        value(getter.get(simon), getter.getSubType());
203                }
204                return (T) this;
205        }
206
207        /**
208         * Apply stringifier on object and write it.
209         *
210         * @param object Object
211         */
212        public T object(Object object) throws IOException {
213                text(stringifierFactory.toString(object));
214                return (T) this;
215        }
216
217        /**
218         * Icon image representing Simon Type.
219         *
220         * @param simonType Simon Type
221         * @param rootPath Path to root of Simon Console resources
222         */
223        public T simonTypeImg(SimonType simonType, String rootPath) throws IOException {
224                String image = null;
225                switch (simonType) {
226                        case COUNTER:
227                                image = "TypeCounter.png";
228                                break;
229                        case STOPWATCH:
230                                image = "TypeStopwatch.png";
231                                break;
232                        case UNKNOWN:
233                                image = "TypeUnknown.png";
234                                break;
235                }
236                String label = simonType.name().toLowerCase();
237                doBegin("img", null, label + " icon");
238                attr("src", rootPath + "resource/images/" + image);
239                attr("alt", label);
240                writer.write(" />");
241                return (T) this;
242        }
243}