Add CSS3 and GSS support in InterfaceGenerator
Change-Id: Ifecfec053c851582b3d1c6a06f721c289aa3c5b6
Bug-Link: https://github.com/gwtproject/gwt/issues/9105
diff --git a/user/src/com/google/gwt/resources/css/InterfaceGenerator.java b/user/src/com/google/gwt/resources/css/InterfaceGenerator.java
index 430e591..434cdb3 100644
--- a/user/src/com/google/gwt/resources/css/InterfaceGenerator.java
+++ b/user/src/com/google/gwt/resources/css/InterfaceGenerator.java
@@ -21,7 +21,20 @@
import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.resources.client.CssResource.ClassName;
-import com.google.gwt.resources.css.ast.CssStylesheet;
+import com.google.gwt.resources.gss.ClassNamesCollector;
+import com.google.gwt.resources.rg.GssResourceGenerator;
+import com.google.gwt.resources.rg.GssResourceGenerator.LoggerErrorManager;
+import com.google.gwt.thirdparty.common.css.compiler.ast.CssTree;
+import com.google.gwt.thirdparty.common.css.compiler.ast.GssParser;
+import com.google.gwt.thirdparty.common.css.compiler.ast.GssParserException;
+import com.google.gwt.thirdparty.common.css.compiler.passes.CollectConstantDefinitions;
+import com.google.gwt.thirdparty.common.css.compiler.passes.CreateDefinitionNodes;
+import com.google.gwt.thirdparty.common.css.compiler.passes.CreateForLoopNodes;
+import com.google.gwt.thirdparty.common.css.compiler.passes.UnrollLoops;
+import com.google.gwt.thirdparty.guava.common.base.CaseFormat;
+import com.google.gwt.thirdparty.guava.common.base.Function;
+import com.google.gwt.thirdparty.guava.common.collect.Iterables;
+import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.user.rebind.SourceWriter;
import com.google.gwt.user.rebind.StringSourceWriter;
import com.google.gwt.util.tools.ArgHandlerFile;
@@ -32,6 +45,8 @@
import java.io.File;
import java.io.PrintWriter;
import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
@@ -228,14 +243,19 @@
private String process() throws MalformedURLException,
UnableToCompleteException {
// Create AST
- CssStylesheet sheet = GenerateCssAst.exec(logger, inputFile.toURI().toURL());
+ CssTree cssTree = createAst(inputFile.toURI().toURL(), logger);
// Sort all names
Set<String> names = new TreeSet<String>(NAME_COMPARATOR);
- names.addAll(ExtractClassNamesVisitor.exec(sheet));
- DefsCollector defs = new DefsCollector();
- defs.accept(sheet);
- names.addAll(defs.getDefs());
+
+ names.addAll(new ClassNamesCollector().getClassNames(cssTree));
+
+ CollectConstantDefinitions collectConstantDefinitionsPass = new CollectConstantDefinitions(
+ cssTree);
+ collectConstantDefinitionsPass.runPass();
+ Collection<String> renamedDefs = renameDefs(collectConstantDefinitionsPass
+ .getConstantDefinitions().getConstantsNames());
+ names.addAll(renamedDefs);
// Deduplicate method names
Set<String> methodNames = new HashSet<String>();
@@ -275,4 +295,40 @@
return sw.toString();
}
+
+ /**
+ * In GSS, constant names are defined in upper case but a method name to access a constant in
+ * a CssResource interface can be written in lower camel case.
+ * <p>
+ * This method converts all constant names in a lower camel case identifier.
+ */
+ private Collection<String> renameDefs(Iterable<String> constantsNames) {
+ return Lists.newArrayList(Iterables.transform(constantsNames, new Function<String, String>() {
+ @Override
+ public String apply(String constantName) {
+ return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, constantName);
+ }
+ }));
+ }
+
+ private CssTree createAst(URL sourceFileUrl, TreeLogger logger)
+ throws UnableToCompleteException {
+ LoggerErrorManager errorManager = new LoggerErrorManager(logger);
+
+ CssTree cssTree;
+
+ try {
+ cssTree = new GssParser(GssResourceGenerator.readUrlContent(sourceFileUrl, logger)).parse();
+ } catch (GssParserException e) {
+ logger.log(TreeLogger.ERROR, "Unable to parse CSS", e);
+ throw new UnableToCompleteException();
+ }
+
+ new CreateDefinitionNodes(cssTree.getMutatingVisitController(), errorManager).runPass();
+ // Can create new style classes
+ new CreateForLoopNodes(cssTree.getMutatingVisitController(), errorManager).runPass();
+ new UnrollLoops(cssTree.getMutatingVisitController(), errorManager).runPass();
+
+ return cssTree;
+ }
}
diff --git a/user/src/com/google/gwt/resources/gss/ClassNamesCollector.java b/user/src/com/google/gwt/resources/gss/ClassNamesCollector.java
index 610ef26..ded019e 100644
--- a/user/src/com/google/gwt/resources/gss/ClassNamesCollector.java
+++ b/user/src/com/google/gwt/resources/gss/ClassNamesCollector.java
@@ -35,6 +35,13 @@
private SortedSet<String> excludedPrefixes;
/**
+ * Extract all CSS class names in the provided stylesheet.
+ */
+ public Set<String> getClassNames(CssTree tree) {
+ return getClassNames(tree, new HashSet<JClassType>());
+ }
+
+ /**
* Extract all CSS class names in the provided stylesheet, modulo those
* imported from another context.
*/
diff --git a/user/src/com/google/gwt/resources/rg/GssResourceGenerator.java b/user/src/com/google/gwt/resources/rg/GssResourceGenerator.java
index bc853a8..b00b2eb 100644
--- a/user/src/com/google/gwt/resources/rg/GssResourceGenerator.java
+++ b/user/src/com/google/gwt/resources/rg/GssResourceGenerator.java
@@ -220,6 +220,43 @@
}
}
+ public static SourceCode readUrlContent(URL fileUrl, TreeLogger logger) throws UnableToCompleteException {
+ TreeLogger branchLogger = logger.branch(TreeLogger.DEBUG,
+ "Reading GSS stylesheet " + fileUrl.toExternalForm());
+ try {
+ ByteSource byteSource = Resources.asByteSource(fileUrl);
+ // default charset
+ Charset charset = Charsets.UTF_8;
+
+ // check if the stylesheet doesn't include a @charset at-rule
+ String styleSheetCharset = extractCharset(byteSource);
+ if (styleSheetCharset != null) {
+ try {
+ charset = Charset.forName(styleSheetCharset);
+ } catch (UnsupportedCharsetException e) {
+ logger.log(Type.ERROR, "Unsupported charset found: " + styleSheetCharset);
+ throw new UnableToCompleteException();
+ }
+ }
+
+ String fileContent = byteSource.asCharSource(charset).read();
+ // If the stylesheet specified a charset, we have to remove the at-rule otherwise the GSS
+ // compiler will fail.
+ if (styleSheetCharset != null) {
+ int charsetAtRuleLength = CHARSET_MIN_LENGTH + styleSheetCharset.length();
+ // replace charset at-rule by blanks to keep correct source location of the rest of
+ // the stylesheet.
+ fileContent = Strings.repeat(" ", charsetAtRuleLength) +
+ fileContent.substring(charsetAtRuleLength);
+ }
+ return new SourceCode(fileUrl.getFile(), fileContent);
+
+ } catch (IOException e) {
+ branchLogger.log(TreeLogger.ERROR, "Unable to parse CSS", e);
+ }
+ throw new UnableToCompleteException();
+ }
+
public static GssOptions getGssOptions(PropertyOracle propertyOracle, TreeLogger logger) throws UnableToCompleteException {
boolean gssEnabled;
boolean gssDefaultInUiBinder;
@@ -267,11 +304,11 @@
* {@link ErrorManager} used to log the errors and warning messages produced by the different
* {@link com.google.gwt.thirdparty.common.css.compiler.ast.CssCompilerPass}.
*/
- private static class LoggerErrorManager implements ErrorManager {
+ public static class LoggerErrorManager implements ErrorManager {
private final TreeLogger logger;
private boolean hasErrors;
- private LoggerErrorManager(TreeLogger logger) {
+ public LoggerErrorManager(TreeLogger logger) {
this.logger = logger;
}
@@ -1048,42 +1085,7 @@
constantNameMappingBuilder.putAll(result.defNameMapping);
} else {
for (URL stylesheet : resources) {
- TreeLogger branchLogger = logger.branch(TreeLogger.DEBUG,
- "Parsing GSS stylesheet " + stylesheet.toExternalForm());
- try {
- ByteSource byteSource = Resources.asByteSource(stylesheet);
- // default charset
- Charset charset = Charsets.UTF_8;
-
- // check if the stylesheet doesn't include a @charset at-rule
- String styleSheetCharset = extractCharset(byteSource);
- if (styleSheetCharset != null) {
- try {
- charset = Charset.forName(styleSheetCharset);
- } catch (UnsupportedCharsetException e) {
- logger.log(Type.ERROR, "Unsupported charset found: " + styleSheetCharset);
- throw new UnableToCompleteException();
- }
- }
-
- String fileContent = byteSource.asCharSource(charset).read();
- // If the stylesheet specified a charset, we have to remove the at-rule otherwise the GSS
- // compiler will fail.
- if (styleSheetCharset != null) {
- int charsetAtRuleLength = CHARSET_MIN_LENGTH + styleSheetCharset.length();
- // replace charset at-rule by blanks to keep correct source location of the rest of
- // the stylesheet.
- fileContent = Strings.repeat(" ", charsetAtRuleLength) +
- fileContent.substring(charsetAtRuleLength);
- }
-
- sourceCodes.add(new SourceCode(stylesheet.getFile(), fileContent));
- continue;
-
- } catch (IOException e) {
- branchLogger.log(TreeLogger.ERROR, "Unable to parse CSS", e);
- }
- throw new UnableToCompleteException();
+ sourceCodes.add(readUrlContent(stylesheet, logger));
}
}
@@ -1115,7 +1117,7 @@
booleanConditionCollector.getBooleanConditions(), constantNameMappingBuilder.build());
}
- private String extractCharset(ByteSource byteSource) throws IOException {
+ private static String extractCharset(ByteSource byteSource) throws IOException {
String firstLine = byteSource.asCharSource(Charsets.UTF_8).readFirstLine();
if (firstLine != null) {