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 27dfb2acb81..d304dc96b6c 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 @@ -8,7 +8,6 @@ Bundle-Version: 0.14.800.qualifier Export-Package: org.eclipse.e4.ui.css.core;x-internal:=true, org.eclipse.e4.ui.css.core.css2;x-friends:="org.eclipse.e4.ui.css.swt.theme,org.eclipse.e4.ui.css.swt,org.eclipse.e4.ui.css.jface", org.eclipse.e4.ui.css.core.dom;x-friends:="org.eclipse.e4.ui.css.swt,org.eclipse.ui.views.properties.tabbed,org.eclipse.ui.forms", - org.eclipse.e4.ui.css.core.dom.parsers;x-internal:=true, org.eclipse.e4.ui.css.core.dom.properties; x-friends:="org.eclipse.e4.ui.css.swt, org.eclipse.ui.workbench, @@ -28,20 +27,16 @@ Export-Package: org.eclipse.e4.ui.css.core;x-internal:=true, org.eclipse.ui.forms", org.eclipse.e4.ui.css.core.exceptions;x-friends:="org.eclipse.e4.ui.css.swt", org.eclipse.e4.ui.css.core.impl.dom;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.impl.parser;x-friends:="org.eclipse.e4.ui.tests.css.core", 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, org.eclipse.e4.ui.css.core.util.impl.resources;x-friends:="org.eclipse.e4.ui.css.swt.theme,org.eclipse.e4.ui.workbench.swt", org.eclipse.e4.ui.css.core.util.resources;x-friends:="org.eclipse.e4.ui.css.swt,org.eclipse.e4.ui.css.swt.theme,org.eclipse.e4.ui.workbench.swt", org.eclipse.e4.ui.css.core.utils;x-friends:="org.eclipse.e4.ui.css.swt" -Import-Package: org.w3c.css.sac;version="1.3.0", - org.w3c.css.sac.helpers;version="1.3.0" -Require-Bundle: org.apache.batik.css;bundle-version="[1.9.1,2.0.0)", - org.eclipse.equinox.common;bundle-version="[3.5.0,4.0.0)", +Import-Package: org.w3c.css.sac;version="1.3.0" +Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.5.0,4.0.0)", org.eclipse.core.runtime;bundle-version="[3.29.0,4.0.0)" Bundle-RequiredExecutionEnvironment: JavaSE-17 Automatic-Module-Name: org.eclipse.e4.ui.css.core diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/SACConstants.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/SACConstants.java deleted file mode 100644 index 45016437e52..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/SACConstants.java +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2013 Angelo Zerr 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: - * Angelo Zerr - initial API and implementation - *******************************************************************************/ -package org.eclipse.e4.ui.css.core; - -/** - * SAC Constants Parsers. - * - * @version 1.0.0 - * @author Angelo ZERR - */ -public class SACConstants { - - /** - * org.apache.batik.css.parser.Parser SAC Parser. - */ - public static final String SACPARSER_BATIK = "org.apache.batik.css.parser.Parser"; - -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/dom/parsers/CSSParser.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/dom/parsers/CSSParser.java deleted file mode 100644 index 9a50950f766..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/dom/parsers/CSSParser.java +++ /dev/null @@ -1,125 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2013 Angelo Zerr 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: - * Angelo Zerr - initial API and implementation - *******************************************************************************/ -package org.eclipse.e4.ui.css.core.dom.parsers; - -import java.io.IOException; - -import org.eclipse.e4.ui.css.core.sac.DocumentHandlerFactory; -import org.eclipse.e4.ui.css.core.sac.ExtendedDocumentHandler; -import org.w3c.css.sac.ConditionFactory; -import org.w3c.css.sac.InputSource; -import org.w3c.css.sac.Parser; -import org.w3c.css.sac.SelectorFactory; -import org.w3c.css.sac.SelectorList; -import org.w3c.dom.css.CSSRule; -import org.w3c.dom.css.CSSStyleDeclaration; -import org.w3c.dom.css.CSSStyleSheet; -import org.w3c.dom.css.CSSValue; - -/** - * CSS Parser interface to parse with SAC {@link Parser} : - * - */ -public interface CSSParser { - - /** - * Parse CSS source style sheet with SAC {@link Parser} and - * return {@link CSSStyleSheet} instance. - * - * @param source - * style sheet. - */ - public CSSStyleSheet parseStyleSheet(InputSource source) throws IOException; - - /** - * Set the parent {@link CSSStyleSheet}. - */ - public void setParentStyleSheet(CSSStyleSheet parentStyleSheet); - - /** - * Parse CSS source style declaration with SAC {@link Parser} - * and return {@link CSSStyleDeclaration} instance. - * - * @param source - * style declaration. - */ - public CSSStyleDeclaration parseStyleDeclaration(InputSource source) - throws IOException; - - /** - * Parse CSS source style declaration with SAC {@link Parser} - * and update the styleDecelaration. - */ - public void parseStyleDeclaration(CSSStyleDeclaration styleDeclaration, - InputSource source) throws IOException; - - /** - * Parse CSS source value with SAC {@link Parser} and return - * {@link CSSValue} instance. - * - * @param source - * CSS value. - */ - public CSSValue parsePropertyValue(InputSource source) throws IOException; - - /** - * Parse CSS source rule value with SAC {@link Parser} and - * return {@link CSSRule} instance. - * - * @param source - * CSS rule. - */ - public CSSRule parseRule(InputSource source) throws IOException; - - /** - * Parse CSS source selectors value with SAC {@link Parser} - * and return {@link SelectorList} instance. - */ - public SelectorList parseSelectors(InputSource source) throws IOException; - - /*------- SAC parser configuration methods ------- */ - - /** - * Set the SAC {@link DocumentHandlerFactory} factory to get SAC - * {@link ExtendedDocumentHandler} handler used by SAC {@link Parser}. - */ - public void setDocumentHandlerFactory( - DocumentHandlerFactory documentHandlerFactory); - - /** - * Get the SAC {@link ConditionFactory} used by SAC {@link Parser}. - */ - public ConditionFactory getConditionFactory(); - - /** - * Set the SAC {@link ConditionFactory} used by SAC {@link Parser}. - */ - public void setConditionFactory(ConditionFactory conditionFactory); - - /** - * Get the SAC {@link SelectorFactory} used by SAC {@link Parser}. - */ - public SelectorFactory getSelectorFactory(); - - /** - * Set the SAC {@link SelectorFactory} used by SAC {@link Parser}. - */ - public void setSelectorFactory(SelectorFactory selectorFactory); - -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/dom/parsers/CSSParserFactory.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/dom/parsers/CSSParserFactory.java deleted file mode 100644 index efcfe7c3230..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/dom/parsers/CSSParserFactory.java +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2013 Angelo Zerr 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: - * Angelo Zerr - initial API and implementation - *******************************************************************************/ -package org.eclipse.e4.ui.css.core.dom.parsers; - -import org.eclipse.e4.ui.css.core.impl.dom.parsers.CSSParserFactoryImpl; - -/** - * CSS Parser factory to manage instance of {@link ICSSParserFactory}. - */ -public abstract class CSSParserFactory implements ICSSParserFactory { - - /** - * Obtain a new instance of a {@link ICSSParserFactory}. - */ - public static ICSSParserFactory newInstance() { - return new CSSParserFactoryImpl(); - } - -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/dom/parsers/ICSSParserFactory.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/dom/parsers/ICSSParserFactory.java deleted file mode 100644 index c8ecd68e771..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/dom/parsers/ICSSParserFactory.java +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2013 Angelo Zerr 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: - * Angelo Zerr - initial API and implementation - *******************************************************************************/ -package org.eclipse.e4.ui.css.core.dom.parsers; - -/** - * CSS Parser factory to manage instance of {@link CSSParser}. - */ -public interface ICSSParserFactory { - - /** - * Return instance of {@link CSSParser}. - */ - public CSSParser makeCSSParser(); -} 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 f036cf16154..225f08fd6eb 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 @@ -23,7 +23,6 @@ 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.dom.Element; import org.w3c.dom.css.CSSStyleDeclaration; import org.w3c.dom.css.CSSStyleSheet; @@ -51,9 +50,10 @@ public interface CSSEngine { StyleSheet parseStyleSheet(InputStream stream) throws IOException; /** - * Parse style sheet from InputSource source. + * Parse style sheet from InputStream stream, using {@code uri} as the base + * location for resolving relative {@code @import} rules. */ - StyleSheet parseStyleSheet(InputSource source) throws IOException; + StyleSheet parseStyleSheet(InputStream stream, String uri) throws IOException; /*--------------- Parse style declaration -----------------*/ @@ -72,11 +72,6 @@ public interface CSSEngine { */ CSSStyleDeclaration parseStyleDeclaration(InputStream stream) throws IOException; - /** - * Parse style declaration from InputSource source. - */ - CSSStyleDeclaration parseStyleDeclaration(InputSource source) throws IOException; - /*--------------- Parse CSS Property Value-----------------*/ /** @@ -94,11 +89,6 @@ public interface CSSEngine { */ CSSValue parsePropertyValue(Reader reader) throws IOException; - /** - * Parse CSSValue from InputSource source. - */ - CSSValue parsePropertyValue(InputSource source) throws IOException; - /*--------------- Apply styles -----------------*/ /** @@ -106,11 +96,6 @@ public interface CSSEngine { */ Selectors.SelectorList parseSelectors(String text); - /** - * Parse Selectors from InputSource value. - */ - Selectors.SelectorList parseSelectors(InputSource source) throws IOException; - /** * Parse Selectors from InputStream. */ @@ -162,11 +147,6 @@ public interface CSSEngine { */ CSSStyleDeclaration parseAndApplyStyleDeclaration(Object node, InputStream stream) throws IOException; - /** - * Parse and apply style declaration from InputSource source. - */ - CSSStyleDeclaration parseAndApplyStyleDeclaration(Object node, InputSource sourcee) throws IOException; - /** * Parse and apply style declaration from String style. */ diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/AbstractCSSNode.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/AbstractCSSNode.java index 56ae34463c7..c63b46808ad 100644 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/AbstractCSSNode.java +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/AbstractCSSNode.java @@ -15,8 +15,6 @@ package org.eclipse.e4.ui.css.core.impl.dom; -import org.eclipse.e4.ui.css.core.dom.parsers.CSSParser; - /** * Abstract CSS Node. */ @@ -26,9 +24,4 @@ public AbstractCSSNode() { super(); } - public CSSParser getCSSParser() { - //TODO not sure why this always returns null - return null; - } - } diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/MediaListImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/MediaListImpl.java index ea7a6188731..d0da1c3f902 100644 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/MediaListImpl.java +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/MediaListImpl.java @@ -17,7 +17,6 @@ import java.util.ArrayList; import java.util.List; -import org.w3c.css.sac.SACMediaList; import org.w3c.dom.DOMException; import org.w3c.dom.stylesheets.MediaList; @@ -25,12 +24,8 @@ public class MediaListImpl implements MediaList { private final List mediaList; - public MediaListImpl(SACMediaList media) { + public MediaListImpl() { mediaList = new ArrayList<>(); - for (int i = 0; i < media.getLength(); i++) { - mediaList.add(media.item(i)); - } - } @Override diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/parsers/AbstractCSSParser.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/parsers/AbstractCSSParser.java deleted file mode 100644 index 5a1c1af6a2f..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/parsers/AbstractCSSParser.java +++ /dev/null @@ -1,196 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2015 Angelo Zerr 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: - * Angelo Zerr - initial API and implementation - * IBM Corporation - * Lars Vogel - Bug 422702 - *******************************************************************************/ -package org.eclipse.e4.ui.css.core.impl.dom.parsers; - -import java.io.IOException; -import java.util.Stack; -import org.eclipse.e4.ui.css.core.dom.parsers.CSSParser; -import org.eclipse.e4.ui.css.core.impl.dom.CSSStyleDeclarationImpl; -import org.eclipse.e4.ui.css.core.impl.dom.CSSValueFactory; -import org.eclipse.e4.ui.css.core.sac.DocumentHandlerFactory; -import org.eclipse.e4.ui.css.core.sac.ExtendedDocumentHandler; -import org.eclipse.e4.ui.css.core.sac.ISACParserFactory; -import org.eclipse.e4.ui.css.core.sac.ParserNotFoundException; -import org.eclipse.e4.ui.css.core.sac.SACParserFactory; -import org.w3c.css.sac.ConditionFactory; -import org.w3c.css.sac.InputSource; -import org.w3c.css.sac.Parser; -import org.w3c.css.sac.SelectorFactory; -import org.w3c.css.sac.SelectorList; -import org.w3c.dom.css.CSSRule; -import org.w3c.dom.css.CSSStyleDeclaration; -import org.w3c.dom.css.CSSStyleSheet; -import org.w3c.dom.css.CSSValue; - -/** - * Abstract {@link CSSParser} implementation. - */ -public class AbstractCSSParser implements CSSParser { - - private static DocumentHandlerFactory defaultDocumentHandlerFactory; - private static ISACParserFactory defaultParserFactory; - static { - defaultDocumentHandlerFactory = DocumentHandlerFactory.newInstance(); - defaultParserFactory = SACParserFactory.newInstance(); - } - - // SAC - private Parser parser; - private DocumentHandlerFactory documentHandlerFactory; - private ISACParserFactory parserFactory; - - private ConditionFactory conditionFactory; - private SelectorFactory selectorFactory; - - @Override - public CSSStyleSheet parseStyleSheet(InputSource source) throws IOException { - ExtendedDocumentHandler documentHandler = getDocumentHandlerFactory().makeDocumentHandler(); - Parser parser = getParser(); - parser.setDocumentHandler(documentHandler); - parser.parseStyleSheet(source); - return (CSSStyleSheet) documentHandler.getNodeRoot(); - } - - @Override - public CSSStyleDeclaration parseStyleDeclaration(InputSource source) throws IOException { - CSSStyleDeclarationImpl styleDeclaration = new CSSStyleDeclarationImpl(null); - parseStyleDeclaration(((styleDeclaration)), source); - return styleDeclaration; - } - - @Override - public void parseStyleDeclaration(CSSStyleDeclaration styleDeclaration, InputSource source) throws IOException { - Stack stack = new Stack<>(); - stack.push(styleDeclaration); - ExtendedDocumentHandler documentHandler = getDocumentHandlerFactory().makeDocumentHandler(); - documentHandler.setNodeStack(stack); - Parser parser = getParser(); - parser.setDocumentHandler(documentHandler); - parser.parseStyleDeclaration(source); - } - - @Override - public CSSValue parsePropertyValue(InputSource source) throws IOException { - Parser parser = getParser(); - ExtendedDocumentHandler documentHandler = getDocumentHandlerFactory().makeDocumentHandler(); - parser.setDocumentHandler(documentHandler); - return CSSValueFactory.newValue(parser.parsePropertyValue(source)); - } - - @Override - public CSSRule parseRule(InputSource source) throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public SelectorList parseSelectors(InputSource source) throws IOException { - ExtendedDocumentHandler documentHandler = getDocumentHandlerFactory().makeDocumentHandler(); - Parser parser = getParser(); - parser.setDocumentHandler(documentHandler); - return parser.parseSelectors(source); - } - - @Override - public void setParentStyleSheet(CSSStyleSheet parentStyleSheet) { - - } - - /** - * Return instance of {@link DocumentHandlerFactory}. - */ - public DocumentHandlerFactory getDocumentHandlerFactory() { - if (documentHandlerFactory == null) { - return defaultDocumentHandlerFactory; - } - return documentHandlerFactory; - } - - /** - * Set instance of {@link DocumentHandlerFactory}. - */ - @Override - public void setDocumentHandlerFactory(DocumentHandlerFactory documentHandlerFactory) { - this.documentHandlerFactory = documentHandlerFactory; - } - - /** - * Return SAC {@link Parser} to use. - */ - public Parser getParser() { - if (parser == null) { - try { - parser = getSACParserFactory().makeParser(); - if (conditionFactory != null) { - parser.setConditionFactory(conditionFactory); - } - if (selectorFactory != null) { - parser.setSelectorFactory(selectorFactory); - } - } catch (Exception e) { - // TODO : manage error. - // e.printStackTrace(); - throw new ParserNotFoundException(e); - } - } - return parser; - } - - /** - * Set SAC {@link Parser} to use. - */ - public void setParser(Parser parser) { - this.parser = parser; - } - - /** - * Return factory {@link ISACParserFactory} to use. - */ - public ISACParserFactory getSACParserFactory() { - if (parserFactory == null) { - return defaultParserFactory; - } - return parserFactory; - } - - /** - * Set factory {@link ISACParserFactory} to use. - */ - public void setSACParserFactory(ISACParserFactory parserFactory) { - this.parserFactory = parserFactory; - } - - @Override - public ConditionFactory getConditionFactory() { - return conditionFactory; - } - - @Override - public void setConditionFactory(ConditionFactory conditionFactory) { - this.conditionFactory = conditionFactory; - } - - @Override - public SelectorFactory getSelectorFactory() { - return selectorFactory; - } - - @Override - public void setSelectorFactory(SelectorFactory selectorFactory) { - this.selectorFactory = selectorFactory; - } - -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/parsers/CSSParserFactoryImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/parsers/CSSParserFactoryImpl.java deleted file mode 100644 index 43806f48a7a..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/parsers/CSSParserFactoryImpl.java +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2015 Angelo Zerr 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: - * Angelo Zerr - initial API and implementation - *******************************************************************************/ -package org.eclipse.e4.ui.css.core.impl.dom.parsers; - -import org.eclipse.e4.ui.css.core.dom.parsers.CSSParser; -import org.eclipse.e4.ui.css.core.dom.parsers.CSSParserFactory; - -/** - * {@link CSSParserFactory} implementation. - * - * @version 1.0.0 - * @author Angelo ZERR - */ -public class CSSParserFactoryImpl extends CSSParserFactory { - - @Override - public CSSParser makeCSSParser() { - return new CSSParserImpl(); - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/parsers/CSSParserImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/parsers/CSSParserImpl.java deleted file mode 100644 index 1cc90983f15..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/dom/parsers/CSSParserImpl.java +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2013 Angelo Zerr 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: - * Angelo Zerr - initial API and implementation - *******************************************************************************/ -package org.eclipse.e4.ui.css.core.impl.dom.parsers; - -import org.eclipse.e4.ui.css.core.dom.parsers.CSSParser; - -/** - * {@link CSSParser} implementation. - * - * @version 1.0.0 - * @author Angelo ZERR - */ -public class CSSParserImpl extends AbstractCSSParser { - -} 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 66e7ede4f84..64d0dddd09f 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 @@ -24,9 +24,10 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.Reader; -import java.io.StringReader; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -44,9 +45,6 @@ import org.eclipse.e4.ui.css.core.dom.ExtendedDocumentCSS; import org.eclipse.e4.ui.css.core.dom.IElementProvider; import org.eclipse.e4.ui.css.core.dom.IStreamingNodeList; -import org.eclipse.e4.ui.css.core.dom.parsers.CSSParser; -import org.eclipse.e4.ui.css.core.dom.parsers.CSSParserFactory; -import org.eclipse.e4.ui.css.core.dom.parsers.ICSSParserFactory; import org.eclipse.e4.ui.css.core.dom.properties.ICSSPropertyCompositeHandler; import org.eclipse.e4.ui.css.core.dom.properties.ICSSPropertyHandler; import org.eclipse.e4.ui.css.core.dom.properties.ICSSPropertyHandlerProvider; @@ -62,7 +60,6 @@ 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.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; @@ -70,9 +67,7 @@ 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.apache.batik.css.parser.DefaultConditionFactory; -import org.apache.batik.css.parser.DefaultSelectorFactory; -import org.w3c.css.sac.InputSource; +import org.eclipse.e4.ui.css.core.impl.parser.CssParser; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -175,24 +170,21 @@ public CSSEngineImpl(ExtendedDocumentCSS documentCSS) { @Override public StyleSheet parseStyleSheet(Reader reader) throws IOException { - InputSource source = new InputSource(); - source.setCharacterStream(reader); - return parseStyleSheet(source); + return parseStyleSheet(readFully(reader), null); } @Override public StyleSheet parseStyleSheet(InputStream stream) throws IOException { - InputSource source = new InputSource(); - source.setByteStream(stream); - return parseStyleSheet(source); + return parseStyleSheet(stream, null); } @Override - public StyleSheet parseStyleSheet(InputSource source) throws IOException { - // Check that CharacterStream or ByteStream is not null - checkInputSource(source); - CSSParser parser = makeCSSParser(); - CSSStyleSheet styleSheet = parser.parseStyleSheet(source); + public StyleSheet parseStyleSheet(InputStream stream, String uri) throws IOException { + return parseStyleSheet(readFully(new InputStreamReader(stream, StandardCharsets.UTF_8)), uri); + } + + private StyleSheet parseStyleSheet(String content, String uri) throws IOException { + CSSStyleSheet styleSheet = CssParser.parseStyleSheet(content); CSSRuleList rules = styleSheet.getCssRules(); int length = rules.getLength(); @@ -209,11 +201,11 @@ public StyleSheet parseStyleSheet(InputSource source) throws IOException { if (importRule.getHref().startsWith("platform")) { url = FileLocator.resolve(new URL(importRule.getHref())); } else { - IPath p = IPath.fromOSString(source.getURI()); + IPath p = IPath.fromOSString(uri); IPath trim = p.removeLastSegments(1); - boolean isArchive = source.getURI().contains(ARCHIVE_IDENTIFIER); + boolean isArchive = uri.contains(ARCHIVE_IDENTIFIER); url = FileLocator - .resolve(new URL(trim.addTrailingSeparator().toString() + ((CSSImportRule) rule).getHref())); + .resolve(new URL(trim.addTrailingSeparator().toString() + importRule.getHref())); File testFile = new File(url.getFile()); if (!isArchive&&!testFile.exists()) { // look in platform default @@ -225,12 +217,10 @@ public StyleSheet parseStyleSheet(InputSource source) throws IOException { } } try (InputStream stream = url.openStream()) { - InputSource tempStream = new InputSource(); - tempStream.setURI(url.toString()); - tempStream.setByteStream(stream); parseImport++; try { - styleSheet = (CSSStyleSheet) this.parseStyleSheet(tempStream); + styleSheet = (CSSStyleSheet) parseStyleSheet( + readFully(new InputStreamReader(stream, StandardCharsets.UTF_8)), url.toString()); } finally { parseImport--; } @@ -268,112 +258,65 @@ private void processNodeList(NodeList nodes, BiConsumer consumer, } } - /** - * Return true if source is valid and false otherwise. - */ - private void checkInputSource(InputSource source) throws IOException { - Reader reader = source.getCharacterStream(); - InputStream stream = source.getByteStream(); - if (reader == null && stream == null) { - throw new IOException( - "CharacterStream or ByteStream cannot be null for the InputSource."); + private static String readFully(Reader reader) throws IOException { + StringBuilder sb = new StringBuilder(); + char[] buffer = new char[4096]; + int read; + while ((read = reader.read(buffer)) != -1) { + sb.append(buffer, 0, read); } + return sb.toString(); } /*--------------- Parse style declaration -----------------*/ @Override public CSSStyleDeclaration parseStyleDeclaration(String style) { - try { - return parseStyleDeclaration(new StringReader(style)); - } catch (IOException e) { - throw new IllegalStateException("StringReader cannot throw IOException", e); //$NON-NLS-1$ - } + return CssParser.parseStyleDeclaration(style); } @Override public CSSStyleDeclaration parseStyleDeclaration(Reader reader) throws IOException { - InputSource source = new InputSource(); - source.setCharacterStream(reader); - return parseStyleDeclaration(source); + return CssParser.parseStyleDeclaration(readFully(reader)); } @Override public CSSStyleDeclaration parseStyleDeclaration(InputStream stream) throws IOException { - InputSource source = new InputSource(); - source.setByteStream(stream); - return parseStyleDeclaration(source); - } - - @Override - public CSSStyleDeclaration parseStyleDeclaration(InputSource source) throws IOException { - checkInputSource(source); - CSSParser parser = makeCSSParser(); - return parser.parseStyleDeclaration(source); + return CssParser.parseStyleDeclaration(readFully(new InputStreamReader(stream, StandardCharsets.UTF_8))); } /*--------------- Parse CSS Selector -----------------*/ @Override public Selectors.SelectorList parseSelectors(String selector) { - try { - return parseSelectors(new StringReader(selector)); - } catch (IOException e) { - throw new IllegalStateException("StringReader cannot throw IOException", e); //$NON-NLS-1$ - } + return CssParser.parseSelectors(selector); } @Override public Selectors.SelectorList parseSelectors(Reader reader) throws IOException { - InputSource source = new InputSource(); - source.setCharacterStream(reader); - return parseSelectors(source); + return CssParser.parseSelectors(readFully(reader)); } @Override public Selectors.SelectorList parseSelectors(InputStream stream) throws IOException { - InputSource source = new InputSource(); - source.setByteStream(stream); - return parseSelectors(source); - } - - @Override - public Selectors.SelectorList parseSelectors(InputSource source) throws IOException { - checkInputSource(source); - CSSParser parser = makeCSSParser(); - return SacTranslator.translate(parser.parseSelectors(source)); + return CssParser.parseSelectors(readFully(new InputStreamReader(stream, StandardCharsets.UTF_8))); } /*--------------- Parse CSS Property Value-----------------*/ @Override public CSSValue parsePropertyValue(Reader reader) throws IOException { - InputSource source = new InputSource(); - source.setCharacterStream(reader); - return parsePropertyValue(source); + return CssParser.parsePropertyValue(readFully(reader)); } @Override public CSSValue parsePropertyValue(InputStream stream) throws IOException { - InputSource source = new InputSource(); - source.setByteStream(stream); - return parsePropertyValue(source); + return CssParser.parsePropertyValue(readFully(new InputStreamReader(stream, StandardCharsets.UTF_8))); } @Override public CSSValue parsePropertyValue(String value) { - try { - return parsePropertyValue(new StringReader(value)); - } catch (IOException e) { - throw new IllegalStateException("StringReader cannot throw IOException", e); //$NON-NLS-1$ - } - } - - @Override - public CSSValue parsePropertyValue(InputSource source) throws IOException { - checkInputSource(source); - CSSParser parser = makeCSSParser(); - return parser.parsePropertyValue(source); + return CssParser.parsePropertyValue(value); } /*--------------- Apply styles -----------------*/ @@ -652,13 +595,6 @@ public CSSStyleDeclaration parseAndApplyStyleDeclaration(Object node, InputStrea return style; } - @Override - public CSSStyleDeclaration parseAndApplyStyleDeclaration(Object node, InputSource source) throws IOException { - CSSStyleDeclaration style = parseStyleDeclaration(source); - this.applyStyleDeclaration(node, style, null); - return style; - } - @Override public CSSStyleDeclaration parseAndApplyStyleDeclaration(Object node, String style) throws IOException { CSSStyleDeclaration styleDeclaration = parseStyleDeclaration(style); @@ -1159,20 +1095,6 @@ public String convert(Object value, Object toType, Object context) return null; } - /** - * 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(DefaultSelectorFactory.INSTANCE); - parser.setConditionFactory(DefaultConditionFactory.INSTANCE); - return parser; - } - public void registerCSSPropertyHandler(Class cl, ICSSPropertyHandler handler) { initHandlerProviderIfNeed(); handlerProvider.registerCSSPropertyHandler(cl, handler); 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 deleted file mode 100644 index 5ee9a3d5dbf..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/engine/selector/SacTranslator.java +++ /dev/null @@ -1,160 +0,0 @@ -/******************************************************************************* - * 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/parser/CssParseException.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/parser/CssParseException.java new file mode 100644 index 00000000000..1c8ec46c2cb --- /dev/null +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/parser/CssParseException.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * 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.parser; + +/** + * Thrown by the hand-written CSS parser when the input cannot be parsed. + * Replaces {@code org.w3c.css.sac.CSSException} for the internal parser, which + * like its SAC predecessor is unchecked. + */ +public class CssParseException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + private final int line; + private final int column; + + public CssParseException(String message, int line, int column) { + super(message + " (line " + line + ", column " + column + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + this.line = line; + this.column = column; + } + + /** 1-based line where parsing failed. */ + public int getLine() { + return line; + } + + /** 1-based column where parsing failed. */ + public int getColumn() { + return column; + } +} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/parser/CssParser.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/parser/CssParser.java new file mode 100644 index 00000000000..9dfa84e3747 --- /dev/null +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/parser/CssParser.java @@ -0,0 +1,505 @@ +/******************************************************************************* + * 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.parser; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.e4.ui.css.core.impl.dom.CSSImportRuleImpl; +import org.eclipse.e4.ui.css.core.impl.dom.CSSPropertyImpl; +import org.eclipse.e4.ui.css.core.impl.dom.CSSRuleListImpl; +import org.eclipse.e4.ui.css.core.impl.dom.CSSStyleDeclarationImpl; +import org.eclipse.e4.ui.css.core.impl.dom.CSSStyleRuleImpl; +import org.eclipse.e4.ui.css.core.impl.dom.CSSStyleSheetImpl; +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.Selectors; +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.eclipse.e4.ui.css.core.impl.parser.CssTokenizer.Kind; +import org.eclipse.e4.ui.css.core.impl.parser.CssTokenizer.Token; +import org.w3c.css.sac.LexicalUnit; +import org.w3c.dom.css.CSSStyleDeclaration; +import org.w3c.dom.css.CSSStyleSheet; +import org.w3c.dom.css.CSSValue; + +/** + * Recursive-descent CSS parser for the Eclipse engine's supported subset. It + * produces the same model objects the SAC/Batik path produced ({@code + * CSSStyleSheetImpl} with {@code CSSStyleRuleImpl} / {@code CSSImportRuleImpl} + * rules, {@code Selectors} selector trees, and {@code CSSValueFactory} values) + * so it can replace Batik without disturbing the rest of the engine. + * + *

+ * Not yet wired into {@code CSSEngineImpl}; the cutover happens in a follow-up. + * {@code @media}, {@code @font-face} and {@code @page} are parsed and discarded; + * {@code !important} is recorded on the declaration; {@code @import} is kept as + * an import rule. + *

+ */ +public final class CssParser { + + private final List tokens; + private int index; + + private CssParser(List tokens) { + this.tokens = tokens; + } + + /** Parse a complete style sheet. */ + public static CSSStyleSheet parseStyleSheet(String css) { + return new CssParser(CssTokenizer.tokenize(css)).styleSheet(); + } + + /** Parse a standalone declaration list, e.g. {@code "color: red; margin: 0"}. */ + public static CSSStyleDeclaration parseStyleDeclaration(String style) { + CssParser parser = new CssParser(CssTokenizer.tokenize(style)); + CSSStyleDeclarationImpl declaration = new CSSStyleDeclarationImpl(null); + parser.declarations(declaration); + return declaration; + } + + /** Parse a single property value, e.g. {@code "1px 2px"} or {@code "#fff"}. */ + public static CSSValue parsePropertyValue(String value) { + CssParser parser = new CssParser(CssTokenizer.tokenize(value)); + LexicalUnitImpl unit = parser.value(); + return unit == null ? null : CSSValueFactory.newValue(unit); + } + + /** Parse a standalone selector group, e.g. for {@code CSSEngine.parseSelectors}. */ + public static Selectors.SelectorList parseSelectors(String selectors) { + CssParser parser = new CssParser(CssTokenizer.tokenize(selectors)); + Selectors.SelectorList list = parser.selectorList(); + parser.skipWhitespace(); + if (parser.peek().kind != Kind.EOF) { + throw parser.error("Unexpected trailing input in selector"); //$NON-NLS-1$ + } + return list; + } + + // ---------- style sheet ---------- + + private CSSStyleSheetImpl styleSheet() { + CSSStyleSheetImpl sheet = new CSSStyleSheetImpl(); + CSSRuleListImpl rules = new CSSRuleListImpl(); + sheet.setRuleList(rules); + + skipWhitespace(); + while (peek().kind != Kind.EOF) { + if (peek().kind == Kind.AT_KEYWORD) { + atRule(sheet, rules); + } else { + styleRule(sheet, rules); + } + skipWhitespace(); + } + return sheet; + } + + private void atRule(CSSStyleSheetImpl sheet, CSSRuleListImpl rules) { + Token at = advance(); // AT_KEYWORD + String name = at.text.toLowerCase(); + if (name.equals("import")) { //$NON-NLS-1$ + skipWhitespace(); + Token target = peek(); + String href = null; + if (target.kind == Kind.URI || target.kind == Kind.STRING) { + href = target.text; + advance(); + } + discardUntilStatementEnd(); + if (href != null) { + rules.add(new CSSImportRuleImpl(sheet, null, href, new MediaListImpl())); + } + } else { + // @media / @font-face / @page / unknown: parse and discard. + discardUntilStatementEnd(); + } + } + + /** Skip the remainder of an at-rule: either a {@code ;} statement or a balanced {@code { ... }} block. */ + private void discardUntilStatementEnd() { + int depth = 0; + while (peek().kind != Kind.EOF) { + Kind kind = peek().kind; + if (kind == Kind.LBRACE) { + depth++; + advance(); + } else if (kind == Kind.RBRACE) { + advance(); + if (--depth <= 0) { + return; + } + } else if (kind == Kind.SEMICOLON && depth == 0) { + advance(); + return; + } else { + advance(); + } + } + if (depth > 0) { + throw error("Unterminated at-rule block"); //$NON-NLS-1$ + } + } + + private void styleRule(CSSStyleSheetImpl sheet, CSSRuleListImpl rules) { + Selectors.SelectorList selectors = selectorList(); + expect(Kind.LBRACE); + CSSStyleRuleImpl rule = new CSSStyleRuleImpl(sheet, null, selectors); + CSSStyleDeclarationImpl declaration = new CSSStyleDeclarationImpl(rule); + rule.setStyle(declaration); + declarations(declaration); + if (peek().kind == Kind.RBRACE) { + advance(); + } + rules.add(rule); + } + + /** Parse declarations up to a closing brace or end of input; the brace is left in place. */ + private void declarations(CSSStyleDeclarationImpl declaration) { + while (true) { + skipWhitespace(); + Kind kind = peek().kind; + if (kind == Kind.RBRACE || kind == Kind.EOF) { + return; + } + if (kind == Kind.SEMICOLON) { + advance(); // tolerate stray and leading semicolons + continue; + } + declaration(declaration); + } + } + + private void declaration(CSSStyleDeclarationImpl declaration) { + Token name = expect(Kind.IDENT); + skipWhitespace(); + expect(Kind.COLON); + LexicalUnitImpl value = value(); + + boolean important = false; + skipWhitespace(); + if (peek().kind == Kind.BANG) { + advance(); + skipWhitespace(); + Token keyword = expect(Kind.IDENT); + if (!keyword.text.equalsIgnoreCase("important")) { //$NON-NLS-1$ + throw error("Expected 'important' after '!'"); //$NON-NLS-1$ + } + important = true; + skipWhitespace(); + } + if (peek().kind == Kind.SEMICOLON) { + advance(); + } + if (value != null) { + declaration.addProperty(new CSSPropertyImpl(name.text, CSSValueFactory.newValue(value), important)); + } + } + + // ---------- values ---------- + + private LexicalUnitImpl value() { + List units = new ArrayList<>(); + while (true) { + skipWhitespace(); + Kind kind = peek().kind; + if (kind == Kind.SEMICOLON || kind == Kind.RBRACE || kind == Kind.BANG || kind == Kind.EOF) { + break; + } + if (kind == Kind.COMMA) { + advance(); + units.add(LexicalUnitImpl.comma()); + continue; + } + units.add(primitive()); + } + if (units.isEmpty()) { + return null; + } + LexicalUnitImpl head = units.get(0); + LexicalUnitImpl tail = head; + for (int i = 1; i < units.size(); i++) { + tail = tail.append(units.get(i)); + } + return head; + } + + private LexicalUnitImpl primitive() { + Token token = peek(); + switch (token.kind) { + case NUMBER: + advance(); + return token.integer ? LexicalUnitImpl.integer((int) token.number) + : LexicalUnitImpl.real((float) token.number); + case DIMENSION: + advance(); + return LexicalUnitImpl.dimension(dimensionType(token.unit), (float) token.number, token.unit); + case PERCENTAGE: + advance(); + return LexicalUnitImpl.dimension(LexicalUnit.SAC_PERCENTAGE, (float) token.number, "%"); //$NON-NLS-1$ + case IDENT: + advance(); + return token.text.equalsIgnoreCase("inherit") ? LexicalUnitImpl.inherit() //$NON-NLS-1$ + : LexicalUnitImpl.ident(token.text); + case STRING: + advance(); + return LexicalUnitImpl.string(token.text); + case URI: + advance(); + return LexicalUnitImpl.uri(token.text); + case HASH: + advance(); + return hexColor(token.text); + case FUNCTION: + return function(token); + default: + throw error("Unexpected token in value: " + token); //$NON-NLS-1$ + } + } + + private LexicalUnitImpl function(Token token) { + if (!token.text.equalsIgnoreCase("rgb")) { //$NON-NLS-1$ + throw error("Unsupported function in value: " + token.text + "()"); //$NON-NLS-1$ //$NON-NLS-2$ + } + advance(); // FUNCTION consumes the '(' + LexicalUnitImpl red = component(); + LexicalUnitImpl green = component(); + LexicalUnitImpl blue = component(); + skipWhitespace(); + expect(Kind.RPAREN); + red.append(LexicalUnitImpl.comma()).append(green).append(LexicalUnitImpl.comma()).append(blue); + return LexicalUnitImpl.rgbColor(red); + } + + /** One numeric component of an {@code rgb(...)} function, with the trailing comma skipped. */ + private LexicalUnitImpl component() { + skipWhitespace(); + LexicalUnitImpl value = primitive(); + skipWhitespace(); + if (peek().kind == Kind.COMMA) { + advance(); + } + return value; + } + + private LexicalUnitImpl hexColor(String hex) { + int r; + int g; + int b; + if (hex.length() == 3) { + r = hexPair(hex.charAt(0), hex.charAt(0)); + g = hexPair(hex.charAt(1), hex.charAt(1)); + b = hexPair(hex.charAt(2), hex.charAt(2)); + } else if (hex.length() == 6) { + r = hexPair(hex.charAt(0), hex.charAt(1)); + g = hexPair(hex.charAt(2), hex.charAt(3)); + b = hexPair(hex.charAt(4), hex.charAt(5)); + } else { + throw error("Invalid hex colour #" + hex); //$NON-NLS-1$ + } + LexicalUnitImpl red = LexicalUnitImpl.integer(r); + red.append(LexicalUnitImpl.comma()).append(LexicalUnitImpl.integer(g)).append(LexicalUnitImpl.comma()) + .append(LexicalUnitImpl.integer(b)); + return LexicalUnitImpl.rgbColor(red); + } + + private int hexPair(char high, char low) { + return Character.digit(high, 16) * 16 + Character.digit(low, 16); + } + + private static short dimensionType(String unit) { + return switch (unit.toLowerCase()) { + case "px" -> LexicalUnit.SAC_PIXEL; //$NON-NLS-1$ + case "em" -> LexicalUnit.SAC_EM; //$NON-NLS-1$ + case "ex" -> LexicalUnit.SAC_EX; //$NON-NLS-1$ + case "cm" -> LexicalUnit.SAC_CENTIMETER; //$NON-NLS-1$ + case "mm" -> LexicalUnit.SAC_MILLIMETER; //$NON-NLS-1$ + case "in" -> LexicalUnit.SAC_INCH; //$NON-NLS-1$ + case "pt" -> LexicalUnit.SAC_POINT; //$NON-NLS-1$ + case "pc" -> LexicalUnit.SAC_PICA; //$NON-NLS-1$ + case "deg" -> LexicalUnit.SAC_DEGREE; //$NON-NLS-1$ + default -> LexicalUnit.SAC_DIMENSION; + }; + } + + // ---------- selectors ---------- + + private Selectors.SelectorList selectorList() { + List alternatives = new ArrayList<>(); + alternatives.add(complex()); + skipWhitespace(); + while (peek().kind == Kind.COMMA) { + advance(); + alternatives.add(complex()); + skipWhitespace(); + } + return new Selectors.SelectorList(alternatives); + } + + private Selector complex() { + skipWhitespace(); // leading space before a compound is not a combinator + Selector left = compound(); + while (true) { + boolean hadWhitespace = skipWhitespace(); + Kind kind = peek().kind; + if (kind == Kind.GT) { + advance(); + skipWhitespace(); + left = new Child(left, compound()); + } else if (kind == Kind.PLUS) { + advance(); + skipWhitespace(); + left = new Adjacent(left, compound()); + } else if (hadWhitespace && startsCompound(kind)) { + left = new Descendant(left, compound()); + } else { + return left; + } + } + } + + private static boolean startsCompound(Kind kind) { + return kind == Kind.STAR || kind == Kind.IDENT || kind == Kind.DOT || kind == Kind.HASH + || kind == Kind.LBRACKET || kind == Kind.COLON; + } + + private Selector compound() { + Selector element; + Kind first = peek().kind; + if (first == Kind.STAR) { + advance(); + element = new Universal(); + } else if (first == Kind.IDENT) { + element = new ElementType(advance().text); + } else { + element = new Universal(); // implicit universal before a condition + } + + List conditions = new ArrayList<>(); + boolean reading = true; + while (reading) { + switch (peek().kind) { + case DOT: + advance(); + conditions.add(new ClassSelector(expect(Kind.IDENT).text)); + break; + case HASH: + conditions.add(new IdSelector(advance().text)); + break; + case LBRACKET: + conditions.add(attribute()); + break; + case COLON: + advance(); + if (peek().kind == Kind.COLON) { + advance(); // pseudo-element ::, modelled as a pseudo-class like the SAC path + } + conditions.add(new PseudoClass(expect(Kind.IDENT).text)); + break; + default: + reading = false; + break; + } + } + + if (conditions.isEmpty()) { + return element; + } + Selector conditionTree = conditions.get(conditions.size() - 1); + for (int i = conditions.size() - 2; i >= 0; i--) { + conditionTree = new And(conditions.get(i), conditionTree); + } + // A universal element before conditions is dropped, matching the SAC + // translator: '.foo' and '*[a]' carry no element-type contribution. + return element instanceof ElementType ? new And(element, conditionTree) : conditionTree; + } + + private Selector attribute() { + expect(Kind.LBRACKET); + skipWhitespace(); + String name = expect(Kind.IDENT).text; + skipWhitespace(); + Kind op = peek().kind; + if (op == Kind.RBRACKET) { + advance(); + return new AttributeSelector(name, null); // presence form [attr] + } + if (op != Kind.EQUALS && op != Kind.INCLUDE_MATCH && op != Kind.DASH_MATCH) { + throw error("Unsupported attribute operator: " + peek()); //$NON-NLS-1$ + } + advance(); + skipWhitespace(); + String value = attributeValue(); + skipWhitespace(); + expect(Kind.RBRACKET); + return switch (op) { + case INCLUDE_MATCH -> new AttributeIncludes(name, value); + case DASH_MATCH -> new AttributeBeginHyphen(name, value); + default -> new AttributeSelector(name, value); + }; + } + + private String attributeValue() { + Token token = peek(); + if (token.kind == Kind.STRING || token.kind == Kind.IDENT) { + advance(); + return token.text; + } + throw error("Expected attribute value, found " + token); //$NON-NLS-1$ + } + + // ---------- token cursor ---------- + + private Token peek() { + return tokens.get(index); + } + + private Token advance() { + return tokens.get(index++); + } + + private boolean skipWhitespace() { + boolean skipped = false; + while (peek().kind == Kind.WS) { + advance(); + skipped = true; + } + return skipped; + } + + private Token expect(Kind kind) { + Token token = peek(); + if (token.kind != kind) { + throw error("Expected " + kind + " but found " + token); //$NON-NLS-1$ //$NON-NLS-2$ + } + return advance(); + } + + private CssParseException error(String message) { + Token token = peek(); + return new CssParseException(message, token.line, token.column); + } +} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/parser/CssTokenizer.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/parser/CssTokenizer.java new file mode 100644 index 00000000000..c3f7382ef82 --- /dev/null +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/parser/CssTokenizer.java @@ -0,0 +1,316 @@ +/******************************************************************************* + * 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.parser; + +import java.util.ArrayList; +import java.util.List; + +/** + * Splits CSS source into a flat token stream for {@link CssParser}. Scoped to + * the CSS subset the Eclipse engine uses: + * type / class / id / attribute / pseudo selectors, the child, descendant and + * adjacent combinators, declarations with length / percentage / number / colour + * / identifier / string / {@code url()} / {@code rgb()} values, {@code !important}, + * and the {@code @import} / {@code @media} / {@code @font-face} at-rules. + */ +final class CssTokenizer { + + enum Kind { + IDENT, FUNCTION, AT_KEYWORD, HASH, STRING, NUMBER, PERCENTAGE, DIMENSION, URI, + COLON, SEMICOLON, COMMA, LBRACE, RBRACE, LBRACKET, RBRACKET, RPAREN, + DOT, STAR, GT, PLUS, TILDE, BAR, EQUALS, INCLUDE_MATCH, DASH_MATCH, BANG, + WS, EOF + } + + static final class Token { + final Kind kind; + final String text; + final double number; + final boolean integer; + final String unit; + final int line; + final int column; + + Token(Kind kind, String text, double number, boolean integer, String unit, int line, int column) { + this.kind = kind; + this.text = text; + this.number = number; + this.integer = integer; + this.unit = unit; + this.line = line; + this.column = column; + } + + @Override + public String toString() { + return kind + (text != null ? "(" + text + ")" : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + } + + private final String input; + private int pos; + private int line = 1; + private int column = 1; + + private CssTokenizer(String input) { + this.input = input; + } + + static List tokenize(String input) { + return new CssTokenizer(input).run(); + } + + private List run() { + List tokens = new ArrayList<>(); + Token token; + do { + token = next(); + tokens.add(token); + } while (token.kind != Kind.EOF); + return tokens; + } + + private Token next() { + skipComments(); + if (pos >= input.length()) { + return token(Kind.EOF, null); + } + int startLine = line; + int startColumn = column; + char c = input.charAt(pos); + + if (isWhitespace(c)) { + while (pos < input.length() && isWhitespace(input.charAt(pos))) { + advance(); + } + return new Token(Kind.WS, " ", 0, false, null, startLine, startColumn); //$NON-NLS-1$ + } + + switch (c) { + case '{': advance(); return new Token(Kind.LBRACE, "{", 0, false, null, startLine, startColumn); //$NON-NLS-1$ + case '}': advance(); return new Token(Kind.RBRACE, "}", 0, false, null, startLine, startColumn); //$NON-NLS-1$ + case '[': advance(); return new Token(Kind.LBRACKET, "[", 0, false, null, startLine, startColumn); //$NON-NLS-1$ + case ']': advance(); return new Token(Kind.RBRACKET, "]", 0, false, null, startLine, startColumn); //$NON-NLS-1$ + case ')': advance(); return new Token(Kind.RPAREN, ")", 0, false, null, startLine, startColumn); //$NON-NLS-1$ + case ':': advance(); return new Token(Kind.COLON, ":", 0, false, null, startLine, startColumn); //$NON-NLS-1$ + case ';': advance(); return new Token(Kind.SEMICOLON, ";", 0, false, null, startLine, startColumn); //$NON-NLS-1$ + case ',': advance(); return new Token(Kind.COMMA, ",", 0, false, null, startLine, startColumn); //$NON-NLS-1$ + case '>': advance(); return new Token(Kind.GT, ">", 0, false, null, startLine, startColumn); //$NON-NLS-1$ + case '+': if (!startsNumber()) { advance(); return new Token(Kind.PLUS, "+", 0, false, null, startLine, startColumn); } break; //$NON-NLS-1$ + case '*': advance(); return new Token(Kind.STAR, "*", 0, false, null, startLine, startColumn); //$NON-NLS-1$ + case '.': if (!startsNumber()) { advance(); return new Token(Kind.DOT, ".", 0, false, null, startLine, startColumn); } break; //$NON-NLS-1$ + case '!': advance(); return new Token(Kind.BANG, "!", 0, false, null, startLine, startColumn); //$NON-NLS-1$ + case '=': advance(); return new Token(Kind.EQUALS, "=", 0, false, null, startLine, startColumn); //$NON-NLS-1$ + case '~': + advance(); + if (peek() == '=') { + advance(); + return new Token(Kind.INCLUDE_MATCH, "~=", 0, false, null, startLine, startColumn); //$NON-NLS-1$ + } + return new Token(Kind.TILDE, "~", 0, false, null, startLine, startColumn); //$NON-NLS-1$ + case '|': + advance(); + if (peek() == '=') { + advance(); + return new Token(Kind.DASH_MATCH, "|=", 0, false, null, startLine, startColumn); //$NON-NLS-1$ + } + return new Token(Kind.BAR, "|", 0, false, null, startLine, startColumn); //$NON-NLS-1$ + case '"': + case '\'': + return readString(c, startLine, startColumn); + case '#': + advance(); + String hashName = readName(); + return new Token(Kind.HASH, hashName, 0, false, null, startLine, startColumn); + case '@': + advance(); + String atName = readName(); + return new Token(Kind.AT_KEYWORD, atName, 0, false, null, startLine, startColumn); + default: + break; + } + + if (startsNumber()) { + return readNumber(startLine, startColumn); + } + if (isNameStart(c)) { + return readIdentLike(startLine, startColumn); + } + // Unknown character: surface it so the parser can decide to skip or fail. + advance(); + return new Token(Kind.IDENT, String.valueOf(c), 0, false, null, startLine, startColumn); + } + + private Token readString(char quote, int startLine, int startColumn) { + advance(); // opening quote + StringBuilder sb = new StringBuilder(); + while (pos < input.length()) { + char c = input.charAt(pos); + if (c == quote) { + advance(); + break; + } + if (c == '\\' && pos + 1 < input.length()) { + advance(); + sb.append(input.charAt(pos)); + advance(); + continue; + } + sb.append(c); + advance(); + } + return new Token(Kind.STRING, sb.toString(), 0, false, null, startLine, startColumn); + } + + private Token readIdentLike(int startLine, int startColumn) { + String name = readName(); + if (peek() == '(') { + advance(); // consume '(' + if (name.equalsIgnoreCase("url")) { //$NON-NLS-1$ + return readUri(startLine, startColumn); + } + return new Token(Kind.FUNCTION, name, 0, false, null, startLine, startColumn); + } + return new Token(Kind.IDENT, name, 0, false, null, startLine, startColumn); + } + + private Token readUri(int startLine, int startColumn) { + while (pos < input.length() && isWhitespace(input.charAt(pos))) { + advance(); + } + String value; + char c = peek(); + if (c == '"' || c == '\'') { + Token string = readString(c, startLine, startColumn); + value = string.text; + } else { + StringBuilder sb = new StringBuilder(); + while (pos < input.length() && input.charAt(pos) != ')' && !isWhitespace(input.charAt(pos))) { + sb.append(input.charAt(pos)); + advance(); + } + value = sb.toString(); + } + while (pos < input.length() && isWhitespace(input.charAt(pos))) { + advance(); + } + if (peek() == ')') { + advance(); + } + return new Token(Kind.URI, value, 0, false, null, startLine, startColumn); + } + + private Token readNumber(int startLine, int startColumn) { + int start = pos; + boolean real = false; + if (peek() == '+' || peek() == '-') { + advance(); + } + while (Character.isDigit(peek())) { + advance(); + } + if (peek() == '.') { + real = true; + advance(); + while (Character.isDigit(peek())) { + advance(); + } + } + String numberText = input.substring(start, pos); + double value = Double.parseDouble(numberText); + + if (peek() == '%') { + advance(); + return new Token(Kind.PERCENTAGE, numberText, value, false, "%", startLine, startColumn); //$NON-NLS-1$ + } + if (isNameStart(peek())) { + String unit = readName(); + return new Token(Kind.DIMENSION, numberText, value, !real, unit, startLine, startColumn); + } + return new Token(Kind.NUMBER, numberText, value, !real, null, startLine, startColumn); + } + + private String readName() { + int start = pos; + while (pos < input.length() && isNamePart(input.charAt(pos))) { + advance(); + } + return input.substring(start, pos); + } + + private void skipComments() { + while (pos + 1 < input.length() && input.charAt(pos) == '/' && input.charAt(pos + 1) == '*') { + advance(); + advance(); + while (pos + 1 < input.length() && !(input.charAt(pos) == '*' && input.charAt(pos + 1) == '/')) { + advance(); + } + if (pos + 1 < input.length()) { + advance(); + advance(); + } else { + pos = input.length(); + } + } + } + + private boolean startsNumber() { + char c = peek(); + if (Character.isDigit(c)) { + return true; + } + if (c == '.') { + return Character.isDigit(charAt(pos + 1)); + } + if (c == '+' || c == '-') { + char d = charAt(pos + 1); + return Character.isDigit(d) || (d == '.' && Character.isDigit(charAt(pos + 2))); + } + return false; + } + + private char peek() { + return charAt(pos); + } + + private char charAt(int index) { + return index < input.length() ? input.charAt(index) : '\0'; + } + + private void advance() { + if (input.charAt(pos) == '\n') { + line++; + column = 1; + } else { + column++; + } + pos++; + } + + private Token token(Kind kind, String text) { + return new Token(kind, text, 0, false, null, line, column); + } + + private static boolean isWhitespace(char c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f'; + } + + private static boolean isNameStart(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '-' || c >= 0x80; + } + + private static boolean isNamePart(char c) { + return isNameStart(c) || (c >= '0' && c <= '9'); + } +} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/parser/LexicalUnitImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/parser/LexicalUnitImpl.java new file mode 100644 index 00000000000..a5a35615509 --- /dev/null +++ b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/parser/LexicalUnitImpl.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * 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.parser; + +import org.w3c.css.sac.LexicalUnit; + +/** + * A hand-built {@link LexicalUnit} produced by the internal CSS value parser. + * + *

+ * The existing DOM-CSS value classes ({@code Measure}, {@code RGBColorImpl}, + * {@code CSSValueListImpl}) and {@code CSSValueFactory} are written against the + * SAC {@link LexicalUnit} interface. Rather than rewrite that value model now, + * the new parser emits these units so {@code CSSValueFactory.newValue} keeps + * producing exactly the same value objects the Batik path produced. The value + * model itself is replaced later (Phase 4); the SAC interface is the only SAC + * type that survives until then. + *

+ */ +final class LexicalUnitImpl implements LexicalUnit { + + private final short type; + private int integerValue; + private float floatValue; + private String text; + private LexicalUnit parameters; + private LexicalUnit next; + private LexicalUnit previous; + + private LexicalUnitImpl(short type) { + this.type = type; + } + + static LexicalUnitImpl integer(int value) { + LexicalUnitImpl unit = new LexicalUnitImpl(SAC_INTEGER); + unit.integerValue = value; + unit.floatValue = value; + return unit; + } + + static LexicalUnitImpl real(float value) { + LexicalUnitImpl unit = new LexicalUnitImpl(SAC_REAL); + unit.floatValue = value; + return unit; + } + + static LexicalUnitImpl dimension(short type, float value, String unitText) { + LexicalUnitImpl unit = new LexicalUnitImpl(type); + unit.floatValue = value; + unit.text = unitText; + return unit; + } + + static LexicalUnitImpl ident(String value) { + LexicalUnitImpl unit = new LexicalUnitImpl(SAC_IDENT); + unit.text = value; + return unit; + } + + static LexicalUnitImpl string(String value) { + LexicalUnitImpl unit = new LexicalUnitImpl(SAC_STRING_VALUE); + unit.text = value; + return unit; + } + + static LexicalUnitImpl uri(String value) { + LexicalUnitImpl unit = new LexicalUnitImpl(SAC_URI); + unit.text = value; + return unit; + } + + static LexicalUnitImpl inherit() { + return new LexicalUnitImpl(SAC_INHERIT); + } + + static LexicalUnitImpl comma() { + return new LexicalUnitImpl(SAC_OPERATOR_COMMA); + } + + static LexicalUnitImpl rgbColor(LexicalUnit parameters) { + LexicalUnitImpl unit = new LexicalUnitImpl(SAC_RGBCOLOR); + unit.text = "rgb"; //$NON-NLS-1$ + unit.parameters = parameters; + return unit; + } + + /** Append {@code unit} after this one and return it for chaining. */ + LexicalUnitImpl append(LexicalUnitImpl unit) { + this.next = unit; + unit.previous = this; + return unit; + } + + @Override + public short getLexicalUnitType() { + return type; + } + + @Override + public LexicalUnit getNextLexicalUnit() { + return next; + } + + @Override + public LexicalUnit getPreviousLexicalUnit() { + return previous; + } + + @Override + public int getIntegerValue() { + return integerValue; + } + + @Override + public float getFloatValue() { + return floatValue; + } + + @Override + public String getDimensionUnitText() { + return text == null ? "" : text; //$NON-NLS-1$ + } + + @Override + public String getFunctionName() { + return text; + } + + @Override + public LexicalUnit getParameters() { + return parameters; + } + + @Override + public String getStringValue() { + return text; + } + + @Override + public LexicalUnit getSubValues() { + return null; + } +} 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 deleted file mode 100644 index 9456b6a94e2..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/CSSDocumentHandlerImpl.java +++ /dev/null @@ -1,223 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 IBM Corporation 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: - * Angelo Zerr - initial API and implementation - *******************************************************************************/ -package org.eclipse.e4.ui.css.core.impl.sac; - -import java.util.Stack; -import org.eclipse.e4.ui.css.core.dom.CSSProperty; -import org.eclipse.e4.ui.css.core.impl.dom.CSSImportRuleImpl; -import org.eclipse.e4.ui.css.core.impl.dom.CSSPageRuleImpl; -import org.eclipse.e4.ui.css.core.impl.dom.CSSPropertyImpl; -import org.eclipse.e4.ui.css.core.impl.dom.CSSRuleListImpl; -import org.eclipse.e4.ui.css.core.impl.dom.CSSStyleDeclarationImpl; -import org.eclipse.e4.ui.css.core.impl.dom.CSSStyleRuleImpl; -import org.eclipse.e4.ui.css.core.impl.dom.CSSStyleSheetImpl; -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; -import org.w3c.css.sac.LexicalUnit; -import org.w3c.css.sac.SACMediaList; -import org.w3c.css.sac.SelectorList; -import org.w3c.dom.css.CSSStyleDeclaration; -import org.w3c.dom.css.CSSStyleSheet; - -/** - * This class provides an implementation for the {@link ExtendedDocumentHandler} - * interface. - */ -public class CSSDocumentHandlerImpl implements ExtendedDocumentHandler { - - private Stack nodeStack; - private Object nodeRoot; - - private CSSStyleSheet parentStyleSheet; - - private boolean ignore; - - public CSSDocumentHandlerImpl() { - } - - public Object getRoot() { - return nodeRoot; - } - - @Override - public void startDocument(InputSource source) throws CSSException { - if (getNodeStack().empty()) { - CSSStyleSheetImpl styleSheet = new CSSStyleSheetImpl(); - parentStyleSheet = styleSheet; - - // Create the rule list - CSSRuleListImpl rules = new CSSRuleListImpl(); - styleSheet.setRuleList(rules); - getNodeStack().push(styleSheet); - getNodeStack().push(rules); - } else { - // Error - } - } - - @Override - public void endDocument(InputSource source) throws CSSException { - - // Pop the rule list and style sheet nodes - getNodeStack().pop(); - nodeRoot = getNodeStack().pop(); - } - - @Override - public void comment(String text) throws CSSException { - } - - @Override - public void ignorableAtRule(String atRule) throws CSSException { - - // Create the unknown rule and add it to the rule list - CSSUnknownRuleImpl ir = new CSSUnknownRuleImpl(parentStyleSheet, null, - atRule); - if (!getNodeStack().empty()) { - ((CSSRuleListImpl) getNodeStack().peek()).add(ir); - } else { - nodeRoot = ir; - } - } - - @Override - public void namespaceDeclaration(String prefix, String uri) - throws CSSException { - //TODO replace with eclipse logging - } - - @Override - public void importStyle(String uri, SACMediaList media, - String defaultNamespaceURI) throws CSSException { - - // Create the import rule and add it to the rule list - CSSImportRuleImpl ir = new CSSImportRuleImpl(parentStyleSheet, null, - uri, new MediaListImpl(media)); - if (!getNodeStack().empty()) { - ((CSSRuleListImpl) getNodeStack().peek()).add(ir); - } else { - nodeRoot = ir; - } - } - - @Override - public void startMedia(SACMediaList media) throws CSSException { - - ignore = true; - } - - @Override - public void endMedia(SACMediaList media) throws CSSException { - - ignore = false; - } - - @Override - public void startPage(String name, String pseudo_page) throws CSSException { - - // // Create the page rule and add it to the rule list - CSSPageRuleImpl pageRule = new CSSPageRuleImpl(parentStyleSheet, null, - name, pseudo_page); - if (!getNodeStack().empty()) { - ((CSSRuleListImpl) getNodeStack().peek()).add(pageRule); - } - - // Create the style declaration - CSSStyleDeclarationImpl decl = new CSSStyleDeclarationImpl(pageRule); - pageRule.setStyle(decl); - getNodeStack().push(pageRule); - getNodeStack().push(decl); - } - - @Override - public void endPage(String name, String pseudo_page) throws CSSException { - - // Pop both the style declaration and the page rule nodes - getNodeStack().pop(); - nodeRoot = getNodeStack().pop(); - } - - @Override - public void startFontFace() throws CSSException { - ignore = true; - } - - @Override - public void endFontFace() throws CSSException { - ignore = false; - } - - @Override - public void startSelector(SelectorList selectors) throws CSSException { - - // 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, - SacTranslator.translate(selectors)); - if (!getNodeStack().empty()) { - ((CSSRuleListImpl) getNodeStack().peek()).add(rule); - } - - // Create the style declaration - CSSStyleDeclarationImpl decl = new CSSStyleDeclarationImpl(rule); - rule.setStyle(decl); - getNodeStack().push(rule); - getNodeStack().push(decl); - } - - @Override - public void endSelector(SelectorList selectors) throws CSSException { - - // Pop both the style declaration and the style rule nodes - getNodeStack().pop(); - nodeRoot = getNodeStack().pop(); - } - - @Override - public void property(String name, LexicalUnit value, boolean important) - throws CSSException { - if (!ignore) { - CSSStyleDeclarationImpl decl = (CSSStyleDeclarationImpl) getNodeStack().peek(); - decl.addProperty(getCSSProperty(decl, name, value, important)); - } - } - - protected CSSProperty getCSSProperty(CSSStyleDeclaration styleDeclaration, - String name, LexicalUnit value, boolean important) { - return new CSSPropertyImpl(name, CSSValueFactory.newValue(value), important); - } - - @Override - public Object getNodeRoot() { - return nodeRoot; - } - - @Override - public void setNodeStack(Stack nodeStack) { - this.nodeStack = nodeStack; - } - - public Stack getNodeStack() { - if (nodeStack == null) { - nodeStack = new Stack<>(); - } - return nodeStack; - } - -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/DocumentHandlerFactoryImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/DocumentHandlerFactoryImpl.java deleted file mode 100644 index c0c26ad8ca0..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/DocumentHandlerFactoryImpl.java +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2015 Angelo Zerr 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: - * Angelo Zerr - initial API and implementation - *******************************************************************************/ -package org.eclipse.e4.ui.css.core.impl.sac; - -import org.eclipse.e4.ui.css.core.sac.DocumentHandlerFactory; -import org.eclipse.e4.ui.css.core.sac.ExtendedDocumentHandler; - -/** - * This class implements the - * {@link org.eclipse.e4.ui.css.core.sac.IDocumentHandlerFactory} interface. - */ -public class DocumentHandlerFactoryImpl extends DocumentHandlerFactory { - - @Override - public ExtendedDocumentHandler makeDocumentHandler() { - return new CSSDocumentHandlerImpl(); - } - -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/SACParserFactoryImpl.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/SACParserFactoryImpl.java deleted file mode 100644 index 096f9b69c24..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/impl/sac/SACParserFactoryImpl.java +++ /dev/null @@ -1,69 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2015 Angelo Zerr 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: - * Angelo Zerr - initial API and implementation - *******************************************************************************/ -package org.eclipse.e4.ui.css.core.impl.sac; - -import java.lang.reflect.InvocationTargetException; -import java.util.HashMap; -import java.util.Map; -import org.eclipse.e4.ui.css.core.SACConstants; -import org.eclipse.e4.ui.css.core.sac.SACParserFactory; -import org.w3c.css.sac.Parser; - -/** - * SAC Parser factory implementation. The Batik SAC parser is the only - * parser shipped with Eclipse and is registered as the default. - */ -public class SACParserFactoryImpl extends SACParserFactory { - - private static Map parsers = new HashMap<>(); - - static { - registerSACParser(SACConstants.SACPARSER_BATIK); - } - - public SACParserFactoryImpl() { - super.setPreferredParserName(SACConstants.SACPARSER_BATIK); - } - - @Override - public Parser makeParser(String name) throws ClassNotFoundException, IllegalAccessException, InstantiationException, - NullPointerException, ClassCastException { - String classNameParser = parsers.get(name); - if (classNameParser != null) { - Class classParser = super.getClass().getClassLoader().loadClass(classNameParser); - try { - return (Parser) classParser.getDeclaredConstructor().newInstance(); - } catch (InvocationTargetException | NoSuchMethodException e) { - throw (InstantiationException) new InstantiationException(classNameParser).initCause(e); - } - } - throw new IllegalAccessException("SAC parser with name=" + name - + " was not registered into SAC parser factory."); - } - - /** - * Register SAC parser name. - */ - public static void registerSACParser(String parser) { - registerSACParser(parser, parser); - } - - /** - * register SAC parser with name name mapped with Class name - * classNameParser. - */ - public static void registerSACParser(String name, String classNameParser) { - parsers.put(name, classNameParser); - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/DocumentHandlerFactory.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/DocumentHandlerFactory.java deleted file mode 100644 index f9485c63371..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/DocumentHandlerFactory.java +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2013 Angelo Zerr 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: - * Angelo Zerr - initial API and implementation - *******************************************************************************/ -package org.eclipse.e4.ui.css.core.sac; - -import org.eclipse.e4.ui.css.core.impl.sac.DocumentHandlerFactoryImpl; - -/** - * Factory to get instance of {@link DocumentHandlerFactory}. - */ -public abstract class DocumentHandlerFactory implements IDocumentHandlerFactory { - - /** - * Return instance of {@link DocumentHandlerFactory}. - */ - public static DocumentHandlerFactory newInstance() { - return new DocumentHandlerFactoryImpl(); - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/ExtendedDocumentHandler.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/ExtendedDocumentHandler.java deleted file mode 100644 index 0850cab3d69..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/ExtendedDocumentHandler.java +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2015 Angelo Zerr 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: - * Angelo Zerr - initial API and implementation - *******************************************************************************/ -package org.eclipse.e4.ui.css.core.sac; - -import java.util.Stack; -import org.w3c.css.sac.DocumentHandler; - -/** - * Extends {@link DocumentHandler} to get the root node. - */ -public interface ExtendedDocumentHandler extends DocumentHandler { - - /** - * Return root node. - */ - public Object getNodeRoot(); - - /** - * Set node stack. - */ - public void setNodeStack(Stack statck); -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/IDocumentHandlerFactory.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/IDocumentHandlerFactory.java deleted file mode 100644 index 19298b238dc..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/IDocumentHandlerFactory.java +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2013 Angelo Zerr 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: - * Angelo Zerr - initial API and implementation - *******************************************************************************/ -package org.eclipse.e4.ui.css.core.sac; - -/** - * Factory interface to get instance of {@link ExtendedDocumentHandler}. - */ -public interface IDocumentHandlerFactory { - - /** - * Return default instance of {@link ExtendedDocumentHandler}. - */ - public ExtendedDocumentHandler makeDocumentHandler(); - -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/ISACParserFactory.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/ISACParserFactory.java deleted file mode 100644 index c500d46687a..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/ISACParserFactory.java +++ /dev/null @@ -1,40 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2013 Angelo Zerr 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: - * Angelo Zerr - initial API and implementation - *******************************************************************************/ -package org.eclipse.e4.ui.css.core.sac; - -import org.w3c.css.sac.Parser; - -/** - * SAC parser factory interface to get instance of SAC {@link Parser}. - */ -public interface ISACParserFactory { - - /** - * Return default instance of SAC Parser. If preferredParserName is filled, - * it return the instance of SAC Parser registered with this name, otherwise - * this method search teh SAC Parser class name to instanciate into System - * property with key org.w3c.css.sac.parser. - */ - public Parser makeParser() throws ClassNotFoundException, - IllegalAccessException, InstantiationException, - NullPointerException, ClassCastException; - - /** - * Return instance of SAC Parser registered into the factory with name - * name. - */ - public abstract Parser makeParser(String name) - throws ClassNotFoundException, IllegalAccessException, - InstantiationException, NullPointerException, ClassCastException; -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/ParserNotFoundException.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/ParserNotFoundException.java deleted file mode 100644 index 29cec72e18d..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/ParserNotFoundException.java +++ /dev/null @@ -1,27 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2013 Angelo Zerr 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: - * Angelo Zerr - initial API and implementation - * IBM Corporation - ongoing development - *******************************************************************************/ -package org.eclipse.e4.ui.css.core.sac; - -/** - * Exception used when SAC parser is not retrieved. - */ -public class ParserNotFoundException extends RuntimeException { - - private static final long serialVersionUID = 4339161134287845644L; - - public ParserNotFoundException(Throwable throwable) { - super(throwable); - } -} diff --git a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/SACParserFactory.java b/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/SACParserFactory.java deleted file mode 100644 index 7e0acb53b6c..00000000000 --- a/bundles/org.eclipse.e4.ui.css.core/src/org/eclipse/e4/ui/css/core/sac/SACParserFactory.java +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2014 Angelo Zerr 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: - * Angelo Zerr - initial API and implementation - *******************************************************************************/ -package org.eclipse.e4.ui.css.core.sac; - -import org.eclipse.e4.ui.css.core.impl.sac.SACParserFactoryImpl; -import org.w3c.css.sac.Parser; -import org.w3c.css.sac.helpers.ParserFactory; - -/** - * SAC Parser Factory. - */ -public abstract class SACParserFactory extends ParserFactory implements - ISACParserFactory { - - private String preferredParserName; - - /** - * Return default instance of SAC Parser. If preferredParserName is filled, - * it return the instance of SAC Parser registered with this name, otherwise - * this method search teh SAC Parser class name to instanciate into System - * property with key org.w3c.css.sac.parser. - */ - @Override - public Parser makeParser() throws ClassNotFoundException, - IllegalAccessException, InstantiationException, - NullPointerException, ClassCastException { - if (preferredParserName != null) { - return makeParser(preferredParserName); - } - return super.makeParser(); - } - - /** - * Return preferred SAC parser name if it is filled and null otherwise. - */ - public String getPreferredParserName() { - return preferredParserName; - } - - /** - * Set the preferred SAC parser name to use when makeParser is called. - */ - public void setPreferredParserName(String preferredParserName) { - this.preferredParserName = preferredParserName; - } - - /** - * Return instance of SACParserFactory - */ - public static ISACParserFactory newInstance() { - // TODO : manage new instance of SAC Parser Factory like - // SAXParserFactory. - return new SACParserFactoryImpl(); - } - - /** - * Return instance of SAC Parser registered into the factory with name - * name. - */ - @Override - public abstract Parser makeParser(String name) - throws ClassNotFoundException, IllegalAccessException, - InstantiationException, NullPointerException, ClassCastException; -} diff --git a/bundles/org.eclipse.e4.ui.css.swt.theme/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.ui.css.swt.theme/META-INF/MANIFEST.MF index 137edc5df3c..f60a78e861e 100644 --- a/bundles/org.eclipse.e4.ui.css.swt.theme/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.e4.ui.css.swt.theme/META-INF/MANIFEST.MF @@ -1,7 +1,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-SymbolicName: org.eclipse.e4.ui.css.swt.theme;singleton:=true -Bundle-Version: 0.15.100.qualifier +Bundle-Version: 0.15.200.qualifier Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-Localization: plugin @@ -15,8 +15,7 @@ Export-Package: org.eclipse.e4.ui.css.swt.internal.theme;x-internal:=true, org.eclipse.e4.ui.css.swt.theme;x-friends:="org.eclipse.e4.ui.workbench.renderers.swt,org.eclipse.e4.ui.workbench.swt,org.eclipse.ui.workbench" Import-Package: org.eclipse.osgi.service.datalocation;version="1.3.0", org.osgi.framework;version="[1.6.0,2)", - org.osgi.service.event;version="[1.3.0,2.0.0)", - org.w3c.css.sac;version="1.3.0" + org.osgi.service.event;version="[1.3.0,2.0.0)" Require-Capability: osgi.extender; filter:="(&(osgi.extender=osgi.component)(version>=1.2)(!(version>=2.0)))" Automatic-Module-Name: org.eclipse.e4.ui.css.swt.theme diff --git a/bundles/org.eclipse.e4.ui.css.swt.theme/src/org/eclipse/e4/ui/css/swt/internal/theme/ThemeEngine.java b/bundles/org.eclipse.e4.ui.css.swt.theme/src/org/eclipse/e4/ui/css/swt/internal/theme/ThemeEngine.java index 1b24728d2d4..42a7a0639a5 100644 --- a/bundles/org.eclipse.e4.ui.css.swt.theme/src/org/eclipse/e4/ui/css/swt/internal/theme/ThemeEngine.java +++ b/bundles/org.eclipse.e4.ui.css.swt.theme/src/org/eclipse/e4/ui/css/swt/internal/theme/ThemeEngine.java @@ -61,7 +61,6 @@ import org.osgi.service.event.Event; import org.osgi.service.event.EventAdmin; import org.osgi.service.prefs.BackingStoreException; -import org.w3c.css.sac.InputSource; import org.w3c.dom.Element; import org.w3c.dom.css.CSSStyleDeclaration; @@ -477,10 +476,7 @@ public void setTheme(ITheme theme, boolean restore, boolean force) { for (CSSEngine engine : cssEngines) { try { stream = url.openStream(); - InputSource source = new InputSource(); - source.setByteStream(stream); - source.setURI(url.toString()); - engine.parseStyleSheet(source); + engine.parseStyleSheet(stream, url.toString()); } catch (IOException e) { ThemeEngineManager.logError(e.getMessage(), e); } finally { diff --git a/bundles/org.eclipse.e4.ui.css.swt/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.ui.css.swt/META-INF/MANIFEST.MF index a636594b7f4..5aaee17f45f 100644 --- a/bundles/org.eclipse.e4.ui.css.swt/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.e4.ui.css.swt/META-INF/MANIFEST.MF @@ -34,6 +34,5 @@ Require-Bundle: org.eclipse.e4.ui.css.core;bundle-version="0.12.200", org.eclipse.core.runtime;bundle-version="[3.29.0,4.0.0)" Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-ActivationPolicy: lazy -Import-Package: org.eclipse.jface.resource, - org.w3c.css.sac;version="1.3.0" +Import-Package: org.eclipse.jface.resource Automatic-Module-Name: org.eclipse.e4.ui.css.swt diff --git a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/css2/CSSPropertyMarginSWTHandler.java b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/css2/CSSPropertyMarginSWTHandler.java index d4a393b7882..2f2a243b260 100644 --- a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/css2/CSSPropertyMarginSWTHandler.java +++ b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/css2/CSSPropertyMarginSWTHandler.java @@ -23,7 +23,6 @@ import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Layout; import org.eclipse.swt.widgets.Widget; -import org.w3c.css.sac.CSSException; import org.w3c.dom.css.CSSPrimitiveValue; import org.w3c.dom.css.CSSValue; import org.w3c.dom.css.CSSValueList; @@ -63,7 +62,7 @@ public void applyCSSPropertyMargin(Object element, CSSValue value, int length = valueList.getLength(); if(length < 2 || length > 4) { - throw new CSSException("Invalid margin property list length"); + throw new IllegalArgumentException("Invalid margin property list length"); } switch (length) { @@ -88,7 +87,7 @@ public void applyCSSPropertyMargin(Object element, CSSValue value, setMargin(element, LEFT, valueList.item(1)); } } else { - throw new CSSException("Invalid margin property value"); + throw new IllegalArgumentException("Invalid margin property value"); } } diff --git a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/css2/CSSPropertyPaddingSWTHandler.java b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/css2/CSSPropertyPaddingSWTHandler.java index a968e6b4d84..e42c7659b9a 100644 --- a/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/css2/CSSPropertyPaddingSWTHandler.java +++ b/bundles/org.eclipse.e4.ui.css.swt/src/org/eclipse/e4/ui/css/swt/properties/css2/CSSPropertyPaddingSWTHandler.java @@ -24,7 +24,6 @@ import org.eclipse.swt.custom.CTabFolderRenderer; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Widget; -import org.w3c.css.sac.CSSException; import org.w3c.dom.css.CSSPrimitiveValue; import org.w3c.dom.css.CSSValue; import org.w3c.dom.css.CSSValueList; @@ -62,7 +61,7 @@ public void applyCSSPropertyPadding(Object element, CSSValue value, int length = valueList.getLength(); if(length < 2 || length > 4) { - throw new CSSException("Invalid padding property list length"); + throw new IllegalArgumentException("Invalid padding property list length"); } switch (length) { @@ -90,7 +89,7 @@ public void applyCSSPropertyPadding(Object element, CSSValue value, setPadding(element, padding); } else { - throw new CSSException("Invalid padding property value"); + throw new IllegalArgumentException("Invalid padding property value"); } } diff --git a/tests/org.eclipse.e4.ui.tests.css.core/META-INF/MANIFEST.MF b/tests/org.eclipse.e4.ui.tests.css.core/META-INF/MANIFEST.MF index 4583c653c78..bc0fccb912f 100644 --- a/tests/org.eclipse.e4.ui.tests.css.core/META-INF/MANIFEST.MF +++ b/tests/org.eclipse.e4.ui.tests.css.core/META-INF/MANIFEST.MF @@ -16,7 +16,6 @@ Export-Package: org.eclipse.e4.ui.tests.css.core;x-internal:=true, Automatic-Module-Name: org.eclipse.e4.ui.tests.css.core Import-Package: org.junit.jupiter.api;version="[5.14.0,6.0.0)", org.junit.jupiter.api.function;version="[5.14.0,6.0.0)", - org.junit.platform.suite.api;version="[1.14.0,2.0.0)", - org.w3c.css.sac;version="1.3.0" + org.junit.platform.suite.api;version="[1.14.0,2.0.0)" Bundle-Vendor: %Bundle-Vendor diff --git a/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/parser/CssParserTest.java b/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/parser/CssParserTest.java new file mode 100644 index 00000000000..a4c7d549de3 --- /dev/null +++ b/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/parser/CssParserTest.java @@ -0,0 +1,225 @@ +/******************************************************************************* + * 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.tests.css.core.parser; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +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.css.core.impl.parser.CssParseException; +import org.eclipse.e4.ui.css.core.impl.parser.CssParser; +import org.junit.jupiter.api.Test; +import org.w3c.dom.css.CSSImportRule; +import org.w3c.dom.css.CSSPrimitiveValue; +import org.w3c.dom.css.CSSRule; +import org.w3c.dom.css.CSSRuleList; +import org.w3c.dom.css.CSSStyleDeclaration; +import org.w3c.dom.css.CSSStyleRule; +import org.w3c.dom.css.CSSStyleSheet; +import org.w3c.dom.css.CSSValue; +import org.w3c.dom.css.CSSValueList; + +/** + * Unit tests for the hand-written {@link CssParser}. They pin the parser's + * output directly (selector trees, specificity, value model, at-rule handling) + * so the cutover that replaces Batik with this parser is gated by behaviour, + * not by inspection. + */ +public class CssParserTest { + + private static Selector firstSelector(String selector) { + return CssParser.parseSelectors(selector).item(0); + } + + private static CSSStyleRule firstStyleRule(String css) { + return (CSSStyleRule) CssParser.parseStyleSheet(css).getCssRules().item(0); + } + + // ---------- selectors ---------- + + @Test + void testSimpleSelectorTypesAndSpecificity() { + assertInstanceOf(Selectors.Universal.class, firstSelector("*")); + assertEquals(0, firstSelector("*").specificity()); + assertInstanceOf(Selectors.ElementType.class, firstSelector("Button")); + assertEquals(1, firstSelector("Button").specificity()); + assertInstanceOf(Selectors.ClassSelector.class, firstSelector(".primary")); + assertEquals(10, firstSelector(".primary").specificity()); + assertInstanceOf(Selectors.IdSelector.class, firstSelector("#go")); + assertEquals(100, firstSelector("#go").specificity()); + } + + @Test + void testCompoundSelectorIsAndTree() { + Selector selector = firstSelector("Button.primary#go"); + assertInstanceOf(Selectors.And.class, selector); + assertEquals(111, selector.specificity()); + assertEquals("Button.primary#go", selector.text()); + } + + @Test + void testCombinators() { + assertInstanceOf(Selectors.Descendant.class, firstSelector("Composite Button")); + assertInstanceOf(Selectors.Child.class, firstSelector("Composite > Button")); + assertInstanceOf(Selectors.Adjacent.class, firstSelector("Composite + Button")); + assertEquals(2, firstSelector("Composite Button").specificity()); + assertEquals("Composite > Button", firstSelector("Composite>Button").text()); + } + + @Test + void testAttributeAndPseudoSelectors() { + assertInstanceOf(Selectors.AttributeSelector.class, firstSelector("[style]")); + assertInstanceOf(Selectors.AttributeIncludes.class, firstSelector("[style~='SWT.CHECK']")); + assertInstanceOf(Selectors.AttributeBeginHyphen.class, firstSelector("[lang|='en']")); + assertEquals(11, firstSelector("Button[style]").specificity()); + assertEquals(11, firstSelector("Button:selected").specificity()); + } + + @Test + void testUniversalElementDroppedBeforeConditions() { + // '.foo' and '*[a]' carry no element-type contribution, matching the + // previous SAC translator. + assertInstanceOf(Selectors.ClassSelector.class, firstSelector(".foo")); + assertInstanceOf(Selectors.AttributeSelector.class, firstSelector("*[a]")); + } + + @Test + void testSelectorListMaxSpecificity() { + Selectors.SelectorList list = CssParser.parseSelectors("Button, .primary, #go"); + assertEquals(3, list.getLength()); + assertEquals(100, list.specificity()); + } + + @Test + void testMalformedSelectorThrows() { + assertThrows(CssParseException.class, () -> CssParser.parseSelectors("Button[")); + } + + // ---------- declarations and values ---------- + + @Test + void testSingleDeclaration() { + CSSStyleRule rule = firstStyleRule("Button { color: red; }"); + assertEquals("Button", rule.getSelectorText()); + CSSStyleDeclaration style = rule.getStyle(); + assertEquals(1, style.getLength()); + assertEquals("color", style.item(0)); + assertEquals("red", style.getPropertyCSSValue("color").getCssText()); + } + + @Test + void testImportantPriority() { + CSSStyleDeclaration style = firstStyleRule("Button { color: red !important; background: blue; }").getStyle(); + assertEquals("important", style.getPropertyPriority("color")); + assertEquals("", style.getPropertyPriority("background")); + } + + @Test + void testTrailingSemicolonAndStraySemicolonsTolerated() { + assertEquals(1, firstStyleRule("Button { color: red }").getStyle().getLength()); + assertEquals(1, firstStyleRule("Button { ; color: red;; }").getStyle().getLength()); + } + + @Test + void testMultiValuePropertyIsValueList() { + CSSValue value = firstStyleRule("Button { margin: 1px 2px 3px 4px; }").getStyle() + .getPropertyCSSValue("margin"); + assertEquals(CSSValue.CSS_VALUE_LIST, value.getCssValueType()); + assertEquals(4, ((CSSValueList) value).getLength()); + } + + @Test + void testLengthAndPercentageUnits() { + CSSPrimitiveValue px = (CSSPrimitiveValue) firstStyleRule("A { width: 10px; }").getStyle() + .getPropertyCSSValue("width"); + assertEquals(CSSPrimitiveValue.CSS_PX, px.getPrimitiveType()); + assertEquals(10.0f, px.getFloatValue(CSSPrimitiveValue.CSS_PX)); + + CSSPrimitiveValue percent = (CSSPrimitiveValue) firstStyleRule("B { width: 50%; }").getStyle() + .getPropertyCSSValue("width"); + assertEquals(CSSPrimitiveValue.CSS_PERCENTAGE, percent.getPrimitiveType()); + } + + @Test + void testHexAndRgbColoursAreRgbColor() { + CSSPrimitiveValue hex = (CSSPrimitiveValue) firstStyleRule("A { color: #ffffff; }").getStyle() + .getPropertyCSSValue("color"); + assertEquals(CSSPrimitiveValue.CSS_RGBCOLOR, hex.getPrimitiveType()); + assertEquals(255.0f, hex.getRGBColorValue().getRed().getFloatValue(CSSPrimitiveValue.CSS_NUMBER)); + + CSSPrimitiveValue rgb = (CSSPrimitiveValue) firstStyleRule("B { color: rgb(255, 0, 128); }").getStyle() + .getPropertyCSSValue("color"); + assertEquals(CSSPrimitiveValue.CSS_RGBCOLOR, rgb.getPrimitiveType()); + assertEquals(128.0f, rgb.getRGBColorValue().getBlue().getFloatValue(CSSPrimitiveValue.CSS_NUMBER)); + } + + @Test + void testShorthandHexExpands() { + CSSPrimitiveValue hex = (CSSPrimitiveValue) firstStyleRule("A { color: #fff; }").getStyle() + .getPropertyCSSValue("color"); + assertEquals(255.0f, hex.getRGBColorValue().getGreen().getFloatValue(CSSPrimitiveValue.CSS_NUMBER)); + } + + @Test + void testCommentsIgnored() { + CSSStyleSheet sheet = CssParser.parseStyleSheet(""" + /* lead */ Button { /* in */ color: red; } /* tail */ + """); + assertEquals(1, sheet.getCssRules().getLength()); + } + + // ---------- at-rules ---------- + + @Test + void testImportRuleKept() { + CSSStyleSheet sheet = CssParser.parseStyleSheet("@import url('other.css');"); + assertEquals(1, sheet.getCssRules().getLength()); + CSSRule rule = sheet.getCssRules().item(0); + assertEquals(CSSRule.IMPORT_RULE, rule.getType()); + assertEquals("other.css", ((CSSImportRule) rule).getHref()); + } + + @Test + void testMediaAndFontFaceDiscardedFollowingRuleKept() { + CSSStyleSheet sheet = CssParser.parseStyleSheet(""" + @font-face { font-family: x; } + @media screen { Hidden { color: red; } } + Label { color: blue; } + """); + // Both at-rules are discarded entirely; only the top-level Label remains. + CSSRuleList rules = sheet.getCssRules(); + assertEquals(1, rules.getLength()); + CSSStyleRule label = (CSSStyleRule) rules.item(0); + assertEquals("Label", label.getSelectorText()); + assertEquals("blue", label.getStyle().getPropertyCSSValue("color").getCssText()); + } + + @Test + void testMultipleRulesPreserveOrder() { + CSSRuleList rules = CssParser.parseStyleSheet("A { color: red; } B { color: green; } C { color: blue; }") + .getCssRules(); + assertEquals(3, rules.getLength()); + assertEquals("A", ((CSSStyleRule) rules.item(0)).getSelectorText()); + assertEquals("C", ((CSSStyleRule) rules.item(2)).getSelectorText()); + } + + @Test + void testEmptyStyleSheet() { + assertEquals(0, CssParser.parseStyleSheet("").getCssRules().getLength()); + assertTrue(CssParser.parseStyleSheet(" \n ").getCssRules().getLength() == 0); + } +} diff --git a/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/parser/ImportTest.java b/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/parser/ImportTest.java index 99d19ff4c54..c2db00a5ab1 100644 --- a/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/parser/ImportTest.java +++ b/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/parser/ImportTest.java @@ -18,10 +18,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.io.StringReader; +import java.nio.charset.StandardCharsets; import org.eclipse.e4.ui.css.core.engine.CSSEngine; import org.eclipse.e4.ui.css.core.impl.dom.DocumentCSSImpl; @@ -30,7 +31,6 @@ import org.eclipse.e4.ui.tests.css.core.util.TestElement; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.w3c.css.sac.InputSource; import org.w3c.dom.css.CSSRule; import org.w3c.dom.css.CSSRuleList; import org.w3c.dom.css.CSSStyleDeclaration; @@ -146,10 +146,8 @@ private String createImport(File importedFile) { } private CSSStyleSheet parseStyleSheet(String sourceUrl, String cssString) throws IOException { - InputSource source = new InputSource(); - source.setURI(sourceUrl); // must not be null - source.setCharacterStream(new StringReader(cssString)); - return (CSSStyleSheet) engine.parseStyleSheet(source); + return (CSSStyleSheet) engine.parseStyleSheet( + new ByteArrayInputStream(cssString.getBytes(StandardCharsets.UTF_8)), sourceUrl); } private ViewCSS createViewCss(String sourceUrl, String cssString) diff --git a/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/parser/MediaRulesTest.java b/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/parser/MediaRulesTest.java index aa279a1d77c..b3c11020709 100644 --- a/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/parser/MediaRulesTest.java +++ b/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/parser/MediaRulesTest.java @@ -37,8 +37,10 @@ void testMediaRule() throws Exception { Label { background-color: #FF0000 }"""; CSSStyleSheet styleSheet = ParserTestUtil.parseCss(css); assertNotNull(styleSheet); - assertEquals(2, styleSheet.getCssRules().getLength()); + // The @media block is discarded entirely; only the following top-level + // rule remains, with its declaration intact. + assertEquals(1, styleSheet.getCssRules().getLength()); assertFalse(styleSheet.getCssRules().item(0).getCssText().contains("line-height")); - assertTrue(styleSheet.getCssRules().item(1).getCssText().contains("background-color")); + assertTrue(styleSheet.getCssRules().item(0).getCssText().contains("background-color")); } } 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 1241cdfc19d..151d8ae20b5 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 @@ -20,10 +20,10 @@ 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.css.core.impl.parser.CssParseException; 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; public class SelectorTest { private CSSEngine engine; @@ -70,6 +70,6 @@ void testAttributeSelector() throws Exception { @Test void testErrorAttributeSelector() { - assertThrows(CSSParseException.class, () -> engine.parseSelectors("*[class='Class1'")); // missing ']' + assertThrows(CssParseException.class, () -> engine.parseSelectors("*[class='Class1'")); // missing ']' } } diff --git a/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/util/ParserTestUtil.java b/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/util/ParserTestUtil.java index e72b95fd186..98c4ea5aa32 100644 --- a/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/util/ParserTestUtil.java +++ b/tests/org.eclipse.e4.ui.tests.css.core/src/org/eclipse/e4/ui/tests/css/core/util/ParserTestUtil.java @@ -16,13 +16,11 @@ import java.io.IOException; import java.io.StringReader; -import org.eclipse.e4.ui.css.core.dom.parsers.CSSParser; import org.eclipse.e4.ui.css.core.engine.CSSEngine; import org.eclipse.e4.ui.css.core.engine.CSSErrorHandler; -import org.eclipse.e4.ui.css.core.impl.engine.CSSEngineImpl; +import org.eclipse.e4.ui.css.core.impl.parser.CssParser; import org.eclipse.e4.ui.css.swt.engine.CSSSWTEngineImpl; import org.eclipse.swt.widgets.Display; -import org.w3c.css.sac.InputSource; import org.w3c.dom.css.CSSStyleSheet; import org.w3c.dom.stylesheets.StyleSheet; @@ -49,12 +47,8 @@ public static CSSStyleSheet parseCss(String css) * want the AST as the parser produced it; {@link #parseCss(String)} runs * the engine's import-inlining pass which fails for placeholder URLs. */ - public static CSSStyleSheet parseCssWithoutImports(String css) - throws IOException { - CSSParser parser = ((CSSEngineImpl) createEngine()).makeCSSParser(); - InputSource source = new InputSource(); - source.setCharacterStream(new StringReader(css)); - return parser.parseStyleSheet(source); + public static CSSStyleSheet parseCssWithoutImports(String css) { + return (CSSStyleSheet) CssParser.parseStyleSheet(css); } public static CSSEngine createEngine() { diff --git a/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/CTabFolderTest.java b/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/CTabFolderTest.java index 85e704e13e1..ad03a4371b3 100644 --- a/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/CTabFolderTest.java +++ b/tests/org.eclipse.e4.ui.tests.css.swt/src/org/eclipse/e4/ui/tests/css/swt/CTabFolderTest.java @@ -262,7 +262,7 @@ void testSingle() { @Test void testUnselectedCloseVisible() { - CTabFolder folderToTest = createTestCTabFolder("CTabFolder { swt-unselected-close-visible true}"); + CTabFolder folderToTest = createTestCTabFolder("CTabFolder { swt-unselected-close-visible: true}"); assertEquals(true, folderToTest.getUnselectedCloseVisible()); assertEquals("true", engine.retrieveCSSProperty(folderToTest, "swt-unselected-close-visible", null)); folderToTest = createTestCTabFolder("CTabFolder { swt-unselected-close-visible: false}");