Package com.adobe.dp.css

Source Code of com.adobe.dp.css.CascadeEngine$MatcherRule

package com.adobe.dp.css;

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import com.adobe.dp.xml.util.SMap;
import com.adobe.dp.xml.util.SMapAttributesAdapter;
import com.adobe.dp.xml.util.SMapImpl;
import com.adobe.dp.xml.util.XMLSerializer;

public class CascadeEngine {

  CascadeResult result;

  Vector matcherLists = new Vector();

  Hashtable classMap = new Hashtable();

  Hashtable tagMap = new Hashtable();

  int depth;

  int order;

  static class CascadeValue extends CSSValue {
    int specificity;

    int importance;

    int order;

    CSSValue value;

    public CascadeValue(CSSValue value, int specificity, int importance, int order) {
      this.value = value;
      this.specificity = specificity;
      this.importance = importance;
      this.order = order;
    }

    int compareSpecificity(CascadeValue other) {
      if (specificity != other.specificity)
        return specificity - other.specificity;
      if (importance != other.importance)
        return importance - other.importance;
      return order - other.order;
    }

    public void serialize(PrintWriter out) {
      // CascadeValue is internal and should never be left in public rules
      throw new RuntimeException("unexpected call");
    }

  }

  static class MatcherList {
    Vector matchers = new Vector();

    Set mediaList;

    CSSStylesheet stylesheet;

    MatcherList(CSSStylesheet stylesheet, Set mediaList) {
      this.stylesheet = stylesheet;
      this.mediaList = mediaList;
    }

    void addSelectorRule(SelectorRule rule, int depth, int order, boolean addSimpleSelectors) {
      for (int i = 0; i < rule.selectors.length; i++) {
        Selector s = rule.selectors[i];
        if (addSimpleSelectors || !(isClassSelector(s) || isTagSelector(s))) {
          ElementMatcher matcher = s.getElementMatcher();
          while (depth > 0) {
            matcher.pushElement(null, "*", null);
            depth--;
          }
          matchers.add(new MatcherRule(matcher, rule, order));
        }
      }
    }
  }

  static class MatcherRule {
    ElementMatcher matcher;

    SelectorRule rule;

    int order;

    public MatcherRule(ElementMatcher matcher, SelectorRule rule, int order) {
      super();
      this.matcher = matcher;
      this.rule = rule;
      this.order = order;
    }
  }

  public CascadeEngine() {

  }

  private static boolean isClassSelector(Selector s) {
    return s instanceof ClassSelector;
  }

  private static boolean isTagSelector(Selector s) {
    return s instanceof NamedElementSelector;
  }

  private void collectSimpleSelectors(SelectorRule rule, int order) {
    for (int i = 0; i < rule.selectors.length; i++) {
      Selector s = rule.selectors[i];
      if (isClassSelector(s)) {
        ClassSelector cs = (ClassSelector) s;
        Vector list = (Vector) classMap.get(cs.className);
        if (list == null) {
          list = new Vector();
          classMap.put(cs.className, list);
        }
        list.add(new MatcherRule(cs.getElementMatcher(), rule, order));
      } else if (isTagSelector(s)) {
        NamedElementSelector ns = (NamedElementSelector) s;
        Vector list = (Vector) tagMap.get(ns.getElementName());
        if (list == null) {
          list = new Vector();
          tagMap.put(ns.getElementName(), list);
        }
        list.add(new MatcherRule(ns.getElementMatcher(), rule, order));
      }
    }
  }

  public void add(CSSStylesheet stylesheet, Set mediaList) {
    MatcherList ml = new MatcherList(stylesheet, mediaList);
    Iterator statements = stylesheet.statements.iterator();
    while (statements.hasNext()) {
      Object statement = statements.next();
      if (statement instanceof SelectorRule) {
        SelectorRule sr = (SelectorRule) statement;
        if (mediaList == null) {
          collectSimpleSelectors(sr, order);
        }
        ml.addSelectorRule(sr, depth, order++, mediaList != null);
      }
    }
    matcherLists.add(ml);
  }

  private void applyRule(Selector selector, int order, BaseRule rule, String pseudoElement, Set mediaList) {
    int specificity = selector.getSpecificity();
    applyRule(specificity, order, rule, pseudoElement, mediaList);
  }

  public void applyInlineRule(InlineRule rule) {
    applyRule(0x7F000000, order, rule, null, null);
  }

