![JAR search and dependency download from the Maven repository](/logo.png)
cz.vutbr.web.domassign.Analyzer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jstyleparser Show documentation
Show all versions of jstyleparser Show documentation
jStyleParser is a CSS parser written in Java. It has its own application interface that is designed to allow an efficient CSS processing in Java and mapping the values to the Java data types. It parses CSS 2.1 style sheets into structures that can be efficiently assigned to DOM elements. It is intended be the primary CSS parser for the CSSBox library. While handling errors, it is user agent conforming according to the CSS specification.
The newest version!
package cz.vutbr.web.domassign;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.traversal.NodeFilter;
import org.w3c.dom.traversal.TreeWalker;
import cz.vutbr.web.css.CSSFactory;
import cz.vutbr.web.css.CombinedSelector;
import cz.vutbr.web.css.Declaration;
import cz.vutbr.web.css.ElementMatcher;
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.PseudoElementType;
import cz.vutbr.web.css.StyleSheet;
/**
* Analyzer allows to apply the given style to any document.
* During the initialization, it divides rules of stylesheet into maps accoring to
* medias and their type. Afterwards, it is able to return CSS declaration for any
* DOM tree and media. It allows to use or not to use inheritance.
*
* @author Karel Piwko 2008
* @author Radek Burget 2008-2014
*
*/
public class Analyzer {
private static final Logger log = LoggerFactory.getLogger(Analyzer.class);
/** The style sheets to be processed. */
protected List sheets;
/** Rule order counter */
protected int currentOrder;
/**
* Holds maps of declared rules classified into groups of
* HolderItem (ID, CLASS, ELEMENT, OTHER).
*/
protected Holder rules;
private MatchCondition matchCond;
private ElementMatcher matcher;
/**
* Creates the analyzer for a single style sheet.
* @param sheet The stylesheet that will be used as the source of rules.
*/
public Analyzer(StyleSheet sheet) {
sheets = new ArrayList(1);
sheets.add(sheet);
matchCond = CSSFactory.getDefaultMatchCondition();
matcher = CSSFactory.getElementMatcher();
}
/**
* Creates the analyzer for multiple style sheets.
* @param sheets A list of stylesheets that will be used as the source of rules.
*/
public Analyzer(List sheets) {
this.sheets = sheets;
matchCond = CSSFactory.getDefaultMatchCondition();
matcher = CSSFactory.getElementMatcher();
}
/**
* Registers a new match condition to be used for matching the elements and
* selector parts.
*
* @param matchCond
* the new match condition
*/
public final void registerMatchCondition(MatchCondition matchCond) {
this.matchCond = matchCond;
}
/**
* Obtains the match condition to be used for matching the elements and
* selector parts.
*
* @return the match condition used by the Analyzer.
*/
public final MatchCondition getMatchCondition() {
return this.matchCond;
}
/**
* Registers a new element matcher to be used for matching the elements and
* selector parts.
*
* @param matcher
* the new element matcher
*/
public final void registerElementMatcher(ElementMatcher matcher) {
this.matcher = matcher;
}
/**
* Obtains the matcher to be used for matching the elements.
*
* @return the matcher used by the Analyzer.
*/
public final ElementMatcher getElementMatcher() {
return matcher;
}
/**
* Evaluates CSS properties of DOM tree
*
* @param doc
* Document tree
* @param media
* Media
* @param inherit
* Use inheritance
* @return Map where each element contains its CSS properties
*/
public StyleMap evaluateDOM(Document doc, MediaSpec media, final boolean inherit) {
DeclarationMap declarations = assingDeclarationsToDOM(doc, media, inherit);
StyleMap nodes = new StyleMap(declarations.size());
Traversal traversal = new Traversal(
doc, (Object) declarations, NodeFilter.SHOW_ELEMENT) {
@Override
protected void processNode(StyleMap result, Node current, Object source) {
NodeData main = CSSFactory.createNodeData();
// for all declarations available in the main list (pseudo=null)
List declarations = ((DeclarationMap) source).get((Element) current, null);
if (declarations != null)
{
for (Declaration d : declarations) {
main.push(d);
}
if (inherit)
main.inheritFrom(result.get((Element) walker.parentNode(), null));
}
// concretize values and store them
result.put((Element) current, null, main.concretize());
//repeat for the pseudo classes (if any)
for (PseudoElementType pseudo : ((DeclarationMap) source).pseudoSet((Element) current))
{
NodeData pdata = CSSFactory.createNodeData();
declarations = ((DeclarationMap) source).get((Element) current, pseudo);
if (declarations != null)
{
for (Declaration d : declarations) {
pdata.push(d);
}
pdata.inheritFrom(main); //always inherit from the main element style
}
// concretize values and store them
result.put((Element) current, pseudo, pdata.concretize());
}
}
};
traversal.levelTraversal(nodes);
return nodes;
}
public StyleMap evaluateDOM(Document doc, String media, final boolean inherit) {
return evaluateDOM(doc, new MediaSpec(media), inherit);
}
/**
* Creates map of declarations assigned to each element of a DOM tree
*
* @param doc
* DOM document
* @param media
* Media type to be used for declarations
* @param inherit
* Inheritance (cascade propagation of values)
* @return Map of elements as keys and their declarations
*/
protected DeclarationMap assingDeclarationsToDOM(Document doc, MediaSpec media, final boolean inherit) {
// classify the rules
classifyAllSheets(media);
// resulting map
DeclarationMap declarations = new DeclarationMap();
// if the holder is empty skip evaluation
if(rules!=null && !rules.isEmpty()) {
Traversal traversal = new Traversal(
doc, (Object) rules, NodeFilter.SHOW_ELEMENT) {
protected void processNode(DeclarationMap result,
Node current, Object source) {
assignDeclarationsToElement(result, walker, (Element) current,
(Holder) source);
}
};
// list traversal will be enough
if (!inherit)
traversal.listTraversal(declarations);
// we will do level traversal to economize blind returning
// in tree
else
traversal.levelTraversal(declarations);
}
return declarations;
}
/**
* Assigns declarations to one element.
*
* @param declarations
* Declarations of all processed elements
* @param walker
* Tree walker
* @param e
* DOM Element
* @param holder
* Wrap
*/
protected void assignDeclarationsToElement(
DeclarationMap declarations, TreeWalker walker,
Element e, 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 : matcher.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 = matcher.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 = matcher.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();
// existing pseudo selectors found
Set pseudos = new HashSet<>();
// for all candidates
for (OrderedRule orule : clist) {
final RuleSet rule = orule.getRule();
StyleSheet sheet = rule.getStyleSheet();
if (sheet == null)
log.warn("No source style sheet set for rule: {}", rule.toString());
StyleSheet.Origin origin = (sheet == null) ? StyleSheet.Origin.AGENT : sheet.getOrigin();
// for all selectors inside
for (CombinedSelector s : rule.getSelectors()) {
// this method does automatic rewind of walker
if (!matchSelector(s, e, walker)) {
log.trace("CombinedSelector \"{}\" NOT matched!", s);
continue;
}
log.trace("CombinedSelector \"{}\" matched", s);
PseudoElementType pseudo = s.getPseudoElementType();
CombinedSelector.Specificity spec = s.computeSpecificity();
if (pseudo == null)
{
// add to main list
for (Declaration d : rule)
eldecl.add(new AssignedDeclaration(d, spec, origin));
}
else
{
// remember the pseudo element
pseudos.add(pseudo);
// add to pseudo lists
for (Declaration d : rule)
declarations.addDeclaration(e, pseudo, 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);
for (PseudoElementType p : pseudos)
declarations.sortDeclarations(e, p); //sort pseudos
// set the main list
declarations.put(e, null, eldecl);
}
protected boolean elementSelectorMatches(final Selector s, final Element e) {
return s.matches(e, matcher, matchCond);
}
protected boolean matchSelector(CombinedSelector sel, Element e, TreeWalker w) {
// store current walker position
Node current = w.getCurrentNode();
boolean retval = false;
Selector.Combinator combinator = null;
// 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, e);
} else if (combinator == Selector.Combinator.ADJACENT) {
Element adjacent = (Element) w.previousSibling();
retval = false;
if (adjacent != null)
retval = this.elementSelectorMatches(s, adjacent);
} else if (combinator == Selector.Combinator.PRECEDING) {
Element preceding;
retval = false;
while (!retval && (preceding = (Element) w.previousSibling()) != null) {
retval = this.elementSelectorMatches(s, preceding);
}
} else if (combinator == Selector.Combinator.DESCENDANT) {
Element ancestor;
retval = false;
while (!retval && (ancestor = (Element) w.parentNode()) != null) {
retval = this.elementSelectorMatches(s, ancestor);
}
} else if (combinator == Selector.Combinator.CHILD) {
Element parent = (Element) w.parentNode();
retval = false;
if (parent != null)
retval = this.elementSelectorMatches(s, parent);
}
// set combinator for next loop
combinator = s.getCombinator();
// leave loop if not matched
if (!retval)
break;
}
// restore walker position
w.setCurrentNode(current);
return retval;
}
/**
* Classifies the rules in all the style sheets.
* @param mediaspec The specification of the media for evaluating the media queries.
*/
protected void classifyAllSheets(MediaSpec mediaspec)
{
rules = new Holder();
AnalyzerUtil.classifyAllSheets(sheets, rules, mediaspec);
}
/**
* Decides about holder item
*
* @author kapy
*/
protected enum HolderItem {
ELEMENT(0), ID(1), CLASS(2), OTHER(3);
private int type;
private HolderItem(int type) {
this.type = type;
}
public int type() {
return type;
}
}
/**
* Holds holder item type and key value, that is two elements that are about
* to be used for storing in holder
*
* @author kapy
*
*/
protected static class HolderSelector {
public HolderItem item;
public String key;
public HolderSelector(HolderItem item, String key) {
this.item = item;
this.key = key;
}
}
/**
* Represents a ruleset and its order in the corresponding style sheet.
*
* @author burgetr
*/
public static final class OrderedRule implements Comparable {
private final RuleSet rule;
private final int order;
public OrderedRule(RuleSet rule, int order) {
this.rule = rule;
this.order = order;
}
public RuleSet getRule() {
return rule;
}
public int getOrder() {
return order;
}
public int compareTo(OrderedRule o) {
return getOrder() - o.getOrder();
}
@Override
public String toString() {
return "OR" + order + ", " + rule;
}
}
/**
* Holds list of maps of list. This is used to classify rulesets into
* structure which is easily accessible by analyzator
*
* @author kapy
*
*/
public static class Holder {
/** HolderItem.* except OTHER are stored there */
private List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy