Author: [email protected]
Date: Wed May 20 16:14:31 2009
New Revision: 5449

Added:
     
trunk/user/src/com/google/gwt/resources/css/ast/CssExternalSelectors.java    
(contents, props changed)
Modified:
    trunk/user/src/com/google/gwt/resources/client/CssResource.java
    trunk/user/src/com/google/gwt/resources/css/CssGenerationVisitor.java
    trunk/user/src/com/google/gwt/resources/css/GenerateCssAst.java
    trunk/user/src/com/google/gwt/resources/css/ast/CssNodeCloner.java
    trunk/user/src/com/google/gwt/resources/css/ast/CssVisitor.java
     
trunk/user/src/com/google/gwt/resources/rebind/context/AbstractClientBundleGenerator.java
    trunk/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
    trunk/user/test/com/google/gwt/resources/client/CSSResourceTest.java
    trunk/user/test/com/google/gwt/resources/client/test.css
    trunk/user/test/com/google/gwt/resources/client/unrelatedDescendants.css

Log:
Add an @external directive to CssResource to allow strict-mode resources to  
work with external/legacy CSS.

Patch by: bobv
Review by: rjrjr


Modified: trunk/user/src/com/google/gwt/resources/client/CssResource.java
==============================================================================
--- trunk/user/src/com/google/gwt/resources/client/CssResource.java      
(original)
+++ trunk/user/src/com/google/gwt/resources/client/CssResource.java     Wed May 
 
20 16:14:31 2009
@@ -47,6 +47,9 @@
   * previously-defined rules, but no forward-references will be  
honored.</li>
   * <li>{...@code @eval NAME Java-expression; .myClass background: NAME;}  
Define a
   * constant based on a Java expression.</li>
+ * <li>{...@code @external class-name, class-name, ...;} Disable obfuscation  
for
+ * specific class selectors and exclude those class selectors from
+ * {...@link Strict} requirements.</li>
   * <li><code>{...@literal @if} [!]property list of values {ruleBlock}</code>  
Include or
   * exclude CSS rules based on the value of a deferred-binding property.  
Also
   * {...@code @elif} and {...@code @else} follow the same pattern.<br/>
@@ -219,11 +222,11 @@
    /**
     * The presence of this annotation on a CssResource accessor method  
indicates
     * that any class selectors that do not correspond with a String accessor
-   * method in the return type should trigger a compilation error. In the  
normal
-   * case, any unobfuscatable class selectors will be emitted as-is. This
-   * annotation ensures that the resource does not contribute any  
unobfuscated
-   * class selectors into the global CSS namespace and is recommended as  
the
-   * default for library-provided CssResource instances.
+   * method in the return type should trigger a compilation error. In the
+   * default case, any unobfuscatable class selectors will be emitted  
as-is.
+   * This annotation ensures that the CssResource does not contribute any
+   * unobfuscated class selectors into the global CSS namespace and is
+   * recommended as the default behavior for CssResources.
     * <p>
     * Given these interfaces:
     *
@@ -241,6 +244,26 @@
     *
     * the source CSS will fail to compile if it does not contain exactly  
the one
     * class selector defined in the MyCss type.
+   * <p>
+   * The {...@code @external} at-rule can be used in strict mode to indicate  
that
+   * certain class selectors are exempt from the strict semantics. Class
+   * selectors marked as external will not be obfuscated and are not  
required to
+   * have string accessor functions. Consider the following example in
+   * conjunction with the above <code>MyCss</code> interface:
+   *
+   * <pre>
+   * {...@literal @external} .foo, .bar;
+   * .foo .someClass .bar { .... }
+   * </pre>
+   *
+   * The resulting CSS would look like:
+   *
+   * <pre>
+   * .foo .A1234 .bar { .... }
+   * </pre>
+   *
+   * If a <code>String foo()</code> method were defined in  
<code>MyCss</code>,
+   * it would return the string value "<code>foo</code>".
     */
    @Documented
    @Target(ElementType.METHOD)

Modified:  
trunk/user/src/com/google/gwt/resources/css/CssGenerationVisitor.java
==============================================================================
--- trunk/user/src/com/google/gwt/resources/css/CssGenerationVisitor.java       
 
(original)
+++ trunk/user/src/com/google/gwt/resources/css/CssGenerationVisitor.java       
 
