From 66e71d3b491c6b5579c17c3ffd54ded1a45b4124 Mon Sep 17 00:00:00 2001 From: Lars Vogel Date: Tue, 2 Jun 2026 10:30:31 +0200 Subject: [PATCH] Replace SAC selector layer with an internal Selector AST and matcher Replaces the vendored Batik/SAC selector tree with a small engine-internal selector model and a static matcher, the SAC parser layer aside. Adds a sealed Selector AST (records for type, class, id, attribute, pseudo, compound, and combinator forms) plus a static SelectorMatcher that walks an Element through it. A SAC->internal translator at the parser-output boundary in CSSDocumentHandlerImpl.startSelector feeds it, and CSSEngine.matches / parseSelectors now work on the internal AST: CSSEngineImpl.matches delegates to SelectorMatcher and applyConditionalPseudoStyle walks the internal AST instead of the SAC tree. The parser is configured with Batik's stock DefaultSelectorFactory and DefaultConditionFactory, so 23 vendored impl/sac/* selector and condition wrappers fall away, along with the dead ExtendedDocumentCSS.queryConditionSelector / querySelector methods and the SAC_*_CONDITION constants behind them. Specificity follows CSS 2.1 and the static-pseudo-instance carve-out from CSSPseudoClassConditionImpl is preserved, so cascade behaviour does not shift. Selectors.SelectorList exposes getLength()/item(int) and every record overrides toString() to return text() for readable selector text in error messages and rule.getSelectorText(). CSSEngineTest and SelectorTest are rewritten on the internal AST, and sixteen new matcher unit tests cover universal, type case-sensitivity, class, id, compound, descendant, child, attribute presence and word-match, pseudo-class, selector lists, and specificity arithmetic. The new package is exported x-friends to org.eclipse.e4.ui.tests.css.core for the tests. Three impl/sac classes (CSSDocumentHandlerImpl, DocumentHandlerFactoryImpl, SACParserFactoryImpl) remain as parser plumbing. Net ~-1,450 LOC; bundles internal (x-friends only), no API surface change. Contributes to #3980 --- .../META-INF/MANIFEST.MF | 1 + .../e4/ui/css/core/dom/ExtendedCSSRule.java | 9 +- .../ui/css/core/dom/ExtendedDocumentCSS.java | 16 +- .../e4/ui/css/core/engine/CSSEngine.java | 13 +- .../css/core/impl/dom/CSSStyleRuleImpl.java | 23 +- .../ui/css/core/impl/dom/DocumentCSSImpl.java | 103 +---- .../e4/ui/css/core/impl/dom/ViewCSSImpl.java | 65 ++-- .../css/core/impl/engine/CSSEngineImpl.java | 176 +++++---- .../impl/engine/selector/SacTranslator.java | 160 ++++++++ .../impl/engine/selector/SelectorMatcher.java | 288 ++++++++++++++ .../core/impl/engine/selector/Selectors.java | 357 ++++++++++++++++++ .../impl/sac/AbstractAttributeCondition.java | 86 ----- .../impl/sac/AbstractCombinatorCondition.java | 93 ----- .../impl/sac/AbstractDescendantSelector.java | 93 ----- .../impl/sac/AbstractElementSelector.java | 87 ----- .../impl/sac/AbstractSiblingSelector.java | 108 ------ .../core/impl/sac/CSSAndConditionImpl.java | 73 ---- .../impl/sac/CSSAttributeConditionImpl.java | 153 -------- .../CSSBeginHyphenAttributeConditionImpl.java | 64 ---- .../core/impl/sac/CSSChildSelectorImpl.java | 102 ----- .../core/impl/sac/CSSClassConditionImpl.java | 65 ---- .../impl/sac/CSSConditionFactoryImpl.java | 213 ----------- .../impl/sac/CSSConditionalSelectorImpl.java | 135 ------- .../impl/sac/CSSDescendantSelectorImpl.java | 109 ------ .../sac/CSSDirectAdjacentSelectorImpl.java | 106 ------ .../core/impl/sac/CSSDocumentHandlerImpl.java | 6 +- .../core/impl/sac/CSSElementSelectorImpl.java | 98 ----- .../css/core/impl/sac/CSSIdConditionImpl.java | 129 ------- .../core/impl/sac/CSSLangConditionImpl.java | 117 ------ .../sac/CSSOneOfAttributeConditionImpl.java | 75 ---- .../impl/sac/CSSPseudoClassConditionImpl.java | 145 ------- .../sac/CSSPseudoElementSelectorImpl.java | 69 ---- .../core/impl/sac/CSSSelectorFactoryImpl.java | 182 --------- .../css/core/impl/sac/ExtendedCondition.java | 47 --- .../css/core/impl/sac/ExtendedSelector.java | 51 --- .../ui/css/swt/engine/CSSSWTEngineImpl.java | 25 +- .../engine/selector/SelectorMatcherTest.java | 223 +++++++++++ .../e4/ui/tests/css/core/CSSEngineTest.java | 14 +- .../tests/css/core/parser/SelectorTest.java | 24 +- 39 files changed, 1225 insertions(+), 2678 deletions(-) create mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/selector/SacTranslator.java create mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/selector/SelectorMatcher.java create mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/selector/Selectors.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractAttributeCondition.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractCombinatorCondition.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractDescendantSelector.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractElementSelector.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractSiblingSelector.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSAndConditionImpl.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSAttributeConditionImpl.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSBeginHyphenAttributeConditionImpl.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSChildSelectorImpl.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSClassConditionImpl.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSConditionFactoryImpl.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSConditionalSelectorImpl.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSDescendantSelectorImpl.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSDirectAdjacentSelectorImpl.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSElementSelectorImpl.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSIdConditionImpl.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSLangConditionImpl.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSOneOfAttributeConditionImpl.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSPseudoClassConditionImpl.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSPseudoElementSelectorImpl.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSSelectorFactoryImpl.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/ExtendedCondition.java delete mode 100644 bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/ExtendedSelector.java create mode 100644 tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/selector/SelectorMatcherTest.java diff --git a/bundles/org.eclipse.e4.ui.css.core/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.ui.css.core/META-INF/MANIFEST.MF index 76e17ec59af..27dfb2acb81 100644 --- a/bundles/org.eclipse.e4.ui.css.core/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.e4.ui.css.core/META-INF/MANIFEST.MF @@ -31,6 +31,7 @@ Export-Package: org.eclipse.e4.ui.css.core;x-internal:=true, org.eclipse.e4.ui.css.core.impl.dom.parsers;x-internal:=true, org.eclipse.e4.ui.css.core.impl.dom.properties;x-friends:="org.eclipse.e4.ui.css.swt", org.eclipse.e4.ui.css.core.impl.engine;x-friends:="org.eclipse.e4.ui.css.swt,org.eclipse.e4.ui.workbench.swt", + org.eclipse.e4.ui.css.core.impl.engine.selector;x-friends:="org.eclipse.e4.ui.tests.css.core", org.eclipse.e4.ui.css.core.impl.sac;x-internal:=true, org.eclipse.e4.ui.css.core.resources;x-friends:="org.eclipse.e4.ui.css.swt,org.eclipse.e4.ui.workbench.renderers.swt", org.eclipse.e4.ui.css.core.sac;x-internal:=true, diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/dom/ExtendedCSSRule.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/dom/ExtendedCSSRule.java index ae6bd4c11f5..1d50bb853ed 100644 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/dom/ExtendedCSSRule.java +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/dom/ExtendedCSSRule.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2013 Angelo Zerr and others. + * Copyright (c) 2008, 2026 Angelo Zerr and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -13,8 +13,7 @@ *******************************************************************************/ package org.eclipse.e4.ui.css.core.dom; -import org.w3c.css.sac.Selector; -import org.w3c.css.sac.SelectorList; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors; import org.w3c.dom.css.CSSRule; /** @@ -28,7 +27,7 @@ public interface ExtendedCSSRule extends CSSRule { public CSSPropertyList getCSSPropertyList(); /** - * Return the list of {@link Selector} of this {@link CSSRule}. + * Return the list of selectors of this {@link CSSRule}. */ - public SelectorList getSelectorList(); + public Selectors.SelectorList getSelectorList(); } diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/dom/ExtendedDocumentCSS.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/dom/ExtendedDocumentCSS.java index f23d30d74cf..d3d7bc717d1 100644 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/dom/ExtendedDocumentCSS.java +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/dom/ExtendedDocumentCSS.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2018 Angelo Zerr and others. + * Copyright (c) 2008, 2026 Angelo Zerr and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -15,9 +15,6 @@ package org.eclipse.e4.ui.css.core.dom; import java.util.EventListener; -import java.util.List; -import org.w3c.css.sac.Condition; -import org.w3c.css.sac.Selector; import org.w3c.dom.css.DocumentCSS; import org.w3c.dom.stylesheets.StyleSheet; @@ -26,21 +23,10 @@ */ public interface ExtendedDocumentCSS extends DocumentCSS { - public static final Integer SAC_ID_CONDITION = Integer.valueOf(Condition.SAC_ID_CONDITION); - public static final Integer SAC_CLASS_CONDITION = Integer.valueOf(Condition.SAC_CLASS_CONDITION); - public static final Integer SAC_PSEUDO_CLASS_CONDITION = Integer.valueOf(Condition.SAC_PSEUDO_CLASS_CONDITION); - public static final Integer OTHER_SAC_CONDITIONAL_SELECTOR = Integer.valueOf(Selector.SAC_CONDITIONAL_SELECTOR); - - public static final Integer OTHER_SAC_SELECTOR = Integer.valueOf(999); - public void addStyleSheet(StyleSheet styleSheet); public void removeAllStyleSheets(); - public List queryConditionSelector(int conditionType); - - public List querySelector(int selectorType, int conditionType); - /** * @since 0.12.200 */ diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/engine/CSSEngine.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/engine/CSSEngine.java index a6624b84852..f036cf16154 100644 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/engine/CSSEngine.java +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/engine/CSSEngine.java @@ -20,11 +20,10 @@ import org.eclipse.e4.ui.css.core.dom.IElementProvider; import org.eclipse.e4.ui.css.core.dom.properties.ICSSPropertyHandler; import org.eclipse.e4.ui.css.core.dom.properties.converters.ICSSValueConverter; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors; import org.eclipse.e4.ui.css.core.resources.IResourcesRegistry; import org.eclipse.e4.ui.css.core.util.resources.IResourcesLocatorManager; import org.w3c.css.sac.InputSource; -import org.w3c.css.sac.Selector; -import org.w3c.css.sac.SelectorList; import org.w3c.dom.Element; import org.w3c.dom.css.CSSStyleDeclaration; import org.w3c.dom.css.CSSStyleSheet; @@ -105,27 +104,27 @@ public interface CSSEngine { /** * Parse Selectors from String value. */ - SelectorList parseSelectors(String text); + Selectors.SelectorList parseSelectors(String text); /** * Parse Selectors from InputSource value. */ - SelectorList parseSelectors(InputSource source) throws IOException; + Selectors.SelectorList parseSelectors(InputSource source) throws IOException; /** * Parse Selectors from InputStream. */ - SelectorList parseSelectors(InputStream stream) throws IOException; + Selectors.SelectorList parseSelectors(InputStream stream) throws IOException; /** * Parse Selectors from String value. */ - SelectorList parseSelectors(Reader reader) throws IOException; + Selectors.SelectorList parseSelectors(Reader reader) throws IOException; /** * Check if the selector matches the object node. */ - boolean matches(Selector selector, Object node, String pseudo); + boolean matches(Selectors.Selector selector, Object node, String pseudo); /*--------------- Apply styles -----------------*/ diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/CSSStyleRuleImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/CSSStyleRuleImpl.java index 01e5822bd4a..23200bae6c2 100644 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/CSSStyleRuleImpl.java +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/CSSStyleRuleImpl.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2015 Angelo Zerr and others. + * Copyright (c) 2008, 2026 Angelo Zerr and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -17,8 +17,7 @@ import org.eclipse.e4.ui.css.core.dom.CSSPropertyList; import org.eclipse.e4.ui.css.core.dom.ExtendedCSSRule; -import org.w3c.css.sac.Selector; -import org.w3c.css.sac.SelectorList; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors; import org.w3c.dom.DOMException; import org.w3c.dom.css.CSSRule; import org.w3c.dom.css.CSSStyleDeclaration; @@ -27,10 +26,10 @@ public class CSSStyleRuleImpl extends CSSRuleImpl implements CSSStyleRule, ExtendedCSSRule { - private final SelectorList selectors; + private final Selectors.SelectorList selectors; private CSSStyleDeclaration styleDeclaration; - public CSSStyleRuleImpl(CSSStyleSheet parentStyleSheet, CSSRule parentRule, SelectorList selectors) { + public CSSStyleRuleImpl(CSSStyleSheet parentStyleSheet, CSSRule parentRule, Selectors.SelectorList selectors) { super(parentStyleSheet, parentRule); this.selectors = selectors; } @@ -55,17 +54,7 @@ public String getCssText() { @Override public String getSelectorText() { - StringBuilder sb = new StringBuilder(); - for (int selID = 0; selID < getSelectorList().getLength(); selID++) { - Selector item = getSelectorList().item(selID); - sb.append(item.toString()); - sb.append(", "); - } - if (getSelectorList().getLength() > 0) { - sb.delete(sb.length() - 2, sb.length()); - } - - return sb.toString(); + return selectors.text(); } @Override @@ -83,7 +72,7 @@ public void setSelectorText(String selectorText) throws DOMException { // Additional methods @Override - public SelectorList getSelectorList() { + public Selectors.SelectorList getSelectorList() { return selectors; } diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/DocumentCSSImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/DocumentCSSImpl.java index f9fba83d961..b87b42b143c 100644 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/DocumentCSSImpl.java +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/DocumentCSSImpl.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2018 Angelo Zerr and others. + * Copyright (c) 2008, 2026 Angelo Zerr and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -18,19 +18,10 @@ package org.eclipse.e4.ui.css.core.impl.dom; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.eclipse.e4.ui.css.core.dom.ExtendedCSSRule; import org.eclipse.e4.ui.css.core.dom.ExtendedDocumentCSS; -import org.w3c.css.sac.ConditionalSelector; -import org.w3c.css.sac.Selector; -import org.w3c.css.sac.SelectorList; import org.w3c.dom.Element; -import org.w3c.dom.css.CSSRule; -import org.w3c.dom.css.CSSRuleList; import org.w3c.dom.css.CSSStyleDeclaration; -import org.w3c.dom.css.CSSStyleSheet; import org.w3c.dom.css.DocumentCSS; import org.w3c.dom.stylesheets.StyleSheet; import org.w3c.dom.stylesheets.StyleSheetList; @@ -42,11 +33,6 @@ public class DocumentCSSImpl implements ExtendedDocumentCSS { private final StyleSheetListImpl styleSheetList = new StyleSheetListImpl(); - /** - * key=selector type, value = CSSStyleDeclaration - */ - private Map> styleDeclarationMap; - private final List styleSheetChangeListeners = new ArrayList<>(1); @Override @@ -72,93 +58,6 @@ public void removeAllStyleSheets() { styleSheetChangeListeners.forEach(l -> l.styleSheetRemoved(styleSheet)); } styleSheetList.removeAllStyleSheets(); - this.styleDeclarationMap = null; - } - - @Override - public List queryConditionSelector(int conditionType) { - return querySelector(Selector.SAC_CONDITIONAL_SELECTOR, conditionType); - } - - @Override - public List querySelector(int selectorType, int conditionType) { - List list = getCSSStyleDeclarationList(selectorType, conditionType); - if (list != null) { - return list; - } - int l = styleSheetList.getLength(); - for (int i = 0; i < l; i++) { - CSSStyleSheet styleSheet = (CSSStyleSheet) styleSheetList.item(i); - CSSRuleList ruleList = styleSheet.getCssRules(); - list = querySelector(ruleList, selectorType, conditionType); - setCSSStyleDeclarationList(list, selectorType, conditionType); - } - return list; - } - - protected List querySelector(CSSRuleList ruleList, int selectorType, int selectorConditionType) { - List list = new ArrayList<>(); - if (selectorType == Selector.SAC_CONDITIONAL_SELECTOR) { - int length = ruleList.getLength(); - for (int i = 0; i < length; i++) { - CSSRule rule = ruleList.item(i); - if (rule.getType() == CSSRule.STYLE_RULE && rule instanceof ExtendedCSSRule r) { - SelectorList selectorList = r.getSelectorList(); - // Loop for SelectorList - int l = selectorList.getLength(); - for (int j = 0; j < l; j++) { - Selector selector = selectorList.item(j); - if (selector.getSelectorType() == selectorType) { - // It's conditional selector - ConditionalSelector conditionalSelector = (ConditionalSelector) selector; - short conditionType = conditionalSelector.getCondition().getConditionType(); - if (selectorConditionType == conditionType) { - // current selector match the current CSS - // Rule - // CSSStyleRule styleRule = (CSSStyleRule) - // rule; - list.add(selector); - } - } - } - } - } - } - return list; - } - - protected List getCSSStyleDeclarationList(int selectorType, int conditionType) { - Integer key = getKey(selectorType, conditionType); - return getStyleDeclarationMap().get(key); - } - - protected void setCSSStyleDeclarationList(List list, int selectorType, int conditionType) { - Integer key = getKey(selectorType, conditionType); - getStyleDeclarationMap().put(key, list); - } - - protected Integer getKey(int selectorType, int conditionType) { - if (selectorType == Selector.SAC_CONDITIONAL_SELECTOR) { - if (conditionType == SAC_CLASS_CONDITION.intValue()) { - return SAC_CLASS_CONDITION; - } - if (conditionType == SAC_ID_CONDITION.intValue()) { - return SAC_ID_CONDITION; - } - if (conditionType == SAC_PSEUDO_CLASS_CONDITION.intValue()) { - return SAC_PSEUDO_CLASS_CONDITION; - } - return OTHER_SAC_CONDITIONAL_SELECTOR; - } - - return OTHER_SAC_SELECTOR; - } - - protected Map> getStyleDeclarationMap() { - if (styleDeclarationMap == null) { - styleDeclarationMap = new HashMap<>(); - } - return styleDeclarationMap; } @Override diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/ViewCSSImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/ViewCSSImpl.java index c628ce9cf41..aa1a5c8a92c 100644 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/ViewCSSImpl.java +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/ViewCSSImpl.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2018 Angelo Zerr and others. + * Copyright (c) 2008, 2026 Angelo Zerr and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -21,11 +21,9 @@ import java.util.List; import org.eclipse.e4.ui.css.core.dom.ExtendedCSSRule; import org.eclipse.e4.ui.css.core.dom.ExtendedDocumentCSS; -import org.eclipse.e4.ui.css.core.impl.sac.ExtendedSelector; -import org.w3c.css.sac.Selector; -import org.w3c.css.sac.SelectorList; +import org.eclipse.e4.ui.css.core.impl.engine.selector.SelectorMatcher; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors; import org.w3c.dom.Element; -import org.w3c.dom.Node; import org.w3c.dom.css.CSSRule; import org.w3c.dom.css.CSSRuleList; import org.w3c.dom.css.CSSStyleDeclaration; @@ -112,47 +110,42 @@ private List getCombinedRules() { } private CSSStyleDeclaration getComputedStyle(List ruleList, Element elt, String pseudoElt) { - Node parent = elt.getParentNode(); - - Node[] hierarchy = null; - if (parent != null) { - List hierarchyList = new ArrayList<>(); - for (Node n = parent; n != null; n = n.getParentNode()) { - hierarchyList.add(n); - } - hierarchy = hierarchyList.toArray(new Node[hierarchyList.size()]); - } - List styleDeclarations = null; StyleWrapper firstStyleDeclaration = null; int position = 0; + + int depth = 0; + for (org.w3c.dom.Node n = elt; n instanceof Element; n = n.getParentNode()) { + depth++; + } + Element[] hierarchy = new Element[depth]; + int idx = 0; + for (org.w3c.dom.Node n = elt; n instanceof Element; n = n.getParentNode()) { + hierarchy[idx++] = (Element) n; + } + for (CSSRule rule : ruleList) { if (rule.getType() != CSSRule.STYLE_RULE || (!(rule instanceof ExtendedCSSRule)) ) { continue; // we only handle the CSSRule.STYLE_RULE and ExtendedCSSRule case } CSSStyleRule styleRule = (CSSStyleRule) rule; ExtendedCSSRule r = (ExtendedCSSRule) rule; - SelectorList selectorList = r.getSelectorList(); - // Loop for SelectorList - int l = selectorList.getLength(); - for (int j = 0; j < l; j++) { - Selector selector = selectorList.item(j); - if (selector instanceof ExtendedSelector extendedSelector) { - if (extendedSelector.match(elt, hierarchy, 0, pseudoElt)) { - CSSStyleDeclaration style = styleRule.getStyle(); - int specificity = extendedSelector.getSpecificity(); - StyleWrapper wrapper = new StyleWrapper(style, specificity, position++); - if (firstStyleDeclaration == null) { - firstStyleDeclaration = wrapper; - } else { - // There is several Style Declarations which - // match the current element - if (styleDeclarations == null) { - styleDeclarations = new ArrayList<>(); - styleDeclarations.add(firstStyleDeclaration); - } - styleDeclarations.add(wrapper); + Selectors.SelectorList selectorList = r.getSelectorList(); + for (Selectors.Selector selector : selectorList.alternatives()) { + if (SelectorMatcher.matches(selector, elt, pseudoElt, hierarchy, 0)) { + CSSStyleDeclaration style = styleRule.getStyle(); + int specificity = selector.specificity(); + StyleWrapper wrapper = new StyleWrapper(style, specificity, position++); + if (firstStyleDeclaration == null) { + firstStyleDeclaration = wrapper; + } else { + // There is several Style Declarations which + // match the current element + if (styleDeclarations == null) { + styleDeclarations = new ArrayList<>(); + styleDeclarations.add(firstStyleDeclaration); } + styleDeclarations.add(wrapper); } } } diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/CSSEngineImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/CSSEngineImpl.java index 2fb2d1829de..66e7ede4f84 100644 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/CSSEngineImpl.java +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/CSSEngineImpl.java @@ -62,23 +62,17 @@ import org.eclipse.e4.ui.css.core.impl.dom.CSSStyleSheetImpl; import org.eclipse.e4.ui.css.core.impl.dom.DocumentCSSImpl; import org.eclipse.e4.ui.css.core.impl.dom.ViewCSSImpl; -import org.eclipse.e4.ui.css.core.impl.sac.CSSConditionFactoryImpl; -import org.eclipse.e4.ui.css.core.impl.sac.CSSSelectorFactoryImpl; -import org.eclipse.e4.ui.css.core.impl.sac.ExtendedSelector; +import org.eclipse.e4.ui.css.core.impl.engine.selector.SacTranslator; +import org.eclipse.e4.ui.css.core.impl.engine.selector.SelectorMatcher; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors; import org.eclipse.e4.ui.css.core.resources.IResourcesRegistry; import org.eclipse.e4.ui.css.core.resources.ResourceRegistryKeyFactory; import org.eclipse.e4.ui.css.core.util.impl.resources.ResourcesLocatorManager; import org.eclipse.e4.ui.css.core.util.resources.IResourcesLocatorManager; import org.eclipse.e4.ui.css.core.utils.StringUtils; -import org.w3c.css.sac.AttributeCondition; -import org.w3c.css.sac.CombinatorCondition; -import org.w3c.css.sac.Condition; -import org.w3c.css.sac.ConditionFactory; -import org.w3c.css.sac.ConditionalSelector; -import org.w3c.css.sac.DescendantSelector; +import org.apache.batik.css.parser.DefaultConditionFactory; +import org.apache.batik.css.parser.DefaultSelectorFactory; import org.w3c.css.sac.InputSource; -import org.w3c.css.sac.Selector; -import org.w3c.css.sac.SelectorList; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -105,9 +99,6 @@ */ public abstract class CSSEngineImpl implements CSSEngine { - public static final ConditionFactory CONDITIONFACTORY_INSTANCE = new CSSConditionFactoryImpl( - null, "class", null, "id"); - /** * Archives are deliberately identified by exclamation mark in URLs */ @@ -324,7 +315,7 @@ public CSSStyleDeclaration parseStyleDeclaration(InputSource source) throws IOEx /*--------------- Parse CSS Selector -----------------*/ @Override - public SelectorList parseSelectors(String selector) { + public Selectors.SelectorList parseSelectors(String selector) { try { return parseSelectors(new StringReader(selector)); } catch (IOException e) { @@ -333,24 +324,24 @@ public SelectorList parseSelectors(String selector) { } @Override - public SelectorList parseSelectors(Reader reader) throws IOException { + public Selectors.SelectorList parseSelectors(Reader reader) throws IOException { InputSource source = new InputSource(); source.setCharacterStream(reader); return parseSelectors(source); } @Override - public SelectorList parseSelectors(InputStream stream) throws IOException { + public Selectors.SelectorList parseSelectors(InputStream stream) throws IOException { InputSource source = new InputSource(); source.setByteStream(stream); return parseSelectors(source); } @Override - public SelectorList parseSelectors(InputSource source) throws IOException { + public Selectors.SelectorList parseSelectors(InputSource source) throws IOException { checkInputSource(source); CSSParser parser = makeCSSParser(); - return parser.parseSelectors(source); + return SacTranslator.translate(parser.parseSelectors(source)); } /*--------------- Parse CSS Property Value-----------------*/ @@ -387,6 +378,28 @@ public CSSValue parsePropertyValue(InputSource source) throws IOException { /*--------------- Apply styles -----------------*/ + private final ThreadLocal> styledElements = new ThreadLocal<>(); + + public void startStylingSession() { + styledElements.set(new HashSet<>()); + } + + public void stopStylingSession() { + styledElements.remove(); + } + + public boolean isElementStyled(Object element) { + Set set = styledElements.get(); + return set != null && set.contains(element); + } + + public void markElementStyled(Object element) { + Set set = styledElements.get(); + if (set != null) { + set.add(element); + } + } + @Override public void applyStyles(Object element, boolean applyStylesToChildNodes) { applyStyles(element, applyStylesToChildNodes, computeDefaultStyle); @@ -399,6 +412,21 @@ public void applyStyles(Object element, boolean applyStylesToChildNodes, boolean return; } + if (isElementStyled(element)) { + if (applyStylesToChildNodes) { + NodeList nodes = elt instanceof ChildVisibilityAwareElement c + ? c.getVisibleChildNodes() + : elt.getChildNodes(); + if (nodes != null) { + processNodeList(nodes, this::applyStyles, applyStylesToChildNodes); + onStylesAppliedToChildNodes(elt, nodes); + } + } + return; + } + + markElementStyled(element); + /* * Compute new Style to apply. */ @@ -491,46 +519,57 @@ protected boolean isVisible(Element elt) { return true; } - private void applyConditionalPseudoStyle(ExtendedCSSRule parentRule, String pseudoInstance, Object element, CSSStyleDeclaration styleWithPseudoInstance) { - SelectorList selectorList = parentRule.getSelectorList(); - for (int j = 0; j < selectorList.getLength(); j++) { - Selector item = selectorList.item(j); - // search for conditional selectors - ConditionalSelector conditional = null; - if (item instanceof ConditionalSelector) { - conditional = (ConditionalSelector) item; - } else if (item instanceof DescendantSelector) { - if (((DescendantSelector) item).getSimpleSelector() instanceof ConditionalSelector) { - conditional = (ConditionalSelector) ((DescendantSelector) item).getSimpleSelector(); - } else if (((DescendantSelector) item).getAncestorSelector() instanceof ConditionalSelector) { - conditional = (ConditionalSelector) ((DescendantSelector) item).getAncestorSelector(); - } - } - if (conditional != null) { - Condition condition = conditional.getCondition(); - // we're only interested in attribute selector conditions - AttributeCondition attr = null; - if (condition instanceof AttributeCondition) { - attr = (AttributeCondition) condition; - } else if (condition instanceof CombinatorCondition) { - if (((CombinatorCondition) condition).getSecondCondition() instanceof AttributeCondition) { - attr = (AttributeCondition) ((CombinatorCondition) condition).getSecondCondition(); - } else if (((CombinatorCondition) condition).getFirstCondition() instanceof AttributeCondition) { - attr = (AttributeCondition) ((CombinatorCondition) condition).getFirstCondition(); - } - } - if (attr != null) { - String value = attr.getValue(); - if (value.equals(pseudoInstance)) { - // if we match the pseudo, apply the style - applyStyleDeclaration(element, styleWithPseudoInstance, pseudoInstance); - return; - } - } + private void applyConditionalPseudoStyle(ExtendedCSSRule parentRule, String pseudoInstance, Object element, + CSSStyleDeclaration styleWithPseudoInstance) { + Selectors.SelectorList selectorList = parentRule.getSelectorList(); + for (Selectors.Selector alternative : selectorList.alternatives()) { + if (matchesPseudoInstanceAttribute(alternative, pseudoInstance)) { + applyStyleDeclaration(element, styleWithPseudoInstance, pseudoInstance); + return; } } } + /** + * Returns {@code true} if {@code selector} carries a pseudo-class or + * attribute selector (anywhere in a compound or descendant combinator) + * whose target value equals {@code pseudoInstance}. Mirrors the legacy + * SAC walker, which handled both {@code :selected} (a pseudo-class) and + * {@code Shell[active='true']} (an attribute) through SAC's shared + * {@code AttributeCondition} interface. + */ + private static boolean matchesPseudoInstanceAttribute(Selectors.Selector selector, String pseudoInstance) { + if (selector instanceof Selectors.PseudoClass pc) { + return pseudoInstance.equals(pc.name()); + } + if (selector instanceof Selectors.AttributeSelector attr) { + return pseudoInstance.equals(attr.value()); + } + if (selector instanceof Selectors.AttributeIncludes attr) { + return pseudoInstance.equals(attr.value()); + } + if (selector instanceof Selectors.AttributeBeginHyphen attr) { + return pseudoInstance.equals(attr.value()); + } + if (selector instanceof Selectors.And and) { + return matchesPseudoInstanceAttribute(and.left(), pseudoInstance) + || matchesPseudoInstanceAttribute(and.right(), pseudoInstance); + } + if (selector instanceof Selectors.Descendant d) { + return matchesPseudoInstanceAttribute(d.descendant(), pseudoInstance) + || matchesPseudoInstanceAttribute(d.ancestor(), pseudoInstance); + } + if (selector instanceof Selectors.Child c) { + return matchesPseudoInstanceAttribute(c.child(), pseudoInstance) + || matchesPseudoInstanceAttribute(c.parent(), pseudoInstance); + } + if (selector instanceof Selectors.Adjacent a) { + return matchesPseudoInstanceAttribute(a.second(), pseudoInstance) + || matchesPseudoInstanceAttribute(a.first(), pseudoInstance); + } + return false; + } + protected String[] getStaticPseudoInstances(Element element) { if (element instanceof CSSStylableElement stylableElement) { return stylableElement.getStaticPseudoInstances(); @@ -933,18 +972,21 @@ protected Map getElementsContext() { } @Override - public boolean matches(Selector selector, Object element, String pseudoElt) { + public boolean matches(Selectors.Selector selector, Object element, String pseudoElt) { Element elt = getElement(element); if (elt == null) { return false; } - if (selector instanceof ExtendedSelector extendedSelector) { - return extendedSelector.match(elt, pseudoElt); - } else { - // TODO : selector is not batik ExtendedSelector, - // Manage this case... + int depth = 0; + for (Node n = elt; n instanceof Element; n = n.getParentNode()) { + depth++; } - return false; + Element[] hierarchy = new Element[depth]; + int idx = 0; + for (Node n = elt; n instanceof Element; n = n.getParentNode()) { + hierarchy[idx++] = (Element) n; + } + return SelectorMatcher.matches(selector, elt, pseudoElt, hierarchy, 0); } /*--------------- Error Handler -----------------*/ @@ -1118,14 +1160,16 @@ public String convert(Object value, Object toType, Object context) } /** - * Return instance of CSS Parser, configured with the Batik selector - * factory and the platform's class/id condition factory. + * Return instance of CSS Parser, configured with Batik's stock selector + * and condition factories. Selectors flow through {@link SacTranslator} + * before they reach engine code, so we no longer need our vendored copies + * of the SAC factory classes. */ public CSSParser makeCSSParser() { ICSSParserFactory factory = CSSParserFactory.newInstance(); CSSParser parser = factory.makeCSSParser(); - parser.setSelectorFactory(CSSSelectorFactoryImpl.INSTANCE); - parser.setConditionFactory(CONDITIONFACTORY_INSTANCE); + parser.setSelectorFactory(DefaultSelectorFactory.INSTANCE); + parser.setConditionFactory(DefaultConditionFactory.INSTANCE); return parser; } diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/selector/SacTranslator.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/selector/SacTranslator.java new file mode 100644 index 00000000000..5ee9a3d5dbf --- /dev/null +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/selector/SacTranslator.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * Copyright (c) 2026 Lars Vogel and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Lars Vogel - initial API and implementation + *******************************************************************************/ +package org.eclipse.e4.ui.css.core.impl.engine.selector; + +import java.util.ArrayList; +import java.util.List; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.Adjacent; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.And; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.AttributeBeginHyphen; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.AttributeIncludes; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.AttributeSelector; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.Child; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.ClassSelector; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.Descendant; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.ElementType; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.IdSelector; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.PseudoClass; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.Selector; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.Universal; +import org.w3c.css.sac.AttributeCondition; +import org.w3c.css.sac.CombinatorCondition; +import org.w3c.css.sac.Condition; +import org.w3c.css.sac.ConditionalSelector; +import org.w3c.css.sac.DescendantSelector; +import org.w3c.css.sac.ElementSelector; +import org.w3c.css.sac.SelectorList; +import org.w3c.css.sac.SiblingSelector; +import org.w3c.css.sac.SimpleSelector; + +/** + * Converts a SAC selector tree (as produced by the Batik parser) into the + * engine's internal {@link Selectors} AST. + * + *

+ * The translator is the single boundary between the SAC parser output and the + * rest of the engine. Once a stylesheet has been parsed, only the internal + * AST flows through {@code CSSEngine.matches}, the rule list, and + * {@link SelectorMatcher}. SAC types do not cross this boundary. + *

+ * + *

+ * Specificity is preserved exactly: the internal records compute it the same + * way the legacy SAC wrappers did (100 per id, 10 per class / attribute / + * pseudo-class, 1 per element, 0 for {@code *}). Combinators sum operands. + *

+ */ +public final class SacTranslator { + + private SacTranslator() { + // statics only + } + + /** Translate an entire {@link SelectorList} into the internal form. */ + public static Selectors.SelectorList translate(SelectorList sacList) { + List alternatives = new ArrayList<>(sacList.getLength()); + for (int i = 0; i < sacList.getLength(); i++) { + alternatives.add(translate(sacList.item(i))); + } + return new Selectors.SelectorList(alternatives); + } + + /** Translate a single SAC {@link org.w3c.css.sac.Selector}. */ + public static Selector translate(org.w3c.css.sac.Selector sac) { + return switch (sac.getSelectorType()) { + case org.w3c.css.sac.Selector.SAC_ELEMENT_NODE_SELECTOR -> translateElement((ElementSelector) sac); + case org.w3c.css.sac.Selector.SAC_PSEUDO_ELEMENT_SELECTOR -> translatePseudoElement((ElementSelector) sac); + case org.w3c.css.sac.Selector.SAC_CONDITIONAL_SELECTOR -> translateConditional((ConditionalSelector) sac); + case org.w3c.css.sac.Selector.SAC_DESCENDANT_SELECTOR -> { + DescendantSelector d = (DescendantSelector) sac; + yield new Descendant(translate(d.getAncestorSelector()), translateSimple(d.getSimpleSelector())); + } + case org.w3c.css.sac.Selector.SAC_CHILD_SELECTOR -> { + DescendantSelector c = (DescendantSelector) sac; + yield new Child(translate(c.getAncestorSelector()), translateSimple(c.getSimpleSelector())); + } + case org.w3c.css.sac.Selector.SAC_DIRECT_ADJACENT_SELECTOR -> { + SiblingSelector s = (SiblingSelector) sac; + yield new Adjacent(translate(s.getSelector()), translateSimple(s.getSiblingSelector())); + } + default -> throw new IllegalArgumentException( + "Unsupported SAC selector type: " + sac.getSelectorType() + " (" + sac + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + }; + } + + private static Selector translateSimple(SimpleSelector simple) { + return translate(simple); + } + + private static Selector translateElement(ElementSelector sel) { + String name = sel.getLocalName(); + return name == null ? new Universal() : new ElementType(name); + } + + private static Selector translatePseudoElement(ElementSelector sel) { + // Pseudo-element form (::first-line). The engine has never matched + // these; treat as the element it appears on (if any) or universal. + // In practice the parser only emits this when a stylesheet uses :: , + // which the supported subset does not. + String name = sel.getLocalName(); + return name == null ? new Universal() : new PseudoClass(name); + } + + private static Selector translateConditional(ConditionalSelector sel) { + Selector left = translate(sel.getSimpleSelector()); + Selector right = translateCondition(sel.getCondition()); + if (left instanceof Universal) { + return right; + } + return new And(left, right); + } + + private static Selector translateCondition(Condition condition) { + return switch (condition.getConditionType()) { + case Condition.SAC_CLASS_CONDITION -> new ClassSelector(((AttributeCondition) condition).getValue()); + case Condition.SAC_ID_CONDITION -> new IdSelector(((AttributeCondition) condition).getValue()); + case Condition.SAC_PSEUDO_CLASS_CONDITION -> new PseudoClass(((AttributeCondition) condition).getValue()); + case Condition.SAC_LANG_CONDITION -> { + // Modeled as a presence-form attribute selector keyed on lang; + // nothing in the supported subset uses :lang(), but the parser + // can still emit it. + AttributeCondition lang = (AttributeCondition) condition; + yield new AttributeSelector("lang", lang.getValue()); //$NON-NLS-1$ + } + case Condition.SAC_ATTRIBUTE_CONDITION -> { + AttributeCondition attr = (AttributeCondition) condition; + // Batik's stock Parser always calls createAttributeCondition with + // specified=false, regardless of whether the source was [attr] or + // [attr='value']. Distinguish the two by whether a value was + // supplied: null means the presence form [attr]. + yield new AttributeSelector(attr.getLocalName(), attr.getValue()); + } + case Condition.SAC_ONE_OF_ATTRIBUTE_CONDITION -> { + AttributeCondition attr = (AttributeCondition) condition; + yield new AttributeIncludes(attr.getLocalName(), attr.getValue()); + } + case Condition.SAC_BEGIN_HYPHEN_ATTRIBUTE_CONDITION -> { + AttributeCondition attr = (AttributeCondition) condition; + yield new AttributeBeginHyphen(attr.getLocalName(), attr.getValue()); + } + case Condition.SAC_AND_CONDITION -> { + CombinatorCondition combo = (CombinatorCondition) condition; + yield new And(translateCondition(combo.getFirstCondition()), + translateCondition(combo.getSecondCondition())); + } + default -> throw new IllegalArgumentException( + "Unsupported SAC condition type: " + condition.getConditionType() + " (" + condition + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + }; + } +} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/selector/SelectorMatcher.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/selector/SelectorMatcher.java new file mode 100644 index 00000000000..82e97624dd3 --- /dev/null +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/selector/SelectorMatcher.java @@ -0,0 +1,288 @@ +/******************************************************************************* + * Copyright (c) 2026 Lars Vogel and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Lars Vogel - initial API and implementation + *******************************************************************************/ +package org.eclipse.e4.ui.css.core.impl.engine.selector; + +import org.eclipse.e4.ui.css.core.dom.CSSStylableElement; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.Adjacent; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.And; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.AttributeBeginHyphen; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.AttributeIncludes; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.AttributeSelector; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.Child; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.ClassSelector; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.Descendant; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.ElementType; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.IdSelector; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.PseudoClass; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.Selector; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.SelectorList; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.Universal; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * Matches a {@link Selector} against an {@link Element}. + * + *

+ * Every method is static; the matcher carries no state. Callers pass the + * element being tested plus an optional pseudo-element string (the same + * argument the SAC engine carried) so that pseudo-class matching can defer + * to the existing {@link CSSStylableElement#isPseudoInstanceOf} contract. + *

+ * + *

+ * Tag-name comparison is case sensitive, matching the existing SAC matcher + * (Phase 1 test {@code testTagNameCaseSensitivity} in {@code CSSEngineTest} + * locks this in). Pseudo-class semantics also follow the existing engine: + * the static-pseudo-instance carve-out from + * {@code CSSPseudoClassConditionImpl} is preserved so cascade behaviour + * does not shift. + *

+ */ +public final class SelectorMatcher { + + private SelectorMatcher() { + // statics only + } + + /** + * @return {@code true} if {@code selector} matches {@code element} for + * the given pseudo state. + */ + public static boolean matches(Selector selector, Element element, String pseudoElement) { + return matches(selector, element, pseudoElement, null, 0); + } + + /** + * @return {@code true} if {@code selector} matches {@code element} for + * the given pseudo state, using a pre-computed ancestor hierarchy array if available. + */ + public static boolean matches(Selector selector, Element element, String pseudoElement, Element[] hierarchy, int hierarchyIndex) { + if (element == null) { + return false; + } + if (selector instanceof Universal) { + return true; + } + if (selector instanceof ElementType type) { + return matchesElementType(type, element); + } + if (selector instanceof ClassSelector cls) { + return matchesClass(cls, element); + } + if (selector instanceof IdSelector id) { + return matchesId(id, element); + } + if (selector instanceof AttributeSelector attr) { + return matchesAttribute(attr, element); + } + if (selector instanceof AttributeIncludes inc) { + return matchesAttributeIncludes(inc, element); + } + if (selector instanceof AttributeBeginHyphen beg) { + return matchesAttributeBeginHyphen(beg, element); + } + if (selector instanceof PseudoClass pc) { + return matchesPseudoClass(pc, element, pseudoElement); + } + if (selector instanceof And and) { + return matches(and.left(), element, pseudoElement, hierarchy, hierarchyIndex) + && matches(and.right(), element, pseudoElement, hierarchy, hierarchyIndex); + } + if (selector instanceof Descendant d) { + return matchesDescendant(d, element, pseudoElement, hierarchy, hierarchyIndex); + } + if (selector instanceof Child c) { + return matchesChild(c, element, pseudoElement, hierarchy, hierarchyIndex); + } + if (selector instanceof Adjacent a) { + return matchesAdjacent(a, element, pseudoElement, hierarchy, hierarchyIndex); + } + if (selector instanceof SelectorList list) { + return matchesAny(list, element, pseudoElement, hierarchy, hierarchyIndex); + } + throw new IllegalStateException("Unknown selector kind: " + selector.getClass()); //$NON-NLS-1$ + } + + private static boolean matchesElementType(ElementType type, Element element) { + String localName = type.localName(); + if (localName == null) { + return true; + } + String elementName = element.getPrefix() == null ? element.getNodeName() : element.getLocalName(); + return localName.equals(elementName); + } + + private static boolean matchesClass(ClassSelector cls, Element element) { + if (!(element instanceof CSSStylableElement stylable)) { + return false; + } + String elementClass = stylable.getCSSClass(); + if (elementClass == null) { + return false; + } + // CSS class attribute can be a whitespace-separated list of classes. + // Walk the string manually to avoid the regex compile + array allocation + // String.split forces on every match evaluation. + return containsWord(elementClass, cls.className()); + } + + private static boolean matchesId(IdSelector id, Element element) { + if (!(element instanceof CSSStylableElement stylable)) { + return false; + } + return id.id().equals(stylable.getCSSId()); + } + + private static boolean matchesAttribute(AttributeSelector attr, Element element) { + String name = attr.name(); + if (!element.hasAttribute(name)) { + return false; + } + String required = attr.value(); + if (required == null) { + // presence form: [attr] + return true; + } + return required.equals(element.getAttribute(name)); + } + + private static boolean matchesAttributeIncludes(AttributeIncludes inc, Element element) { + String actual = element.getAttribute(inc.name()); + if (actual == null) { + return false; + } + return containsWord(actual, inc.value()); + } + + /** + * Returns {@code true} if {@code haystack} contains {@code word} as a + * whitespace-separated token. Equivalent to splitting on + * {@code \s+} and checking for an exact token match, but without the + * regex compile and array allocation each call. + */ + private static boolean containsWord(String haystack, String word) { + if (word == null || word.isEmpty()) { + return false; + } + int wordLength = word.length(); + int length = haystack.length(); + int i = 0; + while (i < length) { + while (i < length && Character.isWhitespace(haystack.charAt(i))) { + i++; + } + int start = i; + while (i < length && !Character.isWhitespace(haystack.charAt(i))) { + i++; + } + if (i - start == wordLength && haystack.regionMatches(start, word, 0, wordLength)) { + return true; + } + } + return false; + } + + private static boolean matchesAttributeBeginHyphen(AttributeBeginHyphen beg, Element element) { + String actual = element.getAttribute(beg.name()); + if (actual == null) { + return false; + } + String value = beg.value(); + return actual.equals(value) || actual.startsWith(value + "-"); + } + + private static boolean matchesPseudoClass(PseudoClass pseudo, Element element, String pseudoElement) { + String name = pseudo.name(); + // If the caller is iterating a static-pseudo cascade, only match the + // pseudo argument on the way down. + if (pseudoElement != null && !pseudoElement.equals(name)) { + return false; + } + if (!(element instanceof CSSStylableElement stylable)) { + return false; + } + if (!stylable.isPseudoInstanceOf(name)) { + return false; + } + if (pseudoElement == null) { + // Same carve-out as CSSPseudoClassConditionImpl: when no pseudo + // element argument is supplied, pseudos that the element + // publishes only as static instances do not match the regular + // cascade. They get applied separately via the default style + // declaration map. + return !stylable.isStaticPseudoInstance(name); + } + return true; + } + + private static boolean matchesDescendant(Descendant d, Element element, String pseudoElement, Element[] hierarchy, int hierarchyIndex) { + if (!matches(d.descendant(), element, pseudoElement, hierarchy, hierarchyIndex)) { + return false; + } + if (hierarchy != null) { + for (int i = hierarchyIndex + 1; i < hierarchy.length; i++) { + if (matches(d.ancestor(), hierarchy[i], null, hierarchy, i)) { + return true; + } + } + return false; + } else { + Node parent = element.getParentNode(); + while (parent instanceof Element parentElement) { + if (matches(d.ancestor(), parentElement, null, null, 0)) { + return true; + } + parent = parentElement.getParentNode(); + } + return false; + } + } + + private static boolean matchesChild(Child c, Element element, String pseudoElement, Element[] hierarchy, int hierarchyIndex) { + if (!matches(c.child(), element, pseudoElement, hierarchy, hierarchyIndex)) { + return false; + } + if (hierarchy != null) { + int parentIdx = hierarchyIndex + 1; + if (parentIdx < hierarchy.length) { + return matches(c.parent(), hierarchy[parentIdx], null, hierarchy, parentIdx); + } + return false; + } else { + Node parent = element.getParentNode(); + return parent instanceof Element parentElement && matches(c.parent(), parentElement, null, null, 0); + } + } + + private static boolean matchesAdjacent(Adjacent a, Element element, String pseudoElement, Element[] hierarchy, int hierarchyIndex) { + if (!matches(a.second(), element, pseudoElement, hierarchy, hierarchyIndex)) { + return false; + } + Node previous = element.getPreviousSibling(); + while (previous != null && !(previous instanceof Element)) { + previous = previous.getPreviousSibling(); + } + return previous instanceof Element previousElement && matches(a.first(), previousElement, null, null, 0); + } + + private static boolean matchesAny(SelectorList list, Element element, String pseudoElement, Element[] hierarchy, int hierarchyIndex) { + for (Selector alternative : list.alternatives()) { + if (matches(alternative, element, pseudoElement, hierarchy, hierarchyIndex)) { + return true; + } + } + return false; + } +} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/selector/Selectors.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/selector/Selectors.java new file mode 100644 index 00000000000..ebca60b165b --- /dev/null +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/selector/Selectors.java @@ -0,0 +1,357 @@ +/******************************************************************************* + * Copyright (c) 2026 Lars Vogel and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Lars Vogel - initial API and implementation + *******************************************************************************/ +package org.eclipse.e4.ui.css.core.impl.engine.selector; + +import java.util.List; + +/** + * Internal CSS selector AST. + * + *

+ * The engine historically exposed W3C SAC selectors + * ({@code org.w3c.css.sac.Selector} and friends) and matched against them + * through a hierarchy of vendored Batik wrapper classes under + * {@code impl/sac/*}. This package replaces both with a small set of records + * that the engine owns end to end. The W3C SAC types stay only as long as + * the parser still emits them; a translator turns the SAC selector tree + * produced by the Batik SAC parser into one of these records before it + * reaches the engine matcher. + *

+ * + *

+ * Specificity follows CSS 2.1: 100 per id, 10 per class / attribute / + * pseudo-class, 1 per element, 0 for the universal selector. Combinators + * sum the specificity of their operands; selector lists report the maximum + * specificity over their alternatives. + *

+ */ +public final class Selectors { + + private Selectors() { + // constants only + } + + /** A parsed CSS selector. Sealed; pattern-match in the matcher. */ + public sealed interface Selector + permits Universal, ElementType, ClassSelector, IdSelector, AttributeSelector, + AttributeIncludes, AttributeBeginHyphen, PseudoClass, And, Descendant, Child, Adjacent, SelectorList { + + /** CSS specificity contribution of this selector. */ + int specificity(); + + /** Best-effort textual reproduction of the selector. */ + String text(); + } + + /** {@code *} — matches any element. */ + public record Universal() implements Selector { + @Override + public int specificity() { + return 0; + } + + @Override + public String text() { + return "*"; //$NON-NLS-1$ + } + + @Override + public String toString() { + return text(); + } + } + + /** {@code Button} — matches by local element name. */ + public record ElementType(String localName) implements Selector { + @Override + public int specificity() { + return 1; + } + + @Override + public String text() { + return localName; + } + + @Override + public String toString() { + return text(); + } + } + + /** {@code .foo} — matches by CSS class. */ + public record ClassSelector(String className) implements Selector { + @Override + public int specificity() { + return 10; + } + + @Override + public String text() { + return "." + className; //$NON-NLS-1$ + } + + @Override + public String toString() { + return text(); + } + } + + /** {@code #foo} — matches by CSS id. */ + public record IdSelector(String id) implements Selector { + @Override + public int specificity() { + return 100; + } + + @Override + public String text() { + return "#" + id; //$NON-NLS-1$ + } + + @Override + public String toString() { + return text(); + } + } + + /** + * {@code [attr]} or {@code [attr='value']}. {@code value} is {@code null} + * for the presence form and the empty string for {@code [attr='']}. + */ + public record AttributeSelector(String name, String value) implements Selector { + @Override + public int specificity() { + return 10; + } + + @Override + public String text() { + return value == null ? "[" + name + "]" : "[" + name + "='" + value + "']"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + + @Override + public String toString() { + return text(); + } + } + + /** {@code [attr~='value']} — matches when {@code attr} contains {@code value} as a whitespace-separated word. */ + public record AttributeIncludes(String name, String value) implements Selector { + @Override + public int specificity() { + return 10; + } + + @Override + public String text() { + return "[" + name + "~='" + value + "']"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + @Override + public String toString() { + return text(); + } + } + + /** {@code [attr|='value']} — matches when {@code attr} equals {@code value} or starts with {@code value-}. */ + public record AttributeBeginHyphen(String name, String value) implements Selector { + @Override + public int specificity() { + return 10; + } + + @Override + public String text() { + return "[" + name + "|='" + value + "']"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + @Override + public String toString() { + return text(); + } + } + + /** + * {@code :name} — matches when the element answers true to + * {@code isPseudoInstanceOf(name)} on its CSS-stylable element wrapper. + */ + public record PseudoClass(String name) implements Selector { + @Override + public int specificity() { + return 10; + } + + @Override + public String text() { + return ":" + name; //$NON-NLS-1$ + } + + @Override + public String toString() { + return text(); + } + } + + /** + * Compound selector: every operand must match the same element. Built + * by the translator for forms like {@code Button.primary#go} or + * {@code [a][b]} where multiple simple selectors apply to one element. + */ + public record And(Selector left, Selector right) implements Selector { + @Override + public int specificity() { + return left.specificity() + right.specificity(); + } + + @Override + public String text() { + return left.text() + right.text(); + } + + @Override + public String toString() { + return text(); + } + } + + /** {@code ancestor descendant} — descendant combinator. */ + public record Descendant(Selector ancestor, Selector descendant) implements Selector { + @Override + public int specificity() { + return ancestor.specificity() + descendant.specificity(); + } + + @Override + public String text() { + return ancestor.text() + " " + descendant.text(); //$NON-NLS-1$ + } + + @Override + public String toString() { + return text(); + } + } + + /** {@code parent > child} — child combinator. */ + public record Child(Selector parent, Selector child) implements Selector { + @Override + public int specificity() { + return parent.specificity() + child.specificity(); + } + + @Override + public String text() { + return parent.text() + " > " + child.text(); //$NON-NLS-1$ + } + + @Override + public String toString() { + return text(); + } + } + + /** {@code first + second} — direct adjacent sibling combinator. */ + public record Adjacent(Selector first, Selector second) implements Selector { + @Override + public int specificity() { + return first.specificity() + second.specificity(); + } + + @Override + public String text() { + return first.text() + " + " + second.text(); //$NON-NLS-1$ + } + + @Override + public String toString() { + return text(); + } + } + + /** + * {@code a, b} — selector list. Specificity reports the maximum over the + * alternatives so cascade ordering can match a list against an element by + * iterating its alternatives. + * + *

+ * A regular final class rather than a record because the cascade reads + * {@link #specificity()} once per matched alternative and we want it + * precomputed; record components cannot host derived state. + *

+ */ + public static final class SelectorList implements Selector { + + private final List alternatives; + private final int specificity; + + public SelectorList(List alternatives) { + this.alternatives = List.copyOf(alternatives); + int max = 0; + for (Selector alternative : this.alternatives) { + int s = alternative.specificity(); + if (s > max) { + max = s; + } + } + this.specificity = max; + } + + public List alternatives() { + return alternatives; + } + + /** Number of alternatives in the list. SAC-style accessor for callers iterating the list. */ + public int getLength() { + return alternatives.size(); + } + + /** {@code i}-th alternative. SAC-style accessor for callers iterating the list. */ + public Selector item(int i) { + return alternatives.get(i); + } + + @Override + public int specificity() { + return specificity; + } + + @Override + public String text() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < alternatives.size(); i++) { + if (i > 0) { + sb.append(", "); //$NON-NLS-1$ + } + sb.append(alternatives.get(i).text()); + } + return sb.toString(); + } + + @Override + public boolean equals(Object o) { + return o instanceof SelectorList other && alternatives.equals(other.alternatives); + } + + @Override + public int hashCode() { + return alternatives.hashCode(); + } + + @Override + public String toString() { + return text(); + } + } +} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractAttributeCondition.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractAttributeCondition.java deleted file mode 100644 index fabffbad828..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractAttributeCondition.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - - Copyright 2002, 2014 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import java.util.Objects; -import org.w3c.css.sac.AttributeCondition; - -/** - * This class provides an abstract implementation of the {@link - * org.w3c.css.sac.AttributeCondition} interface. - */ -public abstract class AbstractAttributeCondition implements AttributeCondition, - ExtendedCondition { - - /** - * The attribute value. - */ - protected String value; - - /** - * Creates a new AbstractAttributeCondition object. - */ - protected AbstractAttributeCondition(String value) { - this.value = value; - } - - /** - * Indicates whether some other object is "equal to" this one. - * - * @param obj - * the reference object with which to compare. - */ - @Override - public boolean equals(Object obj) { - if (obj == null || (obj.getClass() != getClass())) { - return false; - } - AbstractAttributeCondition c = (AbstractAttributeCondition) obj; - return c.value.equals(value); - } - - /** - * equal objects should have equal hashCodes. - * - * @return hashCode of this AbstractAttributeCondition - */ - @Override - public int hashCode() { - return Objects.hashCode(value); - } - - /** - * Returns the specificity of this condition. - */ - @Override - public int getSpecificity() { - return 1 << 8; - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.AttributeCondition#getValue()}. - */ - @Override - public String getValue() { - return value; - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractCombinatorCondition.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractCombinatorCondition.java deleted file mode 100644 index 0de6c81b86c..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractCombinatorCondition.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - - Copyright 2002, 2014 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import org.w3c.css.sac.CombinatorCondition; -import org.w3c.css.sac.Condition; - -/** - * This class provides an abstract implementation of the {@link - * org.w3c.css.sac.CombinatorCondition} interface. - */ -public abstract class AbstractCombinatorCondition implements - CombinatorCondition, ExtendedCondition { - - /** - * The first condition. - */ - protected Condition firstCondition; - - /** - * The second condition. - */ - protected Condition secondCondition; - - /** - * Creates a new CombinatorCondition object. - */ - protected AbstractCombinatorCondition(Condition c1, Condition c2) { - firstCondition = c1; - secondCondition = c2; - } - - /** - * Indicates whether some other object is "equal to" this one. - * - * @param obj - * the reference object with which to compare. - */ - @Override - public boolean equals(Object obj) { - if (obj == null || (obj.getClass() != getClass())) { - return false; - } - AbstractCombinatorCondition c = (AbstractCombinatorCondition) obj; - return (c.firstCondition.equals(firstCondition) && c.secondCondition - .equals(secondCondition)); - } - - /** - * Returns the specificity of this condition. - */ - @Override - public int getSpecificity() { - return ((ExtendedCondition) getFirstCondition()).getSpecificity() - + ((ExtendedCondition) getSecondCondition()).getSpecificity(); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.CombinatorCondition#getFirstCondition()}. - */ - @Override - public Condition getFirstCondition() { - return firstCondition; - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.CombinatorCondition#getSecondCondition()}. - */ - @Override - public Condition getSecondCondition() { - return secondCondition; - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractDescendantSelector.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractDescendantSelector.java deleted file mode 100644 index f183af1ccae..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractDescendantSelector.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - - Copyright 2002, 2014 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import org.w3c.css.sac.DescendantSelector; -import org.w3c.css.sac.Selector; -import org.w3c.css.sac.SimpleSelector; - -/** - * This class provides an abstract implementation of the {@link - * org.w3c.css.sac.DescendantSelector} interface. - */ -public abstract class AbstractDescendantSelector - implements DescendantSelector, - ExtendedSelector { - - /** - * The ancestor selector. - */ - protected Selector ancestorSelector; - - /** - * The simple selector. - */ - protected SimpleSelector simpleSelector; - - /** - * Creates a new DescendantSelector object. - */ - protected AbstractDescendantSelector(Selector ancestor, - SimpleSelector simple) { - ancestorSelector = ancestor; - simpleSelector = simple; - } - - /** - * Indicates whether some other object is "equal to" this one. - * @param obj the reference object with which to compare. - */ - @Override - public boolean equals(Object obj) { - if (obj == null || (obj.getClass() != getClass())) { - return false; - } - AbstractDescendantSelector s = (AbstractDescendantSelector)obj; - return s.simpleSelector.equals(simpleSelector); - } - - /** - * Returns the specificity of this selector. - */ - @Override - public int getSpecificity() { - return ((ExtendedSelector)ancestorSelector).getSpecificity() + - ((ExtendedSelector)simpleSelector).getSpecificity(); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.DescendantSelector#getAncestorSelector()}. - */ - @Override - public Selector getAncestorSelector() { - return ancestorSelector; - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.DescendantSelector#getSimpleSelector()}. - */ - @Override - public SimpleSelector getSimpleSelector() { - return simpleSelector; - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractElementSelector.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractElementSelector.java deleted file mode 100644 index 529b86a0fbb..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractElementSelector.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - - Copyright 2002, 2015 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import java.util.Set; -import org.w3c.css.sac.ElementSelector; - -/** - * This class provides an abstract implementation of the ElementSelector - * interface. - */ -public abstract class AbstractElementSelector implements ElementSelector, ExtendedSelector { - - /** - * The namespace URI. - */ - protected String namespaceURI; - - /** - * The local name. - */ - protected String localName; - - /** - * Creates a new ElementSelector object. - */ - protected AbstractElementSelector(String uri, String name) { - namespaceURI = uri; - localName = name; - } - - /** - * Indicates whether some other object is "equal to" this one. - * @param obj the reference object with which to compare. - */ - @Override - public boolean equals(Object obj) { - if (obj == null || (obj.getClass() != getClass())) { - return false; - } - AbstractElementSelector s = (AbstractElementSelector)obj; - return (s.namespaceURI.equals(namespaceURI) && s.localName.equals(localName)); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.ElementSelector#getNamespaceURI()}. - */ - @Override - public String getNamespaceURI() { - return namespaceURI; - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.ElementSelector#getLocalName()}. - */ - @Override - public String getLocalName() { - return localName; - } - - /** - * Fills the given set with the attribute names found in this selector. - */ - @Override - public void fillAttributeSet(Set attrSet) { - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractSiblingSelector.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractSiblingSelector.java deleted file mode 100644 index bb061dd13e0..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/AbstractSiblingSelector.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - - Copyright 2002, 2014 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import org.w3c.css.sac.Selector; -import org.w3c.css.sac.SiblingSelector; -import org.w3c.css.sac.SimpleSelector; - -/** - * This class provides an abstract implementation of the {@link - * org.w3c.css.sac.SiblingSelector} interface. - */ -public abstract class AbstractSiblingSelector implements SiblingSelector, - ExtendedSelector { - - /** - * The node type. - */ - protected short nodeType; - - /** - * The selector. - */ - protected Selector selector; - - /** - * The simple selector. - */ - protected SimpleSelector simpleSelector; - - /** - * Creates a new SiblingSelector object. - */ - protected AbstractSiblingSelector(short type, Selector sel, - SimpleSelector simple) { - nodeType = type; - selector = sel; - simpleSelector = simple; - } - - /** - * Returns the node type. - */ - @Override - public short getNodeType() { - return nodeType; - } - - /** - * Indicates whether some other object is "equal to" this one. - * - * @param obj - * the reference object with which to compare. - */ - @Override - public boolean equals(Object obj) { - if (obj == null || (obj.getClass() != getClass())) { - return false; - } - AbstractSiblingSelector s = (AbstractSiblingSelector) obj; - return s.simpleSelector.equals(simpleSelector); - } - - /** - * Returns the specificity of this selector. - */ - @Override - public int getSpecificity() { - return ((ExtendedSelector) selector).getSpecificity() - + ((ExtendedSelector) simpleSelector).getSpecificity(); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.SiblingSelector#getSelector()}. - */ - @Override - public Selector getSelector() { - return selector; - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.SiblingSelector#getSiblingSelector()}. - */ - @Override - public SimpleSelector getSiblingSelector() { - return simpleSelector; - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSAndConditionImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSAndConditionImpl.java deleted file mode 100644 index b9d528d05ad..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSAndConditionImpl.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - - Copyright 2002, 2015 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import java.util.Set; -import org.w3c.css.sac.Condition; -import org.w3c.dom.Element; - -/** - * This class provides an implementation of the - * {@link org.w3c.css.sac.CombinatorCondition} interface. - */ -public class CSSAndConditionImpl extends AbstractCombinatorCondition { - /** - * Creates a new CombinatorCondition object. - */ - public CSSAndConditionImpl(Condition c1, Condition c2) { - super(c1, c2); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.Condition#getConditionType()}. - */ - @Override - public short getConditionType() { - return SAC_AND_CONDITION; - } - - /** - * Tests whether this condition matches the given element. - */ - @Override - public boolean match(Element e, String pseudoE) { - return ((ExtendedCondition)getFirstCondition()).match(e, pseudoE) && - ((ExtendedCondition)getSecondCondition()).match(e, pseudoE); - } - - /** - * Fills the given set with the attribute names found in this selector. - */ - @Override - public void fillAttributeSet(Set attrSet) { - ((ExtendedCondition)getFirstCondition()).fillAttributeSet(attrSet); - ((ExtendedCondition)getSecondCondition()).fillAttributeSet(attrSet); - } - - /** - * Returns a text representation of this object. - */ - @Override - public String toString() { - return String.valueOf( getFirstCondition() ) + getSecondCondition(); - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSAttributeConditionImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSAttributeConditionImpl.java deleted file mode 100644 index 3467d068938..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSAttributeConditionImpl.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - - Copyright 2002, 2015 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import java.util.Set; -import org.w3c.dom.Element; - -/** - * This class provides an implementation of the - * {@link org.w3c.css.sac.AttributeCondition} interface. - */ -public class CSSAttributeConditionImpl extends AbstractAttributeCondition { - /** - * The attribute's local name. - */ - protected String localName; - - /** - * The attribute's namespace URI. - */ - protected String namespaceURI; - - /** - * Whether this condition applies to specified attributes. - */ - protected boolean specified; - - /** - * Creates a new CSSAttributeCondition object. - */ - public CSSAttributeConditionImpl(String localName, String namespaceURI, - boolean specified, String value) { - super(value); - this.localName = localName; - this.namespaceURI = namespaceURI; - this.specified = specified; - } - - /** - * Indicates whether some other object is "equal to" this one. - * - * @param obj - * the reference object with which to compare. - */ - @Override - public boolean equals(Object obj) { - if (!super.equals(obj)) { - return false; - } - CSSAttributeConditionImpl c = (CSSAttributeConditionImpl) obj; - return (c.namespaceURI.equals(namespaceURI) - && c.localName.equals(localName) && c.specified == specified); - } - - /** - * equal objects should have equal hashCodes. - * - * @return hashCode of this CSSAttributeCondition - */ - @Override - public int hashCode() { - return namespaceURI.hashCode() ^ localName.hashCode() - ^ Boolean.hashCode(specified); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.Condition#getConditionType()}. - */ - @Override - public short getConditionType() { - return SAC_ATTRIBUTE_CONDITION; - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.AttributeCondition#getNamespaceURI()}. - */ - @Override - public String getNamespaceURI() { - return namespaceURI; - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.AttributeCondition#getLocalName()}. - */ - @Override - public String getLocalName() { - return localName; - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.AttributeCondition#getSpecified()}. - */ - @Override - public boolean getSpecified() { - return specified; - } - - /** - * Tests whether this condition matches the given element. - */ - @Override - public boolean match(Element e, String pseudoE) { - if (!e.hasAttribute(getLocalName())) { - return false; - } - String val = getValue(); - if (val == null) { - return true; - } - return e.getAttribute(getLocalName()).equals(val); - } - - /** - * Fills the given set with the attribute names found in this selector. - */ - @Override - public void fillAttributeSet(Set attrSet) { - attrSet.add(localName); - } - - /** - * Returns a text representation of this object. - */ - @Override - public String toString() { - if (value == null) { - return '[' + localName + ']'; - } - return '[' + localName + "=\"" + value + "\"]"; - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSBeginHyphenAttributeConditionImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSBeginHyphenAttributeConditionImpl.java deleted file mode 100644 index 12064bb8e47..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSBeginHyphenAttributeConditionImpl.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - - Copyright 2002, 2014 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import org.w3c.dom.Element; - -/** - * This class provides an implementation of the - * {@link org.w3c.css.sac.AttributeCondition} interface. - */ -public class CSSBeginHyphenAttributeConditionImpl extends - CSSAttributeConditionImpl { - - /** - * Creates a new CSSAttributeCondition object. - */ - public CSSBeginHyphenAttributeConditionImpl(String localName, - String namespaceURI, boolean specified, String value) { - super(localName, namespaceURI, specified, value); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.Condition#getConditionType()}. - */ - @Override - public short getConditionType() { - return SAC_BEGIN_HYPHEN_ATTRIBUTE_CONDITION; - } - - /** - * Tests whether this condition matches the given element. - */ - @Override - public boolean match(Element e, String pseudoE) { - return e.getAttribute(getLocalName()).startsWith(getValue()); - } - - /** - * Returns a text representation of this object. - */ - @Override - public String toString() { - return '[' + getLocalName() + "|=\"" + getValue() + "\"]"; - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSChildSelectorImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSChildSelectorImpl.java deleted file mode 100644 index 450031025f2..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSChildSelectorImpl.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - - Copyright 2002, 2015 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import java.util.Set; -import org.w3c.css.sac.Selector; -import org.w3c.css.sac.SimpleSelector; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -/** - * This class provides an implementation of the - * {@link org.w3c.css.sac.DescendantSelector} interface. - */ -public class CSSChildSelectorImpl extends AbstractDescendantSelector { - - /** - * Creates a new CSSChildSelector object. - */ - public CSSChildSelectorImpl(Selector ancestor, SimpleSelector simple) { - super(ancestor, simple); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.Selector#getSelectorType()}. - */ - @Override - public short getSelectorType() { - return SAC_CHILD_SELECTOR; - } - - /** - * Tests whether this selector matches the given element. - */ - @Override - public boolean match(Element e, Node[] hierarchy, int parentIndex, String pseudoE) { - if (hierarchy == null || parentIndex >= hierarchy.length) { - return false; - } - - Node n = hierarchy[parentIndex]; - if (n != null && n.getNodeType() == Node.ELEMENT_NODE) { - return ((ExtendedSelector) getAncestorSelector()).match((Element) n, hierarchy, parentIndex + 1, null) - && ((ExtendedSelector) getSimpleSelector()).match(e, hierarchy, parentIndex + 1, pseudoE); - } - return false; - } - - /** - * Tests whether this selector matches the given element. - */ - @Override - public boolean match(Element e, String pseudoE) { - Node n = e.getParentNode(); - if (n != null && n.getNodeType() == Node.ELEMENT_NODE) { - return ((ExtendedSelector) getAncestorSelector()).match((Element) n, - null) - && ((ExtendedSelector) getSimpleSelector()).match(e, pseudoE); - } - return false; - } - - /** - * Fills the given set with the attribute names found in this selector. - */ - @Override - public void fillAttributeSet(Set attrSet) { - ((ExtendedSelector)getAncestorSelector()).fillAttributeSet(attrSet); - ((ExtendedSelector)getSimpleSelector()).fillAttributeSet(attrSet); - } - - /** - * Returns a representation of the selector. - */ - @Override - public String toString() { - SimpleSelector s = getSimpleSelector(); - if (s.getSelectorType() == SAC_PSEUDO_ELEMENT_SELECTOR) { - return String.valueOf( getAncestorSelector() ) + s; - } - return getAncestorSelector() + " > " + s; - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSClassConditionImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSClassConditionImpl.java deleted file mode 100644 index 521c7743af1..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSClassConditionImpl.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - - Copyright 2002, 2014 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import org.eclipse.e4.ui.css.core.dom.CSSStylableElement; -import org.w3c.dom.Element; - -/** - * This class provides an implementation of the - * {@link org.w3c.css.sac.AttributeCondition} interface. - */ -public class CSSClassConditionImpl extends CSSAttributeConditionImpl { - - /** - * Creates a new CSSAttributeCondition object. - */ - public CSSClassConditionImpl(String localName, String namespaceURI, - String value) { - super(localName, namespaceURI, true, value); - } - - @Override - public boolean match(Element e, String pseudoE) { - String attr = null; - if ((e instanceof CSSStylableElement)) { - attr = ((CSSStylableElement) e).getCSSClass(); - } else { - attr = e.getAttribute("class"); - } - if (attr == null || attr.length() < 1) { - return false; - } - String val = getValue(); - int attrLen = attr.length(); - int valLen = val.length(); - for (int i = attr.indexOf(val); i != -1; i = attr.indexOf(val, i - + valLen)) { - if ((i == 0 || Character.isSpaceChar(attr.charAt(i - 1))) - && (i + valLen == attrLen || Character.isSpaceChar(attr - .charAt(i + valLen)))) { - return true; - } - } - - return false; - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSConditionFactoryImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSConditionFactoryImpl.java deleted file mode 100644 index 24676814228..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSConditionFactoryImpl.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - - Copyright 2002, 2014 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import org.w3c.css.sac.AttributeCondition; -import org.w3c.css.sac.CSSException; -import org.w3c.css.sac.CombinatorCondition; -import org.w3c.css.sac.Condition; -import org.w3c.css.sac.ConditionFactory; -import org.w3c.css.sac.ContentCondition; -import org.w3c.css.sac.LangCondition; -import org.w3c.css.sac.NegativeCondition; -import org.w3c.css.sac.PositionalCondition; - -/** - * This class provides an implementation of the - * {@link org.w3c.css.sac.ConditionFactory} interface. - */ -public class CSSConditionFactoryImpl implements ConditionFactory { - - private static final String NOT_IMPLEMENTED_IN_CSS2 = "Not implemented in CSS2"; //$NON-NLS-1$ - - /** - * The class attribute namespace URI. - */ - protected String classNamespaceURI; - - /** - * The class attribute local name. - */ - protected String classLocalName; - - /** - * The id attribute namespace URI. - */ - protected String idNamespaceURI; - - /** - * The id attribute local name. - */ - protected String idLocalName; - - /** - * Creates a new condition factory. - */ - public CSSConditionFactoryImpl(String cns, String cln, String idns, - String idln) { - classNamespaceURI = cns; - classLocalName = cln; - idNamespaceURI = idns; - idLocalName = idln; - } - - /** - * SAC: Implements {@link - * ConditionFactory#createAndCondition(Condition,Condition)}. - */ - @Override - public CombinatorCondition createAndCondition(Condition first, - Condition second) throws CSSException { - return new CSSAndConditionImpl(first, second); - } - - /** - * SAC: Implements {@link - * ConditionFactory#createOrCondition(Condition,Condition)}. - */ - @Override - public CombinatorCondition createOrCondition(Condition first, - Condition second) throws CSSException { - throw new CSSException(NOT_IMPLEMENTED_IN_CSS2); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.ConditionFactory#createNegativeCondition(Condition)}. - */ - @Override - public NegativeCondition createNegativeCondition(Condition condition) - throws CSSException { - throw new CSSException(NOT_IMPLEMENTED_IN_CSS2); - } - - /** - * SAC: Implements {@link - * ConditionFactory#createPositionalCondition(int,boolean,boolean)}. - */ - @Override - public PositionalCondition createPositionalCondition(int position, - boolean typeNode, boolean type) throws CSSException { - throw new CSSException(NOT_IMPLEMENTED_IN_CSS2); - } - - /** - * SAC: Implements {@link - * ConditionFactory#createAttributeCondition(String,String,boolean,String)}. - */ - @Override - public AttributeCondition createAttributeCondition(String localName, - String namespaceURI, boolean specified, String value) - throws CSSException { - return new CSSAttributeConditionImpl(localName, namespaceURI, specified, - value); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.ConditionFactory#createIdCondition(String)}. - */ - @Override - public AttributeCondition createIdCondition(String value) - throws CSSException { - return new CSSIdConditionImpl(idNamespaceURI, idLocalName, value); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.ConditionFactory#createLangCondition(String)}. - */ - @Override - public LangCondition createLangCondition(String lang) throws CSSException { - return new CSSLangConditionImpl(lang); - } - - /** - * SAC: Implements {@link - * ConditionFactory#createOneOfAttributeCondition(String,String,boolean,String)}. - */ - @Override - public AttributeCondition createOneOfAttributeCondition(String localName, - String nsURI, boolean specified, String value) throws CSSException { - return new CSSOneOfAttributeConditionImpl(localName, nsURI, specified, - value); - } - - /** - * SAC: Implements {@link - * ConditionFactory#createBeginHyphenAttributeCondition(String,String,boolean,String)}. - */ - @Override - public AttributeCondition createBeginHyphenAttributeCondition( - String localName, String namespaceURI, boolean specified, - String value) throws CSSException { - return new CSSBeginHyphenAttributeConditionImpl(localName, - namespaceURI, specified, value); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.ConditionFactory#createClassCondition(String,String)}. - */ - @Override - public AttributeCondition createClassCondition(String namespaceURI, - String value) throws CSSException { - return new CSSClassConditionImpl(classLocalName, classNamespaceURI, value); - } - - /** - * SAC: Implements {@link - * ConditionFactory#createPseudoClassCondition(String,String)}. - */ - @Override - public AttributeCondition createPseudoClassCondition(String namespaceURI, - String value) throws CSSException { - return new CSSPseudoClassConditionImpl(namespaceURI, value); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.ConditionFactory#createOnlyChildCondition()}. - */ - @Override - public Condition createOnlyChildCondition() throws CSSException { - throw new CSSException(NOT_IMPLEMENTED_IN_CSS2); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.ConditionFactory#createOnlyTypeCondition()}. - */ - @Override - public Condition createOnlyTypeCondition() throws CSSException { - throw new CSSException(NOT_IMPLEMENTED_IN_CSS2); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.ConditionFactory#createContentCondition(String)}. - */ - @Override - public ContentCondition createContentCondition(String data) - throws CSSException { - throw new CSSException(NOT_IMPLEMENTED_IN_CSS2); - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSConditionalSelectorImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSConditionalSelectorImpl.java deleted file mode 100644 index c4cc09ad9c1..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSConditionalSelectorImpl.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - - Copyright 2002, 2015 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import java.util.Set; -import org.w3c.css.sac.Condition; -import org.w3c.css.sac.ConditionalSelector; -import org.w3c.css.sac.SimpleSelector; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -/** - * This class provides an implementation of the - * {@link org.w3c.css.sac.ConditionalSelector} interface. - */ -public class CSSConditionalSelectorImpl implements ConditionalSelector, ExtendedSelector { - - /** - * The simple selector. - */ - protected SimpleSelector simpleSelector; - - /** - * The condition. - */ - protected Condition condition; - - /** - * Creates a new ConditionalSelector object. - */ - public CSSConditionalSelectorImpl(SimpleSelector s, Condition c) { - simpleSelector = s; - condition = c; - } - - /** - * Indicates whether some other object is "equal to" this one. - * @param obj the reference object with which to compare. - */ - @Override - public boolean equals(Object obj) { - if (obj == null || (obj.getClass() != getClass())) { - return false; - } - CSSConditionalSelectorImpl s = (CSSConditionalSelectorImpl)obj; - return (s.simpleSelector.equals(simpleSelector) && - s.condition.equals(condition)); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.Selector#getSelectorType()}. - */ - @Override - public short getSelectorType() { - return SAC_CONDITIONAL_SELECTOR; - } - - @Override - public boolean match(Element e, Node[] hierarchy, int parentIndex, String pseudoE) { - return ((ExtendedSelector)getSimpleSelector()).match(e, hierarchy, parentIndex, pseudoE) && - ((ExtendedCondition)getCondition()).match(e, pseudoE); - } - - /** - * Tests whether this selector matches the given element. - */ - @Override - public boolean match(Element e, String pseudoE) { - return ((ExtendedSelector)getSimpleSelector()).match(e, pseudoE) && - ((ExtendedCondition)getCondition()).match(e, pseudoE); - } - - /** - * Fills the given set with the attribute names found in this selector. - */ - @Override - public void fillAttributeSet(Set attrSet) { - ((ExtendedSelector)getSimpleSelector()).fillAttributeSet(attrSet); - ((ExtendedCondition)getCondition()).fillAttributeSet(attrSet); - } - - /** - * Returns the specificity of this selector. - */ - @Override - public int getSpecificity() { - return ((ExtendedSelector)getSimpleSelector()).getSpecificity() + - ((ExtendedCondition)getCondition()).getSpecificity(); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.ConditionalSelector#getSimpleSelector()}. - */ - @Override - public SimpleSelector getSimpleSelector() { - return simpleSelector; - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.ConditionalSelector#getCondition()}. - */ - @Override - public Condition getCondition() { - return condition; - } - - /** - * Returns a representation of the selector. - */ - @Override - public String toString() { - return String.valueOf( simpleSelector ) + condition; - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSDescendantSelectorImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSDescendantSelectorImpl.java deleted file mode 100644 index 44f3568dfc3..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSDescendantSelectorImpl.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - - Copyright 2002, 2015 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import java.util.Set; -import org.w3c.css.sac.Selector; -import org.w3c.css.sac.SimpleSelector; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -/** - * This class provides an implementation for the - * {@link org.w3c.css.sac.DescendantSelector} interface. - */ -public class CSSDescendantSelectorImpl extends AbstractDescendantSelector { - - /** - * Creates a new CSSDescendantSelector object. - */ - public CSSDescendantSelectorImpl(Selector ancestor, SimpleSelector simple) { - super(ancestor, simple); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.Selector#getSelectorType()}. - */ - @Override - public short getSelectorType() { - return SAC_DESCENDANT_SELECTOR; - } - - /** - * Tests whether this selector matches the given element. - */ - @Override - public boolean match(Element e, Node[] hierarchy, int parentIndex, String pseudoE) { - ExtendedSelector p = (ExtendedSelector) getAncestorSelector(); - if (!((ExtendedSelector) getSimpleSelector()).match(e, hierarchy, parentIndex, pseudoE)) { - return false; - } - - if (hierarchy == null) { - return false; - } - - Node n; - int length = hierarchy.length; - for (int i = parentIndex; i < length; i++) { - n = hierarchy[i]; - if (n != null && n.getNodeType() == Node.ELEMENT_NODE - && p.match((Element) n, hierarchy, i + 1, null)) { - return true; - } - } - return false; - } - - /** - * Tests whether this selector matches the given element. - */ - @Override - public boolean match(Element e, String pseudoE) { - ExtendedSelector p = (ExtendedSelector) getAncestorSelector(); - if (!((ExtendedSelector) getSimpleSelector()).match(e, pseudoE)) { - return false; - } - for (Node n = e.getParentNode(); n != null; n = n.getParentNode()) { - if (n.getNodeType() == Node.ELEMENT_NODE && p.match((Element) n, null)) { - return true; - } - } - return false; - } - - /** - * Fills the given set with the attribute names found in this selector. - */ - @Override - public void fillAttributeSet(Set attrSet) { - ((ExtendedSelector)getSimpleSelector()).fillAttributeSet(attrSet); - } - - /** - * Returns a representation of the selector. - */ - @Override - public String toString() { - return getAncestorSelector() + " " + getSimpleSelector(); - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSDirectAdjacentSelectorImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSDirectAdjacentSelectorImpl.java deleted file mode 100644 index 73eea3eb684..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSDirectAdjacentSelectorImpl.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - - Copyright 2002, 2015 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import java.util.Set; -import org.w3c.css.sac.Selector; -import org.w3c.css.sac.SimpleSelector; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -/** - * This class provides an implementation for the - * {@link org.w3c.css.sac.DescendantSelector} interface. - */ -public class CSSDirectAdjacentSelectorImpl extends AbstractSiblingSelector { - - /** - * Creates a new CSSDirectAdjacentSelector object. - */ - public CSSDirectAdjacentSelectorImpl(short type, Selector parent, SimpleSelector simple) { - super(type, parent, simple); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.Selector#getSelectorType()}. - */ - @Override - public short getSelectorType() { - return SAC_DIRECT_ADJACENT_SELECTOR; - } - - /** - * Tests whether this selector matches the given element. - */ - @Override - public boolean match(Element e, Node[] hiearchy, int parentIndex, String pseudoE) { - Node n = e; - if (!((ExtendedSelector) getSiblingSelector()).match(e, hiearchy, parentIndex, pseudoE)) { - return false; - } - - while ((n = n.getPreviousSibling()) != null && n.getNodeType() != Node.ELEMENT_NODE) { - } - - if (n == null) { - return false; - } - - return ((ExtendedSelector) getSelector()).match((Element) n, hiearchy, parentIndex, null); - } - - /** - * Tests whether this selector matches the given element. - */ - @Override - public boolean match(Element e, String pseudoE) { - Node n = e; - if (!((ExtendedSelector)getSiblingSelector()).match(e, pseudoE)) { - return false; - } - while ((n = n.getPreviousSibling()) != null && n.getNodeType() != Node.ELEMENT_NODE) { - } - - if (n == null) { - return false; - } - - return ((ExtendedSelector)getSelector()).match((Element)n, null); - } - - /** - * Fills the given set with the attribute names found in this selector. - */ - @Override - public void fillAttributeSet(Set attrSet) { - ((ExtendedSelector)getSelector()).fillAttributeSet(attrSet); - ((ExtendedSelector)getSiblingSelector()).fillAttributeSet(attrSet); - } - - /** - * Returns a representation of the selector. - */ - @Override - public String toString() { - return getSelector() + " + " + getSiblingSelector(); - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSDocumentHandlerImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSDocumentHandlerImpl.java index 150c95c4b7a..9456b6a94e2 100644 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSDocumentHandlerImpl.java +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSDocumentHandlerImpl.java @@ -25,6 +25,7 @@ import org.eclipse.e4.ui.css.core.impl.dom.CSSUnknownRuleImpl; import org.eclipse.e4.ui.css.core.impl.dom.CSSValueFactory; import org.eclipse.e4.ui.css.core.impl.dom.MediaListImpl; +import org.eclipse.e4.ui.css.core.impl.engine.selector.SacTranslator; import org.eclipse.e4.ui.css.core.sac.ExtendedDocumentHandler; import org.w3c.css.sac.CSSException; import org.w3c.css.sac.InputSource; @@ -165,9 +166,10 @@ public void endFontFace() throws CSSException { @Override public void startSelector(SelectorList selectors) throws CSSException { - // Create the style rule and add it to the rule list + // Translate the SAC selector list into the engine's internal AST at + // this boundary so nothing downstream needs to touch SAC types. CSSStyleRuleImpl rule = new CSSStyleRuleImpl(parentStyleSheet, null, - selectors); + SacTranslator.translate(selectors)); if (!getNodeStack().empty()) { ((CSSRuleListImpl) getNodeStack().peek()).add(rule); } diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSElementSelectorImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSElementSelectorImpl.java deleted file mode 100644 index f24e1aeea7f..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSElementSelectorImpl.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - - Copyright 2002, 2014 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import org.w3c.dom.Element; - -/** - * This class implements the {@link org.w3c.css.sac.ElementSelector} interface. - */ -public class CSSElementSelectorImpl extends AbstractElementSelector { - - /** - * Creates a new ElementSelector object. - */ - public CSSElementSelectorImpl(String uri, String name) { - super(uri, name); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.Selector#getSelectorType()}. - */ - @Override - public short getSelectorType() { - return SAC_ELEMENT_NODE_SELECTOR; - } - - /** - * Tests whether this selector matches the given element. - */ - @Override - public boolean match(Element e, String pseudoE) { - String name = getLocalName(); - if (name == null) { - if (namespaceURI != null) { - return namespaceURI.equals(e.getNamespaceURI()); - } else { - return true; - } - } - String eName; - if (e.getPrefix() == null) { - eName = e.getNodeName(); - } else { - eName = e.getLocalName(); - } - // According to CSS 2 section 5.1 element - // names in selectors are case-sensitive for XML. - if (eName.equals(name)) { - if (namespaceURI != null) { - return namespaceURI.equals(e.getNamespaceURI()); - } else { - return true; - } - } - return false; - // For HTML - // return eName.equalsIgnoreCase(name); - } - - /** - * Returns the specificity of this selector. - */ - @Override - public int getSpecificity() { - return (getLocalName() == null) ? 0 : 1; - } - - /** - * Returns a representation of the selector. - */ - @Override - public String toString() { - String name = getLocalName(); - if (name == null) { - return "*"; - } - return name; - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSIdConditionImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSIdConditionImpl.java deleted file mode 100644 index eec162ea03d..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSIdConditionImpl.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - - Copyright 2002, 2015 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import java.util.Set; -import org.eclipse.e4.ui.css.core.dom.CSSStylableElement; -import org.w3c.dom.Element; - -/** - * This class provides an implementation of the - * {@link org.w3c.css.sac.AttributeCondition} interface. - */ -public class CSSIdConditionImpl extends AbstractAttributeCondition { - - /** - * The id attribute namespace URI. - */ - protected String namespaceURI; - - /** - * The id attribute local name. - */ - protected String localName; - - /** - * Creates a new CSSAttributeCondition object. - */ - public CSSIdConditionImpl(String ns, String ln, String value) { - super(value); - namespaceURI = ns; - localName = ln; - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.Condition#getConditionType()}. - */ - @Override - public short getConditionType() { - return SAC_ID_CONDITION; - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.AttributeCondition#getNamespaceURI()}. - */ - @Override - public String getNamespaceURI() { - return namespaceURI; - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.AttributeCondition#getLocalName()}. - */ - @Override - public String getLocalName() { - return localName; - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.AttributeCondition#getSpecified()}. - */ - @Override - public boolean getSpecified() { - return true; - } - - /** - * Tests whether this condition matches the given element. - */ - @Override - public boolean match(Element e, String pseudoE) { - String id = null; - if (e instanceof CSSStylableElement) { - id = ((CSSStylableElement) e).getCSSId(); - } else { - id = e.getAttribute("id"); - } - if (id == null) { - return false; - } - return id.equals(getValue()); - // return super.match(e, pseudoE); - } - - /** - * Fills the given set with the attribute names found in this selector. - */ - @Override - public void fillAttributeSet(Set attrSet) { - attrSet.add(localName); - } - - /** - * Returns the specificity of this condition. - */ - @Override - public int getSpecificity() { - return 1 << 16; - } - - /** - * Returns a text representation of this object. - */ - @Override - public String toString() { - return '#' + getValue(); - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSLangConditionImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSLangConditionImpl.java deleted file mode 100644 index 4789469f7fa..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSLangConditionImpl.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - - Copyright 2002, 2015 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import java.util.Set; -import org.w3c.css.sac.LangCondition; -import org.w3c.dom.Element; - -/** - * This class provides an implementation of the - * {@link org.w3c.css.sac.LangCondition} interface. - */ -public class CSSLangConditionImpl implements LangCondition, ExtendedCondition { - /** - * The language. - */ - protected String lang; - - /** - * The language with a hyphen suffixed. - */ - protected String langHyphen; - - /** - * Creates a new LangCondition object. - */ - public CSSLangConditionImpl(String lang) { - this.lang = lang.toLowerCase(); - this.langHyphen = lang + '-'; - } - - /** - * Indicates whether some other object is "equal to" this one. - * @param obj the reference object with which to compare. - */ - @Override - public boolean equals(Object obj) { - if (obj == null || (obj.getClass() != getClass())) { - return false; - } - CSSLangConditionImpl c = (CSSLangConditionImpl)obj; - return c.lang.equals(lang); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.Condition#getConditionType()}. - */ - @Override - public short getConditionType() { - return SAC_LANG_CONDITION; - } - - /** - * SAC: Implements {@link org.w3c.css.sac.LangCondition#getLang()}. - */ - @Override - public String getLang() { - return lang; - } - - /** - * Returns the specificity of this condition. - */ - @Override - public int getSpecificity() { - return 1 << 8; - } - - /** - * Tests whether this condition matches the given element. - */ - @Override - public boolean match(Element e, String pseudoE) { - String s = e.getAttribute("lang").toLowerCase(); - if (s.equals(lang) || s.startsWith(langHyphen)) { - return true; - } - // s = e.getAttributeNS(XMLConstants.XML_NAMESPACE_URI, - // XMLConstants.XML_LANG_ATTRIBUTE).toLowerCase(); - return s.equals(lang) || s.startsWith(langHyphen); - } - - /** - * Fills the given set with the attribute names found in this selector. - */ - @Override - public void fillAttributeSet(Set attrSet) { - attrSet.add("lang"); - } - - /** - * Returns a text representation of this object. - */ - @Override - public String toString() { - return ":lang(" + lang + ')'; - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSOneOfAttributeConditionImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSOneOfAttributeConditionImpl.java deleted file mode 100644 index fb4ebbd0428..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSOneOfAttributeConditionImpl.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - - Copyright 2002, 2015 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/******************************************************************************* - * Contributors: - * This class was copied from org.apache.batik.css.engine.sac - * Apache Batik project - initial API and implementation - * Alain Le Guennec - Bug 458334 - *******************************************************************************/ -package org.eclipse.e4.ui.css.core.impl.sac; - -import java.util.StringTokenizer; -import org.w3c.dom.Element; - -/** - * This class provides an implementation of the - * {@link org.w3c.css.sac.AttributeCondition} interface. - */ -public class CSSOneOfAttributeConditionImpl extends CSSAttributeConditionImpl { - /** - * Creates a new CSSAttributeCondition object. - */ - public CSSOneOfAttributeConditionImpl(String localName, - String namespaceURI, boolean specified, String value) { - super(localName, namespaceURI, specified, value); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.Condition#getConditionType()}. - */ - @Override - public short getConditionType() { - return SAC_ONE_OF_ATTRIBUTE_CONDITION; - } - - /** - * Tests whether this condition matches the given element. - */ - @Override - public boolean match(Element e, String pseudoE) { - String attr = e.getAttribute(getLocalName()); - String val = getValue(); - for (StringTokenizer tok = new StringTokenizer(attr); tok.hasMoreElements();) { - String candidate = tok.nextToken(); - if (val.equals(candidate)) { - return true; - } - } - return false; - } - - /** - * Returns a text representation of this object. - */ - @Override - public String toString() { - return "[" + getLocalName() + "~=\"" + getValue() + "\"]"; - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSPseudoClassConditionImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSPseudoClassConditionImpl.java deleted file mode 100644 index 1c459d6637b..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSPseudoClassConditionImpl.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - - Copyright 2002, 2015 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import java.util.Set; -import org.eclipse.e4.ui.css.core.dom.CSSStylableElement; -import org.w3c.dom.Element; - -/** - * This class provides an implementation of the - * {@link org.w3c.css.sac.AttributeCondition} interface. - */ -public class CSSPseudoClassConditionImpl extends AbstractAttributeCondition { - /** - * The namespaceURI. - */ - protected String namespaceURI; - - /** - * Creates a new CSSAttributeCondition object. - */ - public CSSPseudoClassConditionImpl(String namespaceURI, String value) { - super(value); - this.namespaceURI = namespaceURI; - } - - /** - * Indicates whether some other object is "equal to" this one. - * - * @param obj - * the reference object with which to compare. - */ - @Override - public boolean equals(Object obj) { - if (!super.equals(obj)) { - return false; - } - CSSPseudoClassConditionImpl c = (CSSPseudoClassConditionImpl) obj; - return c.namespaceURI.equals(namespaceURI); - } - - /** - * equal objects should have equal hashCodes. - * - * @return hashCode of this CSSPseudoClassCondition - */ - @Override - public int hashCode() { - return namespaceURI.hashCode(); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.Condition#getConditionType()}. - */ - @Override - public short getConditionType() { - return SAC_PSEUDO_CLASS_CONDITION; - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.AttributeCondition#getNamespaceURI()}. - */ - @Override - public String getNamespaceURI() { - return namespaceURI; - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.AttributeCondition#getLocalName()}. - */ - @Override - public String getLocalName() { - return null; - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.AttributeCondition#getSpecified()}. - */ - @Override - public boolean getSpecified() { - return false; - } - - /** - * Tests whether this selector matches the given element. - */ - @Override - public boolean match(Element e, String pseudoE) { - if (pseudoE != null && !pseudoE.equals(getValue())) { - // pseudo instance is filled, it is not valid. - return false; - } - if (!(e instanceof CSSStylableElement element)) { - return false; - } - boolean isPseudoInstanceOf = element.isPseudoInstanceOf(getValue()); - if (!isPseudoInstanceOf) { - return false; - } - if (pseudoE == null) { - // pseudo element is not filled. - // test if this CSSPseudoClassCondition is NOT a static pseudo - // instance - return (!element.isStaticPseudoInstance(getValue())); - } - return true; - } - - /** - * Fills the given set with the attribute names found in this selector. - */ - @Override - public void fillAttributeSet(Set attrSet) { - } - - /** - * Returns a text representation of this object. - */ - @Override - public String toString() { - return ":" + getValue(); - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSPseudoElementSelectorImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSPseudoElementSelectorImpl.java deleted file mode 100644 index 5353e2c4885..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSPseudoElementSelectorImpl.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - - Copyright 2002, 2014 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import org.w3c.dom.Element; - -/** - * This class implements the {@link org.w3c.css.sac.ElementSelector} interface. - */ -public class CSSPseudoElementSelectorImpl extends AbstractElementSelector { - - /** - * Creates a new CSSPseudoElementSelector object. - */ - public CSSPseudoElementSelectorImpl(String uri, String name) { - super(uri, name); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.Selector#getSelectorType()}. - */ - @Override - public short getSelectorType() { - return SAC_PSEUDO_ELEMENT_SELECTOR; - } - - /** - * Tests whether this selector matches the given element. - */ - @Override - public boolean match(Element e, String pseudoE) { - return getLocalName().equalsIgnoreCase(pseudoE); - } - - /** - * Returns the specificity of this selector. - */ - @Override - public int getSpecificity() { - return 0; - } - - /** - * Returns a representation of the selector. - */ - @Override - public String toString() { - return ":" + getLocalName(); - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSSelectorFactoryImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSSelectorFactoryImpl.java deleted file mode 100644 index 00370115317..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSSelectorFactoryImpl.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - - Copyright 2002, 2014 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import org.w3c.css.sac.CSSException; -import org.w3c.css.sac.CharacterDataSelector; -import org.w3c.css.sac.Condition; -import org.w3c.css.sac.ConditionalSelector; -import org.w3c.css.sac.DescendantSelector; -import org.w3c.css.sac.ElementSelector; -import org.w3c.css.sac.NegativeSelector; -import org.w3c.css.sac.ProcessingInstructionSelector; -import org.w3c.css.sac.Selector; -import org.w3c.css.sac.SelectorFactory; -import org.w3c.css.sac.SiblingSelector; -import org.w3c.css.sac.SimpleSelector; - -/** - * This class implements the {@link org.w3c.css.sac.SelectorFactory} interface. - */ -public class CSSSelectorFactoryImpl implements SelectorFactory { - - private static final String NOT_IMPLEMENTED_IN_CSS2 = "Not implemented in CSS2"; //$NON-NLS-1$ - - /** - * The instance of this class. - */ - public static final SelectorFactory INSTANCE = new CSSSelectorFactoryImpl(); - - /** - * This class does not need to be instantiated. - */ - protected CSSSelectorFactoryImpl() { - } - - /** - * SAC: Implements {@link - * SelectorFactory#createConditionalSelector(SimpleSelector,Condition)}. - */ - @Override - public ConditionalSelector createConditionalSelector( - SimpleSelector selector, Condition condition) throws CSSException { - return new CSSConditionalSelectorImpl(selector, condition); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.SelectorFactory#createAnyNodeSelector()}. - */ - @Override - public SimpleSelector createAnyNodeSelector() throws CSSException { - throw new CSSException(NOT_IMPLEMENTED_IN_CSS2); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.SelectorFactory#createRootNodeSelector()}. - */ - @Override - public SimpleSelector createRootNodeSelector() throws CSSException { - throw new CSSException(NOT_IMPLEMENTED_IN_CSS2); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.SelectorFactory#createNegativeSelector(SimpleSelector)}. - */ - @Override - public NegativeSelector createNegativeSelector(SimpleSelector selector) - throws CSSException { - throw new CSSException(NOT_IMPLEMENTED_IN_CSS2); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.SelectorFactory#createElementSelector(String,String)}. - */ - @Override - public ElementSelector createElementSelector(String namespaceURI, - String tagName) throws CSSException { - return new CSSElementSelectorImpl(namespaceURI, tagName); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.SelectorFactory#createTextNodeSelector(String)}. - */ - @Override - public CharacterDataSelector createTextNodeSelector(String data) - throws CSSException { - throw new CSSException(NOT_IMPLEMENTED_IN_CSS2); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.SelectorFactory#createCDataSectionSelector(String)}. - */ - @Override - public CharacterDataSelector createCDataSectionSelector(String data) - throws CSSException { - throw new CSSException(NOT_IMPLEMENTED_IN_CSS2); - } - - /** - * SAC: Implements {@link - * SelectorFactory#createProcessingInstructionSelector(String,String)}. - */ - @Override - public ProcessingInstructionSelector createProcessingInstructionSelector( - String target, String data) throws CSSException { - throw new CSSException(NOT_IMPLEMENTED_IN_CSS2); - } - - /** - * SAC: Implements {@link - * org.w3c.css.sac.SelectorFactory#createCommentSelector(String)}. - */ - @Override - public CharacterDataSelector createCommentSelector(String data) - throws CSSException { - throw new CSSException(NOT_IMPLEMENTED_IN_CSS2); - } - - /** - * SAC: Implements {@link - * SelectorFactory#createPseudoElementSelector(String,String)}. - */ - @Override - public ElementSelector createPseudoElementSelector(String namespaceURI, - String pseudoName) throws CSSException { - return new CSSPseudoElementSelectorImpl(namespaceURI, pseudoName); - } - - /** - * SAC: Implements {@link - * SelectorFactory#createDescendantSelector(Selector,SimpleSelector)}. - */ - @Override - public DescendantSelector createDescendantSelector(Selector parent, - SimpleSelector descendant) throws CSSException { - return new CSSDescendantSelectorImpl(parent, descendant); - } - - /** - * SAC: Implements {@link - * SelectorFactory#createChildSelector(Selector,SimpleSelector)}. - */ - @Override - public DescendantSelector createChildSelector(Selector parent, - SimpleSelector child) throws CSSException { - return new CSSChildSelectorImpl(parent, child); - } - - /** - * SAC: Implements {@link - * SelectorFactory#createDirectAdjacentSelector(short,Selector,SimpleSelector)}. - */ - @Override - public SiblingSelector createDirectAdjacentSelector(short nodeType, - Selector child, SimpleSelector directAdjacent) throws CSSException { - return new CSSDirectAdjacentSelectorImpl(nodeType, child, - directAdjacent); - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/ExtendedCondition.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/ExtendedCondition.java deleted file mode 100644 index edc785b6760..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/ExtendedCondition.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - - Copyright 2002, 2015 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import java.util.Set; -import org.w3c.css.sac.Condition; -import org.w3c.dom.Element; - -/** - * This interface provides additional features to the - * {@link org.w3c.css.sac.Condition} interface. - */ -public interface ExtendedCondition extends Condition { - - /** - * Tests whether this condition matches the given element. - */ - boolean match(Element e, String pseudoE); - - /** - * Returns the specificity of this condition. - */ - int getSpecificity(); - - /** - * Fills the given set with the attribute names found in this selector. - */ - void fillAttributeSet(Set attrSet); -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/ExtendedSelector.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/ExtendedSelector.java deleted file mode 100644 index b30db5d0db1..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/ExtendedSelector.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - - Copyright 2002, 2015 The Apache Software Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -/* This class copied from org.apache.batik.css.engine.sac */ - -package org.eclipse.e4.ui.css.core.impl.sac; - -import java.util.Set; -import org.w3c.css.sac.Selector; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -/** - * This interface extends the {@link org.w3c.css.sac.Selector}. - */ -public interface ExtendedSelector extends Selector { - - default boolean match(Element e, Node[] ancestors, int parentIndex, String pseudoE) { - return match(e, pseudoE); - } - - /** - * Tests whether this selector matches the given element. - */ - boolean match(Element e, String pseudoE); - - /** - * Returns the specificity of this selector. - */ - int getSpecificity(); - - /** - * Fills the given set with the attribute names found in this selector. - */ - void fillAttributeSet(Set attrSet); -} diff --git a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/engine/CSSSWTEngineImpl.java b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/engine/CSSSWTEngineImpl.java index 8bfc22e01c4..7adcce44634 100644 --- a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/engine/CSSSWTEngineImpl.java +++ b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/engine/CSSSWTEngineImpl.java @@ -139,17 +139,22 @@ private boolean isApplicableToReset(WidgetElement element) { @Override public void reapply() { - Shell[] shells = display.getShells(); - for (Shell s : shells) { - try { - s.setRedraw(false); - s.reskin(SWT.ALL); - applyStyles(s, true); - } catch (Exception e) { - ILog.of(getClass()).error(e.getMessage(), e); - } finally { - s.setRedraw(true); + startStylingSession(); + try { + Shell[] shells = display.getShells(); + for (Shell s : shells) { + try { + s.setRedraw(false); + s.reskin(SWT.ALL); + applyStyles(s, true); + } catch (Exception e) { + ILog.of(getClass()).error(e.getMessage(), e); + } finally { + s.setRedraw(true); + } } + } finally { + stopStylingSession(); } } } diff --git a/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/selector/SelectorMatcherTest.java b/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/selector/SelectorMatcherTest.java new file mode 100644 index 00000000000..31162dd52c7 --- /dev/null +++ b/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/selector/SelectorMatcherTest.java @@ -0,0 +1,223 @@ +/******************************************************************************* + * Copyright (c) 2026 Lars Vogel and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Lars Vogel - initial API and implementation + *******************************************************************************/ +package org.eclipse.e4.ui.css.core.impl.engine.selector; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; + +import org.eclipse.e4.ui.css.core.impl.engine.CSSEngineImpl; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.Adjacent; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.And; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.AttributeBeginHyphen; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.AttributeIncludes; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.AttributeSelector; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.Child; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.ClassSelector; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.Descendant; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.ElementType; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.IdSelector; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.PseudoClass; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.SelectorList; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.Universal; +import org.eclipse.e4.ui.tests.css.core.util.TestElement; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link SelectorMatcher}. The cases mirror those in the + * Phase 1 {@code CSSEngineTest}, but go through the new internal selector + * AST instead of SAC. When Phase 3 Step 1 wires the engine to use this + * matcher, the SAC-based duplicate tests can be retired. + */ +class SelectorMatcherTest { + + private static final TestCSSEngine ENGINE = new TestCSSEngine(); + + private static final class TestCSSEngine extends CSSEngineImpl { + @Override + public void reapply() { + } + } + + private static TestElement element(String tag, String cssClass, String id) { + TestElement e = new TestElement(tag, ENGINE); + if (cssClass != null) { + e.setClass(cssClass); + } + if (id != null) { + e.setId(id); + } + return e; + } + + @Test + void universalMatchesAnything() { + assertTrue(SelectorMatcher.matches(new Universal(), element("Button", null, null), null)); + assertTrue(SelectorMatcher.matches(new Universal(), element("Label", "x", "y"), null)); + } + + @Test + void typeSelectorIsCaseSensitive() { + assertTrue(SelectorMatcher.matches(new ElementType("Button"), element("Button", null, null), null)); + assertFalse(SelectorMatcher.matches(new ElementType("Button"), element("button", null, null), null)); + assertFalse(SelectorMatcher.matches(new ElementType("Button"), element("Label", null, null), null)); + } + + @Test + void classSelector() { + assertTrue(SelectorMatcher.matches(new ClassSelector("foo"), element("Button", "foo", null), null)); + assertFalse(SelectorMatcher.matches(new ClassSelector("foo"), element("Button", "bar", null), null)); + assertFalse(SelectorMatcher.matches(new ClassSelector("foo"), element("Button", null, null), null)); + } + + @Test + void classSelectorMatchesOneOfMultipleClasses() { + assertTrue(SelectorMatcher.matches(new ClassSelector("foo"), element("Button", "foo bar", null), null)); + assertTrue(SelectorMatcher.matches(new ClassSelector("bar"), element("Button", "foo bar", null), null)); + assertFalse(SelectorMatcher.matches(new ClassSelector("baz"), element("Button", "foo bar", null), null)); + } + + @Test + void idSelector() { + assertTrue(SelectorMatcher.matches(new IdSelector("go"), element("Button", null, "go"), null)); + assertFalse(SelectorMatcher.matches(new IdSelector("go"), element("Button", null, "stop"), null)); + assertFalse(SelectorMatcher.matches(new IdSelector("go"), element("Button", null, null), null)); + } + + @Test + void compoundSelector() { + Selectors.Selector selector = new And(new And(new ElementType("Button"), new ClassSelector("primary")), + new IdSelector("go")); + assertTrue(SelectorMatcher.matches(selector, element("Button", "primary", "go"), null)); + assertFalse(SelectorMatcher.matches(selector, element("Label", "primary", "go"), null)); + assertFalse(SelectorMatcher.matches(selector, element("Button", "secondary", "go"), null)); + assertFalse(SelectorMatcher.matches(selector, element("Button", "primary", "stop"), null)); + } + + @Test + void descendantCombinator() { + Selectors.Selector selector = new Descendant(new ElementType("Composite"), new ElementType("Button")); + TestElement composite = element("Composite", null, null); + TestElement intermediate = new TestElement("Group", composite, ENGINE); + TestElement button = new TestElement("Button", intermediate, ENGINE); + assertTrue(SelectorMatcher.matches(selector, button, null)); + + TestElement orphan = element("Button", null, null); + assertFalse(SelectorMatcher.matches(selector, orphan, null)); + } + + @Test + void childCombinator() { + Selectors.Selector selector = new Child(new ElementType("Composite"), new ElementType("Button")); + TestElement composite = element("Composite", null, null); + TestElement direct = new TestElement("Button", composite, ENGINE); + assertTrue(SelectorMatcher.matches(selector, direct, null)); + + TestElement intermediate = new TestElement("Group", composite, ENGINE); + TestElement grandchild = new TestElement("Button", intermediate, ENGINE); + assertFalse(SelectorMatcher.matches(selector, grandchild, null)); + } + + @Test + void attributePresentMatchesEvenWithEmptyValue() { + AttributeSelector selector = new AttributeSelector("style", null); + TestElement withAttr = element("Button", null, null); + withAttr.setAttribute("style", "SWT.PUSH"); + assertTrue(SelectorMatcher.matches(selector, withAttr, null)); + assertFalse(SelectorMatcher.matches(selector, element("Button", null, null), null)); + } + + @Test + void attributeIncludesIsWordBoundaryMatch() { + AttributeIncludes selector = new AttributeIncludes("style", "SWT.CHECK"); + TestElement match = element("Button", null, null); + match.setAttribute("style", "SWT.CHECK SWT.BORDER"); + assertTrue(SelectorMatcher.matches(selector, match, null)); + + TestElement substring = element("Button", null, null); + substring.setAttribute("style", "SWT.CHECK_DELAYED"); + // 'CHECK' is a substring of 'CHECK_DELAYED' but not a whitespace-separated word. + assertFalse(SelectorMatcher.matches(new AttributeIncludes("style", "CHECK"), substring, null)); + } + + @Test + void attributeBeginHyphen() { + AttributeBeginHyphen selector = new AttributeBeginHyphen("lang", "en"); + TestElement exact = element("p", null, null); + exact.setAttribute("lang", "en"); + TestElement prefixed = element("p", null, null); + prefixed.setAttribute("lang", "en-US"); + TestElement other = element("p", null, null); + other.setAttribute("lang", "fr"); + assertTrue(SelectorMatcher.matches(selector, exact, null)); + assertTrue(SelectorMatcher.matches(selector, prefixed, null)); + assertFalse(SelectorMatcher.matches(selector, other, null)); + } + + @Test + void pseudoClassMatchesViaIsPseudoInstanceOf() { + PseudoClass selector = new PseudoClass("selected"); + TestElement on = new TestElement("Button", ENGINE) { + @Override + public boolean isPseudoInstanceOf(String s) { + return "selected".equals(s); + } + }; + TestElement off = element("Button", null, null); + assertTrue(SelectorMatcher.matches(selector, on, null)); + assertFalse(SelectorMatcher.matches(selector, off, null)); + } + + @Test + void selectorListMatchesAnyAlternative() { + SelectorList list = new SelectorList(List.of(new ClassSelector("a"), new ClassSelector("b"))); + assertTrue(SelectorMatcher.matches(list, element("Button", "a", null), null)); + assertTrue(SelectorMatcher.matches(list, element("Button", "b", null), null)); + assertFalse(SelectorMatcher.matches(list, element("Button", "c", null), null)); + } + + @Test + void specificityMatchesCss21() { + assertEquals(0, new Universal().specificity()); + assertEquals(1, new ElementType("Button").specificity()); + assertEquals(10, new ClassSelector("foo").specificity()); + assertEquals(100, new IdSelector("go").specificity()); + assertEquals(11, new And(new ElementType("Button"), new ClassSelector("primary")).specificity()); + assertEquals(111, new And(new And(new ElementType("Button"), new ClassSelector("primary")), + new IdSelector("go")).specificity()); + } + + @Test + void specificityOfSelectorListIsMaxOverAlternatives() { + // "Button, .foo, #go": max specificity is the id (100). + SelectorList list = new SelectorList( + List.of(new ElementType("Button"), new ClassSelector("foo"), new IdSelector("go"))); + assertEquals(100, list.specificity()); + } + + @Test + void adjacentSiblingCombinatorRequiresSiblingSupport() { + // TestElement's ElementAdapter base returns null from getPreviousSibling, + // so adjacent matching cannot succeed against it. Locks in that the + // matcher returns false rather than throwing on elements without + // sibling support. + Adjacent selector = new Adjacent(new ElementType("Label"), new ElementType("Button")); + TestElement parent = element("Composite", null, null); + new TestElement("Label", parent, ENGINE); + TestElement second = new TestElement("Button", parent, ENGINE); + assertFalse(SelectorMatcher.matches(selector, second, null)); + } +} diff --git a/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/CSSEngineTest.java b/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/CSSEngineTest.java index fa16ea5395e..31916477fd6 100644 --- a/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/CSSEngineTest.java +++ b/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/CSSEngineTest.java @@ -23,10 +23,10 @@ import org.eclipse.e4.ui.css.core.engine.CSSEngine; import org.eclipse.e4.ui.css.core.impl.engine.CSSEngineImpl; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors.Selector; import org.eclipse.e4.ui.tests.css.core.util.TestElement; import org.junit.jupiter.api.Test; -import org.w3c.css.sac.Selector; -import org.w3c.css.sac.SelectorList; import org.w3c.dom.Element; class CSSEngineTest { @@ -55,7 +55,7 @@ private static Selector parse(CSSEngine engine, String selector) throws Exceptio @Test void testSelectorMatch() throws Exception { TestCSSEngine engine = new TestCSSEngine(); - SelectorList list = engine.parseSelectors("Date"); + Selectors.SelectorList list = engine.parseSelectors("Date"); engine.setElementProvider((element, engine1) -> new TestElement(element.getClass().getSimpleName(), engine1)); assertFalse(engine.matches(list.item(0), new Object(), null)); @@ -249,7 +249,7 @@ void testNegativeMatch() throws Exception { @Test void testSelectorListMatch() throws Exception { TestCSSEngine engine = new TestCSSEngine(); - SelectorList list = engine.parseSelectors(".a, .b"); + Selectors.SelectorList list = engine.parseSelectors(".a, .b"); TestElement a = createElement(engine, "Button", "a", null); TestElement b = createElement(engine, "Button", "b", null); TestElement c = createElement(engine, "Button", "c", null); @@ -274,9 +274,9 @@ void testTagNameCaseSensitivity() throws Exception { assertFalse(engine.matches(lower, capitalElement, null)); } - private static boolean matchesAny(CSSEngine engine, SelectorList list, Element element) { - for (int i = 0; i < list.getLength(); i++) { - if (engine.matches(list.item(i), element, null)) { + private static boolean matchesAny(CSSEngine engine, Selectors.SelectorList list, Element element) { + for (Selector selector : list.alternatives()) { + if (engine.matches(selector, element, null)) { return true; } } diff --git a/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/parser/SelectorTest.java b/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/parser/SelectorTest.java index f3419a83eb0..43a3be1899b 100644 --- a/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/parser/SelectorTest.java +++ b/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/parser/SelectorTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013, 2014 IBM Corporation and others. + * Copyright (c) 2013, 2026 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -21,11 +21,11 @@ import java.io.IOException; import org.eclipse.e4.ui.css.core.engine.CSSEngine; +import org.eclipse.e4.ui.css.core.impl.engine.selector.Selectors; import org.eclipse.e4.ui.tests.css.core.util.ParserTestUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.w3c.css.sac.CSSParseException; -import org.w3c.css.sac.SelectorList; public class SelectorTest { private CSSEngine engine; @@ -37,35 +37,37 @@ public void setUp() throws Exception { @Test void testSimpleSelector() throws Exception { - SelectorList list = engine.parseSelectors("Type1"); + Selectors.SelectorList list = engine.parseSelectors("Type1"); assertNotNull(list); assertEquals(1, list.getLength()); - assertEquals("Type1", list.item(0).toString()); + assertEquals("Type1", list.item(0).text()); } @Test void testMultipleSelectors() throws Exception { - SelectorList list = engine.parseSelectors("Type1, Type2"); + Selectors.SelectorList list = engine.parseSelectors("Type1, Type2"); assertNotNull(list); assertEquals(2, list.getLength()); - assertEquals("Type1", list.item(0).toString()); - assertEquals("Type2", list.item(1).toString()); + assertEquals("Type1", list.item(0).text()); + assertEquals("Type2", list.item(1).text()); } @Test void testClassSelector() throws Exception { - SelectorList list = engine.parseSelectors(".Class1"); + Selectors.SelectorList list = engine.parseSelectors(".Class1"); assertNotNull(list); assertEquals(1, list.getLength()); - assertEquals("*[class=\"Class1\"]", list.item(0).toString()); + assertEquals(".Class1", list.item(0).text()); } @Test void testAttributeSelector() throws Exception { - SelectorList list = engine.parseSelectors("*[class='Class1']"); + Selectors.SelectorList list = engine.parseSelectors("*[class='Class1']"); assertNotNull(list); assertEquals(1, list.getLength()); - assertEquals("*[class=\"Class1\"]", list.item(0).toString()); + // The Universal selector ('*') is folded away since the AttributeSelector + // alone carries the full match condition. + assertEquals("[class='Class1']", list.item(0).text()); } @Test