Package org.apache.batik.css

Source Code of org.apache.batik.css.AbstractViewCSS$ComputedStyleCache$Entry

/*****************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved.        *
* ------------------------------------------------------------------------- *
* This software is published under the terms of the Apache Software License *
* version 1.1, a copy of which has been included with this distribution in  *
* the LICENSE file.                                                         *
*****************************************************************************/

package org.apache.batik.css;

import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.batik.css.sac.ExtendedSelector;
import org.apache.batik.css.value.ImmutableInherit;
import org.apache.batik.css.value.RelativeValueResolver;

import org.w3c.css.sac.SelectorList;
import org.w3c.dom.DOMException;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.css.CSSImportRule;
import org.w3c.dom.css.CSSMediaRule;
import org.w3c.dom.css.CSSRule;
import org.w3c.dom.css.CSSRuleList;
import org.w3c.dom.css.CSSStyleDeclaration;
import org.w3c.dom.css.CSSStyleRule;
import org.w3c.dom.css.CSSStyleSheet;
import org.w3c.dom.css.CSSValue;
import org.w3c.dom.css.DocumentCSS;
import org.w3c.dom.css.ElementCSSInlineStyle;
import org.w3c.dom.css.ViewCSS;
import org.w3c.dom.stylesheets.DocumentStyle;
import org.w3c.dom.stylesheets.MediaList;
import org.w3c.dom.stylesheets.StyleSheetList;
import org.w3c.dom.views.DocumentView;

/**
* This class provides an abstract implementation of the
* {@link org.w3c.dom.css.ViewCSS} interface.
*
* @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
* @version $Id: AbstractViewCSS.java,v 1.13 2001/04/23 08:46:37 hillion Exp $
*/
public abstract class AbstractViewCSS implements ViewCSS {

    /**
     * The document of which this object is a view.
     */
    protected DocumentView document;

    /**
     * The cached computed styles.
     */
    protected ComputedStyleCache styles = new ComputedStyleCache();

    /**
     * The media to use for cascading.
     */
    protected MediaList media;

    /**
     * The user-agent style sheet.
     */
    protected CSSStyleSheet userAgentStyleSheet;

    /**
     * The user style sheet.
     */
    protected CSSStyleSheet userStyleSheet;

    /**
     * The relative value resolvers.
     */
    protected List relativeValueResolvers = new LinkedList();

    /**
     * creates a new ViewCSS object.
     * @param doc The document view associated with this abstract view.
     */
    protected AbstractViewCSS(DocumentView doc) {
  document = doc;
    }

    /**
     * <b>DOM</b>: Implements {@link
     * org.w3c.dom.views.AbstractView#getDocument()}.
     */
    public DocumentView getDocument() {
        return document;
    }

    /**
     * <b>DOM</b>: Implements {@link
     * org.w3c.dom.css.ViewCSS#getComputedStyle(Element,String)}.
     */
    public CSSStyleDeclaration getComputedStyle(Element elt,
                                                String pseudoElt) {
  return getComputedStyleInternal(elt, pseudoElt);
    }
    /**
     * Internal version of getComputedStyle().
     */
    public CSSOMReadOnlyStyleDeclaration getComputedStyleInternal(Element elt,
                                                                  String pseudoElt) {
        pseudoElt = (pseudoElt == null) ? "" : pseudoElt;
        CSSOMReadOnlyStyleDeclaration result = styles.get(elt, pseudoElt);
 
        if (result == null) {
            result = computeStyle(elt, pseudoElt);
            styles.put(elt, pseudoElt, result);
        }
  return result;
    }
    /**
     * Sets the computed style in the cache in a way it is not collectable.
     */
    public void setComputedStyle(Element elt,
                                 String pseudoElt,
                                 CSSOMReadOnlyStyleDeclaration sd) {
        pseudoElt = (pseudoElt == null) ? "" : pseudoElt;
        sd.setContext(this, elt);
        styles.putPermanent(elt, pseudoElt, sd);
    }

    /**
     * Disposes the style declarations explicitly setted in the cache.
     */
    public void dispose() {
        styles.dispose();
    }

