Add mix-in behavior to the LinkerContext visible to the Linkers.
Adds an <extend-linker-context> gwt.xml element.
Filter generated resources with a partial path starting with no-deploy/
Add a test case for the no-deploy shim.
Addresses issue 2137.
Patch by: bobv
Review by: bruce, scottb
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2086 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Messages.java b/dev/core/src/com/google/gwt/dev/cfg/Messages.java
index a15b208..a232c18 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/Messages.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/Messages.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * 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
@@ -28,6 +28,9 @@
public static final Message2ClassClass INVALID_CLASS_DERIVATION = new Message2ClassClass(
TreeLogger.ERROR, "Class '$0' must derive from '$1'");
+ public static final Message1String LINKER_NAME_INVALID = new Message1String(
+ TreeLogger.ERROR, "Invalid linker name '$0'");
+
public static final Message1String PROPERTY_NAME_INVALID = new Message1String(
TreeLogger.ERROR, "Invalid property name '$0'");
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
index e224fd2..2ab0fd6 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
@@ -23,11 +23,12 @@
import com.google.gwt.dev.jdt.TypeOracleBuilder;
import com.google.gwt.dev.jdt.URLCompilationUnitProvider;
import com.google.gwt.dev.linker.Linker;
+import com.google.gwt.dev.linker.LinkerContextShim;
import com.google.gwt.dev.util.Empty;
import com.google.gwt.dev.util.FileOracle;
import com.google.gwt.dev.util.FileOracleFactory;
-import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.PerfLogger;
+import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.FileOracleFactory.FileFilter;
import org.apache.tools.ant.types.ZipScanner;
@@ -97,9 +98,9 @@
private TypeOracle lazyTypeOracle;
- private final Map<String, Linker> linkersByName = new HashMap<String, Linker>();
+ private final List<Class<? extends LinkerContextShim>> linkerContextShimTypes = new ArrayList<Class<? extends LinkerContextShim>>();
- private final Map<String, String> linkerTypesByName = new HashMap<String, String>();
+ private final Map<String, Linker> linkersByName = new HashMap<String, Linker>();
private final long moduleDefCreationTime = System.currentTimeMillis();
@@ -131,8 +132,19 @@
gwtXmlFiles.add(xmlFile);
}
- public void addLinker(String name, String className) {
- linkerTypesByName.put(name, className);
+ public void addLinker(String name, Linker linker) {
+ linkersByName.put(name, linker);
+ }
+
+ public void addLinkerContextShim(Class<? extends LinkerContextShim> clazz) {
+ /*
+ * It's possible a shim may be registered more than once, so this check is
+ * used to de-duplicate the final list, which will reflect the order of the
+ * first appearance of any LinkerContextShim type.
+ */
+ if (!linkerContextShimTypes.contains(clazz)) {
+ linkerContextShimTypes.add(clazz);
+ }
}
public synchronized void addPublicPackage(String publicPackage,
@@ -255,6 +267,10 @@
return linkersByName.get(name);
}
+ public List<Class<? extends LinkerContextShim>> getLinkerContextShims() {
+ return linkerContextShimTypes;
+ }
+
public synchronized String getName() {
return name;
}
@@ -419,43 +435,21 @@
branch = Messages.PUBLIC_PATH_LOCATIONS.branch(logger, null);
lazyPublicOracle = publicPathEntries.create(branch);
- /*
- * Validate all linkers before we go off and compile something. badLinker is
- * checked at the end of the method so we can maximize the number of Linkers
- * to report errors about before exiting.
- *
- * It is not legal to have zero linkers defined
- */
- boolean badLinker = false;
- for (Map.Entry<String, String> entry : linkerTypesByName.entrySet()) {
- try {
- Class<?> clazz = Class.forName(entry.getValue());
- Class<? extends Linker> linkerClazz = clazz.asSubclass(Linker.class);
- linkersByName.put(entry.getKey(), linkerClazz.newInstance());
- } catch (ClassCastException e) {
- logger.log(TreeLogger.ERROR, "Not actually a Linker", e);
- badLinker = true;
- } catch (ClassNotFoundException e) {
- logger.log(TreeLogger.ERROR, "Unable to find Linker", e);
- badLinker = true;
- } catch (InstantiationException e) {
- logger.log(TreeLogger.ERROR, "Unable to create Linker", e);
- badLinker = true;
- } catch (IllegalAccessException e) {
- logger.log(TreeLogger.ERROR,
- "Linker does not have a public constructor", e);
- badLinker = true;
- }
- }
-
+ boolean fail = false;
for (String linkerName : activeLinkerNames) {
if (!linkersByName.containsKey(linkerName)) {
logger.log(TreeLogger.ERROR, "Unknown linker name " + linkerName, null);
- badLinker = true;
+ fail = true;
}
}
- if (badLinker) {
+ if (linkersByName.size() == 0 || activeLinkerNames.length == 0) {
+ logger.log(TreeLogger.ERROR, "At least one Linker must be defind and "
+ + "at least one Linker must be active.", null);
+ fail = true;
+ }
+
+ if (fail) {
throw new UnableToCompleteException();
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
index c6698bf..09f2561 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * 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
@@ -25,6 +25,8 @@
import com.google.gwt.dev.js.ast.JsFunction;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsStatement;
+import com.google.gwt.dev.linker.Linker;
+import com.google.gwt.dev.linker.LinkerContextShim;
import com.google.gwt.dev.util.Empty;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.xml.AttributeConverter;
@@ -33,6 +35,7 @@
import java.io.IOException;
import java.io.StringReader;
import java.net.URL;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -56,6 +59,8 @@
protected final String __entry_point_1_class = null;
+ protected final String __extend_linker_context_1_class = null;
+
protected final String __extend_property_1_name = null;
protected final String __extend_property_2_values = null;
@@ -114,15 +119,9 @@
private Schema fChild;
- protected Schema __define_linker_begin(String name, String className)
+ protected Schema __define_linker_begin(LinkerName name, Linker linker)
throws UnableToCompleteException {
- if (!Util.isValidJavaIdent(name)) {
- logger.log(TreeLogger.ERROR, "Linker names must be valid identifiers",
- null);
- throw new UnableToCompleteException();
- }
-
- moduleDef.addLinker(name, className);
+ moduleDef.addLinker(name.name, linker);
return null;
}
@@ -149,6 +148,18 @@
return null;
}
+ protected Schema __extend_linker_context_begin(Class<?> clazz)
+ throws UnableToCompleteException {
+ try {
+ moduleDef.addLinkerContextShim(clazz.asSubclass(LinkerContextShim.class));
+ return null;
+ } catch (ClassCastException e) {
+ Messages.INVALID_CLASS_DERIVATION.log(logger, clazz,
+ LinkerContextShim.class, null);
+ throw new UnableToCompleteException();
+ }
+ }
+
protected Schema __extend_property_begin(Property property,
PropertyValue[] values) {
for (int i = 0; i < values.length; i++) {
@@ -279,8 +290,12 @@
return null;
}
- protected Schema __set_linker_begin(String name) {
- moduleDef.setActiveLinkerNames(name.split(","));
+ protected Schema __set_linker_begin(LinkerName[] names) {
+ String[] asString = new String[names.length];
+ for (int i = 0; i < names.length; i++) {
+ asString[i] = names[i].name;
+ }
+ moduleDef.setActiveLinkerNames(asString);
return null;
}
@@ -451,6 +466,23 @@
}
}
+ /**
+ * Processes attributes of java.lang.Class type.
+ */
+ private final class ClassAttrCvt extends AttributeConverter {
+ @Override
+ public Object convertToArg(Schema schema, int line, String elem,
+ String attr, String value) throws UnableToCompleteException {
+ try {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ return cl.loadClass(value);
+ } catch (ClassNotFoundException e) {
+ Messages.UNABLE_TO_LOAD_CLASS.log(logger, value, e);
+ throw new UnableToCompleteException();
+ }
+ }
+ }
+
private final class ConditionSchema extends Schema {
protected final String __when_property_is_1_name = null;
@@ -543,15 +575,66 @@
}
}
+ private static class LinkerName {
+ public final String name;
+
+ public LinkerName(String name) {
+ this.name = name;
+ }
+ }
+
+ /**
+ * Converts a string into a linker name, validating it in the process.
+ */
+ private final class LinkerNameArrayAttrCvt extends AttributeConverter {
+
+ public Object convertToArg(Schema schema, int line, String elem,
+ String attr, String value) throws UnableToCompleteException {
+ String[] tokens = value.split(",");
+ List<LinkerName> toReturn = new ArrayList<LinkerName>(tokens.length);
+
+ for (String token : tokens) {
+ token = token.trim();
+ if (moduleDef.getLinker(token) == null) {
+ Messages.LINKER_NAME_INVALID.log(logger, token, null);
+ throw new UnableToCompleteException();
+ }
+
+ toReturn.add(new LinkerName(token));
+ }
+
+ // It is a valid list of names.
+ return toReturn.toArray(new LinkerName[tokens.length]);
+ }
+ }
+
+ /**
+ * Converts a string into a linker name, validating it in the process.
+ */
+ private final class LinkerNameAttrCvt extends AttributeConverter {
+
+ public Object convertToArg(Schema schema, int line, String elem,
+ String attr, String value) throws UnableToCompleteException {
+ // Ensure the value is a valid Java identifier
+ if (!Util.isValidJavaIdent(value)) {
+ Messages.LINKER_NAME_INVALID.log(logger, value, null);
+ throw new UnableToCompleteException();
+ }
+
+ // It is a valid name.
+ return new LinkerName(value);
+ }
+ }
+
/**
* Creates singleton instances of objects based on an attribute containing a
* class name.
*/
- private final class ObjAttrCvt extends AttributeConverter {
+ private final class ObjAttrCvt<T> extends AttributeConverter {
- private final Class fReqdSuperclass;
+ private final Class<T> fReqdSuperclass;
- public ObjAttrCvt(Class reqdSuperclass) {
+ public ObjAttrCvt(Class<T> reqdSuperclass) {
fReqdSuperclass = reqdSuperclass;
}
@@ -565,23 +648,21 @@
return found;
}
+ Class<?> foundClass = null;
try {
// Load the class.
//
ClassLoader cl = Thread.currentThread().getContextClassLoader();
- Class clazz = cl.loadClass(attrValue);
+ foundClass = cl.loadClass(attrValue);
+ Class<? extends T> clazz = foundClass.asSubclass(fReqdSuperclass);
- // Make sure it's compatible.
- //
- if (!fReqdSuperclass.isAssignableFrom(clazz)) {
- Messages.INVALID_CLASS_DERIVATION.log(logger, clazz, fReqdSuperclass,
- null);
- throw new UnableToCompleteException();
- }
-
- Object object = clazz.newInstance();
+ T object = clazz.newInstance();
singletonsByName.put(attrValue, object);
return object;
+ } catch (ClassCastException e) {
+ Messages.INVALID_CLASS_DERIVATION.log(logger, foundClass,
+ fReqdSuperclass, null);
+ throw new UnableToCompleteException();
} catch (ClassNotFoundException e) {
Messages.UNABLE_TO_LOAD_CLASS.log(logger, attrValue, e);
throw new UnableToCompleteException();
@@ -747,7 +828,7 @@
}
}
- private static final Map singletonsByName = new HashMap();
+ private static final Map<String, Object> singletonsByName = new HashMap<String, Object>();
private static void addPrefix(String[] strings, String prefix) {
for (int i = 0; i < strings.length; ++i) {
@@ -765,13 +846,19 @@
private final BodySchema bodySchema;
+ private final ClassAttrCvt classAttrCvt = new ClassAttrCvt();
+
private boolean foundAnyPublic;
private boolean foundExplicitSourceOrSuperSource;
-
- private final ObjAttrCvt genAttrCvt = new ObjAttrCvt(Generator.class);
+ private final ObjAttrCvt<Generator> genAttrCvt = new ObjAttrCvt<Generator>(
+ Generator.class);
private final JsParser jsParser = new JsParser();
private final JsProgram jsPgm = new JsProgram();
+ private final ObjAttrCvt<Linker> linkerAttrCvt = new ObjAttrCvt<Linker>(
+ Linker.class);
+ private final LinkerNameArrayAttrCvt linkerNameArrayAttrCvt = new LinkerNameArrayAttrCvt();
+ private final LinkerNameAttrCvt linkerNameAttrCvt = new LinkerNameAttrCvt();
private final ModuleDefLoader loader;
private final TreeLogger logger;
private final ModuleDef moduleDef;
@@ -797,6 +884,10 @@
registerAttributeConverter(PropertyValue.class, propValueAttrCvt);
registerAttributeConverter(PropertyValue[].class, propValueArrayAttrCvt);
registerAttributeConverter(Generator.class, genAttrCvt);
+ registerAttributeConverter(Linker.class, linkerAttrCvt);
+ registerAttributeConverter(LinkerName.class, linkerNameAttrCvt);
+ registerAttributeConverter(LinkerName[].class, linkerNameArrayAttrCvt);
+ registerAttributeConverter(Class.class, classAttrCvt);
}
protected Schema __module_begin() {
diff --git a/dev/core/src/com/google/gwt/dev/linker/LinkerContext.java b/dev/core/src/com/google/gwt/dev/linker/LinkerContext.java
index 4fa6d2c..7767d25 100644
--- a/dev/core/src/com/google/gwt/dev/linker/LinkerContext.java
+++ b/dev/core/src/com/google/gwt/dev/linker/LinkerContext.java
@@ -19,6 +19,7 @@
import com.google.gwt.core.ext.UnableToCompleteException;
import java.io.OutputStream;
+import java.util.Comparator;
import java.util.SortedSet;
/**
@@ -30,6 +31,60 @@
*/
public interface LinkerContext {
/**
+ * Orders CompilationResults by string comparison of their JavaScript.
+ */
+ Comparator<CompilationResult> COMPILATION_RESULT_COMPARATOR = new Comparator<CompilationResult>() {
+ public int compare(CompilationResult o1, CompilationResult o2) {
+ return o1.getJavaScript().compareTo(o2.getJavaScript());
+ }
+ };
+
+ /**
+ * Orders GeneratedResources by string comparison of their partial paths.
+ */
+ Comparator<GeneratedResource> GENERATED_RESOURCE_COMPARATOR = new Comparator<GeneratedResource>() {
+ public int compare(GeneratedResource o1, GeneratedResource o2) {
+ return o1.getPartialPath().compareTo(o2.getPartialPath());
+ }
+ };
+
+ /**
+ * Orders PublicResources by string comparison of their partial paths.
+ */
+ Comparator<PublicResource> PUBLIC_RESOURCE_COMPARATOR = new Comparator<PublicResource>() {
+ public int compare(PublicResource o1, PublicResource o2) {
+ return o1.getPartialPath().compareTo(o2.getPartialPath());
+ }
+ };
+
+ /**
+ * Orders ModuleScriptResources by string comparison of their src attributes.
+ */
+ Comparator<ModuleScriptResource> SCRIPT_RESOURCE_COMPARATOR = new Comparator<ModuleScriptResource>() {
+ public int compare(ModuleScriptResource o1, ModuleScriptResource o2) {
+ return o1.getSrc().compareTo(o2.getSrc());
+ }
+ };
+
+ /**
+ * Orders SelectionProperties by string comparison of their names.
+ */
+ Comparator<SelectionProperty> SELECTION_PROPERTY_COMPARATOR = new Comparator<SelectionProperty>() {
+ public int compare(SelectionProperty o1, SelectionProperty o2) {
+ return o1.getName().compareTo(o2.getName());
+ }
+ };
+
+ /**
+ * Orders ModuleStyleResources by string comparison of their src attributes.
+ */
+ Comparator<ModuleStylesheetResource> STYLE_RESOURCE_COMPARATOR = new Comparator<ModuleStylesheetResource>() {
+ public int compare(ModuleStylesheetResource o1, ModuleStylesheetResource o2) {
+ return o1.getSrc().compareTo(o2.getSrc());
+ }
+ };
+
+ /**
* Finalizes the OutputStream for a given artifact. This method must be called
* in order to actually place the artifact into the output directory. If the
* OutptStream has not already been closed, this method will close the
diff --git a/dev/core/src/com/google/gwt/dev/linker/LinkerContextShim.java b/dev/core/src/com/google/gwt/dev/linker/LinkerContextShim.java
new file mode 100644
index 0000000..59b25c5
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/linker/LinkerContextShim.java
@@ -0,0 +1,124 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.linker;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+
+import java.io.OutputStream;
+import java.util.SortedSet;
+
+/**
+ * This base class allows behaviors to be injected into the
+ * {@link LinkerContext} that is observed by the {@link Linker} types operating
+ * on the output from the compiler. Instances of LinkerContextShim are mapped
+ * into the compilation process by including {@code <extend-linker-context>}
+ * tags in the GWT module definition. Subclasses of LinkerContextShim must
+ * define a two-argument constructor that accepts an instance of TreeLogger and
+ * LinkerContext.
+ * <p>
+ * The default behavior of all methods in this class is to delegate to the
+ * LinkerContext returned from {@link #getParent()}. Separate shim instances
+ * are guaranteed to be used for each Linker instance.
+ * <p>
+ * No guarantees are made on the order in which or number of times any method on
+ * the LinkerContextShim will be invoked. Implementations are encouraged to
+ * precompute all return values for each method in their constructors and return
+ * an unmodifiable wrapper around the collection using
+ * {@link java.util.Collections#unmodifiableSortedSet(SortedSet)}.
+ */
+public abstract class LinkerContextShim implements LinkerContext {
+ private final LinkerContext parent;
+
+ protected LinkerContextShim(TreeLogger logger, LinkerContext parent)
+ throws UnableToCompleteException {
+ this.parent = parent;
+ }
+
+ /**
+ * Finalize all actions performed by the LinkerContextShim. This method will
+ * be called in reverse order; it will not be called on a parent until all of
+ * its children have been committed.
+ */
+ public void commit(TreeLogger logger) throws UnableToCompleteException {
+ }
+
+ public void commit(TreeLogger logger, OutputStream out)
+ throws UnableToCompleteException {
+ getParent().commit(logger, out);
+ }
+
+ public SortedSet<CompilationResult> getCompilations() {
+ return getParent().getCompilations();
+ }
+
+ public SortedSet<GeneratedResource> getGeneratedResources() {
+ return getParent().getGeneratedResources();
+ }
+
+ public String getModuleFunctionName() {
+ return getParent().getModuleFunctionName();
+ }
+
+ public String getModuleName() {
+ return getParent().getModuleName();
+ }
+
+ public SortedSet<ModuleScriptResource> getModuleScripts() {
+ return getParent().getModuleScripts();
+ }
+
+ public SortedSet<ModuleStylesheetResource> getModuleStylesheets() {
+ return getParent().getModuleStylesheets();
+ }
+
+ /**
+ * Obtain a reference to the parent LinkerContext. This method is guaranteed
+ * to return a useful value before any of the other LinkerContext-derived
+ * methods are invoked.
+ */
+ // NB This is final because StandardLinkerContext depends on it to unwind
+ public final LinkerContext getParent() {
+ return parent;
+ }
+
+ public SortedSet<SelectionProperty> getProperties() {
+ return getParent().getProperties();
+ }
+
+ public SortedSet<PublicResource> getPublicResources() {
+ return getParent().getPublicResources();
+ }
+
+ public String optimizeJavaScript(TreeLogger logger, String jsProgram)
+ throws UnableToCompleteException {
+ return getParent().optimizeJavaScript(logger, jsProgram);
+ }
+
+ public OutputStream tryCreateArtifact(TreeLogger logger, String partialPath) {
+ return getParent().tryCreateArtifact(logger, partialPath);
+ }
+
+ public GeneratedResource tryGetGeneratedResource(TreeLogger logger,
+ String partialPath) {
+ return getParent().tryGetGeneratedResource(logger, partialPath);
+ }
+
+ public PublicResource tryGetPublicResource(TreeLogger logger,
+ String partialPath) {
+ return getParent().tryGetPublicResource(logger, partialPath);
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/linker/NoDeployResourcesShim.java b/dev/core/src/com/google/gwt/dev/linker/NoDeployResourcesShim.java
new file mode 100644
index 0000000..fd49ccc
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/linker/NoDeployResourcesShim.java
@@ -0,0 +1,66 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.linker;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+
+import java.util.Collections;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * This class prevents generated resources whose partial path begins with
+ * {@value #PREFIX} from being visible.
+ */
+public class NoDeployResourcesShim extends LinkerContextShim {
+ public static final String PREFIX = "no-deploy/";
+ private final SortedSet<GeneratedResource> generatedResources;
+
+ public NoDeployResourcesShim(TreeLogger logger, LinkerContext parent)
+ throws UnableToCompleteException {
+ super(logger, parent);
+
+ SortedSet<GeneratedResource> mutableSet = new TreeSet<GeneratedResource>(
+ GENERATED_RESOURCE_COMPARATOR);
+
+ SortedSet<GeneratedResource> view = super.getGeneratedResources();
+ for (GeneratedResource res : view) {
+ if (!res.getPartialPath().toLowerCase().startsWith(PREFIX)) {
+ mutableSet.add(res);
+ } else {
+ logger.log(TreeLogger.SPAM, "Excluding generated resource "
+ + res.getPartialPath(), null);
+ }
+ }
+
+ assert mutableSet.size() <= view.size();
+
+ if (mutableSet.size() == view.size()) {
+ // Reuse the existing view
+ generatedResources = view;
+
+ } else {
+ // Ensure that the new view is immutable
+ generatedResources = Collections.unmodifiableSortedSet(mutableSet);
+ }
+ }
+
+ @Override
+ public SortedSet<GeneratedResource> getGeneratedResources() {
+ return generatedResources;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/linker/impl/StandardLinkerContext.java b/dev/core/src/com/google/gwt/dev/linker/impl/StandardLinkerContext.java
index 9786001..e038991 100644
--- a/dev/core/src/com/google/gwt/dev/linker/impl/StandardLinkerContext.java
+++ b/dev/core/src/com/google/gwt/dev/linker/impl/StandardLinkerContext.java
@@ -42,6 +42,7 @@
import com.google.gwt.dev.linker.GeneratedResource;
import com.google.gwt.dev.linker.Linker;
import com.google.gwt.dev.linker.LinkerContext;
+import com.google.gwt.dev.linker.LinkerContextShim;
import com.google.gwt.dev.linker.ModuleScriptResource;
import com.google.gwt.dev.linker.ModuleStylesheetResource;
import com.google.gwt.dev.linker.PublicResource;
@@ -55,12 +56,15 @@
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
+import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
@@ -91,60 +95,6 @@
}
}
- /**
- * Orders CompilationResults by string comparison of their JavaScript.
- */
- public static final Comparator<CompilationResult> COMPILATION_RESULT_COMPARATOR = new Comparator<CompilationResult>() {
- public int compare(CompilationResult o1, CompilationResult o2) {
- return o1.getJavaScript().compareTo(o2.getJavaScript());
- }
- };
-
- /**
- * Orders GeneratedResources by string comparison of their partial paths.
- */
- public static final Comparator<GeneratedResource> GENERATED_RESOURCE_COMPARATOR = new Comparator<GeneratedResource>() {
- public int compare(GeneratedResource o1, GeneratedResource o2) {
- return o1.getPartialPath().compareTo(o2.getPartialPath());
- }
- };
-
- /**
- * Orders PublicResources by string comparison of their partial paths.
- */
- public static final Comparator<PublicResource> PUBLIC_RESOURCE_COMPARATOR = new Comparator<PublicResource>() {
- public int compare(PublicResource o1, PublicResource o2) {
- return o1.getPartialPath().compareTo(o2.getPartialPath());
- }
- };
-
- /**
- * Orders ModuleScriptResources by string comparison of their src attributes.
- */
- public static final Comparator<ModuleScriptResource> SCRIPT_RESOURCE_COMPARATOR = new Comparator<ModuleScriptResource>() {
- public int compare(ModuleScriptResource o1, ModuleScriptResource o2) {
- return o1.getSrc().compareTo(o2.getSrc());
- }
- };
-
- /**
- * Orders SelectionProperties by string comparison of their names.
- */
- public static final Comparator<SelectionProperty> SELECTION_PROPERTY_COMPARATOR = new Comparator<SelectionProperty>() {
- public int compare(SelectionProperty o1, SelectionProperty o2) {
- return o1.getName().compareTo(o2.getName());
- }
- };
-
- /**
- * Orders ModuleStyleResources by string comparison of their src attributes.
- */
- public static final Comparator<ModuleStylesheetResource> STYLE_RESOURCE_COMPARATOR = new Comparator<ModuleStylesheetResource>() {
- public int compare(ModuleStylesheetResource o1, ModuleStylesheetResource o2) {
- return o1.getSrc().compareTo(o2.getSrc());
- }
- };
-
private final File compilationsDir;
private final SortedSet<GeneratedResource> generatedResources;
private final Map<String, GeneratedResource> generatedResourcesByName = new HashMap<String, GeneratedResource>();
@@ -173,10 +123,12 @@
private final Map<String, PublicResource> publicResourcesByName = new HashMap<String, PublicResource>();
private final Map<String, StandardCompilationResult> resultsByStrongName = new HashMap<String, StandardCompilationResult>();
private final SortedSet<ModuleScriptResource> scriptResources;
+ private final List<Class<? extends LinkerContextShim>> shimClasses;
private final SortedSet<ModuleStylesheetResource> stylesheetResources;
public StandardLinkerContext(TreeLogger logger, ModuleDef module,
- File outDir, File generatorDir, JJSOptions jjsOptions) {
+ File outDir, File generatorDir, JJSOptions jjsOptions)
+ throws UnableToCompleteException {
logger = logger.branch(TreeLogger.DEBUG,
"Constructing StandardLinkerContext", null);
@@ -184,6 +136,8 @@
this.moduleFunctionName = module.getFunctionName();
this.moduleName = module.getName();
this.moduleOutDir = outDir;
+ this.shimClasses = new ArrayList<Class<? extends LinkerContextShim>>(
+ module.getLinkerContextShims());
if (moduleOutDir != null) {
compilationsDir = new File(moduleOutDir, ".gwt-compiler/compilations");
@@ -372,7 +326,46 @@
logger = logger.branch(TreeLogger.INFO, "Linking compilation with "
+ linker.getDescription() + " Linker into " + linkerOutDir.getPath(),
null);
- linker.link(logger, this);
+
+ // Instantiate per-Linker instances of the LinkerContextShims
+ LinkerContext shimParent = this;
+ for (Class<? extends LinkerContextShim> clazz : shimClasses) {
+ TreeLogger shimLogger = logger.branch(TreeLogger.DEBUG,
+ "Constructing LinkerContextShim " + clazz.getName(), null);
+ try {
+ Constructor<? extends LinkerContextShim> constructor = clazz.getConstructor(
+ TreeLogger.class, LinkerContext.class);
+ shimParent = constructor.newInstance(shimLogger, shimParent);
+ } catch (InstantiationException e) {
+ shimLogger.log(TreeLogger.ERROR,
+ "Unable to create LinkerContextShim", e);
+ throw new UnableToCompleteException();
+ } catch (InvocationTargetException e) {
+ shimLogger.log(TreeLogger.ERROR,
+ "Unable to create LinkerContextShim", e);
+ throw new UnableToCompleteException();
+ } catch (NoSuchMethodException e) {
+ shimLogger.log(TreeLogger.ERROR,
+ "LinkerContextShim subtypes must implement a two-argument "
+ + "constructor accepting a TreeLogger and a LinkerContext", e);
+ throw new UnableToCompleteException();
+ } catch (IllegalAccessException e) {
+ shimLogger.log(TreeLogger.ERROR,
+ "Unable to create LinkerContextShim", e);
+ throw new UnableToCompleteException();
+ }
+ }
+
+ linker.link(logger, shimParent);
+
+ // Unwind the LinkerContextShim stack
+ while (shimParent != this) {
+ LinkerContextShim shim = (LinkerContextShim) shimParent;
+ shim.commit(logger.branch(TreeLogger.DEBUG,
+ "Committing LinkerContextShim " + shim.getClass().getName(), null));
+ shimParent = shim.getParent();
+ }
+
} finally {
reset();
}
diff --git a/user/src/com/google/gwt/core/Core.gwt.xml b/user/src/com/google/gwt/core/Core.gwt.xml
index 1487956..91aee75 100644
--- a/user/src/com/google/gwt/core/Core.gwt.xml
+++ b/user/src/com/google/gwt/core/Core.gwt.xml
@@ -24,5 +24,7 @@
<define-linker name="sso" class="com.google.gwt.dev.linker.SingleScriptLinker" />
<define-linker name="xs" class="com.google.gwt.dev.linker.XSLinker" />
<set-linker name="std" />
+
+ <!-- Filters generated resources in the no-deploy/ directory -->
+ <extend-linker-context class="com.google.gwt.dev.linker.NoDeployResourcesShim" />
</module>
-
diff --git a/user/test/com/google/gwt/module/ModuleSuite.java b/user/test/com/google/gwt/module/ModuleSuite.java
index 422fa3c..ce6c509 100644
--- a/user/test/com/google/gwt/module/ModuleSuite.java
+++ b/user/test/com/google/gwt/module/ModuleSuite.java
@@ -17,6 +17,7 @@
import com.google.gwt.junit.tools.GWTTestSuite;
import com.google.gwt.module.client.DoubleScriptInjectionTest;
+import com.google.gwt.module.client.NoDeployTest;
import com.google.gwt.module.client.SingleScriptInjectionTest;
import junit.framework.Test;
@@ -27,10 +28,11 @@
public class ModuleSuite {
public static Test suite() {
GWTTestSuite suite = new GWTTestSuite();
-
+
suite.addTestSuite(SingleScriptInjectionTest.class);
suite.addTestSuite(DoubleScriptInjectionTest.class);
-
+ suite.addTestSuite(NoDeployTest.class);
+
return suite;
}
}
diff --git a/user/test/com/google/gwt/module/NoDeployTest.gwt.xml b/user/test/com/google/gwt/module/NoDeployTest.gwt.xml
new file mode 100644
index 0000000..a6af83f
--- /dev/null
+++ b/user/test/com/google/gwt/module/NoDeployTest.gwt.xml
@@ -0,0 +1,21 @@
+<!-- -->
+<!-- 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 -->
+<!-- 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. License for the specific language governing permissions and -->
+<!-- limitations under the License. -->
+
+<module>
+ <inherits name="com.google.gwt.core.Core"/>
+
+ <generate-with class="com.google.gwt.module.rebind.NoDeployGenerator" >
+ <when-type-assignable class="com.google.gwt.module.client.NoDeployTest.NoDeploy" />
+ </generate-with>
+</module>
diff --git a/user/test/com/google/gwt/module/client/NoDeployTest.java b/user/test/com/google/gwt/module/client/NoDeployTest.java
new file mode 100644
index 0000000..eb4038b
--- /dev/null
+++ b/user/test/com/google/gwt/module/client/NoDeployTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.module.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.http.client.Request;
+import com.google.gwt.http.client.RequestBuilder;
+import com.google.gwt.http.client.RequestCallback;
+import com.google.gwt.http.client.RequestException;
+import com.google.gwt.http.client.Response;
+import com.google.gwt.junit.client.GWTTestCase;
+
+/**
+ * Ensure that generated resources in the no-deploy directory aren't in the
+ * output. This validates that the
+ * {@link com.google.gwt.dev.linker.NoDeployResourcesShim} is being loaded and
+ * operating correctly.
+ */
+public class NoDeployTest extends GWTTestCase {
+
+ /**
+ * Used only to trigger the NoDeployGenerator.
+ */
+ private static class NoDeploy {
+ }
+
+ public static final String TEST_TEXT = "Hello world!";
+
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.module.NoDeployTest";
+ }
+
+ public void testDeploy() throws RequestException {
+ GWT.create(NoDeploy.class);
+
+ // Try fetching a file that should exist
+ RequestBuilder builder = new RequestBuilder(RequestBuilder.GET,
+ GWT.getHostPageBaseURL() + "deploy/exists.txt");
+ delayTestFinish(500);
+ builder.sendRequest("", new RequestCallback() {
+
+ public void onError(Request request, Throwable exception) {
+ fail();
+ }
+
+ public void onResponseReceived(Request request, Response response) {
+ assertEquals(TEST_TEXT, response.getText());
+ finishTest();
+ }
+ });
+ }
+
+ public void testNoDeploy() throws RequestException {
+ if (!GWT.isScript()) {
+ // LinkerContextShims aren't used in hosted-mode
+ return;
+ }
+
+ GWT.create(NoDeploy.class);
+
+ // Try fetching a file that shouldn't exist
+ RequestBuilder builder = new RequestBuilder(RequestBuilder.GET,
+ GWT.getHostPageBaseURL() + "no-deploy/inGenerated.txt");
+ delayTestFinish(500);
+ builder.sendRequest("", new RequestCallback() {
+
+ public void onError(Request request, Throwable exception) {
+ fail();
+ }
+
+ public void onResponseReceived(Request request, Response response) {
+ assertEquals(404, response.getStatusCode());
+ finishTest();
+ }
+ });
+ }
+
+ /**
+ * Verify that a no-deploy directory in the public path will be deployed.
+ */
+ public void testNoDeployInPublic() throws RequestException {
+ GWT.create(NoDeploy.class);
+
+ // Try fetching a file that shouldn't exist
+ RequestBuilder builder = new RequestBuilder(RequestBuilder.GET,
+ GWT.getHostPageBaseURL() + "no-deploy/inPublic.txt");
+ delayTestFinish(500);
+ builder.sendRequest("", new RequestCallback() {
+
+ public void onError(Request request, Throwable exception) {
+ fail();
+ }
+
+ public void onResponseReceived(Request request, Response response) {
+ assertEquals(TEST_TEXT, response.getText());
+ finishTest();
+ }
+ });
+ }
+}
diff --git a/user/test/com/google/gwt/module/public/no-deploy/inPublic.txt b/user/test/com/google/gwt/module/public/no-deploy/inPublic.txt
new file mode 100644
index 0000000..6769dd6
--- /dev/null
+++ b/user/test/com/google/gwt/module/public/no-deploy/inPublic.txt
@@ -0,0 +1 @@
+Hello world!
\ No newline at end of file
diff --git a/user/test/com/google/gwt/module/rebind/NoDeployGenerator.java b/user/test/com/google/gwt/module/rebind/NoDeployGenerator.java
new file mode 100644
index 0000000..46b1d94
--- /dev/null
+++ b/user/test/com/google/gwt/module/rebind/NoDeployGenerator.java
@@ -0,0 +1,60 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.module.rebind;
+
+import com.google.gwt.core.ext.Generator;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.linker.NoDeployResourcesShim;
+import com.google.gwt.dev.util.Util;
+import com.google.gwt.module.client.NoDeployTest;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Creates two files in the generated output directory.
+ */
+public class NoDeployGenerator extends Generator {
+
+ @Override
+ public String generate(TreeLogger logger, GeneratorContext context,
+ String typeName) throws UnableToCompleteException {
+
+ try {
+ createFile(logger, context, "deploy/exists.txt");
+ createFile(logger, context, NoDeployResourcesShim.PREFIX + "inGenerated.txt");
+ } catch (IOException e) {
+ logger.log(TreeLogger.ERROR, "Unable to create test file", e);
+ throw new UnableToCompleteException();
+ }
+
+ return typeName;
+ }
+
+ private void createFile(TreeLogger logger, GeneratorContext context,
+ String path) throws UnableToCompleteException, IOException {
+
+ OutputStream out = context.tryCreateResource(logger, path);
+ if (out == null) {
+ return;
+ }
+
+ out.write(Util.getBytes(NoDeployTest.TEST_TEXT));
+ context.commitResource(logger, out);
+ }
+}