/* GNU GENERAL LICENSE Copyright (C) 2006 The Lobo Project. Copyright (C) 2014 - 2017 Lobo Evolution This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either verion 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General License for more details. You should have received a copy of the GNU General Public along with this program. If not, see <http://www.gnu.org/licenses/>. Contact info: lobochief@users.sourceforge.net; ivan.difrancesco@yahoo.it */ package org.lobobrowser.html.info; import java.io.Serializable; import java.util.ArrayList; import java.util.Set; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.lobobrowser.html.domimpl.HTMLElementImpl; import org.lobobrowser.html.style.selectors.SelectorMatcher; import org.w3c.dom.css.CSSStyleRule; /** * The Class StyleRuleInfo. */ public class StyleRuleInfo implements Serializable { /** The Constant serialVersionUID. */ private static final long serialVersionUID = 9165715430607111555L; /** The style rule. */ private CSSStyleRule styleRule; /** The ancestor selectors. */ private final ArrayList<SelectorMatcher> ancestorSelectors; /** * Instantiates a new style rule info. * * @param SelectorMatchers * A collection of SelectorMatcher's. * @param rule * A CSS rule. */ public StyleRuleInfo(ArrayList<SelectorMatcher> SelectorMatchers, CSSStyleRule rule) { super(); ancestorSelectors = SelectorMatchers; setStyleRule(rule); } /** * Affected by pseudo name in ancestor. * * @param element * the element * @param ancestor * the ancestor * @param pseudoName * the pseudo name * @return true, if successful */ public final boolean affectedByPseudoNameInAncestor(HTMLElementImpl element, HTMLElementImpl ancestor, String pseudoName) { ArrayList<SelectorMatcher> as = this.ancestorSelectors; HTMLElementImpl currentElement = element; int size = as.size(); boolean first = true; for (int i = size; --i >= 0;) { SelectorMatcher selectorMatcher = as.get(i); if (first) { if (ancestor == element) { return selectorMatcher.hasPseudoName(pseudoName); } first = false; continue; } String selectorText = selectorMatcher.getSimpleSelectorText(); int dotIdx = selectorText.indexOf('.'); HTMLElementImpl newElement; if (dotIdx != -1) { String elemtl = selectorText.substring(0, dotIdx); String classtl = selectorText.substring(dotIdx + 1); newElement = currentElement.getAncestorWithClass(elemtl, classtl); } else { int poundIdx = selectorText.indexOf('#'); if (poundIdx != -1) { String elemtl = selectorText.substring(0, poundIdx); String idtl = selectorText.substring(poundIdx + 1); newElement = currentElement.getAncestorWithId(elemtl, idtl); } else { String elemtl = selectorText; newElement = currentElement.getAncestor(elemtl); } } if (newElement == null) { return false; } currentElement = newElement; if (currentElement == ancestor) { return selectorMatcher.hasPseudoName(pseudoName); } } return false; } /** * Checks if is selector match. * * @param element * The element to test for a match. * @param pseudoNames * A set of pseudo-names in lowercase. * @return true, if is selector match */ public final boolean isSelectorMatch(HTMLElementImpl element, Set pseudoNames) { ArrayList<SelectorMatcher> as = this.ancestorSelectors; HTMLElementImpl currentElement = element; int size = as.size(); boolean first = true; for (int i = size; --i >= 0;) { SelectorMatcher selectorMatcher = as.get(i); if (first) { if (!selectorMatcher.matches(pseudoNames)) { return false; } first = false; continue; } String selectorText = selectorMatcher.getSimpleSelectorText(); int dotIdx = selectorText.indexOf('.'); int selectorType = selectorMatcher.getSelectorType(); HTMLElementImpl priorElement; if (dotIdx != -1) { String elemtl = selectorText.substring(0, dotIdx); String classtl = selectorText.substring(dotIdx + 1); if (selectorType == SelectorMatcher.ANCESTOR) { priorElement = currentElement.getAncestorWithClass(elemtl, classtl); } else if (selectorType == SelectorMatcher.PARENT) { priorElement = currentElement.getParentWithClass(elemtl, classtl); } else if (selectorType == SelectorMatcher.PRECEEDING_SIBLING) { priorElement = currentElement.getPreceedingSiblingWithClass(elemtl, classtl); } else { throw new IllegalStateException("selectorType=" + selectorType); } } else { int poundIdx = selectorText.indexOf('#'); if (poundIdx != -1) { String elemtl = selectorText.substring(0, poundIdx); String idtl = selectorText.substring(poundIdx + 1); if (selectorType == SelectorMatcher.ANCESTOR) { priorElement = currentElement.getAncestorWithId(elemtl, idtl); } else if (selectorType == SelectorMatcher.PARENT) { priorElement = currentElement.getParentWithId(elemtl, idtl); } else if (selectorType == SelectorMatcher.PRECEEDING_SIBLING) { priorElement = currentElement.getPreceedingSiblingWithId(elemtl, idtl); } else { throw new IllegalStateException("selectorType=" + selectorType); } } else { String elemtl = selectorText; if (selectorType == SelectorMatcher.ANCESTOR) { priorElement = currentElement.getAncestor(elemtl); } else if (selectorType == SelectorMatcher.PARENT) { priorElement = currentElement.getParent(elemtl); } else if (selectorType == SelectorMatcher.PRECEEDING_SIBLING) { priorElement = currentElement.getPreceedingSibling(elemtl); } else { throw new IllegalStateException("selectorType=" + selectorType); } } } if (priorElement == null) { return false; } if (!selectorMatcher.matches(priorElement)) { return false; } currentElement = priorElement; } return true; } /** * Gets the style rule. * * @return the style rule */ public CSSStyleRule getStyleRule() { return styleRule; } /** * Sets the style rule. * * @param styleRule * the new style rule */ public void setStyleRule(CSSStyleRule styleRule) { this.styleRule = styleRule; } /** * get Ancestor Selectors. */ public ArrayList<SelectorMatcher> getAncestorSelectors() { return ancestorSelectors; } }