Improve UiBinder+CssResource compile speed by caching results of GenerateCssAst.exec()
Patch by: bobv
Review by: rjrjr
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6504 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/resources/css/GenerateCssAst.java b/user/src/com/google/gwt/resources/css/GenerateCssAst.java
index 9ddb439..8336034 100644
--- a/user/src/com/google/gwt/resources/css/GenerateCssAst.java
+++ b/user/src/com/google/gwt/resources/css/GenerateCssAst.java
@@ -68,11 +68,13 @@
import java.io.IOException;
import java.io.StringReader;
+import java.lang.ref.SoftReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -641,7 +643,14 @@
}
private static final String LITERAL_FUNCTION_NAME = "literal";
-
+ /**
+ * We cache the stylesheets to prevent repeated parsing of the same source
+ * material. This is a common case if the user is using UiBinder's implicit
+ * stylesheets. It is necessary to use the list of URLs passed to exec because
+ * of the eager variable expansion performed by
+ * {@link GenerationHandler#parseDef(String)}.
+ */
+ private static final Map<List<URL>, SoftReference<CssStylesheet>> SHEETS = new HashMap<List<URL>, SoftReference<CssStylesheet>>();
private static final String VALUE_FUNCTION_NAME = "value";
/**
@@ -651,13 +660,22 @@
*/
public static CssStylesheet exec(TreeLogger logger, URL... stylesheets)
throws UnableToCompleteException {
+
+ List<URL> sheets = Arrays.asList(stylesheets);
+ SoftReference<CssStylesheet> ref = SHEETS.get(sheets);
+ CssStylesheet toReturn = ref == null ? null : ref.get();
+ if (toReturn != null) {
+ logger.log(TreeLogger.DEBUG, "Using cached result");
+ return new CssStylesheet(toReturn);
+ }
+
Parser p = new Parser();
Errors errors = new Errors(logger);
GenerationHandler g = new GenerationHandler(errors);
p.setDocumentHandler(g);
p.setErrorHandler(errors);
- for (URL stylesheet : stylesheets) {
+ for (URL stylesheet : sheets) {
TreeLogger branchLogger = logger.branch(TreeLogger.DEBUG,
"Parsing CSS stylesheet " + stylesheet.toExternalForm());
try {
@@ -678,7 +696,10 @@
throw new UnableToCompleteException();
}
- return g.css;
+ toReturn = g.css;
+ SHEETS.put(new ArrayList<URL>(sheets), new SoftReference<CssStylesheet>(
+ new CssStylesheet(toReturn)));
+ return toReturn;
}
/**
diff --git a/user/src/com/google/gwt/resources/css/ast/CssStylesheet.java b/user/src/com/google/gwt/resources/css/ast/CssStylesheet.java
index 743d54e..f9948c3 100644
--- a/user/src/com/google/gwt/resources/css/ast/CssStylesheet.java
+++ b/user/src/com/google/gwt/resources/css/ast/CssStylesheet.java
@@ -19,11 +19,30 @@
import java.util.List;
/**
- *
+ * An abstract representation of a CSS stylesheet.
*/
public class CssStylesheet extends CssNode implements HasNodes {
private List<CssNode> rules = new ArrayList<CssNode>();
+ public CssStylesheet() {
+ }
+
+ /**
+ * A copy constructor that will clone the contents of an existing
+ * CssStylesheet.
+ */
+ public CssStylesheet(CssStylesheet other) {
+ append(other);
+ }
+
+ /**
+ * Append the given stylesheet. The contents of the other stylesheet will be
+ * cloned.
+ */
+ public void append(CssStylesheet other) {
+ rules.addAll(CssNodeCloner.clone(CssNode.class, other.rules));
+ }
+
public List<CssNode> getNodes() {
return rules;
}
diff --git a/user/src/com/google/gwt/resources/rebind/context/AbstractClientBundleGenerator.java b/user/src/com/google/gwt/resources/rebind/context/AbstractClientBundleGenerator.java
index d668a59..0b13be3 100644
--- a/user/src/com/google/gwt/resources/rebind/context/AbstractClientBundleGenerator.java
+++ b/user/src/com/google/gwt/resources/rebind/context/AbstractClientBundleGenerator.java
@@ -186,28 +186,28 @@
*/
String generatedSimpleSourceName = generateSimpleSourceName(logger,
resourceContext, requirements);
+ String packageName = sourceType.getPackage().getName();
+ String createdClassName = packageName + "." + generatedSimpleSourceName;
- // Begin writing the generated source.
- ClassSourceFileComposerFactory f = new ClassSourceFileComposerFactory(
- sourceType.getPackage().getName(), generatedSimpleSourceName);
-
- // The generated class needs to be able to determine the module base URL
- f.addImport(GWT.class.getName());
-
- // Used by the map methods
- f.addImport(ResourcePrototype.class.getName());
-
- // The whole point of this exercise
- f.addImplementedInterface(sourceType.getQualifiedSourceName());
-
- String createdClassName = f.getCreatedClassName();
-
- // All source gets written through this Writer
- PrintWriter out = generatorContext.tryCreate(logger,
- sourceType.getPackage().getName(), generatedSimpleSourceName);
+ PrintWriter out = generatorContext.tryCreate(logger, packageName,
+ generatedSimpleSourceName);
// If an implementation already exists, we don't need to do any work
if (out != null) {
+ // Begin writing the generated source.
+ ClassSourceFileComposerFactory f = new ClassSourceFileComposerFactory(
+ packageName, generatedSimpleSourceName);
+
+ // The generated class needs to be able to determine the module base URL
+ f.addImport(GWT.class.getName());
+
+ // Used by the map methods
+ f.addImport(ResourcePrototype.class.getName());
+
+ // The whole point of this exercise
+ f.addImplementedInterface(sourceType.getQualifiedSourceName());
+
+ // All source gets written through this Writer
SourceWriter sw = f.createSourceWriter(generatorContext, out);
// Set the now-calculated simple source name