  private void applyRule(int specificity, int order, BaseRule rule, String pseudoElement, Set mediaList) {
    if (rule == null || rule.properties == null)
      return;
    Iterator entries = rule.properties.entrySet().iterator();
    while (entries.hasNext()) {
      Map.Entry entry = (Map.Entry) entries.next();
      String prop = (String) entry.getKey();
      int importance = 0;
      CSSValue value = (CSSValue) entry.getValue();
      if (value instanceof CSSImportant)
        importance = 1;
      Iterator it = (mediaList == null ? null : mediaList.iterator());
      do {
        ElementProperties props;
        if (it == null)
          props = result.getProperties();
        else
          props = result.getPropertiesForMedia((String) it.next());
        InlineRule style;
        if (pseudoElement == null)
          style = props.getPropertySet();
        else
          style = props.getPropertySetForPseudoElement(pseudoElement);
        CascadeValue existing = (CascadeValue) style.get(prop);
        CascadeValue curr = new CascadeValue(value, specificity, importance, order);
        if (existing == null || curr.compareSpecificity(existing) > 0)
          style.set(prop, curr);
      } while (it != null && it.hasNext());
    }
  }

  private void applyClassRules(String ns, String name, SMap attrs) {
    if (attrs != null) {
      String classAttr = ClassElementMatcher.getClassAttribute(ns, name);
      if (classAttr != null) {
        Object classStr = attrs.get(null, classAttr);
        if (classStr != null) {
          StringTokenizer tok = new StringTokenizer(classStr.toString(), " ");
          while (tok.hasMoreTokens()) {
            String className = tok.nextToken();
            Vector list = (Vector) classMap.get(className);
            if (list != null) {
              int len = list.size();
              for (int i = 0; i < len; i++) {
                MatcherRule mr = (MatcherRule) list.get(i);
                applyRule(mr.matcher.getSelector(), mr.order, mr.rule, null, null);
              }
            }
          }
        }
      }
    }
  }

  private void applyTagRules(String ns, String name) {
    Vector list = (Vector) tagMap.get(name);
    if (list != null) {
      int len = list.size();
      for (int i = 0; i < len; i++) {
        MatcherRule mr = (MatcherRule) list.get(i);
        NamedElementSelector s = (NamedElementSelector) mr.matcher.getSelector();
        if (!s.hasElementNamespace() || (ns != null && s.getElementNamespace().equals(ns)))
          applyRule(s, mr.order, mr.rule, null, null);
      }
    }
  }

  /**
   * Styles an element with a given namespace, name and attributes
   *
   * @param ns
   *            element's namespace
   * @param name
   *            element's local name
   * @param attrs
   *            element's attributes
   */
  public void pushElement(String ns, String name, SMap attrs) {
    depth++;
    result = new CascadeResult();
    applyTagRules(ns, name);
    applyClassRules(ns, name, attrs);
    Iterator mli = matcherLists.iterator();
    while (mli.hasNext()) {
      MatcherList ml = (MatcherList) mli.next();
      Iterator matchers = ml.matchers.iterator();
      while (matchers.hasNext()) {
        MatcherRule mr = (MatcherRule) matchers.next();
        MatchResult res = mr.matcher.pushElement(ns, name, attrs);
        if (res != null)
          applyRule(mr.matcher.getSelector(), mr.order, mr.rule, res.pseudoElement, ml.mediaList);
      }
    }
  }

  /**
   * Finish element's processing. Note that pushElement/popElement calls
   * should correspond to the elements nesting.
   */
  public void popElement() {
    depth--;
    Iterator mli = matcherLists.iterator();
    while (mli.hasNext()) {
      MatcherList ml = (MatcherList) mli.next();
      Iterator matchers = ml.matchers.iterator();
      while (matchers.hasNext()) {
        MatcherRule mr = (MatcherRule) matchers.next();
        mr.matcher.popElement();
      }
    }
  }

  BaseRule makeRule(HashMap map) {
    if (map.isEmpty())
      return null;
    BaseRule rule = new InlineRule();
    Iterator entries = map.entrySet().iterator();
    while (entries.hasNext()) {
      Map.Entry entry = (Map.Entry) entries.next();
      String prop = (String) entry.getKey();
      CascadeValue cv = (CascadeValue) entry.getValue();
      rule.set(prop, cv.value);
    }
    return rule;
  }

