Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
HTMLElement |
|
| 0.0;0 |
1 | package net.sf.jolene.dom; | |
2 | ||
3 | import net.sf.jolene.constants.Prefs; | |
4 | import net.sf.jolene.constants.Tags; | |
5 | import net.sf.jolene.html.Attributes; | |
6 | import net.sf.jolene.html.IAttributes; | |
7 | import net.sf.jolene.html.IStyles; | |
8 | import net.sf.jolene.html.Styles; | |
9 | import org.apache.log4j.LogManager; | |
10 | import org.apache.log4j.Logger; | |
11 | ||
12 | import java.util.*; | |
13 | ||
14 | /** | |
15 | * Base class for all html elements. | |
16 | * | |
17 | * @author Dan Howard | |
18 | * @since Oct 11, 2003 | |
19 | */ | |
20 | 0 | public abstract class HTMLElement implements IAttributes, IStyles, Cloneable { |
21 | // ------------------------------ FIELDS ------------------------------ | |
22 | ||
23 | // todo implement default attriutes for objects | |
24 | 1 | static final Logger log = LogManager.getLogger(HTMLElement.class); |
25 | ||
26 | // This will contain the starting chars when streaming. e.g. | |
27 | // INPUT TYPE = 'INPUT' | |
28 | // SELECT = 'SELECT' | |
29 | // LABEL = 'LABEL' | |
30 | Tags tag; | |
31 | ||
32 | // For swapping | |
33 | String swapWith; | |
34 | ||
35 | // Custom properties | |
36 | private String afterText; | |
37 | private String beforeText; | |
38 | ||
39 | // These store the location in the file - internal | |
40 | private int startPoint; | |
41 | private int endPoint; | |
42 | ||
43 | // Common properties. | |
44 | private String name; | |
45 | private String value; | |
46 | private String content; // inner content of an element between ending of start tag and end tag. http://www.w3.org/TR/html401/interact/forms.html#h-17.5 | |
47 | private boolean renderable; | |
48 | ||
49 | // HTML attributes - has access only with package private setters/getters. | |
50 | private IAttributes attributes; | |
51 | ||
52 | private IStyles styles; | |
53 | ||
54 | 669 | protected HTMLElement() { |
55 | 669 | afterText = ""; |
56 | 669 | beforeText = ""; |
57 | 669 | renderable = true; |
58 | 669 | name = ""; |
59 | 669 | value = ""; |
60 | 669 | content = ""; |
61 | 669 | attributes = new Attributes(); |
62 | 669 | styles = new Styles(); |
63 | 669 | tag = Tags.input; |
64 | 669 | startPoint = 0; |
65 | 669 | endPoint = 0; |
66 | 669 | } |
67 | ||
68 | protected HTMLElement(String name) { | |
69 | 0 | this(); |
70 | 0 | this.name = name; |
71 | 0 | } |
72 | ||
73 | /** | |
74 | * Clears this element's attributes. | |
75 | */ | |
76 | public void clear() { | |
77 | 0 | attributes.clear(); |
78 | 0 | } |
79 | ||
80 | /** | |
81 | * Core clone implementation. Safely clones HTMLElement objects. Used by the DocumentFactory. | |
82 | * | |
83 | * @return HTMLElement | |
84 | * @see net.sf.jolene.factories.DocumentFactory | |
85 | */ | |
86 | @Override | |
87 | public HTMLElement clone() { | |
88 | HTMLElement element; | |
89 | 27 | boolean setValue = true; |
90 | ||
91 | 27 | if (this instanceof Button) { |
92 | 1 | element = new Button(); |
93 | 26 | } else if (this instanceof CheckBox) { |
94 | 3 | element = new CheckBox(); |
95 | 23 | } else if (this instanceof Grid) { |
96 | 1 | element = new Grid(); |
97 | 1 | setValue = false; |
98 | 22 | } else if (this instanceof Image) { |
99 | 1 | element = new Image(); |
100 | 1 | setValue = false; |
101 | 21 | } else if (this instanceof Input) { |
102 | 5 | element = new Input(); |
103 | 16 | } else if (this instanceof Label) { |
104 | 1 | element = new Label(); |
105 | 15 | } else if (this instanceof Radio) { |
106 | 1 | element = new Radio(); |
107 | 14 | } else if (this instanceof Select) { |
108 | 3 | element = new Select(); |
109 | 11 | } else if (this instanceof TextArea) { |
110 | 1 | element = new TextArea(); |
111 | 10 | } else if (this instanceof GridCell) { |
112 | 6 | element = new GridCell(); |
113 | 4 | } else if (this instanceof GridRow) { |
114 | 2 | element = new GridRow(); |
115 | 2 | } else if (this instanceof Header) { |
116 | 1 | element = new Header(); |
117 | } else { | |
118 | 1 | element = new Form(); |
119 | 1 | setValue = false; |
120 | } | |
121 | ||
122 | // Standard clone | |
123 | Iterator<String> it; | |
124 | String key; | |
125 | String value; | |
126 | 27 | it = attributes.keySet().iterator(); |
127 | 76 | while (it.hasNext()) { |
128 | 49 | key = it.next(); |
129 | 49 | value = getAttribute(key); |
130 | 49 | element.setAttribute(key, value); |
131 | ||
132 | // Make sure to call setValue | |
133 | 49 | if ("value".equalsIgnoreCase(key)) { |
134 | 7 | element.setValue(value); |
135 | } | |
136 | } | |
137 | // Use setters instead of direct access in case we override these methods (setName is overriden for example). | |
138 | 27 | element.setName(name); |
139 | 27 | element.setAfterText(afterText); |
140 | 27 | element.setBeforeText(beforeText); |
141 | 27 | element.setEndPoint(endPoint); |
142 | 27 | element.setRenderable(renderable); |
143 | 27 | element.setStartPoint(startPoint); |
144 | 27 | element.setTag(tag); |
145 | 27 | element.swapWith(swapWith); |
146 | ||
147 | // Set the value ONLY for objects that have a value | |
148 | // Note that the object could have a null in the case where the object | |
149 | // usually has a value attribute in HTML but was not specified in the | |
150 | // actual document. example: | |
151 | // <INPUT NAME="username" TYPE="text"> | |
152 | //@todo why do we bother checking if element.getValue is null? It means that we always need a default for GridCell, TextArea, etc (ones which override the default get/set value methods) | |
153 | 27 | if (setValue && this.value.trim().length() > 0) { |
154 | 17 | element.setValue(this.value); |
155 | } | |
156 | ||
157 | 27 | return element; |
158 | } | |
159 | ||
160 | /** | |
161 | * Gets the afterText string for the object. | |
162 | * The afterText is any string value that you want to be rendered immediatley after the object itself. | |
163 | * | |
164 | * @return String | |
165 | */ | |
166 | public String getAfterText() { | |
167 | 274 | return afterText; |
168 | } | |
169 | ||
170 | /** | |
171 | * Return the html attribute specified by the key or null if it's not found. | |
172 | * | |
173 | * @param key case insensitive attribute name. | |
174 | * @return attribute value string. | |
175 | */ | |
176 | public final String getAttribute(String key) { | |
177 | 94 | return attributes.getAttribute(key); |
178 | } | |
179 | ||
180 | /** | |
181 | * Gets the beforeText string for the object. | |
182 | * The beforeText is any string value that you want to be rendered immediatley before | |
183 | * the object itself. | |
184 | * | |
185 | * @return String | |
186 | */ | |
187 | public String getBeforeText() { | |
188 | 274 | return beforeText; |
189 | } | |
190 | ||
191 | /** | |
192 | * <p/> | |
193 | * Returns the content of the HTMLElement. The content is part of the element between the end of the start tag | |
194 | * and the beginning of the end tag. In the example below <b>Button text</b> is the content. | |
195 | * </p> | |
196 | * <tt> | |
197 | * <button name="test">Button text</button> | |
198 | * </tt> | |
199 | * <p/> | |
200 | * Note that some elements do not have content. For these elements this content | |
201 | * property has no effect on the rendering of the element. | |
202 | * </p> | |
203 | * <p/> | |
204 | * Elements which have a content. | |
205 | * <ul> | |
206 | * <li>Button</li> | |
207 | * <li>Label</li> | |
208 | * <li>TextArea</li> | |
209 | * <li>Header <i>Some header elements do and some don't.</i></li> | |
210 | * </ul> | |
211 | * <p/> | |
212 | * <br> | |
213 | * Elements which do NOT have a content. | |
214 | * <ul> | |
215 | * <li>ChecBox</li> | |
216 | * <li>Grid</li> | |
217 | * <li>Image</li> | |
218 | * <li>Input</li> | |
219 | * <li>Radio</li> | |
220 | * <li>Select</li> | |
221 | * </ul> | |
222 | * <p/> | |
223 | * <p/> | |
224 | * Note also that for TextArea, Text and Label the content is also the value. In this case the value and content properties are | |
225 | * interchangable. | |
226 | * </p> | |
227 | * | |
228 | * @return String content part of the HTMLElement | |
229 | */ | |
230 | public String getContent() { | |
231 | 79 | return content; |
232 | } | |
233 | ||
234 | /** | |
235 | * Gets the element name. | |
236 | * | |
237 | * @return name of the html element. | |
238 | */ | |
239 | public String getName() { | |
240 | 622 | return name; |
241 | } | |
242 | ||
243 | ||
244 | /** | |
245 | * Get the element style based on a style name. | |
246 | * | |
247 | * @param key case insensitive style name. | |
248 | * @return String value for the specified style or null if not found. | |
249 | */ | |
250 | public String getStyle(String key) { | |
251 | 0 | return styles.getStyle(key); |
252 | } | |
253 | ||
254 | /** | |
255 | * Get the tag for the element. | |
256 | * | |
257 | * @return the tag for the element. | |
258 | */ | |
259 | public Tags getTag() { | |
260 | 30 | return tag; |
261 | } | |
262 | ||
263 | /** | |
264 | * Gets the current value of the element. | |
265 | * | |
266 | * @return String | |
267 | */ | |
268 | public String getValue() { | |
269 | // Default for input type text, buttons etc.... | |
270 | //return getAttribute("value"); | |
271 | 1519 | return value; |
272 | } | |
273 | ||
274 | /** | |
275 | * Indicates if the specified attribute exists. | |
276 | * | |
277 | * @param key case insensitive attribute name. | |
278 | * @return true if the attribute exists. | |
279 | */ | |
280 | public boolean hasAttribute(String key) { | |
281 | 101 | return attributes.hasAttribute(key); |
282 | } | |
283 | ||
284 | /** | |
285 | * Indicates if a style exists. | |
286 | * | |
287 | * @param key case instensitive style key name. | |
288 | * @return true if the style exists in the string. | |
289 | */ | |
290 | public boolean hasStyle(String key) { | |
291 | 0 | return styles.hasStyle(key); |
292 | } | |
293 | ||
294 | ||
295 | /** | |
296 | * Returns true if a Radio or CheckBox is checked. | |
297 | * | |
298 | * @return checked | |
299 | */ | |
300 | public boolean isChecked() { | |
301 | 2 | return hasAttribute("checked"); |
302 | } | |
303 | ||
304 | /** | |
305 | * Returns true if the element is disabled | |
306 | * | |
307 | * @return disabled | |
308 | */ | |
309 | public boolean isDisabled() { | |
310 | 0 | return hasAttribute("disabled"); |
311 | } | |
312 | ||
313 | ||
314 | /** | |
315 | * Returns true if the element is readonly | |
316 | * | |
317 | * @return readonly | |
318 | */ | |
319 | public boolean isReadonly() { | |
320 | 0 | return hasAttribute("readonly"); |
321 | } | |
322 | ||
323 | /** | |
324 | * Returns whether this element is rendered. If this is false then the element will be rendered as an empty string. | |
325 | * | |
326 | * @return boolean | |
327 | */ | |
328 | public boolean isRenderable() { | |
329 | 112 | return renderable; |
330 | } | |
331 | ||
332 | ||
333 | /** | |
334 | * Returns the set of attribute names. | |
335 | * | |
336 | * @return set of attribute names. | |
337 | */ | |
338 | public Set<String> keySet() { | |
339 | 0 | return attributes.keySet(); |
340 | } | |
341 | ||
342 | /** | |
343 | * Remove the specified attribute. | |
344 | * | |
345 | * @param key case insensitive attribute name. | |
346 | * @return previous value associated with specified key, or <tt>null</tt> if there was no mapping for key. | |
347 | */ | |
348 | public String removeAttribute(String key) { | |
349 | 2 | return attributes.removeAttribute(key); |
350 | } | |
351 | ||
352 | /** | |
353 | * Removes a style from the element. | |
354 | * | |
355 | * @param key Style key name to remove. | |
356 | * @return previous value associated with specified key, or <tt>null</tt> if there was no mapping for key. | |
357 | */ | |
358 | public String removeStyle(String key) { | |
359 | 0 | return styles.removeStyle(key); |
360 | } | |
361 | ||
362 | /** | |
363 | * Sets the styles on the element based on the formatted style string, clearing all exisiting styles first. | |
364 | * | |
365 | * @param style string in a format like <tt>azimuth:behind;background:aliceblue;background-color:aquamarine;border-bottom-style:solid;clip:auto;border-top-width:medium;</tt> | |
366 | */ | |
367 | public final void resetStyles(String style) { | |
368 | 2 | styles.resetStyles(style); |
369 | 2 | setAttribute("style", style); |
370 | 2 | } |
371 | ||
372 | /** | |
373 | * Sets the styles on the element based on the Style parameter, clearing all exisiting styles first. | |
374 | * | |
375 | * @param style a Style object. | |
376 | */ | |
377 | public final void resetStyles(Styles style) { | |
378 | 1 | resetStyles(style.toString()); |
379 | 1 | } |
380 | ||
381 | /** | |
382 | * Sets the afterText string for the object. | |
383 | * The afterText is any string value that you want to be rendered immediately after the object itself. | |
384 | * <p/> | |
385 | * Example: | |
386 | * <pre> | |
387 | * document.forms(0).elements("Submit").setAfterText(" Click this button to submit this form."); | |
388 | * </pre> | |
389 | * This would render the following back to the browser: | |
390 | * <pre> | |
391 | * <input type="SUBMIT" name="Submit" value="Submit"> Click this button to submit this form. | |
392 | * </pre> | |
393 | * | |
394 | * @param string text after object | |
395 | */ | |
396 | public void setAfterText(String string) { | |
397 | 28 | afterText = string; |
398 | 28 | } |
399 | ||
400 | /** | |
401 | * Sets the specified attribute. | |
402 | * | |
403 | * @param key case insensitive attribute name. | |
404 | * @param value value of the attribute. | |
405 | * @return previous value associated with specified key, or <tt>null</tt> if there was no attribute for the key. | |
406 | */ | |
407 | public final String setAttribute(String key, String value) { | |
408 | 1589 | if (value == null) { |
409 | 0 | value = ""; |
410 | } | |
411 | ||
412 | 1589 | if ("style".equalsIgnoreCase(key)) { |
413 | 10 | styles.setStyles(value); |
414 | 10 | String style = styles.toString(); |
415 | 10 | attributes.setAttribute(key, style); |
416 | 10 | return style; |
417 | } | |
418 | ||
419 | 1579 | return attributes.setAttribute(key, value); |
420 | } | |
421 | ||
422 | /** | |
423 | * Sets the beforeText string for the object. | |
424 | * The beforeText is any string value that you want to be rendered immediatley before | |
425 | * the object itself. | |
426 | * <p/> | |
427 | * Example: | |
428 | * <pre> | |
429 | * document.forms(0).elements("Name").setBeforeText("Name: "); | |
430 | * </pre> | |
431 | * This would render the following back to the browser: | |
432 | * <pre> | |
433 | * Name: <input type="text" name="Name"> | |
434 | * </pre> | |
435 | * | |
436 | * @param string of text | |
437 | */ | |
438 | public void setBeforeText(String string) { | |
439 | 28 | beforeText = string; |
440 | 28 | } |
441 | ||
442 | /** | |
443 | * Sets the element checked attribute. Used for Radios and Checkboxes. | |
444 | * | |
445 | * @param checked to indicate if the checkbox is checked or not. | |
446 | */ | |
447 | public void setChecked(boolean checked) { | |
448 | 10 | if (checked) { |
449 | 5 | setAttribute("checked", "checked"); |
450 | } else { | |
451 | 5 | if (hasAttribute("checked")) { |
452 | 2 | removeAttribute("checked"); |
453 | } | |
454 | } | |
455 | 10 | } |
456 | ||
457 | /** | |
458 | * Sets the content of the HTMLElement. | |
459 | * | |
460 | * @param content text or value to set. | |
461 | * @see HTMLElement#getContent() | |
462 | */ | |
463 | public void setContent(String content) { | |
464 | 88 | this.content = content; |
465 | 88 | } |
466 | ||
467 | /** | |
468 | * Set the element to be disabled. | |
469 | * | |
470 | * @param disabled indicator for a disabled element. | |
471 | */ | |
472 | public void setDisabled(boolean disabled) { | |
473 | 1 | if (disabled) { |
474 | 1 | setAttribute("disabled", "disabled"); |
475 | } else { | |
476 | 0 | if (hasAttribute("disabled")) { |
477 | 0 | removeAttribute("disabled"); |
478 | } | |
479 | } | |
480 | 1 | } |
481 | ||
482 | /** | |
483 | * Set the element name. | |
484 | * | |
485 | * @param name name of the html element. | |
486 | */ | |
487 | public void setName(String name) { | |
488 | 267 | this.name = name; |
489 | 267 | } |
490 | ||
491 | /** | |
492 | * Set the element to be readonly. | |
493 | * | |
494 | * @param readonly indicator for a readonly element. | |
495 | */ | |
496 | public void setReadonly(boolean readonly) { | |
497 | 1 | if (readonly) { |
498 | 1 | setAttribute("readonly", "readonly"); |
499 | } else { | |
500 | 0 | if (hasAttribute("readonly")) { |
501 | 0 | removeAttribute("readonly"); |
502 | } | |
503 | } | |
504 | 1 | } |
505 | ||
506 | ||
507 | /** | |
508 | * Sets whether this element is rendered. If this is false then the element will be rendered as an empty string. | |
509 | * | |
510 | * @param renderable true or false | |
511 | */ | |
512 | public void setRenderable(boolean renderable) { | |
513 | 27 | this.renderable = renderable; |
514 | 27 | } |
515 | ||
516 | /** | |
517 | * Set the element style. | |
518 | * | |
519 | * @param key Style key name. | |
520 | * @param value Style value. | |
521 | * @return previous value associated with specified key, or <tt>null</tt> if there was no mapping for key. | |
522 | */ | |
523 | public String setStyle(String key, String value) { | |
524 | 3 | return setAttribute("style", key + ":" + value + ";"); |
525 | } | |
526 | ||
527 | /** | |
528 | * Sets the styles on the element based on a formatted style string. | |
529 | * Expects the string to be in style format. | |
530 | * | |
531 | * @param style string in a format like <tt>azimuth:behind;background:aliceblue;background-color:aquamarine;border-bottom-style:solid;clip:auto;border-top-width:medium;</tt> | |
532 | */ | |
533 | public final void setStyles(String style) { | |
534 | 2 | styles.setStyles(style); |
535 | 2 | setAttribute("style", styles.toString()); |
536 | 2 | } |
537 | ||
538 | /** | |
539 | * Sets styles on the Style object based on the Style parameter. | |
540 | * | |
541 | * @param styles a Style object. | |
542 | */ | |
543 | public final void setStyles(Styles styles) { | |
544 | 1 | setStyles(styles.toString()); |
545 | 1 | } |
546 | ||
547 | ||
548 | // Non-public | |
549 | void setTag(Tags tag) { | |
550 | 27 | this.tag = tag; |
551 | 27 | } |
552 | ||
553 | /** | |
554 | * Sets the value of the element. | |
555 | * | |
556 | * @param value value of object | |
557 | */ | |
558 | public void setValue(String value) { | |
559 | // Note textarea, select, label override this. | |
560 | // Default for setValue, textarea, select, label should override. | |
561 | // setAttribute("value", value); | |
562 | 1733 | this.value = value; |
563 | 1733 | } |
564 | ||
565 | /** | |
566 | * Replace this element's string representation with the specified string. | |
567 | * | |
568 | * @param swapWith any string you wish to render instead of this object. | |
569 | */ | |
570 | public void swapWith(String swapWith) { | |
571 | 31 | this.swapWith = swapWith; |
572 | 31 | } |
573 | ||
574 | /** | |
575 | * Renders the element as an html string. | |
576 | */ | |
577 | @Override | |
578 | public String toString() { | |
579 | 794 | if (swapWith != null) { |
580 | 1 | return swapWith; |
581 | } | |
582 | ||
583 | 793 | if (!renderable && !log.isDebugEnabled()) { |
584 | 0 | return ""; |
585 | } | |
586 | ||
587 | 793 | StringBuffer sb = new StringBuffer(""); |
588 | 793 | Iterator iterator = attributes.keySet().iterator(); |
589 | ||
590 | 793 | sb.append('<').append(tag); |
591 | ||
592 | 793 | List<BooleanAttributes> booleanAttributes = Arrays.asList(BooleanAttributes.values()); |
593 | ||
594 | 2476 | while (iterator.hasNext()) { |
595 | 1683 | String key = (String) iterator.next(); |
596 | 1683 | String value = attributes.getAttribute(key); |
597 | 1683 | if (value == null) { |
598 | 0 | log.warn("Value is null? setting to blank string"); |
599 | 0 | value = ""; |
600 | } | |
601 | 1683 | if (booleanAttributes.contains(BooleanAttributes.fromString(key.toLowerCase()))) { |
602 | 22 | sb.append(' ').append(key).append('=').append('\"').append(key.toLowerCase()).append('\"'); |
603 | } else { | |
604 | 1661 | String quote = "\""; |
605 | // If the key itself contains a " character | |
606 | // use the single ' quote. | |
607 | 1661 | if (value.indexOf("\"") > -1) { |
608 | 26 | quote = "'"; |
609 | } | |
610 | ||
611 | 1661 | sb.append(' ').append(key).append('=').append(quote).append(value).append(quote); |
612 | } | |
613 | 1683 | } |
614 | ||
615 | 793 | if (iterator.hasNext()) { |
616 | 0 | sb.append(' '); |
617 | } else { | |
618 | 793 | if ("true".equalsIgnoreCase(Prefs.XHTMLStrict.getValue())) { |
619 | //@todo /> is buggy! | |
620 | 0 | sb.append(" />"); |
621 | } else { | |
622 | 793 | sb.append('>'); |
623 | } | |
624 | } | |
625 | 793 | if (log.isDebugEnabled()) { |
626 | 0 | sb.append("Debug: "); |
627 | 0 | sb.append("BeforeText: ").append(beforeText).append(' '); |
628 | 0 | sb.append("AfterText: ").append(afterText).append(' '); |
629 | 0 | sb.append("Start point: ").append(startPoint).append(' '); |
630 | 0 | sb.append("End point: ").append(endPoint).append(' '); |
631 | 0 | sb.append("Render? ").append(renderable).append(' '); |
632 | 0 | sb.append("TAG: ").append(tag); |
633 | } | |
634 | 793 | return new String(sb); |
635 | } | |
636 | ||
637 | /* not used or needed we have keyset.... | |
638 | public Collection<String> values() { | |
639 | return attributes.values(); | |
640 | } | |
641 | */ | |
642 | ||
643 | // Not public | |
644 | IAttributes getAttributes() { | |
645 | 0 | return attributes; |
646 | } | |
647 | ||
648 | int getEndPoint() { | |
649 | 213 | return endPoint; |
650 | } | |
651 | ||
652 | int getStartPoint() { | |
653 | 213 | return startPoint; |
654 | } | |
655 | ||
656 | // Not public | |
657 | IStyles getStyles() { | |
658 | 2 | return styles; |
659 | } | |
660 | ||
661 | // Not public | |
662 | void setAttributes(IAttributes attributes) { | |
663 | 187 | this.attributes = attributes; |
664 | 187 | } |
665 | ||
666 | void setEndPoint(int endPoint) { | |
667 | 378 | this.endPoint = endPoint; |
668 | 378 | } |
669 | ||
670 | void setStartPoint(int startPoint) { | |
671 | 378 | this.startPoint = startPoint; |
672 | 378 | } |
673 | } |