Coverage Report - net.sf.jolene.dom.HTMLElement
 
Classes in this File Line Coverage Branch Coverage Complexity
HTMLElement
83%
149/179
79%
54/68
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  
      * &lt;button name="test"&gt;Button text&lt;/button&gt;
 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  
      * &lt;input type="SUBMIT" name="Submit" value="Submit"&gt; 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: &lt;input type="text" name="Name"&gt;
 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  
 }