1 20 23 package org.lobobrowser.html.domimpl; 24 25 import org.lobobrowser.html.FormInput; 26 import org.lobobrowser.html.parser.HtmlParser; 27 import org.lobobrowser.html.style.*; 28 import org.lobobrowser.util.*; 29 import org.w3c.css.sac.InputSource; 30 import org.w3c.dom.*; 31 import org.w3c.dom.css.CSSStyleDeclaration; 32 import org.w3c.dom.html2.*; 33 import com.steadystate.css.parser.CSSOMParser; 34 35 import java.io.*; 36 import java.util.ArrayList ; 37 import java.util.Collection ; 38 import java.util.Iterator ; 39 import java.util.Map ; 40 import java.util.StringTokenizer ; 41 import java.util.logging.*; 42 43 public class HTMLElementImpl extends ElementImpl implements HTMLElement, CSS2PropertiesContext { 44 private final boolean noStyleSheet; 45 46 public HTMLElementImpl(String name, boolean noStyleSheet) { 47 super(name); 48 this.noStyleSheet = noStyleSheet; 49 } 50 51 public HTMLElementImpl(String name) { 52 super(name); 53 this.noStyleSheet = false; 54 } 55 56 private volatile CSS2PropertiesImpl currentStyleDeclarationState; 57 private volatile CSS2PropertiesImpl localStyleDeclarationState; 58 59 protected final void forgetLocalStyle() { 60 synchronized(this) { 61 this.currentStyleDeclarationState = null; 62 this.localStyleDeclarationState = null; 63 } 64 } 65 66 protected final void forgetStyle(boolean deep) { 67 synchronized(this) { 70 this.currentStyleDeclarationState = null; 71 if(deep) { 72 java.util.ArrayList nl = this.nodeList; 73 if(nl != null) { 74 Iterator i = nl.iterator(); 75 while(i.hasNext()) { 76 Object node = i.next(); 77 if(node instanceof HTMLElementImpl) { 78 ((HTMLElementImpl) node).forgetStyle(deep); 79 } 80 } 81 } 82 } 83 } 84 } 85 86 90 public CSS2PropertiesImpl getCurrentStyle() { 91 CSS2PropertiesImpl sds; 92 synchronized(this) { 93 sds = this.currentStyleDeclarationState; 94 if(sds != null) { 95 return sds; 96 } 97 } 98 sds = this.addStyleSheetDeclarations(sds); 101 CSS2PropertiesImpl localStyle = this.getStyle(); 103 if(sds == null) { 104 sds = new CSS2PropertiesImpl(this); 105 sds.setLocalStyleProperties(localStyle); 106 } 107 else { 108 sds.setLocalStyleProperties(localStyle); 109 } 110 synchronized(this) { 111 CSS2PropertiesImpl setProps = this.currentStyleDeclarationState; 114 if(setProps != null) { 115 return setProps; 116 } 117 this.currentStyleDeclarationState = sds; 118 return sds; 119 } 120 } 121 122 161 166 public CSS2PropertiesImpl getStyle() { 167 CSS2PropertiesImpl sds; 168 synchronized(this) { 169 sds = this.localStyleDeclarationState; 170 if(sds != null) { 171 return sds; 172 } 173 sds = new CSS2PropertiesImpl(this); 174 String style = this.getAttribute("style"); 176 if(style != null && style.length() != 0) { 177 CSSOMParser parser = new CSSOMParser(); 178 InputSource inputSource = this.getCssInputSourceForDecl(style); 179 try { 180 CSSStyleDeclaration sd = parser.parseStyleDeclaration(inputSource); 181 sds.addStyleDeclaration(sd); 182 } catch(Exception err) { 183 String id = this.getId(); 184 String withId = id == null ? "" : " with ID '" + id + "'"; 185 this.warn("Unable to parse style attribute value for element " + this.getTagName() + withId + ".", err); 186 } 187 } 188 this.localStyleDeclarationState = sds; 189 } 190 return sds; 192 } 193 194 251 public void setStyle(Object value) { 252 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot set style property"); 253 } 254 255 public void setCurrentStyle(Object value) { 256 throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot set currentStyle property"); 257 } 258 259 public String getClassName() { 260 String className = this.getAttribute("class"); 261 return className == null ? "" : className; 263 } 264 265 public void setClassName(String className) { 266 this.setAttribute("class", className); 267 } 268 269 public String getCharset() { 270 return this.getAttribute("charset"); 271 } 272 273 public void setCharset(String charset) { 274 this.setAttribute("charset", charset); 275 } 276 277 public void warn(String message, Throwable err) { 278 logger.log(Level.WARNING, message, err); 279 } 280 281 public void warn(String message) { 282 logger.log(Level.WARNING, message); 283 } 284 285 protected int getAttributeAsInt(String name, int defaultValue) { 286 String value = this.getAttribute(name); 287 try { 288 return Integer.parseInt(value); 289 } catch(Exception err) { 290 this.warn("Bad integer", err); 291 return defaultValue; 292 } 293 } 294 295 public boolean getAttributeAsBoolean(String name) { 296 String value = this.getAttribute(name); 297 return name.equalsIgnoreCase(value); 298 } 299 300 protected void assignAttributeField(String normalName, String value) { 301 if(!this.notificationsSuspended) { 302 this.informInvalidAttibute(normalName); 303 } 304 else { 305 if("style".equals(normalName)) { 306 this.forgetLocalStyle(); 307 } 308 } 309 super.assignAttributeField(normalName, value); 310 } 311 312 protected final InputSource getCssInputSourceForDecl(String text) { 313 java.io.Reader reader = new StringReader("{" + text + "}"); 314 InputSource is = new InputSource(reader); 315 return is; 316 } 317 318 325 protected final CSS2PropertiesImpl addStyleSheetDeclarations(CSS2PropertiesImpl style) { 326 Node pn = this.parentNode; 327 if(pn == null) { 328 return style; 330 } 331 String classNames = this.getClassName(); 332 if(classNames != null && classNames.length() != 0) { 333 String id = this.getId(); 334 String elementName = this.getTagName(); 335 String [] classNameArray = Strings.split(classNames); 336 for(int i = classNameArray.length; --i >= 0;) { 337 String className = classNameArray[i]; 338 Collection sds = this.findStyleDeclarations(elementName, id, className); 339 if(sds != null) { 340 Iterator sdsi = sds.iterator(); 341 while(sdsi.hasNext()) { 342 CSSStyleDeclaration sd = (CSSStyleDeclaration) sdsi.next(); 343 if(style == null) { 344 style = new CSS2PropertiesImpl(this); 345 } 346 style.addStyleDeclaration(sd); 347 } 348 } 349 } 350 } 351 else { 352 String id = this.getId(); 353 String elementName = this.getTagName(); 354 Collection sds = this.findStyleDeclarations(elementName, id, null); 355 if(sds != null) { 356 Iterator sdsi = sds.iterator(); 357 while(sdsi.hasNext()) { 358 CSSStyleDeclaration sd = (CSSStyleDeclaration) sdsi.next(); 359 if(style == null) { 360 style = new CSS2PropertiesImpl(this); 361 } 362 style.addStyleDeclaration(sd); 363 } 364 } 365 } 366 return style; 367 } 368 369 protected final Collection findStyleDeclarations(String elementName, String id, String className) { 370 HTMLDocumentImpl doc = (HTMLDocumentImpl) this.document; 371 if(doc == null) { 372 return null; 373 } 374 StyleSheetAggregator ssa = doc.getStyleSheetAggregator(); 375 return ssa.getStyleDeclarations(this, elementName, id, className); 376 } 377 378 public void informInvalid() { 379 this.forgetStyle(false); 381 super.informInvalid(); 382 } 383 384 public void informInvalidAttibute(String normalName) { 385 if("style".equals(normalName)) { 388 this.forgetLocalStyle(); 389 } 390 else if("id".equals(normalName) || "class".equals(normalName)) { 391 this.forgetStyle(false); 392 } 393 super.informInvalid(); 395 } 396 397 public void informLayoutInvalid() { 398 super.informLayoutInvalid(); 401 } 402 403 407 protected FormInput[] getFormInputs() { 408 return null; 410 } 411 412 private boolean classMatch(String classTL) { 413 String classNames = this.getClassName(); 414 if(classNames == null || classNames.length() == 0) { 415 return classTL == null; 416 } 417 StringTokenizer tok = new StringTokenizer (classNames, " \t\r\n"); 418 while(tok.hasMoreTokens()) { 419 String token = tok.nextToken(); 420 if(token.toLowerCase().equals(classTL)) { 421 return true; 422 } 423 } 424 return false; 425 } 426 427 433 public HTMLElementImpl getAncestorWithClass(String elementTL, String classTL) { 434 Object nodeObj = this.getParentNode(); 435 if(nodeObj instanceof HTMLElementImpl) { 436 HTMLElementImpl parentElement = (HTMLElementImpl) nodeObj; 437 String pelementTL = parentElement.getTagName().toLowerCase(); 438 if(("*".equals(elementTL) || elementTL.equals(pelementTL)) && parentElement.classMatch(classTL)) { 439 return parentElement; 440 } 441 return parentElement.getAncestorWithClass(elementTL, classTL); 442 } 443 else { 444 return null; 445 } 446 } 447 448 public HTMLElementImpl getAncestorWithId(String elementTL, String idTL) { 449 Object nodeObj = this.getParentNode(); 450 if(nodeObj instanceof HTMLElementImpl) { 451 HTMLElementImpl parentElement = (HTMLElementImpl) nodeObj; 452 String pelementTL = parentElement.getTagName().toLowerCase(); 453 String pid = parentElement.getId(); 454 String pidTL = pid == null ? null : pid.toLowerCase(); 455 if(("*".equals(elementTL) || elementTL.equals(pelementTL)) && idTL.equals(pidTL)) { 456 return parentElement; 457 } 458 return parentElement.getAncestorWithId(elementTL, idTL); 459 } 460 else { 461 return null; 462 } 463 } 464 465 public HTMLElementImpl getAncestor(String elementTL) { 466 Object nodeObj = this.getParentNode(); 467 if(nodeObj instanceof HTMLElementImpl) { 468 HTMLElementImpl parentElement = (HTMLElementImpl) nodeObj; 469 if("*".equals(elementTL)) { 470 return parentElement; 471 } 472 String pelementTL = parentElement.getTagName().toLowerCase(); 473 if(elementTL.equals(pelementTL)) { 474 return parentElement; 475 } 476 return parentElement.getAncestor(elementTL); 477 } 478 else { 479 return null; 480 } 481 } 482 483 protected Object getAncestorForJavaClass(Class javaClass) { 484 Object nodeObj = this.getParentNode(); 485 if(nodeObj == null || javaClass.isInstance(nodeObj)) { 486 return nodeObj; 487 } 488 else if(nodeObj instanceof HTMLElementImpl) { 489 return ((HTMLElementImpl) nodeObj).getAncestorForJavaClass(javaClass); 490 } 491 else { 492 return null; 493 } 494 } 495 496 public void setInnerHTML(String newHtml) { 497 HTMLDocumentImpl document = (HTMLDocumentImpl) this.document; 498 if(document == null) { 499 this.warn("setInnerHTML(): Element " + this + " does not belong to a document."); 500 return; 501 } 502 HtmlParser parser = new HtmlParser(document.getUserAgentContext(), document, null, null, null); 503 synchronized(this) { 504 ArrayList nl = this.nodeList; 505 if (nl != null) { 506 nl.clear(); 507 } 508 } 509 try { 511 Reader reader = new StringReader(newHtml); 512 try { 513 parser.parse(reader, this); 514 } finally { 515 reader.close(); 516 } 517 } catch(Exception thrown) { 518 this.warn("setInnerHTML(): Error setting inner HTML.", thrown); 519 } 520 } 521 522 public String getInnerHTML() { 523 StringBuffer buffer = new StringBuffer (); 524 synchronized(this) { 525 this.appendInnerHTMLImpl(buffer); 526 } 527 return buffer.toString(); 528 } 529 530 public String getOuterHTML() { 531 StringBuffer buffer = new StringBuffer (); 532 synchronized(this) { 533 this.appendOuterHTMLImpl(buffer); 534 } 535 return buffer.toString(); 536 } 537 538 protected void appendInnerHTMLImpl(StringBuffer buffer) { 539 ArrayList nl = this.nodeList; 540 int size; 541 if (nl != null && (size = nl.size()) > 0) { 542 for (int i = 0; i < size; i++) { 543 Node child = (Node) nl.get(i); 544 if (child instanceof HTMLElementImpl) { 545 ((HTMLElementImpl) child).appendOuterHTMLImpl(buffer); 546 } 547 else if(child instanceof Comment) { 548 buffer.append("<!--" + ((Comment) child).getTextContent() + "-->"); 549 } 550 else if (child instanceof Text) { 551 String text = ((Text) child).getTextContent(); 552 String encText = Strings.strictHtmlEncode(text); 553 buffer.append(encText); 554 } 555 } 556 } 557 } 558 559 protected void appendOuterHTMLImpl(StringBuffer buffer) { 560 String tagName = this.getTagName(); 561 buffer.append('<'); 562 buffer.append(tagName); 563 Map attributes = this.attributes; 564 if(attributes != null) { 565 Iterator i = attributes.entrySet().iterator(); 566 while(i.hasNext()) { 567 Map.Entry entry = (Map.Entry ) i.next(); 568 String value = (String ) entry.getValue(); 569 if(value != null) { 570 buffer.append(' '); 571 buffer.append(entry.getKey()); 572 buffer.append("=\""); 573 buffer.append(Strings.strictHtmlEncode(value)); 574 buffer.append("\""); 575 } 576 } 577 } 578 ArrayList nl = this.nodeList; 579 if(nl == null || nl.size() == 0) { 580 buffer.append("/>"); 581 return; 582 } 583 buffer.append('>'); 584 this.appendInnerHTMLImpl(buffer); 585 buffer.append("</"); 586 buffer.append(tagName); 587 buffer.append('>'); 588 } 589 590 protected RenderState createRenderState(RenderState prevRenderState) { 591 return new StyleSheetRenderState(prevRenderState, this); 594 } 595 596 public int getOffsetTop() { 597 CSS2PropertiesImpl style = this.getCurrentStyle(); 598 if(style == null) { 599 return 0; 600 } 601 String topText = style.getTop(); 602 return topText == null ? 0 : HtmlValues.getPixelSize(topText, this.getRenderState(), 0); 603 } 604 605 public int getOffsetLeft() { 606 CSS2PropertiesImpl style = this.getCurrentStyle(); 607 if(style == null) { 608 return 0; 609 } 610 String leftText = style.getLeft(); 611 return leftText == null ? 0 : HtmlValues.getPixelSize(leftText, this.getRenderState(), 0); 612 } 613 614 public int getOffsetWidth() { 615 CSS2PropertiesImpl style = this.getCurrentStyle(); 616 if(style == null) { 617 return 0; 618 } 619 String valueText = style.getWidth(); 620 return valueText == null ? 0 : HtmlValues.getPixelSize(valueText, this.getRenderState(), 0); 621 } 622 623 public int getOffsetHeight() { 624 CSS2PropertiesImpl style = this.getCurrentStyle(); 625 if(style == null) { 626 return 0; 627 } 628 String valueText = style.getHeight(); 629 return valueText == null ? 0 : HtmlValues.getPixelSize(valueText, this.getRenderState(), 0); 630 } 631 632 public CSS2PropertiesImpl getParentStyle() { 633 Object parent = this.parentNode; 634 if(parent instanceof HTMLElementImpl) { 635 return ((HTMLElementImpl) parent).getCurrentStyle(); 636 } 637 return null; 638 } 639 640 public String toString() { 641 return super.toString() + "[currentStyle=" + this.getCurrentStyle() + "]"; 642 } 643 } 644 | Popular Tags |