    /**
     * Sets the media to use to compute the styles.
     * @param mediaText The text representation of the media.
     */
    public void setMedia(String mediaText) {
  media = new DOMMediaList(mediaText);
    }

    /**
     * Sets the user-agent style sheet to use for cascading.
     */
    public void setUserAgentStyleSheet(CSSStyleSheet ss) {
  userAgentStyleSheet = ss;
    }

    /**
     * Sets the user style sheet to use for cascading.
     */
    public void setUserStyleSheet(CSSStyleSheet ss) {
  userStyleSheet = ss;
    }

    /**
     * Adds a resolver to the resolver list.
     */
    protected void addRelativeValueResolver(RelativeValueResolver rvr) {
  relativeValueResolvers.add(rvr);
    }

    /**
     * Computes the cascaded style for the given element and pseudo element.
     */
    public CSSOMReadOnlyStyleDeclaration computeStyle(Element elt,
                                            String pseudoElt) {
        CSSOMReadOnlyStyleDeclaration result;
        result = new CSSOMReadOnlyStyleDeclaration(this, elt);

  addUserAgentProperties(elt, pseudoElt, result);
  addUserProperties(elt, pseudoElt, result);
  addNonCSSPresentationalHints(elt, pseudoElt, result);
  addAuthorStyleSheetProperties(elt, pseudoElt, result);
  addInlineStyleProperties(elt, pseudoElt, result);
  addOverrideStyleProperties(elt, pseudoElt, result);

  computeRelativeValues(elt, pseudoElt, result);

  return result;
    }

    /**
     * Computes the relative values in the given style declaration for the
     * given element and pseudo-element.
     * @param e The element to match.
     * @param pe The pseudo-element to match.
     * @param rd The result style declaration.
     */
    protected void computeRelativeValues(Element e,
           String pe,
           CSSOMReadOnlyStyleDeclaration rd) {
  CSSOMReadOnlyStyleDeclaration sd;
  CSSOMReadOnlyValue val;
  int orig;
  String prio;
  String prop;
  Iterator it = relativeValueResolvers.iterator();
  while (it.hasNext()) {
      RelativeValueResolver rvr = (RelativeValueResolver)it.next();
      prop = rvr.getPropertyName();
      val = (CSSOMReadOnlyValue)rd.getLocalPropertyCSSValue(prop);
            prio = rd.getLocalPropertyPriority(prop);
            orig = rd.getLocalPropertyOrigin(prop);

            if (val == null &&
                (!rvr.isInheritedProperty() ||
                 HiddenChildElementSupport.getParentElement(e) == null)) {
                val = rvr.getDefaultValue();
            } else if (val != null &&
                       (val.getImmutableValue() ==
                        ImmutableInherit.INSTANCE) &&
                       HiddenChildElementSupport.getParentElement(e) != null) {
                val = null;
            }
            rd.setPropertyCSSValue(prop, val, "",
                             CSSOMReadOnlyStyleDeclaration.AUTHOR_ORIGIN);
            if (val != null) {
                rvr.resolveValue(e, pe, this, rd, val, prio, orig);
            }
  }
    }

    /**
     * Adds the user-agent style sheets properties matching the given element
     * and pseudo-element to the given style declaration.
     * @param e The element to match.
     * @param pe The pseudo-element to match.
     * @param rd The result style declaration.
     */
    protected void addUserAgentProperties(Element e,
            String pe,
            CSSOMReadOnlyStyleDeclaration rd) {
  CSSOMRuleList uaRules = new CSSOMRuleList();
 
  if (userAgentStyleSheet != null) {
      addMatchingRules(userAgentStyleSheet.getCssRules(), e, pe,
                             uaRules);
      uaRules = sortRules(uaRules, e, pe);
      for (int i = 0; i < uaRules.getLength(); i++) {
    CSSStyleRule rule = (CSSStyleRule)uaRules.item(i);
    CSSStyleDeclaration decl = rule.getStyle();
    int len = decl.getLength();
    for (int j = 0; j < len; j++) {
        setUserAgentProperty(decl.item(j), decl, rd);
    }
      }
  }
    }

