diff --git a/src/main/java/cz/vutbr/web/css/Selector.java b/src/main/java/cz/vutbr/web/css/Selector.java
index 276b7b27..702547ab 100644
--- a/src/main/java/cz/vutbr/web/css/Selector.java
+++ b/src/main/java/cz/vutbr/web/css/Selector.java
@@ -142,6 +142,12 @@ private PseudoDeclaration(String value, boolean isElement) {
*/
public PseudoDeclaration getPseudoElement();
+ /**
+ * Checks where the specified pseudo declaration is in this selector
+ * @return true
if the selector has the specified pseudo declaration
+ */
+ public boolean hasPseudoDeclaration(final PseudoDeclaration pd);
+
/**
* Modifies specificity according to CSS standard
* @param spec Specificity to be modified
diff --git a/src/main/java/cz/vutbr/web/csskit/SelectorImpl.java b/src/main/java/cz/vutbr/web/csskit/SelectorImpl.java
index 1a59c568..d5d03e5c 100644
--- a/src/main/java/cz/vutbr/web/csskit/SelectorImpl.java
+++ b/src/main/java/cz/vutbr/web/csskit/SelectorImpl.java
@@ -96,6 +96,19 @@ public PseudoDeclaration getPseudoElement() {
return ret;
}
+ public boolean hasPseudoDeclaration(final PseudoDeclaration pd) {
+ for(SelectorPart item : list) {
+ if(item instanceof PseudoPage)
+ {
+ final PseudoDeclaration ret = ((PseudoPage)item).getDeclaration();
+ if (ret == pd) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
public boolean matches(Element e) {
// check other items of simple selector
diff --git a/src/main/java/cz/vutbr/web/domassign/Analyzer.java b/src/main/java/cz/vutbr/web/domassign/Analyzer.java
index 24e3f6bf..c08711f4 100644
--- a/src/main/java/cz/vutbr/web/domassign/Analyzer.java
+++ b/src/main/java/cz/vutbr/web/domassign/Analyzer.java
@@ -20,11 +20,8 @@
import cz.vutbr.web.css.CombinedSelector;
import cz.vutbr.web.css.Declaration;
import cz.vutbr.web.css.MatchCondition;
-import cz.vutbr.web.css.MediaQuery;
import cz.vutbr.web.css.MediaSpec;
import cz.vutbr.web.css.NodeData;
-import cz.vutbr.web.css.Rule;
-import cz.vutbr.web.css.RuleMedia;
import cz.vutbr.web.css.RuleSet;
import cz.vutbr.web.css.Selector;
import cz.vutbr.web.css.Selector.PseudoDeclaration;
@@ -388,139 +385,10 @@ protected boolean matchSelector(CombinedSelector sel, Element e, TreeWalker w) {
protected void classifyAllSheets(MediaSpec mediaspec)
{
rules = new Holder();
- for (StyleSheet sheet : sheets)
- classifyRules(sheet, mediaspec);
- }
-
- /**
- * Divides rules in sheet into different categories to be easily and more
- * quickly parsed afterward
- *
- * @param sheet The style sheet to be classified
- * @param mediaspec The specification of the media for evaluating the media queries.
- */
- protected void classifyRules(StyleSheet sheet, MediaSpec mediaspec) {
-
- // create a new holder if it does not exist
- if (rules == null) {
- rules = new Holder();
- }
-
- for (Rule> rule : sheet) {
- // this rule conforms to all media
- if (rule instanceof RuleSet) {
- RuleSet ruleset = (RuleSet) rule;
- for (CombinedSelector s : ruleset.getSelectors()) {
- insertClassified(rules, classifySelector(s), ruleset);
- }
- }
- // this rule conforms to different media
- else if (rule instanceof RuleMedia) {
- RuleMedia rulemedia = (RuleMedia) rule;
-
- boolean mediaValid = false;
- if(rulemedia.getMediaQueries()==null || rulemedia.getMediaQueries().isEmpty()) {
- //no media queries actually
- mediaValid = mediaspec.matchesEmpty();
- } else {
- //find a matching query
- for (MediaQuery media : rulemedia.getMediaQueries()) {
- if (mediaspec.matches(media)) {
- mediaValid = true;
- break;
- }
- }
- }
-
- if (mediaValid)
- {
- // for all rules in media set
- for (RuleSet ruleset : rulemedia) {
- // for all selectors in there
- for (CombinedSelector s : ruleset.getSelectors()) {
- insertClassified(rules, classifySelector(s), ruleset);
- }
- }
- }
- }
- }
-
- // logging
- if (log.isDebugEnabled()) {
- log.debug("For media \"{}\" we have {} rules", mediaspec, rules.contentCount());
- if(log.isTraceEnabled()) {
- log.trace("Detailed view: \n{}", rules);
- }
- }
-
- }
-
- /**
- * Classify CSS rule according its selector for to be of specified item(s)
- *
- * @param selector
- * CombinedSelector of rules
- * @return List of HolderSelectors to which selectors conforms
- */
- private List classifySelector(CombinedSelector selector) {
-
- List hs = new ArrayList();
-
- try {
- // last simple selector decided about all selector
- Selector last = selector.getLastSelector();
-
- // is element or other (wildcard)
- String element = last.getElementName();
- if (element != null) {
- // wildcard
- if (Selector.ElementName.WILDCARD.equals(element))
- hs.add(new HolderSelector(HolderItem.OTHER, null));
- // element
- else
- hs.add(new HolderSelector(HolderItem.ELEMENT, element
- .toLowerCase()));
- }
-
- // is class name
- String className = last.getClassName();
- if (className != null)
- hs.add(new HolderSelector(HolderItem.CLASS, className
- .toLowerCase()));
-
- // is id
- String id = last.getIDName();
- if (id != null)
- hs.add(new HolderSelector(HolderItem.ID, id.toLowerCase()));
-
- // is in others
- if (hs.size() == 0)
- hs.add(new HolderSelector(HolderItem.OTHER, null));
-
- return hs;
- } catch (UnsupportedOperationException e) {
- log
- .error("CombinedSelector does not include any selector, this should not happen!");
- return Collections.emptyList();
- }
+ AnalyzerUtil.classifyAllSheets(sheets, rules, mediaspec);
}
-
- /**
- * Inserts rules into holder
- *
- * @param holder
- * Wrap to be inserted
- * @param hs
- * Wrap's selector and key
- * @param value
- * Value to be inserted
- */
- private void insertClassified(Holder holder, List hs, RuleSet value) {
- for (HolderSelector h : hs)
- holder.insert(h.item, h.key, new OrderedRule(value, currentOrder++));
- }
-
+
/**
* Decides about holder item
*
@@ -547,7 +415,7 @@ public int type() {
* @author kapy
*
*/
- protected class HolderSelector {
+ protected static class HolderSelector {
public HolderItem item;
public String key;
@@ -562,7 +430,7 @@ public HolderSelector(HolderItem item, String key) {
*
* @author burgetr
*/
- protected final class OrderedRule implements Comparable {
+ public static final class OrderedRule implements Comparable {
private final RuleSet rule;
private final int order;
@@ -582,6 +450,12 @@ public int getOrder() {
public int compareTo(OrderedRule o) {
return getOrder() - o.getOrder();
}
+
+ @Override
+ public String toString() {
+ return "OR" + order + ", " + rule;
+ }
+
}
/**
diff --git a/src/main/java/cz/vutbr/web/domassign/AnalyzerUtil.java b/src/main/java/cz/vutbr/web/domassign/AnalyzerUtil.java
new file mode 100644
index 00000000..856be572
--- /dev/null
+++ b/src/main/java/cz/vutbr/web/domassign/AnalyzerUtil.java
@@ -0,0 +1,487 @@
+package cz.vutbr.web.domassign;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import cz.vutbr.web.css.CSSFactory;
+import cz.vutbr.web.css.CombinedSelector;
+import cz.vutbr.web.css.Declaration;
+import cz.vutbr.web.css.MatchCondition;
+import cz.vutbr.web.css.MediaQuery;
+import cz.vutbr.web.css.MediaSpec;
+import cz.vutbr.web.css.NodeData;
+import cz.vutbr.web.css.Rule;
+import cz.vutbr.web.css.RuleMedia;
+import cz.vutbr.web.css.RuleSet;
+import cz.vutbr.web.css.Selector;
+import cz.vutbr.web.css.Selector.PseudoDeclaration;
+import cz.vutbr.web.css.StyleSheet;
+import cz.vutbr.web.csskit.ElementUtil;
+import cz.vutbr.web.domassign.Analyzer.Holder;
+import cz.vutbr.web.domassign.Analyzer.HolderItem;
+import cz.vutbr.web.domassign.Analyzer.HolderSelector;
+import cz.vutbr.web.domassign.Analyzer.OrderedRule;
+
+/**
+ * A pure (state-less) Analyser.
+ *
+ * Can be used by clients that need more control over what computation is cached.
+ *
+ */
+public final class AnalyzerUtil {
+
+ private static final Logger log = LoggerFactory.getLogger(AnalyzerUtil.class);
+
+ /**
+ * Returns all applicable rules for an element
+ *
+ * @param sheets
+ * @param element
+ * @param mediaspec
+ * @return
+ */
+ public static OrderedRule[] getApplicableRules(final List sheets, final Element element, final MediaSpec mediaspec) {
+ final Holder rules = new Holder();
+ AnalyzerUtil.classifyAllSheets(sheets, rules, mediaspec);
+ return getApplicableRules(element, rules);
+ }
+
+ public static NodeData getElementStyle(Element el, PseudoDeclaration pseudo, MatchCondition matchCond, OrderedRule[] applicableRules)
+ {
+ return makeNodeData(computeDeclarations(el, pseudo, applicableRules, matchCond));
+ }
+
+ private static OrderedRule[] getApplicableRules(final Element e, final Holder holder)
+ {
+ // create set of possible candidates applicable to given element
+ // set is automatically filtered to not contain duplicates
+ final Set candidates = new HashSet();
+
+ // match element classes
+ for (final String cname : ElementUtil.elementClasses(e)) {
+ // holder contains rule with given class
+ final List classRules = holder.get(HolderItem.CLASS, cname.toLowerCase());
+ if (classRules != null)
+ candidates.addAll(classRules);
+ }
+ log.trace("After CLASSes {} total candidates.", candidates.size());
+
+ // match IDs
+ final String id = ElementUtil.elementID(e);
+ if (id != null && id.length() != 0) {
+ final List idRules = holder.get(HolderItem.ID, id.toLowerCase());
+ if (idRules != null)
+ candidates.addAll(idRules);
+ }
+ log.trace("After IDs {} total candidates.", candidates.size());
+
+ // match elements
+ final String name = ElementUtil.elementName(e);
+ if (name != null) {
+ final List nameRules = holder.get(HolderItem.ELEMENT, name.toLowerCase());
+ if (nameRules != null)
+ candidates.addAll(nameRules);
+ }
+ log.trace("After ELEMENTs {} total candidates.", candidates.size());
+
+ // others
+ candidates.addAll(holder.get(HolderItem.OTHER, null));
+
+ final int totalCandidates = candidates.size();
+ log.debug("Totally {} candidates.", totalCandidates);
+
+ // transform to array to speed up traversal
+ // and sort rules in order as they were found in CSS definition
+ final OrderedRule[] clist = (OrderedRule[]) candidates.toArray(new OrderedRule[totalCandidates]);
+ Arrays.sort(clist);
+
+ log.trace("With values: {}", Arrays.toString(clist));
+
+ return clist;
+ }
+
+ static NodeData makeNodeData(final List decls)
+ {
+ final NodeData main = CSSFactory.createNodeData();
+ for (final Declaration d : decls)
+ main.push(d);
+
+ return main;
+ }
+
+ /**
+ * Classifies the rules in all the style sheets.
+ * @param mediaspec The specification of the media for evaluating the media queries.
+ */
+ static void classifyAllSheets(final List sheets, final Holder rules, final MediaSpec mediaspec)
+ {
+ for (final StyleSheet sheet : sheets)
+ classifyRules(sheet, mediaspec, rules);
+ }
+
+ static boolean elementSelectorMatches(final Selector s, final Element e, final MatchCondition matchCond) {
+ return matchCond == null ? s.matches(e) : s.matches(e, matchCond);
+ }
+
+ private static boolean nodeSelectorMatches(final Selector s, final Node n, final MatchCondition matchCond) {
+ if (n.getNodeType() == Node.ELEMENT_NODE) {
+ final Element e = (Element) n;
+ return matchCond == null ? s.matches(e) : s.matches(e, matchCond);
+ } else {
+ return false;
+ }
+ }
+
+ static List computeDeclarations(final Element e, final PseudoDeclaration pseudo, final OrderedRule[] clist, final MatchCondition matchCond) {
+ // resulting list of declaration for this element with no pseudo-selectors (main list)(local cache)
+ final List eldecl = new ArrayList();
+
+ // for all candidates
+ for (final OrderedRule orule : clist) {
+
+ final RuleSet rule = orule.getRule();
+ final StyleSheet sheet = rule.getStyleSheet();
+ final StyleSheet.Origin origin = (sheet == null) ? StyleSheet.Origin.AGENT : sheet.getOrigin();
+
+ // for all selectors inside
+ for (final CombinedSelector s : rule.getSelectors()) {
+
+ if (!AnalyzerUtil.matchSelector(s, e, matchCond)) {
+ log.trace("CombinedSelector \"{}\" NOT matched!", s);
+ continue;
+ }
+
+ log.trace("CombinedSelector \"{}\" matched", s);
+
+ final PseudoDeclaration psel = s.getPseudoElement();
+ final CombinedSelector.Specificity spec = s.computeSpecificity();
+ if (psel == pseudo)
+ {
+ // add to the resulting list
+ for (final Declaration d : rule)
+ eldecl.add(new AssignedDeclaration(d, spec, origin));
+ }
+ }
+ }
+
+ // sort declarations
+ Collections.sort(eldecl); //sort the main list
+ log.debug("Sorted {} declarations.", eldecl.size());
+ log.trace("With values: {}", eldecl);
+
+ return eldecl;
+ }
+
+ public static boolean hasPseudoSelector(final OrderedRule[] rules, final Element e, final MatchCondition matchCond, PseudoDeclaration pd)
+ {
+ for (final OrderedRule rule : rules) {
+ for (final CombinedSelector cs : rule.getRule().getSelectors()) {
+ final Selector lastSelector = cs.get(cs.size() - 1);
+ if (lastSelector.hasPseudoDeclaration(pd)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public static boolean hasPseudoSelectorForAncestor(final OrderedRule[] rules, final Element e, final Element targetAncestor, final MatchCondition matchCond, PseudoDeclaration pd)
+ {
+ for (final OrderedRule rule : rules) {
+ for (final CombinedSelector cs : rule.getRule().getSelectors()) {
+ if (hasPseudoSelectorForAncestor(cs, e, targetAncestor, matchCond, pd)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasPseudoSelectorForAncestor(final CombinedSelector sel, final Element e, final Element targetAncestor, final MatchCondition matchCond, PseudoDeclaration pd)
+ {
+ boolean retval = false;
+ Selector.Combinator combinator = null;
+ Element current = e;
+ // traverse simple selector backwards
+ for (int i = sel.size() - 1; i >= 0; i--) {
+ // last simple selector
+ final Selector s = sel.get(i);
+
+ // decide according to combinator anti-pattern
+ if (combinator == null) {
+ retval = elementSelectorMatches(s, current, matchCond);
+ } else if (combinator == Selector.Combinator.ADJACENT) {
+ Node adjacent = current;
+ do {
+ adjacent = adjacent.getPreviousSibling();
+ } while (adjacent != null && adjacent.getNodeType() != Node.ELEMENT_NODE);
+ retval = false;
+ if (adjacent != null && adjacent.getNodeType() == Node.ELEMENT_NODE)
+ {
+ current = (Element) adjacent;
+ retval = elementSelectorMatches(s, current, matchCond);
+ }
+ } else if (combinator == Selector.Combinator.PRECEDING) {
+ Node preceding = current.getPreviousSibling();
+ retval = false;
+ do
+ {
+ if (preceding != null)
+ {
+ if (nodeSelectorMatches(s, preceding, matchCond))
+ {
+ current = (Element) preceding;
+ retval = true;
+ }
+ else
+ preceding = preceding.getPreviousSibling();
+ }
+ } while (!retval && preceding != null);
+ } else if (combinator == Selector.Combinator.DESCENDANT) {
+ Node ancestor = current.getParentNode();
+ retval = false;
+ do
+ {
+ if (ancestor != null)
+ {
+ if (nodeSelectorMatches(s, ancestor, matchCond))
+ {
+ current = (Element) ancestor;
+ retval = true;
+ }
+ else
+ ancestor = ancestor.getParentNode();
+ }
+ } while (!retval && ancestor != null);
+ } else if (combinator == Selector.Combinator.CHILD) {
+ final Node parent = current.getParentNode();
+ retval = false;
+ if (parent != null && parent.getNodeType() == Node.ELEMENT_NODE)
+ {
+ current = (Element) parent;
+ retval = elementSelectorMatches(s, current, matchCond);
+ }
+ }
+
+ // set combinator for next loop
+ combinator = s.getCombinator();
+
+ // leave loop if not matched
+ if (!retval) {
+ break;
+ } else if (current == targetAncestor) {
+ return s.hasPseudoDeclaration(pd);
+ }
+ }
+ return false;
+ }
+
+ protected static boolean matchSelector(final CombinedSelector sel, final Element e, final MatchCondition matchCond)
+ {
+ boolean retval = false;
+ Selector.Combinator combinator = null;
+ Element current = e;
+ // traverse simple selector backwards
+ for (int i = sel.size() - 1; i >= 0; i--) {
+ // last simple selector
+ final Selector s = sel.get(i);
+ log.trace("Iterating loop with selector {}, combinator {}",
+ s, combinator);
+
+ // decide according to combinator anti-pattern
+ if (combinator == null) {
+ retval = elementSelectorMatches(s, current, matchCond);
+ } else if (combinator == Selector.Combinator.ADJACENT) {
+ Node adjacent = current;
+ do {
+ adjacent = adjacent.getPreviousSibling();
+ } while (adjacent != null && adjacent.getNodeType() != Node.ELEMENT_NODE);
+ retval = false;
+ if (adjacent != null && adjacent.getNodeType() == Node.ELEMENT_NODE)
+ {
+ current = (Element) adjacent;
+ retval = elementSelectorMatches(s, current, matchCond);
+ }
+ } else if (combinator == Selector.Combinator.PRECEDING) {
+ Node preceding = current.getPreviousSibling();
+ retval = false;
+ do
+ {
+ if (preceding != null)
+ {
+ if (nodeSelectorMatches(s, preceding, matchCond))
+ {
+ current = (Element) preceding;
+ retval = true;
+ }
+ else
+ preceding = preceding.getPreviousSibling();
+ }
+ } while (!retval && preceding != null);
+ } else if (combinator == Selector.Combinator.DESCENDANT) {
+ Node ancestor = current.getParentNode();
+ retval = false;
+ do
+ {
+ if (ancestor != null)
+ {
+ if (nodeSelectorMatches(s, ancestor, matchCond))
+ {
+ current = (Element) ancestor;
+ retval = true;
+ }
+ else
+ ancestor = ancestor.getParentNode();
+ }
+ } while (!retval && ancestor != null);
+ } else if (combinator == Selector.Combinator.CHILD) {
+ final Node parent = current.getParentNode();
+ retval = false;
+ if (parent != null && parent.getNodeType() == Node.ELEMENT_NODE)
+ {
+ current = (Element) parent;
+ retval = elementSelectorMatches(s, current, matchCond);
+ }
+ }
+
+ // set combinator for next loop
+ combinator = s.getCombinator();
+
+ // leave loop if not matched
+ if (!retval)
+ break;
+ }
+ return retval;
+ }
+
+ /**
+ * Classify CSS rule according its selector for to be of specified item(s)
+ *
+ * @param selector
+ * CombinedSelector of rules
+ * @return List of HolderSelectors to which selectors conforms
+ */
+ private static List classifySelector(final CombinedSelector selector) {
+
+ final List hs = new ArrayList();
+
+ try {
+ // last simple selector decided about all selector
+ final Selector last = selector.getLastSelector();
+
+ // is element or other (wildcard)
+ final String element = last.getElementName();
+ if (element != null) {
+ // wildcard
+ if (Selector.ElementName.WILDCARD.equals(element))
+ hs.add(new HolderSelector(HolderItem.OTHER, null));
+ // element
+ else
+ hs.add(new HolderSelector(HolderItem.ELEMENT, element
+ .toLowerCase()));
+ }
+
+ // is class name
+ final String className = last.getClassName();
+ if (className != null)
+ hs.add(new HolderSelector(HolderItem.CLASS, className
+ .toLowerCase()));
+
+ // is id
+ final String id = last.getIDName();
+ if (id != null)
+ hs.add(new HolderSelector(HolderItem.ID, id.toLowerCase()));
+
+ // is in others
+ if (hs.size() == 0)
+ hs.add(new HolderSelector(HolderItem.OTHER, null));
+
+ return hs;
+
+ } catch (final UnsupportedOperationException e) {
+ log
+ .error("CombinedSelector does not include any selector, this should not happen!");
+ return Collections.emptyList();
+ }
+ }
+
+ private static class Counter {
+ private int count = 0;
+ public int getAndIncrement() {
+ return count++;
+ }
+ }
+
+ private static void insertClassified(final Holder holder, final List hs, final RuleSet value, final Counter orderCounter) {
+ for (final HolderSelector h : hs)
+ holder.insert(h.item, h.key, new OrderedRule(value, orderCounter.getAndIncrement()));
+ }
+
+ /**
+ * Divides rules in sheet into different categories to be easily and more
+ * quickly parsed afterward
+ *
+ * @param sheet The style sheet to be classified
+ * @param mediaspec The specification of the media for evaluating the media queries.
+ */
+ private static void classifyRules(final StyleSheet sheet, final MediaSpec mediaspec, final Holder rules) {
+ final Counter orderCounter = new Counter();
+
+ for (final Rule> rule : sheet) {
+ // this rule conforms to all media
+ if (rule instanceof RuleSet) {
+ final RuleSet ruleset = (RuleSet) rule;
+ for (final CombinedSelector s : ruleset.getSelectors()) {
+ insertClassified(rules, classifySelector(s), ruleset, orderCounter);
+ }
+ }
+ // this rule conforms to different media
+ else if (rule instanceof RuleMedia) {
+ final RuleMedia rulemedia = (RuleMedia) rule;
+
+ boolean mediaValid = false;
+ if(rulemedia.getMediaQueries()==null || rulemedia.getMediaQueries().isEmpty()) {
+ //no media queries actually
+ mediaValid = mediaspec.matchesEmpty();
+ } else {
+ //find a matching query
+ for (final MediaQuery media : rulemedia.getMediaQueries()) {
+ if (mediaspec.matches(media)) {
+ mediaValid = true;
+ break;
+ }
+ }
+ }
+
+ if (mediaValid)
+ {
+ // for all rules in media set
+ for (final RuleSet ruleset : rulemedia) {
+ // for all selectors in there
+ for (final CombinedSelector s : ruleset.getSelectors()) {
+ insertClassified(rules, classifySelector(s), ruleset, orderCounter);
+ }
+ }
+ }
+ }
+ }
+
+ // logging
+ if (log.isDebugEnabled()) {
+ log.debug("For media \"{}\" we have {} rules", mediaspec, rules.contentCount());
+ if(log.isTraceEnabled()) {
+ log.trace("Detailed view: \n{}", rules);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/cz/vutbr/web/domassign/DirectAnalyzer.java b/src/main/java/cz/vutbr/web/domassign/DirectAnalyzer.java
index e5ab9665..568707bd 100644
--- a/src/main/java/cz/vutbr/web/domassign/DirectAnalyzer.java
+++ b/src/main/java/cz/vutbr/web/domassign/DirectAnalyzer.java
@@ -5,28 +5,16 @@
*/
package cz.vutbr.web.domassign;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import cz.vutbr.web.css.CSSFactory;
-import cz.vutbr.web.css.CombinedSelector;
-import cz.vutbr.web.css.Declaration;
-import cz.vutbr.web.css.MatchCondition;
import cz.vutbr.web.css.MediaSpec;
import cz.vutbr.web.css.NodeData;
-import cz.vutbr.web.css.RuleSet;
-import cz.vutbr.web.css.Selector;
import cz.vutbr.web.css.Selector.PseudoDeclaration;
import cz.vutbr.web.css.StyleSheet;
-import cz.vutbr.web.csskit.ElementUtil;
/**
* A simple ananalyzer that computes a style for the individual DOM nodes with no mapping and caching.
@@ -67,18 +55,10 @@ public DirectAnalyzer(List sheets)
*/
public NodeData getElementStyle(Element el, PseudoDeclaration pseudo, MediaSpec media)
{
- if (rules == null)
- classifyAllSheets(media);
-
- List decls = getDeclarationsForElement(el, pseudo, rules);
-
- NodeData main = CSSFactory.createNodeData();
- for (Declaration d : decls)
- main.push(d);
-
- return main;
+ final OrderedRule[] applicableRules = AnalyzerUtil.getApplicableRules(sheets, el, media);
+ return AnalyzerUtil.getElementStyle(el, pseudo, getMatchCondition(), applicableRules);
}
-
+
/**
* Computes the style of an element with an eventual pseudo element for the given media.
* @param el The DOM element.
@@ -92,181 +72,5 @@ public NodeData getElementStyle(Element el, PseudoDeclaration pseudo, String med
}
//==========================================================================================
-
- protected List getDeclarationsForElement(Element e, PseudoDeclaration pseudo, Holder holder)
- {
- if(log.isDebugEnabled()) {
- log.debug("Traversal of {} {}.", e.getNodeName(), e.getNodeValue());
- }
-
- // create set of possible candidates applicable to given element
- // set is automatically filtered to not contain duplicates
- Set candidates = new HashSet();
-
- // match element classes
- for (String cname : ElementUtil.elementClasses(e)) {
- // holder contains rule with given class
- List rules = holder.get(HolderItem.CLASS, cname.toLowerCase());
- if (rules != null)
- candidates.addAll(rules);
- }
- log.trace("After CLASSes {} total candidates.", candidates.size());
-
- // match IDs
- String id = ElementUtil.elementID(e);
- if (id != null && id.length() != 0) {
- List rules = holder.get(HolderItem.ID, id.toLowerCase());
- if (rules != null)
- candidates.addAll(rules);
- }
- log.trace("After IDs {} total candidates.", candidates.size());
-
- // match elements
- String name = ElementUtil.elementName(e);
- if (name != null) {
- List rules = holder.get(HolderItem.ELEMENT, name.toLowerCase());
- if (rules != null)
- candidates.addAll(rules);
- }
- log.trace("After ELEMENTs {} total candidates.", candidates.size());
-
- // others
- candidates.addAll(holder.get(HolderItem.OTHER, null));
-
- // transform to list to speed up traversal
- // and sort rules in order as they were found in CSS definition
- List clist = new ArrayList(candidates);
- Collections.sort(clist);
-
- log.debug("Totally {} candidates.", candidates.size());
- log.trace("With values: {}", clist);
-
- // resulting list of declaration for this element with no pseudo-selectors (main list)(local cache)
- List eldecl = new ArrayList();
-
- // for all candidates
- for (OrderedRule orule : clist) {
-
- final RuleSet rule = orule.getRule();
- StyleSheet sheet = rule.getStyleSheet();
- StyleSheet.Origin origin = (sheet == null) ? StyleSheet.Origin.AGENT : sheet.getOrigin();
-
- // for all selectors inside
- for (CombinedSelector s : rule.getSelectors()) {
-
- if (!matchSelector(s, e)) {
- log.trace("CombinedSelector \"{}\" NOT matched!", s);
- continue;
- }
-
- log.trace("CombinedSelector \"{}\" matched", s);
-
- PseudoDeclaration psel = s.getPseudoElement();
- CombinedSelector.Specificity spec = s.computeSpecificity();
- if (psel == pseudo)
- {
- // add to the resulting list
- for (Declaration d : rule)
- eldecl.add(new AssignedDeclaration(d, spec, origin));
- }
- }
- }
- // sort declarations
- Collections.sort(eldecl); //sort the main list
- log.debug("Sorted {} declarations.", eldecl.size());
- log.trace("With values: {}", eldecl);
-
- return eldecl;
- }
-
- private boolean nodeSelectorMatches(final Selector s, final Node n) {
- if (n.getNodeType() == Node.ELEMENT_NODE) {
- final Element e = (Element) n;
- final MatchCondition matchCond = this.getMatchCondition();
- return matchCond == null ? s.matches(e) : s.matches(e, matchCond);
- } else {
- return false;
- }
- }
-
- protected boolean matchSelector(CombinedSelector sel, Element e)
- {
- boolean retval = false;
- Selector.Combinator combinator = null;
- Element current = e;
- // traverse simple selector backwards
- for (int i = sel.size() - 1; i >= 0; i--) {
- // last simple selector
- Selector s = sel.get(i);
- log.trace("Iterating loop with selector {}, combinator {}",
- s, combinator);
-
- // decide according to combinator anti-pattern
- if (combinator == null) {
- retval = this.elementSelectorMatches(s, current);
- } else if (combinator == Selector.Combinator.ADJACENT) {
- Node adjacent = current;
- do {
- adjacent = adjacent.getPreviousSibling();
- } while (adjacent != null && adjacent.getNodeType() != Node.ELEMENT_NODE);
- retval = false;
- if (adjacent != null && adjacent.getNodeType() == Node.ELEMENT_NODE)
- {
- current = (Element) adjacent;
- retval = this.elementSelectorMatches(s, current);
- }
- } else if (combinator == Selector.Combinator.PRECEDING) {
- Node preceding = current.getPreviousSibling();
- retval = false;
- do
- {
- if (preceding != null)
- {
- if (this.nodeSelectorMatches(s, preceding))
- {
- current = (Element) preceding;
- retval = true;
- }
- else
- preceding = preceding.getPreviousSibling();
- }
- } while (!retval && preceding != null);
- } else if (combinator == Selector.Combinator.DESCENDANT) {
- Node ancestor = current.getParentNode();
- retval = false;
- do
- {
- if (ancestor != null)
- {
- if (this.nodeSelectorMatches(s, ancestor))
- {
- current = (Element) ancestor;
- retval = true;
- }
- else
- ancestor = ancestor.getParentNode();
- }
- } while (!retval && ancestor != null);
- } else if (combinator == Selector.Combinator.CHILD) {
- Node parent = current.getParentNode();
- retval = false;
- if (parent != null && parent.getNodeType() == Node.ELEMENT_NODE)
- {
- current = (Element) parent;
- retval = this.elementSelectorMatches(s, current);
- }
- }
-
- // set combinator for next loop
- combinator = s.getCombinator();
-
- // leave loop if not matched
- if (!retval)
- break;
- }
- return retval;
- }
-
-
}