Wed May 20 16:14:31 2009
@@ -19,6 +19,7 @@
  import com.google.gwt.resources.css.ast.Context;
  import com.google.gwt.resources.css.ast.CssDef;
  import com.google.gwt.resources.css.ast.CssEval;
+import com.google.gwt.resources.css.ast.CssExternalSelectors;
  import com.google.gwt.resources.css.ast.CssIf;
  import com.google.gwt.resources.css.ast.CssMediaRule;
  import com.google.gwt.resources.css.ast.CssNoFlip;
@@ -124,6 +125,19 @@
    public boolean visit(CssEval x, Context ctx) {
      // These are not valid CSS
      out.printOpt("/* CssEval */");
+    out.newlineOpt();
+    return false;
+  }
+
+  @Override
+  public boolean visit(CssExternalSelectors x, Context ctx) {
+    // These are not valid CSS
+    out.printOpt("/* @external");
+    for (String className : x.getClasses()) {
+      out.printOpt(" ");
+      out.printOpt(className);
+    }
+    out.printOpt("; */");
      out.newlineOpt();
      return false;
    }

Modified: trunk/user/src/com/google/gwt/resources/css/GenerateCssAst.java
==============================================================================
--- trunk/user/src/com/google/gwt/resources/css/GenerateCssAst.java      
(original)
+++ trunk/user/src/com/google/gwt/resources/css/GenerateCssAst.java     Wed May 
 
20 16:14:31 2009
@@ -19,6 +19,7 @@
  import com.google.gwt.core.ext.UnableToCompleteException;
  import com.google.gwt.resources.css.ast.CssDef;
  import com.google.gwt.resources.css.ast.CssEval;
+import com.google.gwt.resources.css.ast.CssExternalSelectors;
  import com.google.gwt.resources.css.ast.CssIf;
  import com.google.gwt.resources.css.ast.CssMediaRule;
  import com.google.gwt.resources.css.ast.CssNoFlip;
@@ -71,6 +72,7 @@
  import java.net.URISyntaxException;
  import java.net.URL;
  import java.util.ArrayList;
+import java.util.Collections;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
@@ -407,6 +409,16 @@

        CssEval eval = new CssEval(parts[1], parts[2]);
        addNode(eval);
+    }
+
+    void parseExternal(String atRule) throws CSSException {
+      // @external .foo, bar; Drop the dots and commas
+      String[] parts = atRule.substring(10, atRule.length() -  
1).replaceAll(
+          "(, *)|( +)", " ").replaceAll("\\.", "").split(" ");
+
+      CssExternalSelectors externals = new CssExternalSelectors();
+      Collections.addAll(externals.getClasses(), parts);
+      addNode(externals);
      }

      void parseIf(String atRule) throws CSSException {

Added:  
trunk/user/src/com/google/gwt/resources/css/ast/CssExternalSelectors.java
==============================================================================
--- (empty file)
+++  
trunk/user/src/com/google/gwt/resources/css/ast/CssExternalSelectors.java       
 
Wed May 20 16:14:31 2009
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may  
not
+ * use this file except in compliance with the License. You may obtain a  
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,  
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations  
under
+ * the License.
+ */
+package com.google.gwt.resources.css.ast;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * An AST node that allows the developer to indicate that certain class
+ * selectors appearing in the stylesheet should be considered external and  
not
+ * subject to obfuscation requirements.
+ *
+ * @see CssResource.Strict
+ */
+public class CssExternalSelectors extends CssNode {
+
+  private final Set<String> classes = new HashSet<String>();
+
+  public Set<String> getClasses() {
+    return classes;
+  }
+
+  public void traverse(CssVisitor visitor, Context context) {
+    visitor.visit(this, context);
+    visitor.endVisit(this, context);
+  }
+
+}

Modified: trunk/user/src/com/google/gwt/resources/css/ast/CssNodeCloner.java
==============================================================================
--- trunk/user/src/com/google/gwt/resources/css/ast/CssNodeCloner.java   
(original)
+++ trunk/user/src/com/google/gwt/resources/css/ast/CssNodeCloner.java  Wed  
May 20 16:14:31 2009
@@ -131,6 +131,14 @@
      return true;
    }