    /**
     * Sets a user-agent value to a computed style declaration.
     * @param name The property name.
     * @param decl The style declaration.
     * @param dest The result style declaration.
     */
    protected void setUserAgentProperty(String name,
          CSSStyleDeclaration decl,
          CSSOMReadOnlyStyleDeclaration dest) {
  CSSOMValue         val   = (CSSOMValue)decl.getPropertyCSSValue(name);
  String             prio  = decl.getPropertyPriority(name);
  CSSOMReadOnlyValue value = val.createReadOnlyCopy();
  dest.setPropertyCSSValue(name,
         value,
         prio,
            CSSOMReadOnlyStyleDeclaration.USER_AGENT_ORIGIN);
    }
 
    /**
     * Adds the user style sheets properties matching the given element
     * and pseudo-element to the given style declaration.
     * @param e The element to match.
     * @param pe The pseudo-element to match.
     * @param rd The result style declaration.
     */
    protected void addUserProperties(Element e,
             String pe,
             CSSOMReadOnlyStyleDeclaration rd) {
  CSSOMRuleList uaRules = new CSSOMRuleList();
 
  if (userStyleSheet != null) {
      addMatchingRules(userStyleSheet.getCssRules(), e, pe, uaRules);
      uaRules = sortRules(uaRules, e, pe);
      for (int i = 0; i < uaRules.getLength(); i++) {
    CSSStyleRule rule = (CSSStyleRule)uaRules.item(i);
    CSSStyleDeclaration decl = rule.getStyle();
    int len = decl.getLength();
    for (int j = 0; j < len; j++) {
        setUserProperty(decl.item(j), decl, rd);
    }
      }
  }
    }

    /**
     * Sets a user value to a computed style declaration.
     * @param name The property name.
     * @param decl The style declaration.
     * @param dest The result style declaration.
     */
    protected void setUserProperty(String name,
           CSSStyleDeclaration decl,
           CSSOMReadOnlyStyleDeclaration dest) {
  CSSOMValue         val   = (CSSOMValue)decl.getPropertyCSSValue(name);
  String             prio  = decl.getPropertyPriority(name);
  CSSOMReadOnlyValue value = val.createReadOnlyCopy();
  dest.setPropertyCSSValue(name,
         value,
         prio,
         CSSOMReadOnlyStyleDeclaration.USER_ORIGIN);
    }
   
    /**
     * Adds the non-CSS presentational hints to the given style declaration.
     * @param e The element to match.
     * @param pe The pseudo-element to match.
     * @param rd The result style declaration.
     */
    protected void addNonCSSPresentationalHints
        (Element e, String pe, CSSOMReadOnlyStyleDeclaration rd) {
  if ((pe == null || pe.equals("")) &&
      e instanceof ElementNonCSSPresentationalHints) {
      ElementNonCSSPresentationalHints elt;
      elt = (ElementNonCSSPresentationalHints)e;
      CSSStyleDeclaration nonCSSDecl;
            nonCSSDecl = elt.getNonCSSPresentationalHints();
      if (nonCSSDecl != null) {
    int len = nonCSSDecl.getLength();
    for (int i = 0; i < len; i++) {
        setAuthorProperty(nonCSSDecl.item(i), nonCSSDecl, rd);
    }
      }
  }
    }

    /**
     * Adds the author style sheets properties matching the given element
     * and pseudo-element to the given style declaration.
     * @param e The element to match.
     * @param pe The pseudo-element to match.
     * @param rd The result style declaration.
     */
    protected void addAuthorStyleSheetProperties
        (Element e, String pe, CSSOMReadOnlyStyleDeclaration rd) {
        try {
            CSSOMRuleList authorRules = new CSSOMRuleList();
            StyleSheetList l = ((DocumentStyle)document).getStyleSheets();
            for (int i = 0; i < l.getLength(); i++) {
                addMatchingRules(((CSSStyleSheet)l.item(i)).getCssRules(),
                                 e,
                                 pe,
                                 authorRules);
            }
            authorRules = sortRules(authorRules, e, pe);
            for (int i = 0; i < authorRules.getLength(); i++) {
                CSSStyleRule rule = (CSSStyleRule)authorRules.item(i);
                CSSStyleDeclaration decl = rule.getStyle();
                int len = decl.getLength();
                for (int j = 0; j < len; j++) {
                    setAuthorProperty(decl.item(j), decl, rd);
                }
            }
  } catch (DOMException ex) {
            throw CSSDOMExceptionFactory.createDOMException
                (ex.code, "style.sheet",
                 new Object[] { ex.getMessage() });
        }
    }

