Allows CSS @def declarations to be retrieved as a String in a CssResource
implementation. Detection was added for colliding class names and @def names,
but you can dis-ambiguate them with the @ClassName() annotation.
Patch by: zundel
Review by: bobv
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5664 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/resources/client/CssResource.java b/user/src/com/google/gwt/resources/client/CssResource.java
index e566e53..dc3a446 100644
--- a/user/src/com/google/gwt/resources/client/CssResource.java
+++ b/user/src/com/google/gwt/resources/client/CssResource.java
@@ -32,9 +32,10 @@
* <li>{@code String someClassName();} will allow the css class
* <code>.someClassName</code> to be obfuscated at runtime. The function will
* return the obfuscated class name.</li>
- * <li>{@code <primitive numeric type> someDefName();} will allow access to the
- * values defined by {@literal @def} rules within the CSS file. The defined
- * value must be a raw number, a CSS length, or a percentage value.
+ * <li>{@code <primitive numeric type or String> someDefName();} will allow
+ * access to the values defined by {@literal @def} rules within the CSS file.
+ * The defined value must be a raw number, a CSS length, or a percentage value
+ * if it is to be returned as a numeric type.
* </ul>
*
* <p>
diff --git a/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java b/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
index 2a4a7e5..dff332a 100644
--- a/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
+++ b/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
@@ -115,6 +115,7 @@
*/
private final Map<String, Map<JMethod, String>> classReplacementsWithPrefix;
private final Pattern classSelectorPattern = Pattern.compile("\\.([^ :>+#.]*)");
+ private final Set<String> cssDefs = new HashSet<String>();
private final Set<String> externalClasses;
private final TreeLogger logger;
private final Set<JMethod> missingClasses;
@@ -137,6 +138,11 @@
}
@Override
+ public void endVisit(CssDef x, Context ctx) {
+ cssDefs.add(x.getKey());
+ }
+
+ @Override
public void endVisit(CssSelector x, Context ctx) {
String sel = x.getSelector();
@@ -193,6 +199,19 @@
@Override
public void endVisit(CssStylesheet x, Context ctx) {
boolean stop = false;
+
+ // Skip names corresponding to @def entries. They too can be declared as
+ // String accessors.
+ List<JMethod> toRemove = new ArrayList<JMethod>();
+ for (JMethod method : missingClasses) {
+ if (cssDefs.contains(method.getName())) {
+ toRemove.add(method);
+ }
+ }
+ for (JMethod method : toRemove) {
+ missingClasses.remove(method);
+ }
+
if (!missingClasses.isEmpty()) {
stop = true;
TreeLogger errorLogger = logger.branch(TreeLogger.INFO,
@@ -231,6 +250,15 @@
}
}
+ static class DefsCollector extends CssVisitor {
+ private final Set<String> defs = new HashSet<String>();
+
+ @Override
+ public void endVisit(CssDef x, Context ctx) {
+ defs.add(x.getKey());
+ }
+ }
+
/**
* Collects all {@code @external} declarations in the stylesheet.
*/
@@ -1212,8 +1240,8 @@
private boolean prettyOutput;
private Map<JClassType, Map<JMethod, String>> replacementsByClassAndMethod;
private Map<JMethod, String> replacementsForSharedMethods;
- private Map<JMethod, CssStylesheet> stylesheetMap;
private JClassType stringType;
+ private Map<JMethod, CssStylesheet> stylesheetMap;
@Override
public String createAssignment(TreeLogger logger, ResourceContext context,
@@ -1701,34 +1729,39 @@
NumberValue numberValue = def.getValues().get(0).isNumberValue();
- if (numberValue == null) {
- logger.log(TreeLogger.ERROR, "The define named " + name
- + " does not define a numeric value");
- throw new UnableToCompleteException();
+ String returnExpr = "";
+ JClassType classReturnType = toImplement.getReturnType().isClass();
+ if (classReturnType != null
+ && "java.lang.String".equals(classReturnType.getQualifiedSourceName())) {
+ returnExpr = "\"" + Generator.escape(def.getValues().get(0).toString())
+ + "\"";
+ } else {
+ JPrimitiveType returnType = toImplement.getReturnType().isPrimitive();
+ if (returnType == null) {
+ logger.log(TreeLogger.ERROR, toImplement.getName()
+ + ": Return type must be primitive type or String for "
+ + "@def accessors");
+ throw new UnableToCompleteException();
+ }
+ if (returnType == JPrimitiveType.INT || returnType == JPrimitiveType.LONG) {
+ returnExpr = "" + Math.round(numberValue.getValue());
+ } else if (returnType == JPrimitiveType.FLOAT) {
+ returnExpr = numberValue.getValue() + "F";
+ } else if (returnType == JPrimitiveType.DOUBLE) {
+ returnExpr = "" + numberValue.getValue();
+ } else {
+ logger.log(TreeLogger.ERROR, returnType.getQualifiedSourceName()
+ + " is not a valid primitive return type for @def accessors");
+ throw new UnableToCompleteException();
+ }
}
-
- JPrimitiveType returnType = toImplement.getReturnType().isPrimitive();
- assert returnType != null;
-
sw.print(toImplement.getReadableDeclaration(false, false, false, false,
true));
sw.println(" {");
sw.indent();
- if (returnType == JPrimitiveType.INT || returnType == JPrimitiveType.LONG) {
- sw.println("return " + Math.round(numberValue.getValue()) + ";");
- } else if (returnType == JPrimitiveType.FLOAT) {
- sw.println("return " + numberValue.getValue() + "F;");
- } else if (returnType == JPrimitiveType.DOUBLE) {
- sw.println("return " + numberValue.getValue() + ";");
- } else {
- logger.log(TreeLogger.ERROR, returnType.getQualifiedSourceName()
- + " is not a valid return type for @def accessors");
- throw new UnableToCompleteException();
- }
+ sw.println("return " + returnExpr + ";");
sw.outdent();
sw.println("}");
-
- numberValue.getValue();
}
/**
@@ -1738,20 +1771,31 @@
CssStylesheet sheet, JMethod[] methods,
Map<JMethod, String> obfuscatedClassNames)
throws UnableToCompleteException {
+
+ // Get list of @defs
+ DefsCollector collector = new DefsCollector();
+ collector.accept(sheet);
+
for (JMethod toImplement : methods) {
String name = toImplement.getName();
if ("getName".equals(name) || "getText".equals(name)) {
continue;
}
- if (toImplement.getReturnType().equals(stringType)
- && toImplement.getParameters().length == 0) {
- writeClassAssignment(sw, toImplement, obfuscatedClassNames);
+ // Bomb out if there is a collision between @def and a style name
+ if (collector.defs.contains(name)
+ && obfuscatedClassNames.containsKey(toImplement)) {
+ logger.log(TreeLogger.ERROR, "@def shadows CSS class name: " + name
+ + ". Fix by renaming the @def name or the CSS class name.");
+ throw new UnableToCompleteException();
+ }
- } else if (toImplement.getReturnType().isPrimitive() != null
+ if (collector.defs.contains(toImplement.getName())
&& toImplement.getParameters().length == 0) {
writeDefAssignment(logger, sw, toImplement, sheet);
-
+ } else if (toImplement.getReturnType().equals(stringType)
+ && toImplement.getParameters().length == 0) {
+ writeClassAssignment(sw, toImplement, obfuscatedClassNames);
} else {
logger.log(TreeLogger.ERROR, "Don't know how to implement method "
+ toImplement.getName());
diff --git a/user/test/com/google/gwt/resources/client/CSSResourceTest.java b/user/test/com/google/gwt/resources/client/CSSResourceTest.java
index a07e86c..6cc3eca 100644
--- a/user/test/com/google/gwt/resources/client/CSSResourceTest.java
+++ b/user/test/com/google/gwt/resources/client/CSSResourceTest.java
@@ -35,10 +35,19 @@
}
interface CssWithDefines extends CssResource {
+ String colorString();
+
double lengthFloat();
int lengthInt();
+ String lengthString();
+
+ int overrideInt();
+
+ @ClassName("overrideInt")
+ String overrideIntClass();
+
double percentFloat();
int percentInt();
@@ -258,6 +267,14 @@
assertEquals(50, defines.percentInt());
assertEquals(50.5, defines.percentFloat());
+
+ assertEquals("100px", defines.lengthString());
+ assertEquals("#f00", defines.colorString());
+
+ assertEquals(10, defines.overrideInt());
+ assertNotNull(defines.overrideIntClass());
+ assertFalse("10px".equals(defines.overrideIntClass()));
+ assertFalse("10".equals(defines.overrideIntClass()));
}
public void testMultipleBundles() {
diff --git a/user/test/com/google/gwt/resources/client/deftest.css b/user/test/com/google/gwt/resources/client/deftest.css
index 0db8a81..f168b53 100644
--- a/user/test/com/google/gwt/resources/client/deftest.css
+++ b/user/test/com/google/gwt/resources/client/deftest.css
@@ -22,4 +22,24 @@
@def percentFloat 50.5%;
@def lengthInt 50px;
- @def lengthFloat 1.5px;
\ No newline at end of file
+ @def lengthFloat 1.5px;
+
+ @def lengthString 100px;
+ @def colorString #f00;
+
+ /* Uncomment this, and you should get an error about a @def shadowing a name */
+ /*
+ .colorString {
+ background-color: #f00;
+ }
+ */
+
+ /* Shouldn't get an error here because the method to access the style name
+ * is annotated with @ClassName()
+ */
+ @def overrideInt 10px;
+ .overrideInt {
+ width: 10px;
+ }
+
+
\ No newline at end of file