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)} 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 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 }