    /**
     * Adds the inline style properties to the given style declaration.
     * @param e The element to match.
     * @param pe The pseudo-element to match.
     * @param rd The result style declaration.
     */
    protected void addInlineStyleProperties(Element e,
              String pe,
              CSSOMReadOnlyStyleDeclaration rd) {
        try {
            if (e instanceof ElementCSSInlineStyle) {
                boolean hasStyle = true;
                if (pe == null || pe.equals("") ||
                    e instanceof ExtendedElementCSSInlineStyle) {
                    hasStyle = ((ExtendedElementCSSInlineStyle)e).hasStyle();
                }
                if (hasStyle) {
                    CSSStyleDeclaration inlineDecl;
                    inlineDecl = ((ElementCSSInlineStyle)e).getStyle();
                    int len = inlineDecl.getLength();
                    for (int i = 0; i < len; i++) {
                        setAuthorProperty(inlineDecl.item(i), inlineDecl, rd);
                    }
                }
            }
  } catch (DOMException ex) {
            throw CSSDOMExceptionFactory.createDOMException
                (ex.code, "inline.style",
                 new Object[] { e.getTagName(), ex.getMessage() });
        }
    }

    /**
     * Adds the override style properties to the given style declaration.
     * @param e The element to match.
     * @param pe The pseudo-element to match.
     * @param rd The result style declaration.
     */
    protected void addOverrideStyleProperties
        (Element e, String pe, CSSOMReadOnlyStyleDeclaration rd) {
  CSSStyleDeclaration overrideDecl;
  overrideDecl = ((DocumentCSS)document).getOverrideStyle(e, pe);
  if ((pe == null || pe.equals("")) &&
      overrideDecl != null) {
      int len = overrideDecl.getLength();
      for (int i = 0; i < len; i++) {
    setAuthorProperty(overrideDecl.item(i), overrideDecl, rd);
      }
  }
    }

    /**
     * Sets a author value to a computed style declaration.
     * @param name The property name.
     * @param decl The style declaration.
     * @param dest The result style declaration.
     */
    protected void setAuthorProperty(String name,
                                     CSSStyleDeclaration decl,
                                     CSSOMReadOnlyStyleDeclaration dest) {
  CSSOMValue         val   = (CSSOMValue)decl.getPropertyCSSValue(name);
  String             prio  = decl.getPropertyPriority(name);
  CSSOMReadOnlyValue value = val.createReadOnlyCopy();

  CSSValue           dval   = dest.getLocalPropertyCSSValue(name);
        int                dorg   = dest.getLocalPropertyOrigin(name);
  String             dprio  = dest.getLocalPropertyPriority(name);

        if (dval == null ||
            dorg != CSSOMReadOnlyStyleDeclaration.USER_ORIGIN ||
            dprio.length() == 0) {
            dest.setPropertyCSSValue(name,
                                     value,
                                     prio,
                                     CSSOMReadOnlyStyleDeclaration.AUTHOR_ORIGIN);
        }
    }
   
