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