001// Copyright 2005 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007//     http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.tapestry.describe;
016
017import java.util.Collection;
018import java.util.Iterator;
019
020import org.apache.hivemind.util.Defense;
021import org.apache.tapestry.IMarkupWriter;
022
023/**
024 * Implementation of {@link org.apache.tapestry.describe.DescriptionReceiver} that produces HTML
025 * output using a {@link org.apache.tapestry.IMarkupWriter}.
026 * <p>
027 * TODO: Make {@link #describeAlternate(Object)} exclusive with the other methods
028 * {@link #title(String)}, {@link #property(String, Object)}, etc.
029 * 
030 * @author Howard M. Lewis Ship
031 * @since 4.0
032 */
033public class HTMLDescriptionReceiver implements RootDescriptionReciever
034{
035    // Emitted for null values.
036
037    static final String NULL_VALUE = "<NULL>";
038
039    private final IMarkupWriter _writer;
040
041    private boolean _emitDefault = true;
042
043    private String _title;
044
045    private String _section;
046
047    private DescribableStrategy _strategy;
048
049    private HTMLDescriptionReceiverStyles _styles;
050
051    private boolean _even = true;
052
053    public HTMLDescriptionReceiver(IMarkupWriter writer, DescribableStrategy adapter)
054    {
055        this(writer, adapter, new HTMLDescriptionReceiverStyles());
056    }
057
058    public HTMLDescriptionReceiver(IMarkupWriter writer, DescribableStrategy strategy,
059            HTMLDescriptionReceiverStyles styles)
060    {
061        Defense.notNull(writer, "writer");
062        Defense.notNull(strategy, "strategy");
063        Defense.notNull(styles, "styles");
064
065        _writer = writer;
066        _strategy = strategy;
067        _styles = styles;
068    }
069
070    /*
071     * (non-Javadoc)
072     * 
073     * @see org.apache.tapestry.describe.RootDescriptionReciever#describe(java.lang.Object)
074     */
075    public void describe(Object object)
076    {
077        if (object == null)
078        {
079            _writer.print(NULL_VALUE);
080            return;
081        }
082
083        _strategy.describeObject(object, this);
084
085        finishUp(object);
086    }
087
088    public void describeAlternate(Object alternate)
089    {
090        _strategy.describeObject(alternate, this);
091    }
092
093    public void finishUp()
094    {
095        // When false, a <table> was started, which must be closed.
096
097        if (!_emitDefault)
098            _writer.end("table");
099
100        _writer.println();
101
102        _emitDefault = true;
103        _title = null;
104        _section = null;
105        _even = true;
106    }
107
108    void finishUp(Object object)
109    {
110        if (_emitDefault)
111        {
112            String value = _title != null ? _title : object.toString();
113
114            _writer.print(value);
115        }
116
117        finishUp();
118    }
119
120    public void title(String title)
121    {
122        Defense.notNull(title, "title");
123
124        if (_title != null)
125            throw new IllegalStateException(DescribeMessages.setTitleOnce());
126
127        _title = title;
128    }
129
130    public void section(String section)
131    {
132        Defense.notNull(section, "section");
133
134        if (_title == null)
135            throw new IllegalStateException(DescribeMessages.mustSetTitleBeforeSection());
136
137        _section = section;
138    }
139
140    private void assertTitleSet()
141    {
142        if (_title == null)
143            throw new IllegalStateException(DescribeMessages.mustSetTitleBeforeProperty());
144    }
145
146    /**
147     * Invoked to ensure that the section portion has been output, before any properties within the
148     * section are output.
149     */
150
151    private void emitSection()
152    {
153        if (_emitDefault)
154        {
155            _emitDefault = false;
156
157            _writer.begin("div");
158            _writer.attribute("class", _styles.getHeaderClass());
159            _writer.print(_title);
160            _writer.end();
161            _writer.println();
162
163            _writer.begin("table");
164            _writer.attribute("class", _styles.getTableClass());
165            _writer.println();
166
167            _even = true;
168        }
169
170        if (_section != null)
171        {
172            _writer.begin("tr");
173            _writer.attribute("class", _styles.getSubheaderClass());
174            _writer.begin("th");
175            _writer.attribute("colspan", 2);
176            _writer.print(_section);
177            _writer.end("tr");
178            _writer.println();
179
180            _section = null;
181
182            _even = true;
183        }
184
185    }
186
187    private void pair(String key, String value)
188    {
189        assertTitleSet();
190        emitSection();
191
192        _writer.begin("tr");
193        writeRowClass();
194
195        _writer.begin("th");
196        _writer.print(key);
197        _writer.end();
198        _writer.begin("td");
199        _writer.print(value);
200        _writer.end("tr");
201        _writer.println();
202
203    }
204
205    private void writeRowClass()
206    {
207        _writer.attribute("class", _even ? "even" : "odd");
208        _even = !_even;
209    }
210
211    public void property(String key, Object value)
212    {
213        Defense.notNull(key, "key");
214
215        assertTitleSet();
216        emitSection();
217
218        _writer.begin("tr");
219        writeRowClass();
220
221        _writer.begin("th");
222        _writer.print(key);
223        _writer.end();
224        _writer.begin("td");
225
226        describeNested(value);
227
228        _writer.end("tr");
229        _writer.println();
230    }
231
232    private void describeNested(Object value)
233    {
234        if (value == null)
235        {
236            _writer.print(NULL_VALUE);
237            return;
238        }
239
240        new HTMLDescriptionReceiver(_writer, _strategy, _styles).describe(value);
241    }
242
243    public void property(String key, boolean value)
244    {
245        Defense.notNull(key, "key");
246
247        // toString is JDK 1.4 and above, so we'll provide our own.
248
249        pair(key, value ? "true" : "false");
250    }
251
252    public void property(String key, byte value)
253    {
254        Defense.notNull(key, "key");
255
256        pair(key, Byte.toString(value));
257    }
258
259    public void property(String key, short value)
260    {
261        Defense.notNull(key, "key");
262
263        pair(key, Short.toString(value));
264    }
265
266    public void property(String key, int value)
267    {
268        Defense.notNull(key, "key");
269
270        pair(key, Integer.toString(value));
271    }
272
273    public void property(String key, long value)
274    {
275        Defense.notNull(key, "key");
276
277        pair(key, Long.toString(value));
278    }
279
280    public void property(String key, float value)
281    {
282        Defense.notNull(key, "key");
283
284        pair(key, Float.toString(value));
285    }
286
287    public void property(String key, double value)
288    {
289        Defense.notNull(key, "key");
290
291        pair(key, Double.toString(value));
292    }
293
294    public void property(String key, char value)
295    {
296        Defense.notNull(key, "key");
297
298        pair(key, Character.toString(value));
299    }
300
301    public void array(String key, Object[] values)
302    {
303        Defense.notNull(key, "key");
304
305        assertTitleSet();
306
307        if (values == null || values.length == 0)
308            return;
309
310        emitSection();
311
312        for (int i = 0; i < values.length; i++)
313        {
314            _writer.begin("tr");
315            writeRowClass();
316
317            _writer.begin("th");
318
319            if (i == 0)
320                _writer.print(key);
321
322            _writer.end();
323
324            _writer.begin("td");
325
326            describeNested(values[i]);
327
328            _writer.end("tr");
329            _writer.println();
330        }
331
332    }
333
334    public void collection(String key, Collection values)
335    {
336        Defense.notNull(key, "key");
337
338        assertTitleSet();
339
340        if (values == null || values.isEmpty())
341            return;
342
343        emitSection();
344
345        Iterator i = values.iterator();
346        boolean first = true;
347
348        while (i.hasNext())
349        {
350            _writer.begin("tr");
351            writeRowClass();
352
353            _writer.begin("th");
354
355            if (first)
356                _writer.print(key);
357
358            _writer.end();
359            _writer.begin("td");
360
361            describeNested(i.next());
362
363            _writer.end("tr");
364            _writer.println();
365
366            first = false;
367        }
368    }
369}