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    
015    package org.apache.tapestry.describe;
016    
017    import java.util.Collection;
018    import java.util.Iterator;
019    
020    import org.apache.hivemind.util.Defense;
021    import 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)}&nbsp;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     */
033    public class HTMLDescriptionReceiver implements DescriptionReceiver
034    {
035        // Emitted for null values.
036    
037        static final String NULL_VALUE = "<NULL>";
038    
039        private 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        public void describe(Object object)
071        {
072            if (object == null)
073            {
074                _writer.print(NULL_VALUE);
075                return;
076            }
077    
078            _strategy.describeObject(object, this);
079    
080            finishUp(object);
081        }
082    
083        public void describeAlternate(Object alternate)
084        {
085            _strategy.describeObject(alternate, this);
086        }
087    
088        void finishUp(Object object)
089        {
090            if (_emitDefault)
091            {
092                String value = _title != null ? _title : object.toString();
093    
094                _writer.print(value);
095            }
096            // Not emit default .. means a property was emitted, so a table was started and must be
097            // finished.
098            else
099                _writer.end("table");
100    
101            _writer.println();
102        }
103    
104        public void title(String title)
105        {
106            Defense.notNull(title, "title");
107    
108            if (_title != null)
109                throw new IllegalStateException(DescribeMessages.setTitleOnce());
110    
111            _title = title;
112        }
113    
114        public void section(String section)
115        {
116            Defense.notNull(section, "section");
117    
118            if (_title == null)
119                throw new IllegalStateException(DescribeMessages.mustSetTitleBeforeSection());
120    
121            _section = section;
122        }
123    
124        private void assertTitleSet()
125        {
126            if (_title == null)
127                throw new IllegalStateException(DescribeMessages.mustSetTitleBeforeProperty());
128        }
129    
130        /**
131         * Invoked to ensure that the section portion has been output, before any properties within the
132         * section are output.
133         */
134    
135        private void emitSection()
136        {
137            if (_emitDefault)
138            {
139                _emitDefault = false;
140    
141                _writer.begin("div");
142                _writer.attribute("class", _styles.getHeaderClass());
143                _writer.print(_title);
144                _writer.end();
145                _writer.println();
146    
147                _writer.begin("table");
148                _writer.attribute("class", _styles.getTableClass());
149                _writer.println();
150    
151                _even = true;
152            }
153    
154            if (_section != null)
155            {
156                _writer.begin("tr");
157                _writer.attribute("class", _styles.getSubheaderClass());
158                _writer.begin("th");
159                _writer.attribute("colspan", 2);
160                _writer.print(_section);
161                _writer.end("tr");
162                _writer.println();
163    
164                _section = null;
165    
166                _even = true;
167            }
168    
169        }
170    
171        private void pair(String key, String value)
172        {
173            assertTitleSet();
174            emitSection();
175    
176            _writer.begin("tr");
177            writeRowClass();
178    
179            _writer.begin("th");
180            _writer.print(key);
181            _writer.end();
182            _writer.begin("td");
183            _writer.print(value);
184            _writer.end("tr");
185            _writer.println();
186    
187        }
188    
189        private void writeRowClass()
190        {
191            _writer.attribute("class", _even ? "even" : "odd");
192            _even = !_even;
193        }
194    
195        public void property(String key, Object value)
196        {
197            Defense.notNull(key, "key");
198    
199            assertTitleSet();
200            emitSection();
201    
202            _writer.begin("tr");
203            writeRowClass();
204    
205            _writer.begin("th");
206            _writer.print(key);
207            _writer.end();
208            _writer.begin("td");
209    
210            describeNested(value);
211    
212            _writer.end("tr");
213            _writer.println();
214        }
215    
216        private void describeNested(Object value)
217        {
218            if (value == null)
219            {
220                _writer.print(NULL_VALUE);
221                return;
222            }
223    
224            new HTMLDescriptionReceiver(_writer, _strategy, _styles).describe(value);
225        }
226    
227        public void property(String key, boolean value)
228        {
229            Defense.notNull(key, "key");
230    
231            // toString is JDK 1.4 and above, so we'll provide our own.
232    
233            pair(key, value ? "true" : "false");
234        }
235    
236        public void property(String key, byte value)
237        {
238            Defense.notNull(key, "key");
239    
240            pair(key, Byte.toString(value));
241        }
242    
243        public void property(String key, short value)
244        {
245            Defense.notNull(key, "key");
246    
247            pair(key, Short.toString(value));
248        }
249    
250        public void property(String key, int value)
251        {
252            Defense.notNull(key, "key");
253    
254            pair(key, Integer.toString(value));
255        }
256    
257        public void property(String key, long value)
258        {
259            Defense.notNull(key, "key");
260    
261            pair(key, Long.toString(value));
262        }
263    
264        public void property(String key, float value)
265        {
266            Defense.notNull(key, "key");
267    
268            pair(key, Float.toString(value));
269        }
270    
271        public void property(String key, double value)
272        {
273            Defense.notNull(key, "key");
274    
275            pair(key, Double.toString(value));
276        }
277    
278        public void property(String key, char value)
279        {
280            Defense.notNull(key, "key");
281    
282            pair(key, Character.toString(value));
283        }
284    
285        public void array(String key, Object[] values)
286        {
287            Defense.notNull(key, "key");
288    
289            assertTitleSet();
290    
291            if (values == null || values.length == 0)
292                return;
293    
294            emitSection();
295    
296            for (int i = 0; i < values.length; i++)
297            {
298                _writer.begin("tr");
299                writeRowClass();
300    
301                _writer.begin("th");
302    
303                if (i == 0)
304                    _writer.print(key);
305    
306                _writer.end();
307    
308                _writer.begin("td");
309    
310                describeNested(values[i]);
311    
312                _writer.end("tr");
313                _writer.println();
314            }
315    
316        }
317    
318        public void collection(String key, Collection values)
319        {
320            Defense.notNull(key, "key");
321    
322            assertTitleSet();
323    
324            if (values == null || values.isEmpty())
325                return;
326    
327            emitSection();
328    
329            Iterator i = values.iterator();
330            boolean first = true;
331    
332            while (i.hasNext())
333            {
334                _writer.begin("tr");
335                writeRowClass();
336    
337                _writer.begin("th");
338    
339                if (first)
340                    _writer.print(key);
341    
342                _writer.end();
343                _writer.begin("td");
344    
345                describeNested(i.next());
346    
347                _writer.end("tr");
348                _writer.println();
349    
350                first = false;
351            }
352        }
353    }