| 1 18 package org.apache.batik.css.engine; 19 20 import java.io.IOException ; 21 import java.io.StringReader ; 22 import java.net.MalformedURLException ; 23 import java.net.URL ; 24 import java.util.ArrayList ; 25 import java.util.Collections ; 26 import java.util.HashSet ; 27 import java.util.LinkedList ; 28 import java.util.List ; 29 import java.util.Set ; 30 31 import org.apache.batik.css.engine.sac.CSSConditionFactory; 32 import org.apache.batik.css.engine.sac.CSSSelectorFactory; 33 import org.apache.batik.css.engine.sac.ExtendedSelector; 34 import org.apache.batik.css.engine.value.ComputedValue; 35 import org.apache.batik.css.engine.value.InheritValue; 36 import org.apache.batik.css.engine.value.ShorthandManager; 37 import org.apache.batik.css.engine.value.Value; 38 import org.apache.batik.css.engine.value.ValueManager; 39 import org.apache.batik.css.parser.ExtendedParser; 40 import org.apache.batik.util.CSSConstants; 41 import org.apache.batik.util.ParsedURL; 42 import org.w3c.css.sac.CSSException; 43 import org.w3c.css.sac.DocumentHandler; 44 import org.w3c.css.sac.InputSource; 45 import org.w3c.css.sac.LexicalUnit; 46 import org.w3c.css.sac.SACMediaList; 47 import org.w3c.css.sac.SelectorList; 48 import org.w3c.dom.DOMException ; 49 import org.w3c.dom.Document ; 50 import org.w3c.dom.Element ; 51 import org.w3c.dom.NamedNodeMap ; 52 import org.w3c.dom.Node ; 53 import org.w3c.dom.events.Event ; 54 import org.w3c.dom.events.EventListener ; 55 import org.w3c.dom.events.EventTarget ; 56 import org.w3c.dom.events.MutationEvent ; 57 58 64 public abstract class CSSEngine { 65 66 70 protected List fontFaces = new LinkedList (); 71 72 76 public List getFontFaces() { return fontFaces; } 77 78 CSSEngineUserAgent userAgent = null; 79 80 83 public static CSSStylableElement getParentCSSStylableElement(Element elt) { 84 Element e = getParentElement(elt); 85 while (e != null) { 86 if (e instanceof CSSStylableElement) { 87 return (CSSStylableElement)e; 88 } 89 e = getParentElement(e); 90 } 91 return null; 92 } 93 94 98 public static Element getParentElement(Element elt) { 99 Node n = elt.getParentNode(); 100 while (n != null) { 101 n = getLogicalParentNode(n); 102 if (n.getNodeType() == Node.ELEMENT_NODE) { 103 return (Element )n; 104 } 105 n = n.getParentNode(); 106 } 107 return null; 108 } 109 110 113 public static Node getLogicalParentNode(Node parent) { 114 Node node = parent; 115 if (node != null) { 116 if (node instanceof CSSImportedElementRoot) { 117 return ((CSSImportedElementRoot)node).getCSSParentElement(); 118 } else { 119 return node; 120 } 121 } 122 return null; 123 } 124 125 128 public static CSSImportedElementRoot getImportedChild(Node node) { 129 if (node instanceof CSSImportNode) { 130 CSSImportNode inode = (CSSImportNode)node; 131 CSSImportedElementRoot r = inode.getCSSImportedElementRoot(); 132 return r; 133 } 134 return null; 135 } 136 137 140 protected CSSContext cssContext; 141 142 145 protected Document document; 146 147 150 protected URL documentURI; 151 152 155 protected StringIntMap indexes; 156 157 160 protected StringIntMap shorthandIndexes; 161 162 165 protected ValueManager[] valueManagers; 166 167 170 protected ShorthandManager[] shorthandManagers; 171 172 175 protected ExtendedParser parser; 176 177 180 protected String [] pseudoElementNames; 181 182 185 protected int fontSizeIndex = -1; 186 187 190 protected int lineHeightIndex = -1; 191 192 195 protected int colorIndex = -1; 196 197 200 protected StyleSheet userAgentStyleSheet; 201 202 205 protected StyleSheet userStyleSheet; 206 207 210 protected SACMediaList media; 211 212 215 protected List styleSheetNodes; 216 217 220 protected String styleNamespaceURI; 221 222 225 protected String styleLocalName; 226 227 230 protected String classNamespaceURI; 231 232 235 protected String classLocalName; 236 237 240 protected Set nonCSSPresentationalHints; 241 242 245 protected String nonCSSPresentationalHintsNamespaceURI; 246 247 250 protected StyleDeclarationDocumentHandler styleDeclarationDocumentHandler = 251 new StyleDeclarationDocumentHandler(); 252 253 256 protected StyleDeclarationUpdateHandler styleDeclarationUpdateHandler; 257 258 261 protected StyleSheetDocumentHandler styleSheetDocumentHandler = 262 new StyleSheetDocumentHandler(); 263 264 268 protected StyleDeclarationBuilder styleDeclarationBuilder = 269 new StyleDeclarationBuilder(); 270 271 274 protected CSSStylableElement element; 275 276 279 protected URL cssBaseURI; 280 281 284 protected String alternateStyleSheet; 285 286 289 protected EventListener domAttrModifiedListener; 290 291 294 protected EventListener domNodeInsertedListener; 295 296 299 protected EventListener domNodeRemovedListener; 300 301 304 protected EventListener domSubtreeModifiedListener; 305 306 309 protected EventListener domCharacterDataModifiedListener; 310 311 314 protected boolean styleSheetRemoved; 315 316 319 protected Node removedStylableElementSibling; 320 321 324 protected List listeners = Collections.synchronizedList(new LinkedList ()); 325 326 329 protected Set selectorAttributes; 330 331 334 protected final int[] ALL_PROPERTIES; 335 336 339 protected CSSConditionFactory cssConditionFactory; 340 341 360 protected CSSEngine(Document doc, 361 URL uri, 362 ExtendedParser p, 363 ValueManager[] vm, 364 ShorthandManager[] sm, 365 String [] pe, 366 String sns, 367 String sln, 368 String cns, 369 String cln, 370 boolean hints, 371 String hintsNS, 372 CSSContext ctx) { 373 document = doc; 374 documentURI = uri; 375 parser = p; 376 pseudoElementNames = pe; 377 styleNamespaceURI = sns; 378 styleLocalName = sln; 379 classNamespaceURI = cns; 380 classLocalName = cln; 381 cssContext = ctx; 382 383 cssConditionFactory = new CSSConditionFactory(cns, cln, null, "id"); 384 385 int len = vm.length; 386 indexes = new StringIntMap(len); 387 valueManagers = vm; 388 389 for (int i = len - 1; i >= 0; --i) { 390 String pn = vm[i].getPropertyName(); 391 indexes.put(pn, i); 392 if (fontSizeIndex == -1 && 393 pn.equals(CSSConstants.CSS_FONT_SIZE_PROPERTY)) { 394 fontSizeIndex = i; 395 } 396 if (lineHeightIndex == -1 && 397 pn.equals(CSSConstants.CSS_LINE_HEIGHT_PROPERTY)) { 398 lineHeightIndex = i; 399 } 400 if (colorIndex == -1 && 401 pn.equals(CSSConstants.CSS_COLOR_PROPERTY)) { 402 colorIndex = i; 403 } 404 } 405 406 len = sm.length; 407 shorthandIndexes = new StringIntMap(len); 408 shorthandManagers = sm; 409 for (int i = len - 1; i >= 0; --i) { 410 shorthandIndexes.put(sm[i].getPropertyName(), i); 411 } 412 413 if (hints) { 414 nonCSSPresentationalHints = new HashSet (vm.length+sm.length); 415 nonCSSPresentationalHintsNamespaceURI = hintsNS; 416 len = vm.length; 417 for (int i = 0; i < len; i++) { 418 String pn = vm[i].getPropertyName(); 419 nonCSSPresentationalHints.add(pn); 420 } 421 len = sm.length; 422 for (int i = 0; i < len; i++) { 423 String pn = sm[i].getPropertyName(); 424 nonCSSPresentationalHints.add(pn); 425 } 426 } 427 428 if (cssContext.isDynamic() && 429 (document instanceof EventTarget )) { 430 EventTarget et = (EventTarget )document; 432 domAttrModifiedListener = new DOMAttrModifiedListener(); 433 et.addEventListener("DOMAttrModified", 434 domAttrModifiedListener, 435 false); 436 domNodeInsertedListener = new DOMNodeInsertedListener(); 437 et.addEventListener("DOMNodeInserted", 438 domNodeInsertedListener, 439 false); 440 domNodeRemovedListener = new DOMNodeRemovedListener(); 441 et.addEventListener("DOMNodeRemoved", 442 domNodeRemovedListener, 443 false); 444 domSubtreeModifiedListener = new DOMSubtreeModifiedListener(); 445 et.addEventListener("DOMSubtreeModified", 446 domSubtreeModifiedListener, 447 false); 448 domCharacterDataModifiedListener = 449 new DOMCharacterDataModifiedListener(); 450 et.addEventListener("DOMCharacterDataModified", 451 domCharacterDataModifiedListener, 452 false); 453 styleDeclarationUpdateHandler = 454 new StyleDeclarationUpdateHandler(); 455 } 456 457 ALL_PROPERTIES = new int[getNumberOfProperties()]; 458 for (int i = getNumberOfProperties() - 1; i >= 0; --i) { 459 ALL_PROPERTIES[i] = i; 460 } 461 } 462 463 466 public void dispose() { 467 setCSSEngineUserAgent(null); 468 disposeStyleMaps(document.getDocumentElement()); 469 if (document instanceof EventTarget ) { 470 EventTarget et = (EventTarget )document; 472 et.removeEventListener("DOMAttrModified", 473 domAttrModifiedListener, 474 false); 475 et.removeEventListener("DOMNodeInserted", 476 domNodeInsertedListener, 477 false); 478 et.removeEventListener("DOMNodeRemoved", 479 domNodeRemovedListener, 480 false); 481 et.removeEventListener("DOMSubtreeModified", 482 domSubtreeModifiedListener, 483 false); 484 et.removeEventListener("DOMCharacterDataModified", 485 domCharacterDataModifiedListener, 486 false); 487 } 488 } 489 490 private void disposeStyleMaps(Node node) { 491 if (node instanceof CSSStylableElement) { 492 ((CSSStylableElement)node).setComputedStyleMap(null, null); 493 } 494 for (Node n = node.getFirstChild(); 495 n != null; 496 n = n.getNextSibling()) { 497 if (n.getNodeType() == Node.ELEMENT_NODE) { 498 disposeStyleMaps(n); 499 } 500 Node c = getImportedChild(n); 501 if (c != null) { 502 disposeStyleMaps(c); 503 } 504 } 505 } 506 507 510 public CSSContext getCSSContext() { 511 return cssContext; 512 } 513 514 517 public Document getDocument() { 518 return document; 519 } 520 521 524 public int getFontSizeIndex() { 525 return fontSizeIndex; 526 } 527 528 531 public int getLineHeightIndex() { 532 return lineHeightIndex; 533 } 534 535 538 public int getColorIndex() { 539 return colorIndex; 540 } 541 542 545 public int getNumberOfProperties() { 546 return valueManagers.length; 547 } 548 549 552 public int getPropertyIndex(String name) { 553 return indexes.get(name); 554 } 555 556 559 public int getShorthandIndex(String name) { 560 return shorthandIndexes.get(name); 561 } 562 563 566 public String getPropertyName(int idx) { 567 return valueManagers[idx].getPropertyName(); 568 } 569 570 public void setCSSEngineUserAgent(CSSEngineUserAgent userAgent) { 571 this.userAgent = userAgent; 572 } 573 574 public CSSEngineUserAgent getCSSEngineUserAgent() { 575 return userAgent; 576 } 577 578 581 public void setUserAgentStyleSheet(StyleSheet ss) { 582 userAgentStyleSheet = ss; 583 } 584 585 588 public void setUserStyleSheet(StyleSheet ss) { 589 userStyleSheet = ss; 590 } 591 592 595 public ValueManager[] getValueManagers() { 596 return valueManagers; 597 } 598 599 602 public void setMedia(String str) { 603 try { 604 media = parser.parseMedia(str); 605 } catch (Exception e) { 606 String m = e.getMessage(); 607 if (m == null) m = ""; 608 String s =Messages.formatMessage 609 ("media.error", new Object [] { str, m }); 610 throw new DOMException (DOMException.SYNTAX_ERR, s); 611 } 612 } 613 614 617 public void setAlternateStyleSheet(String str) { 618 alternateStyleSheet = str; 619 } 620 621 625 public void importCascadedStyleMaps(Element src, 626 CSSEngine srceng, 627 Element dest) { 628 if (src instanceof CSSStylableElement) { 629 CSSStylableElement csrc = (CSSStylableElement)src; 630 CSSStylableElement cdest = (CSSStylableElement)dest; 631 632 StyleMap sm = srceng.getCascadedStyleMap(csrc, null); 633 sm.setFixedCascadedStyle(true); 634 cdest.setComputedStyleMap(null, sm); 635 636 if (pseudoElementNames != null) { 637 int len = pseudoElementNames.length; 638 for (int i = 0; i < len; i++) { 639 String pe = pseudoElementNames[i]; 640 sm = srceng.getCascadedStyleMap(csrc, pe); 641 cdest.setComputedStyleMap(pe, sm); 642 } 643 } 644 } 645 646 for (Node dn = dest.getFirstChild(), sn = src.getFirstChild(); 647 dn != null; 648 dn = dn.getNextSibling(), sn = sn.getNextSibling()) { 649 if (sn.getNodeType() == Node.ELEMENT_NODE) { 650 importCascadedStyleMaps((Element )sn, srceng, (Element )dn); 651 } 652 } 653 } 654 655 658 public URL getCSSBaseURI() { 659 if (cssBaseURI == null) { 660 cssBaseURI = element.getCSSBase(); 661 } 662 return cssBaseURI; 663 } 664 665 670 public StyleMap getCascadedStyleMap(CSSStylableElement elt, 671 String pseudo) { 672 int props = getNumberOfProperties(); 673 final StyleMap result = new StyleMap(props); 674 675 if (userAgentStyleSheet != null) { 677 List rules = new ArrayList (); 678 addMatchingRules(rules, userAgentStyleSheet, elt, pseudo); 679 addRules(elt, pseudo, result, rules, StyleMap.USER_AGENT_ORIGIN); 680 } 681 682 if (userStyleSheet != null) { 684 List rules = new ArrayList (); 685 addMatchingRules(rules, userStyleSheet, elt, pseudo); 686 addRules(elt, pseudo, result, rules, StyleMap.USER_ORIGIN); 687 } 688 689 element = elt; 690 try { 691 ShorthandManager.PropertyHandler ph = 693 new ShorthandManager.PropertyHandler() { 694 public void property(String pname, LexicalUnit lu, 695 boolean important) { 696 int idx = getPropertyIndex(pname); 697 if (idx != -1) { 698 ValueManager vm = valueManagers[idx]; 699 Value v = vm.createValue(lu, CSSEngine.this); 700 putAuthorProperty(result, idx, v, important, 701 StyleMap.NON_CSS_ORIGIN); 702 return; 703 } 704 idx = getShorthandIndex(pname); 705 if (idx == -1) 706 return; shorthandManagers[idx].setValues 709 (CSSEngine.this, this, lu, important); 710 } 711 }; 712 713 if (nonCSSPresentationalHints != null) { 714 NamedNodeMap attrs = elt.getAttributes(); 715 int len = attrs.getLength(); 716 for (int i = 0; i < len; i++) { 717 Node attr = attrs.item(i); 718 String an = attr.getNodeName(); 719 if (nonCSSPresentationalHints.contains(an)) { 720 try { 721 LexicalUnit lu; 722 lu = parser.parsePropertyValue(attr.getNodeValue()); 723 ph.property(an, lu, false); 724 } catch (Exception e) { 725 String m = e.getMessage(); 726 if (m == null) m = ""; 727 String u = ((documentURI == null)?"<unknown>": 728 documentURI.toString()); 729 String s = Messages.formatMessage 730 ("property.syntax.error.at", 731 new Object [] { u, an, attr.getNodeValue(),m}); 732 DOMException de = new DOMException (DOMException.SYNTAX_ERR, s); 733 if (userAgent == null) throw de; 734 userAgent.displayError(de); 735 } 736 } 737 } 738 } 739 740 List snodes = getStyleSheetNodes(); 742 int slen = snodes.size(); 743 if (slen > 0) { 744 List rules = new ArrayList (); 745 for (int i = 0; i < slen; i++) { 746 CSSStyleSheetNode ssn = (CSSStyleSheetNode)snodes.get(i); 747 StyleSheet ss = ssn.getCSSStyleSheet(); 748 if (ss != null && 749 (!ss.isAlternate() || 750 ss.getTitle() == null || 751 ss.getTitle().equals(alternateStyleSheet)) && 752 mediaMatch(ss.getMedia())) { 753 addMatchingRules(rules, ss, elt, pseudo); 754 } 755 } 756 addRules(elt, pseudo, result, rules, StyleMap.AUTHOR_ORIGIN); 757 } 758 759 if (styleLocalName != null) { 761 String style = elt.getAttributeNS(styleNamespaceURI, 762 styleLocalName); 763 if (style.length() > 0) { 764 try { 765 parser.setSelectorFactory(CSSSelectorFactory.INSTANCE); 766 parser.setConditionFactory(cssConditionFactory); 767 styleDeclarationDocumentHandler.styleMap = result; 768 parser.setDocumentHandler 769 (styleDeclarationDocumentHandler); 770 parser.parseStyleDeclaration(style); 771 styleDeclarationDocumentHandler.styleMap = null; 772 } catch (Exception e) { 773 &
|