+  @Override
+  public boolean visit(CssExternalSelectors x, Context ctx) {
+    CssExternalSelectors newExternals = new CssExternalSelectors();
+    newExternals.getClasses().addAll(x.getClasses());
+    addToNodes(newExternals);
+    return true;
+  }
+
    /**
     * A CssIf has two lists of nodes, so we want to handle traversal in this
     * visitor.

Modified: trunk/user/src/com/google/gwt/resources/css/ast/CssVisitor.java
==============================================================================
--- trunk/user/src/com/google/gwt/resources/css/ast/CssVisitor.java      
(original)
+++ trunk/user/src/com/google/gwt/resources/css/ast/CssVisitor.java     Wed May 
 
20 16:14:31 2009
@@ -18,7 +18,10 @@
  import java.util.List;

  /**
- * The base class for visiting a CSS tree.
+ * The base class for visiting a CSS tree. Traversal is initiated with a  
call to
+ * one of the <code>accept</code> methods. The default behavior of the
+ * <code>visit</code> methods is to return <code>true</code> to indicate  
that
+ * the calling node should traverse its descendant nodes.
   */
  public class CssVisitor {
    protected static final Context UNMODIFIABLE_CONTEXT = new Context() {
@@ -66,6 +69,9 @@
    public void endVisit(CssEval x, Context ctx) {
    }

+  public void endVisit(CssExternalSelectors x, Context ctx) {
+  }
+
    public void endVisit(CssIf x, Context ctx) {
    }

@@ -101,6 +107,10 @@
    }

    public boolean visit(CssEval x, Context ctx) {
+    return true;
+  }
+
+  public boolean visit(CssExternalSelectors x, Context ctx) {
      return true;
    }


Modified:  
trunk/user/src/com/google/gwt/resources/rebind/context/AbstractClientBundleGenerator.java
==============================================================================
---  
trunk/user/src/com/google/gwt/resources/rebind/context/AbstractClientBundleGenerator.java
        
(original)
+++  
trunk/user/src/com/google/gwt/resources/rebind/context/AbstractClientBundleGenerator.java
        
Wed May 20 16:14:31 2009
@@ -268,7 +268,8 @@

        try {
          rhs = rg.createAssignment(logger.branch(TreeLogger.DEBUG,
-            "Creating assignment for " + m.getName()), resourceContext, m);
+            "Creating assignment for " + m.getName() + "()"),  
resourceContext,
+            m);
        } catch (UnableToCompleteException e) {
          fail = true;
          continue;

Modified:  
trunk/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
==============================================================================
--- trunk/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java        
 
(original)
+++ trunk/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java        
 
Wed May 20 16:14:31 2009
@@ -31,6 +31,7 @@
  import com.google.gwt.dev.util.DefaultTextOutput;
  import com.google.gwt.dev.util.Util;
  import com.google.gwt.dom.client.Element;
+import com.google.gwt.i18n.client.LocaleInfo;
  import com.google.gwt.resources.client.CssResource;
  import com.google.gwt.resources.client.DataResource;
  import com.google.gwt.resources.client.ImageResource;
@@ -48,6 +49,7 @@
  import com.google.gwt.resources.css.ast.CssCompilerException;
  import com.google.gwt.resources.css.ast.CssDef;
  import com.google.gwt.resources.css.ast.CssEval;
+import com.google.gwt.resources.css.ast.CssExternalSelectors;
  import com.google.gwt.resources.css.ast.CssIf;
  import com.google.gwt.resources.css.ast.CssMediaRule;
  import com.google.gwt.resources.css.ast.CssModVisitor;
@@ -100,11 +102,19 @@
   * Provides implementations of CSSResources.
   */
  public class CssResourceGenerator extends AbstractResourceGenerator {
-  private static final String[] DEFAULT_EXTENSIONS = new String[] {".css"};
-
    static class ClassRenamer extends CssVisitor {
+    private final Map<JMethod, String> actualReplacements = new  
IdentityHashMap<JMethod, String>();
+
+    /**
+     * This is a map of local prefixes to the obfuscated names of imported
+     * methods. If a CssResource makes use of the {...@link Import}  
annotation, the
+     * keys of this map will correspond to the {...@link ImportedWithPrefix}  
value
+     * defined on the imported CssResource. The zero-length string key  
holds the
+     * obfuscated names for the CssResource that is being generated.
+     */
      private final Map<String, Map<JMethod, String>>  
classReplacementsWithPrefix;
      private final Pattern classSelectorPattern =  
Pattern.compile("\\.([^ :>+#.]*)");
+    private final Set<String> externalClasses;
      private final TreeLogger logger;
      private final Set<JMethod> missingClasses;
      private final Set<String> replacedClasses = new HashSet<String>();
@@ -113,10 +123,11 @@

      public ClassRenamer(TreeLogger logger,
          Map<String, Map<JMethod, String>> classReplacementsWithPrefix,
-        boolean strict) {
+        boolean strict, Set<String> externalClasses) {
        this.logger = logger.branch(TreeLogger.DEBUG, "Replacing CSS class  
names");
        this.classReplacementsWithPrefix = classReplacementsWithPrefix;
        this.strict = strict;
+      this.externalClasses = externalClasses;

        // Require a definition for all classes in the default namespace
        assert classReplacementsWithPrefix.containsKey("");
@@ -132,22 +143,31 @@
        for (Map.Entry<String, Map<JMethod, String>> outerEntry :  
classReplacementsWithPrefix.entrySet()) {
          String prefix = outerEntry.getKey();
          for (Map.Entry<JMethod, String> entry :  
outerEntry.getValue().entrySet()) {
-          String name = entry.getKey().getName();
+          JMethod method = entry.getKey();
+          String sourceClassName = method.getName();
+          String obfuscatedClassName = entry.getValue();

-          ClassName className =  
entry.getKey().getAnnotation(ClassName.class);
+          ClassName className = method.getAnnotation(ClassName.class);
            if (className != null) {
-            name = className.value();
+            sourceClassName = className.value();
            }
-          name = prefix + name;

-          Pattern p = Pattern.compile("(.*)\\.(" + Pattern.quote(name)
-              + ")([ :>+#.].*|$)");
+          sourceClassName = prefix + sourceClassName;
+
+          Pattern p = Pattern.compile("(.*)\\.("
+              + Pattern.quote(sourceClassName) + ")([ :>+#.].*|$)");
            Matcher m = p.matcher(sel);
            if (m.find()) {
-            sel = m.group(1) + "." + entry.getValue() + m.group(3);
-            missingClasses.remove(entry.getKey());
+            if (externalClasses.contains(sourceClassName)) {
+              actualReplacements.put(method, sourceClassName);
+            } else {
+              sel = m.group(1) + "." + obfuscatedClassName + m.group(3);
+              actualReplacements.put(method, obfuscatedClassName);
+            }
+
+            missingClasses.remove(method);
              if (strict) {
-              replacedClasses.add(entry.getValue());
+              replacedClasses.add(obfuscatedClassName);
              }
            }
          }
@@ -159,7 +179,8 @@
          Matcher m = classSelectorPattern.matcher(sel);
          while (m.find()) {
            String classSelector = m.group(1);
-          if (!replacedClasses.contains(classSelector)) {
+          if (!replacedClasses.contains(classSelector)
+              && !externalClasses.contains(classSelector)) {
              unknownClasses.add(classSelector);
            }
          }
@@ -200,6 +221,26 @@
          throw new CssCompilerException("Missing a CSS replacement");
        }
      }
+
+    public Map<JMethod, String> getReplacements() {
+      return actualReplacements;
+    }
+  }
+
+  /**
+   * Collects all {...@code @external} declarations in the stylesheet.
+   */
+  static class ExternalClassesCollector extends CssVisitor {
+    private final Set<String> classes = new HashSet<String>();
+
+    @Override
+    public void endVisit(CssExternalSelectors x, Context ctx) {
+      classes.addAll(x.getClasses());
+    }
+
+    public Set<String> getClasses() {
+      return classes;
+    }
    }

    /**
@@ -224,12 +265,11 @@
            String propertyName = x.getPropertyName();
            String propValue = null;
            try {
-            SelectionProperty selProp
-                = oracle.getSelectionProperty(logger, propertyName);
+            SelectionProperty selProp = oracle.getSelectionProperty(logger,
+                propertyName);
              propValue = selProp.getCurrentValue();
            } catch (BadPropertyValueException e) {
-            ConfigurationProperty confProp
-                = oracle.getConfigurationProperty(propertyName);
+            ConfigurationProperty confProp =  
oracle.getConfigurationProperty(propertyName);
              propValue = confProp.getValues().get(0);
            }

@@ -922,6 +962,8 @@
     */
    private static final int CONCAT_EXPRESSION_LIMIT = 20;

+  private static final String[] DEFAULT_EXTENSIONS = new String[] {".css"};
+
    public static void main(String[] args) {
      for (int i = 0; i < 1000; i++) {
        System.out.println(makeIdent(i));
@@ -1158,11 +1200,8 @@
    private boolean enableMerge;
    private boolean prettyOutput;
    private Map<JClassType, Map<JMethod, String>>  
replacementsByClassAndMethod;
-
    private Map<JMethod, String> replacementsForSharedMethods;
-
    private Map<JMethod, CssStylesheet> stylesheetMap;
-
    private JClassType stringType;

    @Override
@@ -1206,57 +1245,13 @@
        }
      }

-    /*
-     * getOverridableMethods is used to handle CssResources extending
-     * non-CssResource types. See the discussion in  
computeReplacementsForType.
-     */
-    for (JMethod toImplement : cssResourceSubtype.getOverridableMethods())  
{
-      String name = toImplement.getName();
-      if ("getName".equals(name) || "getText".equals(name)) {
-        continue;
-      }
-
-      if (toImplement.getReturnType().equals(stringType)
-          && toImplement.getParameters().length == 0) {
-        writeClassAssignment(sw, toImplement,  
replacementsWithPrefix.get(""));
-
-      } else if (toImplement.getReturnType().isPrimitive() != null
-          && toImplement.getParameters().length == 0) {
-        writeDefAssignment(logger, sw, toImplement,  
stylesheetMap.get(method));
-
-      } else {
-        logger.log(TreeLogger.ERROR, "Don't know how to implement method "
-            + toImplement.getName());
-        throw new UnableToCompleteException();
-      }
-    }
-
      sw.println("public String getText() {");
      sw.indent();
-
-    boolean strict = method.getAnnotation(Strict.class) != null;
-    if (!strict) {
-      /*
-       * The developer may choose to force strict behavior onto the  
system. If
-       * the method does already have the @Strict annotation, print a  
warning.
-       */
-      try {
-        PropertyOracle propertyOracle =  
context.getGeneratorContext().getPropertyOracle();
-        ConfigurationProperty prop =  
propertyOracle.getConfigurationProperty(
-            "CssResource.strictAccessors");
-        String propertyValue = prop.getValues().get(0);
-        if (Boolean.valueOf(propertyValue)) {
-          logger.log(TreeLogger.WARN, "CssResource.forceStrict is true,  
but "
-              + method.getName() + "() is missing the @Strict  
annotation.");
-          strict = true;
-        }
-      } catch (BadPropertyValueException e) {
-        // Ignore
-      }
-    }
-
+    boolean strict = isStrict(logger, context, method);
+    Map<JMethod, String> actualReplacements = new IdentityHashMap<JMethod,  
String>();
      String cssExpression = makeExpression(logger, context,  
cssResourceSubtype,
-        stylesheetMap.get(method), replacementsWithPrefix, strict);
+        stylesheetMap.get(method), replacementsWithPrefix, strict,
+        actualReplacements);
      sw.println("return " + cssExpression + ";");
      sw.outdent();
      sw.println("}");
@@ -1267,6 +1262,13 @@
      sw.outdent();
      sw.println("}");

+    /*
+     * getOverridableMethods is used to handle CssResources extending
+     * non-CssResource types. See the discussion in  
computeReplacementsForType.
+     */
+    writeUserMethods(logger, sw, stylesheetMap.get(method),
+        cssResourceSubtype.getOverridableMethods(), actualReplacements);
+
      sw.outdent();
      sw.println("}");

@@ -1278,19 +1280,15 @@
        throws UnableToCompleteException {
      try {
        PropertyOracle propertyOracle =  
context.getGeneratorContext().getPropertyOracle();
-      ConfigurationProperty styleProp
-          = propertyOracle.getConfigurationProperty("CssResource.style");
+      ConfigurationProperty styleProp =  
propertyOracle.getConfigurationProperty("CssResource.style");
        String style = styleProp.getValues().get(0);
        prettyOutput = style.equals("pretty");

-      ConfigurationProperty mergeProp
-          =  
propertyOracle.getConfigurationProperty("CssResource.mergeEnabled");
+      ConfigurationProperty mergeProp =  
propertyOracle.getConfigurationProperty("CssResource.mergeEnabled");
        String merge = mergeProp.getValues().get(0);
        enableMerge = merge.equals("true");

-      ConfigurationProperty classPrefixProp
-          = propertyOracle.getConfigurationProperty(
-              "CssResource.obfuscationPrefix");
+      ConfigurationProperty classPrefixProp =  
propertyOracle.getConfigurationProperty("CssResource.obfuscationPrefix");
        classPrefix = classPrefixProp.getValues().get(0);
      } catch (BadPropertyValueException e) {
        logger.log(TreeLogger.WARN, "Unable to query module property", e);
@@ -1504,14 +1502,46 @@
      return false;
    }

+  private boolean isStrict(TreeLogger logger, ResourceContext context,
+      JMethod method) {
+    boolean strict = method.getAnnotation(Strict.class) != null;
+    if (!strict) {
+      /*
+       * The developer may choose to force strict behavior onto the  
system. If
+       * the method does not already have an @Strict annotation, print a
+       * warning.
+       */
+      try {
+        PropertyOracle propertyOracle =  
context.getGeneratorContext().getPropertyOracle();
+        ConfigurationProperty prop =  
propertyOracle.getConfigurationProperty("CssResource.strictAccessors");
+        String propertyValue = prop.getValues().get(0);
+        if (Boolean.valueOf(propertyValue)) {
+          logger.log(TreeLogger.WARN,
+              "CssResource.strictAccessors is true, but " +  
method.getName()
+                  + "() is missing the @Strict annotation.");
+          strict = true;
+        }
+      } catch (BadPropertyValueException e) {
+        // Ignore
+      }
+    }
+    return strict;
+  }
+
    /**
     * Create a Java expression that evaluates to the string representation  
of the
     * stylesheet resource.
+   *
+   * @param actualReplacements An out parameter that will be populated by  
the
+   *          obfuscated class names that should be used for the particular
+   *          instance of the CssResource, based on any substitution
+   *          modifications encoded in the source CSS file
     */
    private String makeExpression(TreeLogger logger, ResourceContext context,
        JClassType cssResourceType, CssStylesheet sheet,
        Map<String, Map<JMethod, String>> classReplacementsWithPrefix,
-      boolean strict) throws UnableToCompleteException {
+      boolean strict, Map<JMethod, String> actualReplacements)
+      throws UnableToCompleteException {

      try {

@@ -1528,12 +1558,19 @@
        (new IfEvaluator(logger,
             
context.getGeneratorContext().getPropertyOracle())).accept(sheet);

-      // Rename css .class selectors
-      (new ClassRenamer(logger, classReplacementsWithPrefix,  
strict)).accept(sheet);
+      /*
+       * Rename css .class selectors. We look for all @external  
declarations in
+       * the stylesheet and then compute the per-instance replacements.
+       */
+      ExternalClassesCollector externalClasses = new  
ExternalClassesCollector();
+      externalClasses.accept(sheet);
+      ClassRenamer renamer = new ClassRenamer(logger,
+          classReplacementsWithPrefix, strict,  
externalClasses.getClasses());
+      renamer.accept(sheet);
+      actualReplacements.putAll(renamer.getReplacements());

        // Combine rules with identical selectors
        if (enableMerge) {
-        // TODO This is an off-switch while this is being developed; remove
          (new SplitRulesVisitor()).accept(sheet);
          (new MergeIdenticalSelectorsVisitor()).accept(sheet);
          (new MergeRulesByContentVisitor()).accept(sheet);
@@ -1547,7 +1584,7 @@
        String reversed = makeExpression(logger, context, cssResourceType,  
sheet,
            prettyOutput);

-       
return "com.google.gwt.i18n.client.LocaleInfo.getCurrentLocale().isRTL() ?  
("
+      return LocaleInfo.class.getName() + ".getCurrentLocale().isRTL() ? ("
            + reversed + ") : (" + standard + ")";

      } catch (CssCompilerException e) {
@@ -1628,5 +1665,34 @@
      sw.println("}");

      numberValue.getValue();
+  }
+
+  /**
+   * Write all of the user-defined methods in the CssResource subtype.
+   */
+  private void writeUserMethods(TreeLogger logger, SourceWriter sw,
+      CssStylesheet sheet, JMethod[] methods,
+      Map<JMethod, String> obfuscatedClassNames)
+      throws UnableToCompleteException {
+    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);
+
+      } else if (toImplement.getReturnType().isPrimitive() != null
+          && toImplement.getParameters().length == 0) {
+        writeDefAssignment(logger, sw, toImplement, sheet);
+
+      } else {
+        logger.log(TreeLogger.ERROR, "Don't know how to implement method "
+            + toImplement.getName());
+        throw new UnableToCompleteException();
+      }
+    }
    }
  }

Modified:  
trunk/user/test/com/google/gwt/resources/client/CSSResourceTest.java
==============================================================================
--- trunk/user/test/com/google/gwt/resources/client/CSSResourceTest.java        
 
(original)
+++ trunk/user/test/com/google/gwt/resources/client/CSSResourceTest.java        
 
Wed May 20 16:14:31 2009
@@ -23,7 +23,7 @@
  import com.google.gwt.resources.client.CssResource.Strict;

  /**
- *
+ * Contains various full-stack tests of the CssResource system.
   */
  public class CSSResourceTest extends GWTTestCase {

@@ -48,6 +48,10 @@
      int rawInt();
    }

+  interface HasDescendants extends CssResource {
+    String foo();
+  }
+
    interface MyCssResource extends CssResource, MyNonCssResource {
      @ClassName("replacement-not-java-ident")
      String nameOverride();
@@ -71,6 +75,8 @@
     * Check type inheritance.
     */
    interface MyCssResourceWithSprite extends MyCssResource {
+    String externalA();
+
      String extraSpriteClass();

      String multiClassA();
@@ -105,7 +111,7 @@
      @Source("unrelatedDescendants.css")
      @Import(value = {MyCssResourceA.class, MyCssResourceB.class})
      @Strict
-    CssResource descendants();
+    HasDescendants descendants();

      @Source("16x16.png")
      ImageResource spriteMethod();
@@ -223,6 +229,12 @@

      // Check commonly-used CSS3 constructs
      assertTrue(text.contains("background-color:rgba(0,0,0,0.5);"));
+
+    // Check external references
+    assertEquals("externalA", css.externalA());
+    assertTrue(text.contains(".externalA ." + css.replacement()));
+    assertTrue(text.contains(".externalB"));
+    assertTrue(text.contains(".externalC"));
    }

    public void testDefines() {
@@ -254,7 +266,10 @@

      String text = r1.descendants().getText();
      report(text);
+    assertEquals("foo", r1.descendants().foo());
      assertTrue(text.contains("." + r1.a().local() + " ." +  
r1.b().local()));
+    assertTrue(text.contains("." + r1.descendants().foo()));
+    assertTrue(text.contains(".bar"));
    }

    public void testSiblingCSS() {

Modified: trunk/user/test/com/google/gwt/resources/client/test.css
==============================================================================
--- trunk/user/test/com/google/gwt/resources/client/test.css    (original)
+++ trunk/user/test/com/google/gwt/resources/client/test.css    Wed May 20  
16:14:31 2009
@@ -208,3 +208,7 @@
  .css3Color {
    background-color: rgba(0, 0, 0, 0.5);
  }
+
+/* The @external parser attempts to be flexible */
+...@external externalA, .externalB externalC;
+.externalA .replacement, .externalB, .externalC {border: GREEN;}

Modified:  
trunk/user/test/com/google/gwt/resources/client/unrelatedDescendants.css
==============================================================================
---  
trunk/user/test/com/google/gwt/resources/client/unrelatedDescendants.css        
 
(original)
+++  
trunk/user/test/com/google/gwt/resources/client/unrelatedDescendants.css        
 
Wed May 20 16:14:31 2009
@@ -18,3 +18,9 @@
  .MyCssResourceA-local .gwt-MyCssResourceB-local {
    background: green;
  }
+
+/* These selectors are not to be obfuscated */
+...@external .foo, bar;
+.foo, .bar {
+  background: red;
+}

--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---

Reply via email to