    /**
     * Adds the style rules that match the given element and pseudo-element
     * to the given rule list.
     * @param l The input rule list.
     * @param e The element to match.
     * @param pe The pseudo-element to match.
     * @param rl The result rule list.
     */
    protected void addMatchingRules(CSSRuleList l,
            Element e,
            String pe,
            CSSOMRuleList rl) {
  int llen = l.getLength();
  for (int i = 0; i < llen; i++) {
      CSSRule rule = l.item(i);
      switch (rule.getType()) {
      case CSSRule.STYLE_RULE:
    CSSOMStyleRule sr = (CSSOMStyleRule)rule;
    SelectorList sl = sr.getSelectors();
    int slen = sl.getLength();
    for (int j = 0; j < slen; j++) {
        ExtendedSelector s = (ExtendedSelector)sl.item(j);
        if (s.match(e, pe)) {
      rl.append(rule);
        }
    }
    break;
      case CSSRule.IMPORT_RULE:
    CSSImportRule ir = (CSSImportRule)rule;
    CSSStyleSheet   is = ir.getStyleSheet();
    if (is != null) {
        addMatchingRules(is.getCssRules(), e, pe, rl);
    }
    break;
      case CSSRule.MEDIA_RULE:
    CSSMediaRule mr = (CSSMediaRule)rule;
    if (mediaMatch(mr.getMedia())) {
        addMatchingRules(mr.getCssRules(), e, pe, rl);
    }
    break;
      }
  }
    }

    /**
     * Sorts the rules in the given rule list by specificity.
     * @param l The rule list. The list is cleared by the methods.
     * @param e The element to match.
     * @param pe The pseudo-element to match.
     * @return The sorted list.
     */
    protected CSSOMRuleList sortRules(CSSOMRuleList l, Element e, String pe) {
  CSSOMRuleList result = new CSSOMRuleList();
  int llen;
  while ((llen = l.getLength()) > 0) {
      int min = Integer.MAX_VALUE;
      int imin = 0;
      for (int i = 0; i < llen; i++) {
    CSSOMStyleRule rule = (CSSOMStyleRule)l.item(i);
    SelectorList sl = rule.getSelectors();
    int spec = 0;
    int slen;
    if ((slen = sl.getLength()) == 1) {
        spec = ((ExtendedSelector)sl.item(0)).getSpecificity();
    } else {
        for (int j = 0; j < slen; j++) {
      ExtendedSelector s = (ExtendedSelector)sl.item(j);
      if (s.match(e, pe)) {
          spec = s.getSpecificity();
          break;
      }
        }
    }
    if (spec < min) {
        min = spec;
        imin = i;
    }
      }
      result.append(l.item(imin));
      l.delete(imin);
  }
  return result;
    }

    /**
     * Whether the given media list matches the media list of this
     * ViewCSS object.
     */
    protected boolean mediaMatch(MediaList ml) {
  if (media == null || media.getLength() == 0 || ml.getLength() == 0) {
      return true;
  }
  for (int i = 0; i < ml.getLength(); i++) {
      for (int j = 0; j < media.getLength(); j++) {
    if (ml.item(i).equalsIgnoreCase(media.item(j))) {
        return true;
    }
      }
  }
  return false;
    }

    /**
     * To cache the computed styles.
     */
    protected static class ComputedStyleCache {

        /**
         * The table used to store the style.
         */
        protected Entry[] table;

        /**
         * The number of entries
         */
        protected int count;

        /**
         * Creates a new ComputedStyleCache.
         */
        public ComputedStyleCache() {
            table = new Entry[11];
        }

        /**
         * Caches the given computed style.
         */
        public void put(Element elt, String pe, CSSOMReadOnlyStyleDeclaration sd) {
            update();

            int hash  = hashCode(elt, pe) & 0x7FFFFFFF;
            int index = hash % table.length;
 
            for (Entry e = table[index]; e != null; e = e.next) {
                if ((e.hash == hash) && e.match(elt, pe)) {
                    e.computedStyleReference = new SoftReference(sd);
                    return;
                }
            }

            // The key is not in the hash table
            int len = table.length;
            if (count++ >= (len * 3) >>> 2) {
                rehash();
                index = hash % table.length;
            }
           
            Entry e = new Entry(hash, elt, pe, new SoftReference(sd), table[index]);
            table[index] = e;
        }

