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 }