Revision: 6218
Author: b...@google.com
Date: Fri Sep 25 12:26:41 2009
Log: Speed CssResource class selector rewriting.

Patch by: amirkashani, bobv
Review by: bobv, amirkashani
http://code.google.com/p/google-web-toolkit/source/detail?r=6218

Modified:
  /trunk/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java

=======================================
--- /trunk/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java       
 
Wed Sep  9 10:35:52 2009
+++ /trunk/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java       
 
Fri Sep 25 12:26:41 2009
@@ -1,12 +1,12 @@
  /*
   * Copyright 2008 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
@@ -97,7 +97,6 @@
  import java.util.SortedSet;
  import java.util.TreeSet;
  import java.util.regex.Matcher;
-import java.util.regex.Pattern;
  import java.util.zip.Adler32;

  /**
@@ -105,21 +104,59 @@
   */
  public final class CssResourceGenerator extends AbstractResourceGenerator {
    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.
+     * A tag to indicate that an externally-defined CSS class has no  
JMethod
+     * that is used to access it.
       */
-    private final Map<String, Map<JMethod, String>>  
classReplacementsWithPrefix;
+    private static final Replacement UNREFERENCED_EXTERNAL = new  
Replacement(
+        null, null);
+
+    /*
+     * TODO: Replace with Pair<A, B>.
+     */
+    private static class Replacement {
+
+      private JMethod method;
+      private String obfuscatedClassName;
+
+      public Replacement(JMethod method, String obfuscatedClassName) {
+        this.method = method;
+        this.obfuscatedClassName = obfuscatedClassName;
+      }
+
+      public JMethod getMethod() {
+        return method;
+      }
+
+      public String getObfuscatedClassName() {
+        return obfuscatedClassName;
+      }
+
+      /**
+       * For debugging use only.
+       */
+      public String toString() {
+        if (this == UNREFERENCED_EXTERNAL) {
+          return "Unreferenced external class name";
+        } else {
+          return method.getName() + "=" + obfuscatedClassName;
+        }
+      }
+    }
+
+    /**
+     * Records replacements that have actually been performed.
+     */
+    private final Map<JMethod, String> actualReplacements = new  
IdentityHashMap<JMethod, String>();
      private final Set<String> cssDefs = new HashSet<String>();
-    private final Set<String> externalClasses;
+
+    /**
+     * The task-list of replacements to perform in the stylesheet.
+     */
+    private final Map<String, Replacement> potentialReplacements;
      private final TreeLogger logger;
      private final Set<JMethod> missingClasses;
-    private final Set<String> replacedClasses = new HashSet<String>();
      private final boolean strict;
      private final Set<String> unknownClasses = new HashSet<String>();

@@ -127,9 +164,10 @@
          Map<String, Map<JMethod, String>> classReplacementsWithPrefix,
          boolean strict, Set<String> externalClasses) {
        this.logger = logger.branch(TreeLogger.DEBUG, "Replacing CSS class  
names");
-      this.classReplacementsWithPrefix = classReplacementsWithPrefix;
        this.strict = strict;
-      this.externalClasses = externalClasses;
+
+      potentialReplacements =  
computeReplacements(classReplacementsWithPrefix,
+          externalClasses);

        // Require a definition for all classes in the default namespace
        assert classReplacementsWithPrefix.containsKey("");
@@ -144,56 +182,45 @@

      @Override
      public void endVisit(CssSelector x, Context ctx) {
+
        String sel = x.getSelector();
-
-      // TODO This would be simplified by having a class hierarchy for  
selectors
-      for (Map.Entry<String, Map<JMethod, String>> outerEntry :  
classReplacementsWithPrefix.entrySet()) {
-        String prefix = outerEntry.getKey();
-        for (Map.Entry<JMethod, String> entry :  
outerEntry.getValue().entrySet()) {
-          JMethod method = entry.getKey();
-          String sourceClassName = method.getName();
-          String obfuscatedClassName = entry.getValue();
-
-          ClassName className = method.getAnnotation(ClassName.class);
-          if (className != null) {
-            sourceClassName = className.value();
-          }
-
-          sourceClassName = prefix + sourceClassName;
-
-          Pattern p = Pattern.compile("(.*)\\.("
-              + Pattern.quote(sourceClassName) + ")([ :>+#.].*|$)");
-          Matcher m = p.matcher(sel);
-          if (m.find()) {
-            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(obfuscatedClassName);
-            }
-          }
-        }
-      }
-
-      sel = sel.trim();
-
-      if (strict) {
-        Matcher m = CssSelector.CLASS_SELECTOR_PATTERN.matcher(sel);
-        while (m.find()) {
-          String classSelector = m.group(1);
-          if (!replacedClasses.contains(classSelector)
-              && !externalClasses.contains(classSelector)) {
-            unknownClasses.add(classSelector);
-          }
-        }
+      int originalLength = sel.length();
+
+      Matcher ma = CssSelector.CLASS_SELECTOR_PATTERN.matcher(sel);
+      StringBuilder sb = new StringBuilder(originalLength);
+      int start = 0;
+
+      while (ma.find()) {
+        String sourceClassName = ma.group(1);
+
+        Replacement entry = potentialReplacements.get(sourceClassName);
+
+        if (entry == null) {
+          unknownClasses.add(sourceClassName);
+          continue;
+
+        } else if (entry == UNREFERENCED_EXTERNAL) {
+          // An @external without an accessor method. This is OK.
+          continue;
+        }
+
+        JMethod method = entry.getMethod();
+        String obfuscatedClassName = entry.getObfuscatedClassName();
+
+        // Consume the interstitial portion of the original selector
+        sb.append(sel.subSequence(start, ma.start(1)));
+        sb.append(obfuscatedClassName);
+        start = ma.end(1);
+
+        actualReplacements.put(method, obfuscatedClassName);
+        missingClasses.remove(method);
        }

-      x.setSelector(sel);
+      if (start != 0) {
+        // Consume the remainder and update the selector
+        sb.append(sel.subSequence(start, originalLength));
+        x.setSelector(sb.toString());
+      }
      }

      @Override
@@ -245,9 +272,66 @@
        }
      }