        /**
         * Caches the given computed style without possibility of collection.
         */
        public void putPermanent(Element elt, String pe,
                                 CSSOMReadOnlyStyleDeclaration sd) {
            update();

            int hash  = hashCode(elt, pe) & 0x7FFFFFFF;
            int index = hash % table.length;
 
            for (Entry e = table[index]; e != null; e = e.next) {
                if ((e.hash == hash) && e.match(elt, pe)) {
                    e.computedStyleReference = new StrongReference(sd);
                    return;
                }
            }

            // The key is not in the hash table
            int len = table.length;
            if (count++ >= (len * 3) >>> 2) {
                rehash();
                index = hash % table.length;
            }
           
            Entry e = new Entry(hash, elt, pe, new StrongReference(sd), table[index]);
            table[index] = e;
        }

        /**
         * Returns the computed style mapped with the given element
         * and pseudo-element, if any.
         */
        public CSSOMReadOnlyStyleDeclaration get(Element elt, String pe) {
            update();
           
            int hash  = hashCode(elt, pe) & 0x7FFFFFFF;
            int index = hash % table.length;
 
            for (Entry e = table[index]; e != null; e = e.next) {
                if ((e.hash == hash) && e.match(elt, pe)) {
                    return (CSSOMReadOnlyStyleDeclaration)e.computedStyleReference.get();
                }
            }
            return null;
        }

        /**
         * Rehash the table
         */
        protected void rehash () {
            Entry[] oldTable = table;
 
            table = new Entry[oldTable.length * 2 + 1];
 
            for (int i = oldTable.length-1; i >= 0; i--) {
                for (Entry old = oldTable[i]; old != null;) {
                    Entry e = old;
                    old = old.next;
                   
                    int index = e.hash % table.length;
                    e.next = table[index];
                    table[index] = e;
                }
            }
        }

        /**
         * Updates the table.
         */
        protected void update() {
            for (int i = table.length - 1; i >= 0; --i) {
                Entry e = table[i];
                Entry p = null;
                if (e != null) {
                    if (e.computedStyleReference.get() == null) {
                        table[i] = e.next;
                        count--;
                    }
                    p = e;
                    e = e.next;
                }
                while (e != null) {
                    if (e.computedStyleReference.get() == null) {
                        p.next = e.next;
                        count--;
                    }
                    p = e;
                    e = e.next;
                }
            }
        }

        /**
         * Removes the permanently cached style declarations.
         */
        public void dispose() {
            for (int i = table.length - 1; i >= 0; --i) {
                Entry e = table[i];
                Entry p = null;
                if (e != null) {
                    if (e.computedStyleReference instanceof StrongReference) {
                        table[i] = e.next;
                        count--;
                    }
                    p = e;
                    e = e.next;
                }
                while (e != null) {
                    if (e.computedStyleReference instanceof StrongReference) {
                        p.next = e.next;
                        count--;
                    }
                    p = e;
                    e = e.next;
                }
            }
        }

        /**
         * Computes a hash code for the given element and pseudo-element.
         */
        protected int hashCode(Element e, String pe) {
            return e.hashCode() ^ pe.hashCode();
        }

        /**
         * To store computed style with a strong reference.
         */
        protected static class StrongReference extends SoftReference {
           
            /**
             * A strong reference.
             */
            protected Object reference;

            /**
             * Creates a new strong reference.
             */
            public StrongReference(Object o) {
                super(o);
                reference = o;
            }
        }

        /**
         * To manage collisions in the table.
         */
        protected static class Entry {
           
            /**
             * The hash code
             */
            public int hash;
 
            /**
             * The element.
             */
            public Element element;

            /**
             * The pseudo-element.
             */
            public String pseudoElement;

            /**
             * The computed style.
             */
            public SoftReference computedStyleReference;

            /**
             * The next entry.
             */
            public Entry next;

            /**
             * Creates a new entry.
             */
            public Entry(int h, Element e, String pe, SoftReference sd, Entry n) {
                hash = h;
                element = e;
                pseudoElement = pe;
                computedStyleReference = sd;
                next = n;
            }

            /**
             * Whether this entry match the given keys.
             */
            public boolean match(Element e, String pe) {
                if (e == element) {
                    if (pe.equals(pseudoElement)) {
                        return true;
                    }
                }
                return false;
            }
        }
    }
}
TOP

Related Classes of org.apache.batik.css.AbstractViewCSS$ComputedStyleCache$Entry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.