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}