001package com.hfg.html; 002 003import java.awt.*; 004import java.io.OutputStream; 005import java.io.PrintWriter; 006import java.util.*; 007import java.util.List; 008 009import com.hfg.css.CSS; 010import com.hfg.css.CSSDeclaration; 011import com.hfg.css.CSSProperty; 012import com.hfg.graphics.ColorSpec; 013import com.hfg.util.BooleanUtil; 014import com.hfg.util.Case; 015import com.hfg.util.StringUtil; 016import com.hfg.util.collection.CollectionUtil; 017import com.hfg.util.io.GZIP; 018import com.hfg.xml.XMLAttribute; 019import com.hfg.xml.XMLName; 020import com.hfg.xml.XMLNode; 021import com.hfg.xml.XMLTag; 022import com.hfg.util.StringBuilderPlus; 023import com.hfg.css.CssUtil; 024import com.hfg.xml.XMLizable; 025 026//------------------------------------------------------------------------------ 027/** 028 * Base class for HTML elements. 029 * @author J. Alex Taylor, hairyfatguy.com 030 */ 031//------------------------------------------------------------------------------ 032// com.hfg XML/HTML Coding Library 033// 034// This library is free software; you can redistribute it and/or 035// modify it under the terms of the GNU Lesser General Public 036// License as published by the Free Software Foundation; either 037// version 2.1 of the License, or (at your option) any later version. 038// 039// This library is distributed in the hope that it will be useful, 040// but WITHOUT ANY WARRANTY; without even the implied warranty of 041// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 042// Lesser General Public License for more details. 043// 044// You should have received a copy of the GNU Lesser General Public 045// License along with this library; if not, write to the Free Software 046// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 047// 048// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com 049// jataylor@hairyfatguy.com 050//------------------------------------------------------------------------------ 051 052public class HTMLTag extends XMLTag implements HTMLNode, Cloneable 053{ 054 055 056 //########################################################################### 057 // CONSTRUCTORS 058 //########################################################################### 059 060 //-------------------------------------------------------------------------- 061 public HTMLTag(String inName) 062 { 063 super(inName); 064 } 065 066 //-------------------------------------------------------------------------- 067 public HTMLTag(String inName, Hashtable<String, String> inAttributes) 068 { 069 super(inName, inAttributes); 070 } 071 072 //-------------------------------------------------------------------------- 073 public HTMLTag(String inName, Hashtable inAttributes, String inContent) 074 { 075 super(inName, inAttributes, inContent); 076 } 077 078 //-------------------------------------------------------------------------- 079 public HTMLTag(XMLNode inXMLNode) 080 { 081 super(inXMLNode.getTagName()); 082 initFromXMLNode(inXMLNode); 083 } 084 085 //########################################################################### 086 // PUBLIC METHODS 087 //########################################################################### 088 089 //-------------------------------------------------------------------------- 090 public String toHTML() 091 { 092 return toXML(); 093 } 094 095 //-------------------------------------------------------------------------- 096 public void toHTML(OutputStream inStream) 097 { 098 toXML(inStream); 099 } 100 101 //-------------------------------------------------------------------------- 102 public void toHTML(PrintWriter inWriter) 103 { 104 toXML(inWriter); 105 } 106 107 //-------------------------------------------------------------------------- 108 public String toIndentedHTML(int inInitialIndentLevel, int inIndentSize) 109 { 110 return toIndentedXML(inInitialIndentLevel, inIndentSize); 111 } 112 113 //-------------------------------------------------------------------------- 114 public void toIndentedHTML(OutputStream inOutputStream, int inInitialIndentLevel, int inIndentSize) 115 { 116 toIndentedXML(inOutputStream, inInitialIndentLevel, inIndentSize); 117 } 118 119 //-------------------------------------------------------------------------- 120 public void toIndentedHTML(PrintWriter inWriter, int inInitialIndentLevel, int inIndentSize) 121 { 122 toIndentedXML(inWriter, inInitialIndentLevel, inIndentSize); 123 } 124 125 126 //-------------------------------------------------------------------------- 127 /** 128 Returns output stripped of HTML tags. 129 * @return raw text string 130 */ 131 public String toText() 132 { 133 StringBuilderPlus buffer = new StringBuilderPlus(); 134 if (mContentAndSubtagList != null) 135 { 136 for (Object content : mContentAndSubtagList) 137 { 138 if (content instanceof String) 139 { 140 buffer.append(content.toString()); 141 } 142 else if (content instanceof byte[]) 143 { 144 buffer.append(GZIP.uncompressToString((byte[]) content)); 145 } 146 else 147 { 148 XMLizable subtag = (XMLizable) content; 149 if (subtag instanceof HTMLTag) 150 { 151 buffer.append(((HTMLTag)subtag).toText()); 152 } 153 } 154 } 155 } 156 157 return buffer.toString(); 158 } 159 160 //-------------------------------------------------------------------------- 161 @Override 162 public HTMLTag clone() 163 { 164 return (HTMLTag) super.clone(); 165 } 166 167 //-------------------------------------------------------------------------- 168 public HTMLTag setId(String inValue) 169 { 170 setAttribute(HTML.ID, inValue); 171 return this; 172 } 173 174 //-------------------------------------------------------------------------- 175 public String getId() 176 { 177 return getAttributeValue(HTML.ID); 178 } 179 180 //-------------------------------------------------------------------------- 181 @Override 182 public HTMLTag setAttribute(String inName, Object inValue) 183 { 184 super.setAttribute(inName, inValue); 185 return this; 186 } 187 188 //-------------------------------------------------------------------------- 189 @Override 190 public HTMLTag setAttribute(XMLName inName, Object inValue) 191 { 192 super.setAttribute(inName, inValue); 193 return this; 194 } 195 196 //-------------------------------------------------------------------------- 197 public HTMLTag setClass(String inValue) 198 { 199 setAttribute(HTML.CLASS, inValue); 200 return this; 201 } 202 203 //-------------------------------------------------------------------------- 204 public HTMLTag addClass(String inValue) 205 { 206 String oldValue = getAttributeValue(HTML.CLASS); 207 if (oldValue != null) 208 { 209 inValue = oldValue + " " + inValue; 210 } 211 setAttribute(HTML.CLASS, inValue); 212 return this; 213 } 214 215 //-------------------------------------------------------------------------- 216 public HTMLTag removeClass(String inValue) 217 { 218 String oldValue = getAttributeValue(HTML.CLASS); 219 if (oldValue != null) 220 { 221 List<String> pieces = Arrays.asList(oldValue.split("\\s+")); 222 for (int i = 0; i < pieces.size(); i++) 223 { 224 if (pieces.get(i).equals(inValue)) 225 { 226 pieces.remove(i); 227 break; 228 } 229 } 230 231 setAttribute(HTML.CLASS, StringUtil.join(pieces, " ")); 232 } 233 234 return this; 235 } 236 237 238 //-------------------------------------------------------------------------- 239 /** 240 Not called getClass() for obvious reasons. 241 @return the value of the 'class' attribute specifying one or more CSS classes 242 */ 243 public String getClassAttribute() 244 { 245 return getAttributeValue(HTML.CLASS); 246 } 247 248 //-------------------------------------------------------------------------- 249 public HTMLTag setStyle(CharSequence inValue) 250 { 251 setAttribute(HTML.STYLE, inValue); 252 return this; 253 } 254 255 //-------------------------------------------------------------------------- 256 public String getStyle() 257 { 258 return getAttributeValue(HTML.STYLE); 259 } 260 261 //-------------------------------------------------------------------------- 262 public HTMLTag addStyle(String inValue) 263 { 264 CssUtil.addStyle(this, inValue); 265 return this; 266 } 267 268 //-------------------------------------------------------------------------- 269 public HTMLTag removeStyleProperty(CSSProperty inValue) 270 { 271 CssUtil.removeStyleProperty(this, inValue); 272 return this; 273 } 274 275 //-------------------------------------------------------------------------- 276 public HTMLTag applyColorSpec(ColorSpec inValue) 277 { 278 if (inValue != null) 279 { 280 if (inValue.getForegroundColor() != null) 281 { 282 addStyle(CSS.color(inValue.getForegroundColor())); 283 } 284 285 if (inValue.getBackgroundColor() != null) 286 { 287 addStyle(CSS.bgColor(inValue.getBackgroundColor())); 288 } 289 } 290 291 return this; 292 } 293 294 //-------------------------------------------------------------------------- 295 /** 296 Sets the CSS color declaration in the existing style attribute content or, 297 if no color declaration exists, one is added. 298 * @param inValue Color value to use. A value of null will cause an existing 299 * color declaration to be removed. 300 * @return this HTMLTag object to facilitate method chaining 301 */ 302 public HTMLTag setStyleColor(Color inValue) 303 { 304 String style = getStyle(); 305 boolean found = false; 306 if (StringUtil.isSet(style)) 307 { 308 List<CSSDeclaration> cssDeclarations = CSSDeclaration.parse(style); 309 for (CSSDeclaration cssDeclaration : cssDeclarations) 310 { 311 if (cssDeclaration.getProperty().equals(CSSProperty.color)) 312 { 313 found = true; 314 if (inValue != null) 315 { 316 cssDeclaration.setValue(CssUtil.colorToCssValue(inValue)); 317 } 318 else 319 { 320 cssDeclarations.remove(cssDeclaration); 321 } 322 323 setStyle(StringUtil.join(cssDeclarations, ";")); 324 break; 325 } 326 } 327 } 328 329 if (! found 330 && inValue != null) 331 { 332 addStyle(CSS.color(inValue)); 333 } 334 335 return this; 336 } 337 338 //-------------------------------------------------------------------------- 339 public HTMLTag setDraggable(boolean inValue) 340 { 341 setAttribute(HTML.DRAGGABLE, inValue); 342 return this; 343 } 344 345 //-------------------------------------------------------------------------- 346 public boolean getDraggable() 347 { 348 return BooleanUtil.valueOf(getAttributeValue(HTML.DRAGGABLE)); 349 } 350 351 //--------------------------------------------------------------------------- 352 protected void initFromXMLNode(XMLNode inXMLNode) 353 { 354 inXMLNode.verifyTagName(getTagName(), Case.INSENSITIVE); 355 356 if (CollectionUtil.hasValues(inXMLNode.getAttributes())) 357 { 358 for (XMLAttribute attr : inXMLNode.getAttributes()) 359 { 360 setAttribute(attr.clone()); 361 } 362 } 363 364 if (inXMLNode instanceof XMLTag) 365 { 366 XMLTag xmlTag = (XMLTag) inXMLNode; 367 List subtagsAndContent = xmlTag.getContentPlusSubtagList(); 368 if (CollectionUtil.hasValues(subtagsAndContent)) 369 { 370 for (Object object : subtagsAndContent) 371 { 372 if (object instanceof XMLTag) 373 { 374 addSubtag(HTML.constructFromXMLNode((XMLNode) object)); 375 } 376 else 377 { 378 // Process the content 379 String content; 380 if (object instanceof byte[]) 381 { 382 // Byte arrays are gzip compressed text chunks 383 content = GZIP.uncompressToString((byte[]) object); 384 } 385 else if (object instanceof String) 386 { 387 content = (String) object; 388 } 389 else 390 { 391 content = object.toString(); 392 } 393 394 addContent(content); 395 } 396 } 397 } 398 } 399 } 400 401}