+    /**
+     * Reports the replacements that were actually performed by this  
visitor.
+     */
      public Map<JMethod, String> getReplacements() {
        return actualReplacements;
      }
+
+    /**
+     * Flatten class name lookups to speed selector rewriting.
+     *
+     * @param classReplacementsWithPrefix 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.
+     * @return A flattened version of the classReplacementWithPrefix map,  
where
+     *         the keys are the source class name (with prefix included),  
and
+     *         values have the obfuscated class name and associated  
JMethod.
+     */
+    private Map<String, Replacement> computeReplacements(
+        Map<String, Map<JMethod, String>> classReplacementsWithPrefix,
+        Set<String> externalClasses) {
+
+      Map<String, Replacement> toReturn = new HashMap<String,  
Replacement>();
+
+      for (String externalClass : externalClasses) {
+        toReturn.put(externalClass, UNREFERENCED_EXTERNAL);
+      }
+
+      for (Map.Entry<String, Map<JMethod, String>> outerEntry :  
classReplacementsWithPrefix.entrySet()) {
+        String prefix = outerEntry.getKey();
+
+        for (Map.Entry<JMethod, String> entry :  
outerEntry.getValue().entrySet()) {
+          JMethod method = entry.getKey();
+          String sourceClassName = method.getName();
+          String obfuscatedClassName = entry.getValue();
+
+          ClassName className = method.getAnnotation(ClassName.class);
+          if (className != null) {
+            sourceClassName = className.value();
+          }
+
+          sourceClassName = prefix + sourceClassName;
+
+          if (externalClasses.contains(sourceClassName)) {
+            /*
+             * It simplifies the sanity-checking logic to treat external  
classes
+             * as though they were simply obfuscated to exactly the value  
the
+             * user wants.
+             */
+            obfuscatedClassName = sourceClassName;
+          }
+
+          toReturn.put(sourceClassName, new Replacement(method,
+              obfuscatedClassName));
+        }
+      }
+      return Collections.unmodifiableMap(toReturn);
+    }
    }

    static class DefsCollector extends CssVisitor {
@@ -1086,7 +1170,7 @@
       * Very large concatenation expressions using '+' cause the GWT  
compiler to
       * overflow the stack due to deep AST nesting. The workaround for now  
is to
       * force it to be more balanced using intermediate concatenation  
groupings.
-     *
+     *
       * This variable is used to track the number of subexpressions within  
the
       * current parenthetical expression.
       */
@@ -1159,7 +1243,7 @@
    /**
     * Check if number of concat expressions currently exceeds limit and  
either
     * append '+' if the limit isn't reached or ') + (' if it is.
-   *
+   *
     * @return numExpressions + 1 or 0 if limit was exceeded.
     */
    private static int concatOp(int numExpressions, StringBuilder b) {
@@ -1439,7 +1523,8 @@
            name = classNameOverride.value();
          }

-        String obfuscatedClassName = classPrefix +  
makeIdent(classCounter.next());
+        String obfuscatedClassName = classPrefix
+            + makeIdent(classCounter.next());
          if (prettyOutput) {
            obfuscatedClassName += "-"
                + type.getQualifiedSourceName().replaceAll("[.$]", "-") + "-"
@@ -1571,7 +1656,7 @@
       * result regardless of the order in which the generators fired. (It no
       * longer behaves that way, as that scheme prevented the generation of  
new
       * CssResource interfaces, but the complexity lives on.)
-     *
+     *
       * TODO(rjrjr,bobv) These days scottb tells us we're guaranteed that  
the
       * recompiling the same code will fire the generators in a consistent  
order,
       * so the old gymnastics aren't really justified anyway. It would  
probably
@@ -1657,7 +1742,7 @@
    /**
     * 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

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

Reply via email to