  public CascadeResult getCascadeResult() {
    CascadeResult r = new CascadeResult();
    Iterator mediaList = result.media();
    ElementProperties elementProperties = result.getProperties();
    if (mediaList != null) {
      while (mediaList.hasNext()) {
        String media = (String) mediaList.next();
        ElementProperties mediaProps = result.getPropertiesForMedia(media);
        Iterator pel = mediaProps.pseudoElements();
        if (pel != null) {
          while (pel.hasNext()) {
            String pseudoElement = (String) pel.next();
            InlineRule mps = mediaProps.getPropertySetForPseudoElement(pseudoElement);
            InlineRule ps = elementProperties.getPropertySetForPseudoElement(pseudoElement);
            Iterator properties = mps.properties();
            while (properties.hasNext()) {
              String property = (String) properties.next();
              CascadeValue mediaSpecific = (CascadeValue) mps.get(property);
              CascadeValue generic = (CascadeValue) ps.get(property);
              if (generic == null || mediaSpecific.compareSpecificity(generic) > 0) {
                CSSValue value = mediaSpecific.value;
                ElementProperties rm = r.getPropertiesForMedia(media);
                rm.getPropertySetForPseudoElement(pseudoElement).set(property, value);
              }
            }
          }
        }
        InlineRule mps = mediaProps.getPropertySet();
        InlineRule ps = elementProperties.getPropertySet();
        Iterator properties = mps.properties();
        while (properties.hasNext()) {
          String property = (String) properties.next();
          CascadeValue mediaSpecific = (CascadeValue) mps.get(property);
          CascadeValue generic = (CascadeValue) ps.get(property);
          if (generic == null || mediaSpecific.compareSpecificity(generic) > 0) {
            CSSValue value = mediaSpecific.value;
            ElementProperties rm = r.getPropertiesForMedia(media);
            rm.getPropertySet().set(property, value);
          }
        }
      }
    }
    Iterator pel = elementProperties.pseudoElements();
    if (pel != null) {
      while (pel.hasNext()) {
        String pseudoElement = (String) pel.next();
        InlineRule ps = elementProperties.getPropertySetForPseudoElement(pseudoElement);
        Iterator properties = ps.properties();
        while (properties.hasNext()) {
          String property = (String) properties.next();
          CascadeValue cv = (CascadeValue) ps.get(property);
          ElementProperties rp = r.getProperties();
          rp.getPropertySetForPseudoElement(pseudoElement).set(property, cv.value);
        }
      }
    }
    InlineRule ps = elementProperties.getPropertySet();
    Iterator properties = ps.properties();
    while (properties.hasNext()) {
      String property = (String) properties.next();
      CascadeValue cv = (CascadeValue) ps.get(property);
      ElementProperties rp = r.getProperties();
      rp.getPropertySet().set(property, cv.value);
    }
    return r;
  }

  public static void main(String[] args) {
    try {
      InputStream in = new FileInputStream(args[0]);
      InputStream cssin = new FileInputStream(args[1]);
      CSSStylesheet stylesheet = (new CSSParser()).readStylesheet(cssin);

      SAXParserFactory factory = SAXParserFactory.newInstance();
      factory.setNamespaceAware(true);

      final CascadeEngine styler = new CascadeEngine();
      styler.add(stylesheet, null);

      final XMLSerializer ser = new XMLSerializer(System.out);
      ser.startDocument("1.0", "UTF-8");

      SAXParser parser = factory.newSAXParser();
      XMLReader reader = parser.getXMLReader();
      reader.setContentHandler(new ContentHandler() {
        public void characters(char[] text, int offset, int len) throws SAXException {
          ser.text(text, offset, len);
        }

        public void endDocument() throws SAXException {
        }

        public void endElement(String ns, String local, String qname) throws SAXException {
          styler.popElement();
          ser.endElement(ns, local);
        }

        public void endPrefixMapping(String arg0) throws SAXException {
        }

        public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException {
        }

        public void processingInstruction(String arg0, String arg1) throws SAXException {
        }

        public void setDocumentLocator(Locator arg0) {
        }

        public void skippedEntity(String arg0) throws SAXException {
        }

        public void startDocument() throws SAXException {
        }

        public void startElement(String ns, String local, String qname, Attributes attrs) throws SAXException {
          SMapImpl smap = new SMapImpl(new SMapAttributesAdapter(attrs));
          styler.pushElement(ns, local, smap);
          BaseRule es = styler.getCascadeResult().getProperties().getPropertySet();
          smap.put(null, "style", (es.isEmpty() ? null : es.toString()));
          ser.startElement(ns, local, smap, qname.indexOf(':') < 0);
        }

        public void startPrefixMapping(String arg0, String arg1) throws SAXException {
        }

      });

      reader.parse(new InputSource(in));
      ser.endDocument();

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

}
TOP

Related Classes of com.adobe.dp.css.CascadeEngine$MatcherRule

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.