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