Implement conditional <set-property> module directives.
Patch by: bobv
Review by: bruce
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5970 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/SelectionProperty.java b/dev/core/src/com/google/gwt/core/ext/linker/SelectionProperty.java
index e0f17bf..8940edd 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/SelectionProperty.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/SelectionProperty.java
@@ -23,8 +23,8 @@
*
* SelectionProperty implementations must support object identity comparisons.
*
- * @see com.google.gwt.core.ext.SelectionProperty A similarly-named interface used
- * in generators.
+ * @see com.google.gwt.core.ext.SelectionProperty A similarly-named interface
+ * used in generators.
*/
public interface SelectionProperty {
/**
@@ -44,6 +44,13 @@
String getPropertyProvider();
/**
+ * Returns <code>true</code> if the value of the SelectionProperty is always
+ * derived from other SelectionProperties and, as a consequence, the property
+ * provider never needs to be evaluated.
+ */
+ boolean isDerived();
+
+ /**
* Returns the defined value for the deferred binding property or
* <code>null</code> if the value of the property is not constant.
*
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
index 62a8cf9..cad65aa 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
@@ -176,7 +176,7 @@
protected String generatePropertyProvider(SelectionProperty prop) {
StringBuffer toReturn = new StringBuffer();
- if (prop.tryGetValue() == null) {
+ if (prop.tryGetValue() == null && !prop.isDerived()) {
toReturn.append("providers['" + prop.getName() + "'] = function()");
toReturn.append(prop.getPropertyProvider());
toReturn.append(";");
@@ -315,6 +315,8 @@
for (SelectionProperty p : context.getProperties()) {
if (p.tryGetValue() != null) {
continue;
+ } else if (p.isDerived()) {
+ continue;
}
if (needsIndexMarkers) {
text.append("][");
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSelectionProperty.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSelectionProperty.java
index b3ed31f..92b8670 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSelectionProperty.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSelectionProperty.java
@@ -31,20 +31,18 @@
private static final String FALLBACK_TOKEN = "/*-FALLBACK-*/";
private final String activeValue;
+ private final boolean isDerived;
private final String name;
private final String provider;
private final SortedSet<String> values;
public StandardSelectionProperty(BindingProperty p) {
- if (p.getAllowedValues().length == 1) {
- activeValue = p.getAllowedValues()[0];
- } else {
- activeValue = null;
- }
+ activeValue = p.getConstrainedValue();
+ isDerived = p.isDerived();
name = p.getName();
String fallback = p.getFallback();
- provider = p.getProvider() == null ? null :
- p.getProvider().getBody().replace(FALLBACK_TOKEN, fallback);
+ provider = p.getProvider() == null ? null
+ : p.getProvider().getBody().replace(FALLBACK_TOKEN, fallback);
values = Collections.unmodifiableSortedSet(new TreeSet<String>(
Arrays.asList(p.getDefinedValues())));
}
@@ -61,6 +59,10 @@
return provider;
}
+ public boolean isDerived() {
+ return isDerived;
+ }
+
@Override
public String toString() {
StringBuffer b = new StringBuffer();
diff --git a/dev/core/src/com/google/gwt/dev/Link.java b/dev/core/src/com/google/gwt/dev/Link.java
index 2501a60..1305ccf 100644
--- a/dev/core/src/com/google/gwt/dev/Link.java
+++ b/dev/core/src/com/google/gwt/dev/Link.java
@@ -253,6 +253,12 @@
* values.
*/
continue;
+ } else if (key.isDerived()) {
+ /*
+ * The property provider does not need to be invoked, because the
+ * value is determined entirely by other properties.
+ */
+ continue;
}
unboundProperties.put(key, orderedPropValues[i]);
}
diff --git a/dev/core/src/com/google/gwt/dev/Precompile.java b/dev/core/src/com/google/gwt/dev/Precompile.java
index e137fea..a529f16 100644
--- a/dev/core/src/com/google/gwt/dev/Precompile.java
+++ b/dev/core/src/com/google/gwt/dev/Precompile.java
@@ -402,8 +402,9 @@
public static Precompilation precompile(TreeLogger logger,
JJSOptions jjsOptions, ModuleDef module, File genDir,
File generatorResourcesDir, File dumpSignatureFile) {
- return precompile(logger, jjsOptions, module, 0, 0,
- module.getProperties().numPermutations(), genDir,
+ PropertyPermutations allPermutations = new PropertyPermutations(
+ module.getProperties());
+ return precompile(logger, jjsOptions, module, 0, allPermutations, genDir,
generatorResourcesDir, dumpSignatureFile);
}
@@ -458,8 +459,8 @@
private static Precompilation precompile(TreeLogger logger,
JJSOptions jjsOptions, ModuleDef module, int permutationBase,
- int firstPerm, int numPerms, File genDir, File generatorResourcesDir,
- File dumpSignatureFile) {
+ PropertyPermutations allPermutations, File genDir,
+ File generatorResourcesDir, File dumpSignatureFile) {
try {
CompilationState compilationState = module.getCompilationState(logger);
@@ -477,10 +478,7 @@
ArtifactSet generatedArtifacts = new ArtifactSet();
DistillerRebindPermutationOracle rpo = new DistillerRebindPermutationOracle(
- module,
- compilationState,
- generatedArtifacts,
- new PropertyPermutations(module.getProperties(), firstPerm, numPerms),
+ module, compilationState, generatedArtifacts, allPermutations,
genDir, generatorResourcesDir);
PerfLogger.start("Precompile");
UnifiedAst unifiedAst = JavaToJavaScriptCompiler.precompile(logger,
@@ -556,7 +554,9 @@
} else {
TreeLogger branch = logger.branch(TreeLogger.INFO,
"Precompiling module " + module.getName());
- int potentialPermutations = module.getProperties().numPermutations();
+ PropertyPermutations allPermutations = new PropertyPermutations(
+ module.getProperties());
+ int potentialPermutations = allPermutations.size();
int permutationsPerIteration = options.getMaxPermsPerPrecompile();
if (permutationsPerIteration <= 0) {
@@ -594,10 +594,13 @@
options.setCompilationStateRetained(originalCompilationStateRetained);
}
+ // Select only the range of property permutations that we want
+ PropertyPermutations localPermutations = new PropertyPermutations(
+ allPermutations, potentialFirstPerm, numPermsToPrecompile);
+
Precompilation precompilation = precompile(branch, options, module,
- actualPermutations, potentialFirstPerm, numPermsToPrecompile,
- options.getGenDir(), compilerWorkDir,
- options.getDumpSignatureFile());
+ actualPermutations, localPermutations, options.getGenDir(),
+ compilerWorkDir, options.getDumpSignatureFile());
if (precompilation == null) {
branch.log(TreeLogger.ERROR, "Precompilation failed");
return false;
diff --git a/dev/core/src/com/google/gwt/dev/cfg/BindingProperty.java b/dev/core/src/com/google/gwt/dev/cfg/BindingProperty.java
index 57a03c3..6eb07a5 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/BindingProperty.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/BindingProperty.java
@@ -15,7 +15,13 @@
*/
package com.google.gwt.dev.cfg;
+import com.google.gwt.dev.util.collect.Sets;
+
import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
@@ -28,18 +34,15 @@
public class BindingProperty extends Property {
private static final String EMPTY = "";
-
- private SortedSet<String> allowedValues;
+
+ private final Map<Condition, SortedSet<String>> conditionalValues = new LinkedHashMap<Condition, SortedSet<String>>();
private final SortedSet<String> definedValues = new TreeSet<String>();
private PropertyProvider provider;
private String fallback;
+ private final ConditionAll rootCondition = new ConditionAll();
{
- /*
- * This is initially an alias for definedValues and is only set differently
- * if the user calls setAllowedValues().
- */
- allowedValues = definedValues;
+ conditionalValues.put(rootCondition, new TreeSet<String>());
}
public BindingProperty(String name) {
@@ -47,17 +50,48 @@
fallback = EMPTY;
}
- public void addDefinedValue(String newValue) {
+ public void addDefinedValue(Condition condition, String newValue) {
definedValues.add(newValue);
+ SortedSet<String> set = conditionalValues.get(condition);
+ if (set == null) {
+ set = new TreeSet<String>();
+ set.addAll(conditionalValues.get(rootCondition));
+ conditionalValues.put(condition, set);
+ }
+ set.add(newValue);
}
/**
- * Returns the set of allowed values in sorted order.
+ * Returns the set of allowed values in sorted order when a certain condition
+ * is satisfied.
*/
- public String[] getAllowedValues() {
+ public String[] getAllowedValues(Condition condition) {
+ Set<String> allowedValues = conditionalValues.get(condition);
return allowedValues.toArray(new String[allowedValues.size()]);
}
+ public Map<Condition, SortedSet<String>> getConditionalValues() {
+ return Collections.unmodifiableMap(conditionalValues);
+ }
+
+ /**
+ * If the BindingProperty has exactly one value across all conditions and
+ * permutations, return that value otherwise return <code>null</code>.
+ */
+ public String getConstrainedValue() {
+ String constrainedValue = null;
+ for (SortedSet<String> allowedValues : conditionalValues.values()) {
+ if (allowedValues.size() != 1) {
+ return null;
+ } else if (constrainedValue == null) {
+ constrainedValue = allowedValues.iterator().next();
+ } else if (!constrainedValue.equals(allowedValues.iterator().next())) {
+ return null;
+ }
+ }
+ return constrainedValue;
+ }
+
/**
* Returns the set of defined values in sorted order.
*/
@@ -68,12 +102,33 @@
public String getFallback() {
return fallback;
}
+
public PropertyProvider getProvider() {
return provider;
}
+ public Set<String> getRequiredProperties() {
+ Set<String> toReturn = Sets.create();
+ for (Condition cond : conditionalValues.keySet()) {
+ toReturn = Sets.addAll(toReturn, cond.getRequiredProperties());
+ }
+ return toReturn;
+ }
+
+ public ConditionAll getRootCondition() {
+ return rootCondition;
+ }
+
+ /**
+ * Returns true if the supplied value is legal under some condition.
+ */
public boolean isAllowedValue(String value) {
- return allowedValues.contains(value);
+ for (Set<String> values : conditionalValues.values()) {
+ if (values.contains(value)) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -86,20 +141,47 @@
}
/**
+ * Returns <code>true</code> if the value of this BindingProperty is always
+ * derived from other BindingProperties. That is, for each Condition in the
+ * BindingProperty, there is exactly one allowed value.
+ */
+ public boolean isDerived() {
+ for (Set<String> allowedValues : conditionalValues.values()) {
+ if (allowedValues.size() != 1) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Set the currently allowed values. The values provided must be a subset of
* the currently-defined values.
*
* @throws IllegalArgumentException if any of the provided values were not
* provided to {@link #addDefinedValue(String)}.
*/
- public void setAllowedValues(String... values) {
+ public void setAllowedValues(Condition condition, String... values) {
SortedSet<String> temp = new TreeSet<String>(Arrays.asList(values));
if (!definedValues.containsAll(temp)) {
throw new IllegalArgumentException(
"Attempted to set an allowed value that was not previously defined");
}
- allowedValues = temp;
+ // XML has a last-one-wins semantic which we reflect in our evaluation order
+ if (condition == rootCondition) {
+ /*
+ * An unconditional set-property would undo any previous conditional
+ * setters, so we can just clear out this map.
+ */
+ conditionalValues.clear();
+ } else {
+ /*
+ * Otherwise, we'll just ensure that this condition is moved to the end.
+ */
+ conditionalValues.remove(condition);
+ }
+ conditionalValues.put(condition, temp);
}
public void setFallback(String token) {
diff --git a/dev/core/src/com/google/gwt/dev/cfg/CompoundCondition.java b/dev/core/src/com/google/gwt/dev/cfg/CompoundCondition.java
index b9ff8de..08aecd6 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/CompoundCondition.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/CompoundCondition.java
@@ -15,6 +15,11 @@
*/
package com.google.gwt.dev.cfg;
+import com.google.gwt.dev.util.collect.Sets;
+
+import java.util.Iterator;
+import java.util.Set;
+
/**
* Abstract base class for various kinds of compound deferred binding
* conditions.
@@ -26,4 +31,13 @@
public Conditions getConditions() {
return conditions;
}
+
+ @Override
+ public Set<String> getRequiredProperties() {
+ Set<String> toReturn = Sets.create();
+ for (Iterator<Condition> it = conditions.iterator(); it.hasNext();) {
+ toReturn = Sets.addAll(toReturn, it.next().getRequiredProperties());
+ }
+ return toReturn;
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Condition.java b/dev/core/src/com/google/gwt/dev/cfg/Condition.java
index e93c1cc..0ff89e7 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/Condition.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/Condition.java
@@ -15,16 +15,28 @@
*/
package com.google.gwt.dev.cfg;
-import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.util.collect.Sets;
+
+import java.util.Set;
/**
* Abstract base class for various kinds of deferred binding conditions.
*/
public abstract class Condition {
- public final boolean isTrue(TreeLogger logger, GeneratorContext context,
- String testType) throws UnableToCompleteException {
+ /**
+ * Returns the set of property names that the Condition requires in order to
+ * be evaluated.
+ */
+ public Set<String> getRequiredProperties() {
+ return Sets.create();
+ }
+
+ public final boolean isTrue(TreeLogger logger, PropertyOracle propertyOracle,
+ TypeOracle typeOracle, String testType) throws UnableToCompleteException {
boolean logDebug = logger.isLoggable(TreeLogger.DEBUG);
@@ -33,7 +45,7 @@
logger = logger.branch(TreeLogger.DEBUG, startMsg, null);
}
- boolean result = doEval(logger, context, testType);
+ boolean result = doEval(logger, propertyOracle, typeOracle, testType);
if (logDebug) {
String afterMsg = getEvalAfterMessage(testType, result);
@@ -44,7 +56,7 @@
}
protected abstract boolean doEval(TreeLogger logger,
- GeneratorContext context, String testType)
+ PropertyOracle propertyOracle, TypeOracle typeOracle, String testType)
throws UnableToCompleteException;
protected abstract String getEvalAfterMessage(String testType, boolean result);
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionAll.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionAll.java
index e8f948e..94a47cb 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionAll.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionAll.java
@@ -15,9 +15,10 @@
*/
package com.google.gwt.dev.cfg;
-import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
import java.util.Iterator;
@@ -30,11 +31,11 @@
public ConditionAll() {
}
- protected boolean doEval(TreeLogger logger, GeneratorContext context,
- String testType) throws UnableToCompleteException {
- for (Iterator iter = getConditions().iterator(); iter.hasNext();) {
- Condition condition = (Condition) iter.next();
- if (!condition.isTrue(logger, context, testType)) {
+ protected boolean doEval(TreeLogger logger, PropertyOracle propertyOracle,
+ TypeOracle typeOracle, String testType) throws UnableToCompleteException {
+ for (Iterator<Condition> iter = getConditions().iterator(); iter.hasNext();) {
+ Condition condition = iter.next();
+ if (!condition.isTrue(logger, propertyOracle, typeOracle, testType)) {
return false;
}
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionAny.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionAny.java
index 7273f12..edd34c0 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionAny.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionAny.java
@@ -15,9 +15,10 @@
*/
package com.google.gwt.dev.cfg;
-import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
import java.util.Iterator;
@@ -29,11 +30,11 @@
public ConditionAny() {
}
- protected boolean doEval(TreeLogger logger, GeneratorContext context,
- String testType) throws UnableToCompleteException {
- for (Iterator iter = getConditions().iterator(); iter.hasNext();) {
- Condition condition = (Condition) iter.next();
- if (condition.isTrue(logger, context, testType)) {
+ protected boolean doEval(TreeLogger logger, PropertyOracle propertyOracle,
+ TypeOracle typeOracle, String testType) throws UnableToCompleteException {
+ for (Iterator<Condition> iter = getConditions().iterator(); iter.hasNext();) {
+ Condition condition = iter.next();
+ if (condition.isTrue(logger, propertyOracle, typeOracle, testType)) {
return true;
}
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionNone.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionNone.java
index 5134914..5199a7c 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionNone.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionNone.java
@@ -15,9 +15,10 @@
*/
package com.google.gwt.dev.cfg;
-import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
import java.util.Iterator;
@@ -30,11 +31,11 @@
public ConditionNone() {
}
- protected boolean doEval(TreeLogger logger, GeneratorContext context,
- String testType) throws UnableToCompleteException {
- for (Iterator iter = getConditions().iterator(); iter.hasNext();) {
+ protected boolean doEval(TreeLogger logger, PropertyOracle propertyOracle,
+ TypeOracle typeOracle, String testType) throws UnableToCompleteException {
+ for (Iterator<Condition> iter = getConditions().iterator(); iter.hasNext();) {
Condition condition = (Condition) iter.next();
- if (condition.isTrue(logger, context, testType)) {
+ if (condition.isTrue(logger, propertyOracle, typeOracle, testType)) {
return false;
}
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenPropertyIs.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenPropertyIs.java
index 566c24d..ac8205d 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenPropertyIs.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenPropertyIs.java
@@ -17,11 +17,14 @@
import com.google.gwt.core.ext.BadPropertyValueException;
import com.google.gwt.core.ext.ConfigurationProperty;
-import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.core.ext.SelectionProperty;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.util.collect.Sets;
+
+import java.util.Set;
/**
* A deferred binding condition to determine whether a named property has a
@@ -38,22 +41,25 @@
this.value = value;
}
+ @Override
+ public Set<String> getRequiredProperties() {
+ return Sets.create(propName);
+ }
+
public String toString() {
return "<when-property-is name='" + propName + "' value='" + value + "'/>";
}
- protected boolean doEval(TreeLogger logger, GeneratorContext context,
- String testType) throws UnableToCompleteException {
+ protected boolean doEval(TreeLogger logger, PropertyOracle propertyOracle,
+ TypeOracle typeOracle, String testType) throws UnableToCompleteException {
String testValue;
try {
- PropertyOracle propertyOracle = context.getPropertyOracle();
try {
- SelectionProperty prop
- = propertyOracle.getSelectionProperty(logger, propName);
+ SelectionProperty prop = propertyOracle.getSelectionProperty(logger,
+ propName);
testValue = prop.getCurrentValue();
} catch (BadPropertyValueException e) {
- ConfigurationProperty prop
- = propertyOracle.getConfigurationProperty(propName);
+ ConfigurationProperty prop = propertyOracle.getConfigurationProperty(propName);
testValue = prop.getValues().get(0);
}
logger.log(TreeLogger.DEBUG, "Property value is '" + testValue + "'",
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeAssignableTo.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeAssignableTo.java
index bc8e472..04d1df4 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeAssignableTo.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeAssignableTo.java
@@ -15,7 +15,7 @@
*/
package com.google.gwt.dev.cfg;
-import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
@@ -42,10 +42,8 @@
return "<when-assignable class='" + assignableToTypeName + "'/>";
}
- protected boolean doEval(TreeLogger logger, GeneratorContext context,
- String testType) throws UnableToCompleteException {
- TypeOracle typeOracle = context.getTypeOracle();
-
+ protected boolean doEval(TreeLogger logger, PropertyOracle propertyOracle,
+ TypeOracle typeOracle, String testType) throws UnableToCompleteException {
JClassType fromType = typeOracle.findType(testType);
if (fromType == null) {
Util.logMissingTypeErrorWithHints(logger, testType);
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeIs.java b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeIs.java
index 732d78c..68204b5 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeIs.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenTypeIs.java
@@ -15,8 +15,9 @@
*/
package com.google.gwt.dev.cfg;
-import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
/**
* A deferred binding condition to determine whether the type being rebound is
@@ -34,8 +35,8 @@
return "<when-type-is class='" + exactTypeName + "'/>";
}
- protected boolean doEval(TreeLogger logger, GeneratorContext context,
- String testType) {
+ protected boolean doEval(TreeLogger logger, PropertyOracle propertyOracle,
+ TypeOracle typeOracle, String testType) {
return exactTypeName.equals(testType);
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Conditions.java b/dev/core/src/com/google/gwt/dev/cfg/Conditions.java
index e18dd95..8993fae 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/Conditions.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/Conditions.java
@@ -24,7 +24,7 @@
*/
public class Conditions {
- private final List list = new ArrayList();
+ private final List<Condition> list = new ArrayList<Condition>();
/**
* Appends a condition.
@@ -33,7 +33,7 @@
list.add(condition);
}
- public Iterator iterator() {
+ public Iterator<Condition> iterator() {
return list.iterator();
}
}
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 3431ef6..2947db1 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
@@ -449,7 +449,7 @@
* Create a default property provider for any properties with more than
* one possible value and no existing provider.
*/
- if (prop.getProvider() == null && prop.getAllowedValues().length > 1) {
+ if (prop.getProvider() == null && prop.getConstrainedValue() == null) {
String src = "{";
src += "return __gwt_getMetaProperty(\"";
src += prop.getName();
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 888cd49..fb63205 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
@@ -46,7 +46,7 @@
* Configures a module definition object using XML.
*/
public class ModuleDefSchema extends Schema {
-
+
private final class BodySchema extends Schema {
protected final String __add_linker_1_name = null;
@@ -133,6 +133,12 @@
protected final String __super_source_5_casesensitive = "true";
+ /**
+ * Used to accumulate binding property conditions before recording the
+ * newly-allowed values.
+ */
+ private ConditionAll bindingPropertyCondition;
+
private Schema fChild;
protected Schema __add_linker_begin(LinkerName name)
@@ -144,7 +150,7 @@
moduleDef.addLinker(name.name);
return null;
}
-
+
protected Schema __clear_configuration_property_begin(PropertyName name)
throws UnableToCompleteException {
// Don't allow configuration properties with the same name as a
@@ -174,14 +180,13 @@
protected Schema __define_configuration_property_begin(PropertyName name,
String is_multi_valued) throws UnableToCompleteException {
boolean isMultiValued = toPrimitiveBoolean(is_multi_valued);
-
+
// Don't allow configuration properties with the same name as a
// deferred-binding property.
Property existingProperty = moduleDef.getProperties().find(name.token);
if (existingProperty == null) {
// Create the property
- moduleDef.getProperties().createConfiguration(name.token,
- isMultiValued);
+ moduleDef.getProperties().createConfiguration(name.token, isMultiValued);
if (!propertyDefinitions.containsKey(name.token)) {
propertyDefinitions.put(name.token, moduleName);
}
@@ -189,36 +194,38 @@
// Allow redefinition only if the 'is-multi-valued' setting is identical
// The previous definition may have been explicit, via
// <define-configuration-property>, or implicit, via
- // <set-configuration-property>. In the latter case, the
+ // <set-configuration-property>. In the latter case, the
// value of the 'is-multi-valued' attribute was taken as false.
String originalDefinition = propertyDefinitions.get(name.token);
- if (((ConfigurationProperty) existingProperty).allowsMultipleValues()
- != isMultiValued) {
+ if (((ConfigurationProperty) existingProperty).allowsMultipleValues() != isMultiValued) {
if (originalDefinition != null) {
- logger.log(TreeLogger.ERROR, "The configuration property named "
- + name.token
- + " is already defined with a different 'is-multi-valued' setting");
+ logger.log(
+ TreeLogger.ERROR,
+ "The configuration property named "
+ + name.token
+ + " is already defined with a different 'is-multi-valued' setting");
} else {
- logger.log(TreeLogger.ERROR, "The configuration property named "
- + name.token
- + " is already defined implicitly by 'set-configuration-property'"
- + " in " + propertySettings.get(name.token)
- + " with 'is-multi-valued' set to 'false'");
+ logger.log(
+ TreeLogger.ERROR,
+ "The configuration property named "
+ + name.token
+ + " is already defined implicitly by 'set-configuration-property'"
+ + " in " + propertySettings.get(name.token)
+ + " with 'is-multi-valued' set to 'false'");
}
throw new UnableToCompleteException();
} else {
if (originalDefinition != null) {
logger.log(TreeLogger.WARN,
"Ignoring identical definition of the configuration property named "
- + name.token + " (originally defined in "
- + originalDefinition
- + ", redefined in " + moduleName + ")");
+ + name.token + " (originally defined in "
+ + originalDefinition + ", redefined in " + moduleName + ")");
} else {
logger.log(TreeLogger.WARN,
"Definition of already set configuration property named "
- + name.token + " in " + moduleName
- + " (set in " + propertySettings.get(name.token)
- + "). This may be disallowed in the future.");
+ + name.token + " in " + moduleName + " (set in "
+ + propertySettings.get(name.token)
+ + "). This may be disallowed in the future.");
}
}
} else {
@@ -233,7 +240,7 @@
}
throw new UnableToCompleteException();
}
-
+
// No children.
return null;
}
@@ -279,7 +286,7 @@
BindingProperty prop = moduleDef.getProperties().createBinding(name.token);
for (int i = 0; i < values.length; i++) {
- prop.addDefinedValue(values[i].token);
+ prop.addDefinedValue(prop.getRootCondition(), values[i].token);
}
// No children.
@@ -316,7 +323,7 @@
protected Schema __extend_property_begin(BindingProperty property,
PropertyValue[] values) {
for (int i = 0; i < values.length; i++) {
- property.addDefinedValue(values[i].token);
+ property.addDefinedValue(property.getRootCondition(), values[i].token);
}
// No children.
@@ -326,13 +333,13 @@
protected Schema __fail_begin() {
RuleFail rule = new RuleFail();
moduleDef.getRules().prepend(rule);
- return new ConditionSchema(rule.getRootCondition());
+ return new FullConditionSchema(rule.getRootCondition());
}
protected Schema __generate_with_begin(Generator gen) {
RuleGenerateWith rule = new RuleGenerateWith(gen);
moduleDef.getRules().prepend(rule);
- return new ConditionSchema(rule.getRootCondition());
+ return new FullConditionSchema(rule.getRootCondition());
}
protected Schema __inherits_begin(String name)
@@ -393,7 +400,7 @@
protected Schema __replace_with_begin(String className) {
RuleReplaceWith rule = new RuleReplaceWith(className);
moduleDef.getRules().prepend(rule);
- return new ConditionSchema(rule.getRootCondition());
+ return new FullConditionSchema(rule.getRootCondition());
}
/**
@@ -444,7 +451,7 @@
protected Schema __set_configuration_property_begin(PropertyName name,
String value) throws UnableToCompleteException {
-
+
Property existingProperty = moduleDef.getProperties().find(name.token);
if (existingProperty == null) {
// If a property is created by "set-configuration-property" without
@@ -456,10 +463,8 @@
propertySettings.put(name.token, moduleName);
}
- logger.log(TreeLogger.WARN,
- "Setting configuration property named "
- + name.token + " in "
- + moduleName
+ logger.log(TreeLogger.WARN, "Setting configuration property named "
+ + name.token + " in " + moduleName
+ " that has not been previously defined."
+ " This may be disallowed in the future.");
} else if (!(existingProperty instanceof ConfigurationProperty)) {
@@ -482,6 +487,12 @@
protected Schema __set_property_begin(BindingProperty prop,
PropertyValue[] value) throws UnableToCompleteException {
+ bindingPropertyCondition = new ConditionAll();
+ return new PropertyConditionSchema(bindingPropertyCondition);
+ }
+
+ protected void __set_property_end(BindingProperty prop,
+ PropertyValue[] value) throws UnableToCompleteException {
boolean error = false;
String[] stringValues = new String[value.length];
for (int i = 0, len = stringValues.length; i < len; i++) {
@@ -495,16 +506,18 @@
throw new UnableToCompleteException();
}
- prop.setAllowedValues(stringValues);
+ // No conditions were specified, so use the property's root condition
+ if (!bindingPropertyCondition.getConditions().iterator().hasNext()) {
+ bindingPropertyCondition = prop.getRootCondition();
+ }
- // No children.
- return null;
+ prop.setAllowedValues(bindingPropertyCondition, stringValues);
}
protected Schema __set_property_fallback_begin(BindingProperty prop,
PropertyValue value) throws UnableToCompleteException {
boolean error = true;
- for (String possibleValue : prop.getAllowedValues()) {
+ for (String possibleValue : prop.getAllowedValues(prop.getRootCondition())) {
if (possibleValue.equals(value.token)) {
error = false;
break;
@@ -512,8 +525,8 @@
}
if (error) {
logger.log(TreeLogger.ERROR, "The fallback value '" + value.token
- + "' was not previously defined for property '"
- + prop.getName() + "'");
+ + "' was not previously defined for property '" + prop.getName()
+ + "'");
throw new UnableToCompleteException();
}
prop.setFallback(value.token);
@@ -697,52 +710,17 @@
}
}
- private final class ConditionSchema extends Schema {
-
- protected final String __when_property_is_1_name = null;
-
- protected final String __when_property_is_2_value = null;
+ /**
+ * All conditional expressions, including those based on types.
+ */
+ private final class FullConditionSchema extends PropertyConditionSchema {
protected final String __when_type_assignable_1_class = null;
protected final String __when_type_is_1_class = null;
- private final CompoundCondition parentCondition;
-
- public ConditionSchema(CompoundCondition parentCondition) {
- this.parentCondition = parentCondition;
- }
-
- protected Schema __all_begin() {
- CompoundCondition cond = new ConditionAll();
- parentCondition.getConditions().add(cond);
- return new ConditionSchema(cond);
- }
-
- protected Schema __any_begin() {
- CompoundCondition cond = new ConditionAny();
- parentCondition.getConditions().add(cond);
- return new ConditionSchema(cond);
- }
-
- protected Schema __none_begin() {
- CompoundCondition cond = new ConditionNone();
- parentCondition.getConditions().add(cond);
- return new ConditionSchema(cond);
- }
-
- /*
- * We intentionally use the BindingProperty type here for tough-love on
- * module writers. It prevents them from trying to create property providers
- * for unknown properties.
- */
- protected Schema __when_property_is_begin(BindingProperty prop,
- PropertyValue value) {
- Condition cond = new ConditionWhenPropertyIs(prop.getName(), value.token);
- parentCondition.getConditions().add(cond);
-
- // No children allowed.
- return null;
+ public FullConditionSchema(CompoundCondition parentCondition) {
+ super(parentCondition);
}
protected Schema __when_type_assignable_begin(String className) {
@@ -760,6 +738,11 @@
// No children allowed.
return null;
}
+
+ @Override
+ protected Schema subSchema(CompoundCondition cond) {
+ return new FullConditionSchema(cond);
+ }
}
private static final class IncludeExcludeSchema extends Schema {
@@ -942,6 +925,57 @@
}
}
+ /**
+ * A limited number of conditional predicates based only on properties.
+ */
+ private class PropertyConditionSchema extends Schema {
+ protected final String __when_property_is_1_name = null;
+
+ protected final String __when_property_is_2_value = null;
+
+ protected final CompoundCondition parentCondition;
+
+ public PropertyConditionSchema(CompoundCondition parentCondition) {
+ this.parentCondition = parentCondition;
+ }
+
+ protected Schema __all_begin() {
+ CompoundCondition cond = new ConditionAll();
+ parentCondition.getConditions().add(cond);
+ return subSchema(cond);
+ }
+
+ protected Schema __any_begin() {
+ CompoundCondition cond = new ConditionAny();
+ parentCondition.getConditions().add(cond);
+ return subSchema(cond);
+ }
+
+ protected Schema __none_begin() {
+ CompoundCondition cond = new ConditionNone();
+ parentCondition.getConditions().add(cond);
+ return subSchema(cond);
+ }
+
+ /*
+ * We intentionally use the BindingProperty type here for tough-love on
+ * module writers. It prevents them from trying to create property providers
+ * for unknown properties.
+ */
+ protected Schema __when_property_is_begin(BindingProperty prop,
+ PropertyValue value) {
+ Condition cond = new ConditionWhenPropertyIs(prop.getName(), value.token);
+ parentCondition.getConditions().add(cond);
+
+ // No children allowed.
+ return null;
+ }
+
+ protected Schema subSchema(CompoundCondition cond) {
+ return new PropertyConditionSchema(cond);
+ }
+ }
+
private static class PropertyName {
public final String token;
@@ -1075,15 +1109,13 @@
* Map of property names to the modules that defined them explicitly using
* <define-configuration-property>, used to generate warning messages.
*/
- private static final HashMap<String,String> propertyDefinitions
- = new HashMap<String,String>();
-
+ private static final HashMap<String, String> propertyDefinitions = new HashMap<String, String>();
+
/**
* Map of property names to the modules that defined them implicitly using
* <set-configuration-property>, used to generate warning messages.
*/
- private static final HashMap<String,String> propertySettings
- = new HashMap<String,String>();
+ private static final HashMap<String, String> propertySettings = new HashMap<String, String>();
private static final Map<String, Object> singletonsByName = new HashMap<String, Object>();
@@ -1100,7 +1132,7 @@
private static boolean toPrimitiveBoolean(String s) {
return "yes".equalsIgnoreCase(s) || "true".equalsIgnoreCase(s);
}
-
+
protected final String __module_1_rename_to = "";
private final PropertyAttrCvt bindingPropAttrCvt = new PropertyAttrCvt(
BindingProperty.class);
@@ -1126,7 +1158,8 @@
private final PropertyValueAttrCvt propValueAttrCvt = new PropertyValueAttrCvt();
public ModuleDefSchema(TreeLogger logger, ModuleDefLoader loader,
- String moduleName, URL moduleURL, String modulePackageAsPath, ModuleDef toConfigure) {
+ String moduleName, URL moduleURL, String modulePackageAsPath,
+ ModuleDef toConfigure) {
this.logger = logger;
this.loader = loader;
this.moduleName = moduleName;
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Properties.java b/dev/core/src/com/google/gwt/dev/cfg/Properties.java
index 37979ff..3a8a278 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/Properties.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/Properties.java
@@ -47,8 +47,10 @@
* Creates the specified configuration property, or returns an existing one by
* the specified name if present.
*/
- public ConfigurationProperty createConfiguration(String name, boolean allowMultipleValues) {
- ConfigurationProperty prop = create(name, allowMultipleValues, ConfigurationProperty.class);
+ public ConfigurationProperty createConfiguration(String name,
+ boolean allowMultipleValues) {
+ ConfigurationProperty prop = create(name, allowMultipleValues,
+ ConfigurationProperty.class);
configProps.add(prop);
return prop;
}
@@ -72,35 +74,17 @@
return map.values().iterator();
}
- /**
- * Count the total number of permutations that this property set supports.
- */
- public int numPermutations() {
- BindingProperty[] bindingPropsArray = bindingProps.toArray(
- new BindingProperty[bindingProps.size()]);
-
- int count = 1;
-
- for (BindingProperty prop : bindingPropsArray) {
- String[] options = prop.getAllowedValues();
- assert (options.length > 0);
- count *= options.length;
- }
-
- return count;
- }
-
private <T extends Property> T create(String name, Class<T> clazz) {
return create(name, false, false, clazz);
}
-
- private <T extends Property> T create(String name, boolean flag, Class<T> clazz) {
+
+ private <T extends Property> T create(String name, boolean flag,
+ Class<T> clazz) {
return create(name, flag, true, clazz);
}
-
- private <T extends Property> T create(String name,
- boolean flag, boolean useFlagArgument,
- Class<T> clazz) {
+
+ private <T extends Property> T create(String name, boolean flag,
+ boolean useFlagArgument, Class<T> clazz) {
if (clazz == null) {
throw new NullPointerException("clazz");
} else if (name == null) {
@@ -122,8 +106,8 @@
try {
T newInstance;
if (useFlagArgument) {
- newInstance = clazz.getConstructor(String.class,
- boolean.class).newInstance(name, flag);
+ newInstance = clazz.getConstructor(String.class, boolean.class).newInstance(
+ name, flag);
} else {
newInstance = clazz.getConstructor(String.class).newInstance(name);
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Property.java b/dev/core/src/com/google/gwt/dev/cfg/Property.java
index 7c1f7b4..8019ae8 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/Property.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/Property.java
@@ -31,7 +31,7 @@
public int compareTo(Property o) {
return name.compareTo(o.name);
}
-
+
@Override
public boolean equals(Object o) {
if (!(o instanceof Property)) {
@@ -43,7 +43,7 @@
public String getName() {
return name;
}
-
+
@Override
public int hashCode() {
return name.hashCode();
diff --git a/dev/core/src/com/google/gwt/dev/cfg/PropertyPermutations.java b/dev/core/src/com/google/gwt/dev/cfg/PropertyPermutations.java
index 1e77ee2..682b2bf 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/PropertyPermutations.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/PropertyPermutations.java
@@ -15,10 +15,17 @@
*/
package com.google.gwt.dev.cfg;
+import com.google.gwt.core.ext.PropertyOracle;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+
import java.util.ArrayList;
import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
-import java.util.SortedSet;
+import java.util.Map;
+import java.util.Set;
/**
* Generates all possible permutations of properties in a module. Each
@@ -36,12 +43,9 @@
private static List<String[]> allPermutationsOf(Properties properties) {
BindingProperty[] bindingProperties = getOrderedPropertiesOf(properties);
- int permCount = properties.numPermutations();
-
- List<String[]> permutations = new ArrayList<String[]>(permCount);
+ List<String[]> permutations = new ArrayList<String[]>();
if (bindingProperties.length > 0) {
permute(bindingProperties, null, 0, permutations);
- assert (permCount == permutations.size());
} else {
permutations.add(new String[0]);
}
@@ -49,8 +53,44 @@
}
private static BindingProperty[] getOrderedPropertiesOf(Properties properties) {
- SortedSet<BindingProperty> bindingProps = properties.getBindingProperties();
- return bindingProps.toArray(new BindingProperty[bindingProps.size()]);
+ /*
+ * We delete items from this set, but want to retain the original order as
+ * much as possible.
+ */
+ Set<BindingProperty> bindingProps = new LinkedHashSet<BindingProperty>(
+ properties.getBindingProperties());
+
+ // Accumulates the order in which the properties should be evaluated
+ Map<String, BindingProperty> evaluationOrder = new LinkedHashMap<String, BindingProperty>(
+ bindingProps.size());
+
+ /*
+ * Insert a property after all of the properties that it depends upon have
+ * been inserted.
+ */
+ while (!bindingProps.isEmpty()) {
+ boolean changed = false;
+
+ for (Iterator<BindingProperty> it = bindingProps.iterator(); it.hasNext();) {
+ BindingProperty prop = it.next();
+
+ Set<String> deps = prop.getRequiredProperties();
+ if (evaluationOrder.keySet().containsAll(deps)) {
+ it.remove();
+ evaluationOrder.put(prop.getName(), prop);
+ changed = true;
+ }
+ }
+
+ if (!changed) {
+ throw new IllegalStateException(
+ "Cycle detected within remaining property dependencies "
+ + bindingProps.toString());
+ }
+ }
+
+ return evaluationOrder.values().toArray(
+ new BindingProperty[evaluationOrder.size()]);
}
private static void permute(BindingProperty[] properties, String[] soFar,
@@ -58,8 +98,32 @@
int lastProp = properties.length - 1;
BindingProperty prop = properties[whichProp];
- String[] options = prop.getAllowedValues();
+ // Find the last-one-wins Condition
+ Condition winner = null;
+ if (prop.getConditionalValues().size() == 1) {
+ winner = prop.getRootCondition();
+ } else {
+ BindingProperty[] answerable = new BindingProperty[soFar.length];
+ System.arraycopy(properties, 0, answerable, 0, soFar.length);
+ PropertyOracle propertyOracle = new StaticPropertyOracle(answerable,
+ soFar, new ConfigurationProperty[0]);
+
+ for (Condition cond : prop.getConditionalValues().keySet()) {
+ try {
+ if (cond.isTrue(TreeLogger.NULL, propertyOracle, null, null)) {
+ winner = cond;
+ }
+ } catch (UnableToCompleteException e) {
+ throw new IllegalStateException(
+ "Should never get here for simple properties", e);
+ }
+ }
+ }
+
+ assert winner != null;
+
+ String[] options = prop.getAllowedValues(winner);
for (int i = 0; i < options.length; i++) {
String knownValue = options[i];
@@ -92,6 +156,12 @@
firstPerm + numPerms);
}
+ public PropertyPermutations(PropertyPermutations allPermutations,
+ int firstPerm, int numPerms) {
+ this.properties = allPermutations.properties;
+ values = allPermutations.values.subList(firstPerm, firstPerm + numPerms);
+ }
+
public BindingProperty[] getOrderedProperties() {
return getOrderedPropertiesOf(properties);
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Rule.java b/dev/core/src/com/google/gwt/dev/cfg/Rule.java
index 8096193..d92146f 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/Rule.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/Rule.java
@@ -32,7 +32,8 @@
public boolean isApplicable(TreeLogger logger, GeneratorContext context,
String typeName) throws UnableToCompleteException {
- return rootCondition.isTrue(logger, context, typeName);
+ return rootCondition.isTrue(logger, context.getPropertyOracle(),
+ context.getTypeOracle(), typeName);
}
public abstract String realize(TreeLogger logger, GeneratorContext context,
diff --git a/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java b/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java
index b744f20..c709487 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java
@@ -19,11 +19,14 @@
import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.core.ext.SelectionProperty;
import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.cfg.BindingProperty;
+import com.google.gwt.dev.cfg.Condition;
import com.google.gwt.dev.cfg.ConfigurationProperty;
import com.google.gwt.dev.cfg.Properties;
import com.google.gwt.dev.cfg.Property;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -47,9 +50,8 @@
this.props = props;
}
- public com.google.gwt.core.ext.ConfigurationProperty
- getConfigurationProperty(String propertyName)
- throws BadPropertyValueException {
+ public com.google.gwt.core.ext.ConfigurationProperty getConfigurationProperty(
+ String propertyName) throws BadPropertyValueException {
Property prop = getProperty(propertyName);
if (prop instanceof ConfigurationProperty) {
final ConfigurationProperty cprop = (ConfigurationProperty) prop;
@@ -118,14 +120,20 @@
if (prop instanceof BindingProperty) {
final BindingProperty cprop = (BindingProperty) prop;
final String name = cprop.getName();
- final String value = computePropertyValue(logger, propertyName, cprop);
+ final String value;
+ if (prevAnswers.containsKey(propertyName)) {
+ value = prevAnswers.get(propertyName);
+ } else {
+ value = computePropertyValue(logger, propertyName, cprop);
+ prevAnswers.put(propertyName, value);
+ }
final String fallback = cprop.getFallback();
final SortedSet<String> possibleValues = new TreeSet<String>();
for (String v : cprop.getDefinedValues()) {
possibleValues.add(v);
}
return new com.google.gwt.core.ext.SelectionProperty() {
-
+
public String getCurrentValue() {
return value;
}
@@ -147,6 +155,26 @@
}
}
+ private Condition computeActiveCondition(TreeLogger logger,
+ BindingProperty prop) throws BadPropertyValueException {
+ // Last-one-wins
+ Condition winner = null;
+ for (Condition cond : prop.getConditionalValues().keySet()) {
+ try {
+ if (cond.isTrue(logger, this, null, null)) {
+ winner = cond;
+ }
+ } catch (UnableToCompleteException e) {
+ BadPropertyValueException t = new BadPropertyValueException(
+ prop.getName());
+ t.initCause(e);
+ throw t;
+ }
+ }
+ assert winner != null : "No active Condition for " + prop.getName();
+ return winner;
+ }
+
/**
* Returns the value of the specified property.
*
@@ -157,12 +185,18 @@
private String computePropertyValue(TreeLogger logger, String propertyName,
BindingProperty prop) throws BadPropertyValueException {
- if (prop.getAllowedValues().length == 1) {
+ String value = prop.getConstrainedValue();
+ if (value != null) {
// If there is only one legal value, use that.
- return prop.getAllowedValues()[0];
+ return value;
}
- String value;
+ Condition winner = computeActiveCondition(logger, prop);
+ String[] values = prop.getAllowedValues(winner);
+ if (values.length == 1) {
+ return values[0];
+ }
+
// Invokes the script function.
//
try {
@@ -180,7 +214,7 @@
}
// value may be null if the provider returned an unknown property value.
- if (prop.isAllowedValue(value)) {
+ if (Arrays.asList(values).contains(value)) {
return value;
} else {
// Bad value due to the provider returning an unknown value.
diff --git a/dev/core/test/com/google/gwt/dev/util/test/PropertyPermutationsTest.java b/dev/core/test/com/google/gwt/dev/util/test/PropertyPermutationsTest.java
index d8774df..97aa1c6 100644
--- a/dev/core/test/com/google/gwt/dev/util/test/PropertyPermutationsTest.java
+++ b/dev/core/test/com/google/gwt/dev/util/test/PropertyPermutationsTest.java
@@ -15,25 +15,62 @@
*/
package com.google.gwt.dev.util.test;
+import com.google.gwt.dev.cfg.BindingProperty;
+import com.google.gwt.dev.cfg.ConditionAny;
+import com.google.gwt.dev.cfg.ConditionWhenPropertyIs;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.cfg.Properties;
-import com.google.gwt.dev.cfg.BindingProperty;
import com.google.gwt.dev.cfg.PropertyPermutations;
import junit.framework.TestCase;
import java.util.Iterator;
+/**
+ * Tests the PropertyPermutations code.
+ */
public class PropertyPermutationsTest extends TestCase {
+ /**
+ * Make sure that a cycle doesn't cause an infinite loop.
+ */
+ public void testCycle() {
+ // This is what you'd get with a conditional <set-property value="false">
+ ModuleDef md = new ModuleDef("testCycle");
+ Properties props = md.getProperties();
+
+ {
+ BindingProperty prop = props.createBinding("A");
+ prop.addDefinedValue(prop.getRootCondition(), "a1");
+ prop.addDefinedValue(prop.getRootCondition(), "a2");
+
+ prop.addDefinedValue(new ConditionWhenPropertyIs("B", "b3"), "a3");
+ }
+
+ {
+ BindingProperty prop = props.createBinding("B");
+ prop.addDefinedValue(prop.getRootCondition(), "b1");
+ prop.addDefinedValue(prop.getRootCondition(), "b2");
+
+ prop.addDefinedValue(new ConditionWhenPropertyIs("A", "a3"), "b3");
+ }
+
+ try {
+ new PropertyPermutations(props);
+ fail();
+ } catch (IllegalStateException e) {
+ // OK
+ }
+ }
+
public void testOneDimensionPerm() {
ModuleDef md = new ModuleDef("testOneDimensionPerm");
Properties props = md.getProperties();
{
BindingProperty prop = props.createBinding("debug");
- prop.addDefinedValue("false");
- prop.addDefinedValue("true");
+ prop.addDefinedValue(prop.getRootCondition(), "false");
+ prop.addDefinedValue(prop.getRootCondition(), "true");
}
// Permutations and their values are in stable alphabetical order.
@@ -57,15 +94,15 @@
{
BindingProperty prop = props.createBinding("user.agent");
- prop.addDefinedValue("moz");
- prop.addDefinedValue("ie6");
- prop.addDefinedValue("opera");
+ prop.addDefinedValue(prop.getRootCondition(), "moz");
+ prop.addDefinedValue(prop.getRootCondition(), "ie6");
+ prop.addDefinedValue(prop.getRootCondition(), "opera");
}
{
BindingProperty prop = props.createBinding("debug");
- prop.addDefinedValue("false");
- prop.addDefinedValue("true");
+ prop.addDefinedValue(prop.getRootCondition(), "false");
+ prop.addDefinedValue(prop.getRootCondition(), "true");
}
// String[]s and their values are in stable alphabetical order.
@@ -104,4 +141,134 @@
assertEquals("true", perm[0]);
assertEquals("opera", perm[1]);
}
+
+ public void testTwoDimensionPermWithExpansion() {
+ ModuleDef md = new ModuleDef("testTwoDimensionsWithExpansion");
+ Properties props = md.getProperties();
+
+ {
+ BindingProperty prop = props.createBinding("user.agent");
+ prop.addDefinedValue(prop.getRootCondition(), "moz");
+ prop.addDefinedValue(prop.getRootCondition(), "ie6");
+ prop.addDefinedValue(prop.getRootCondition(), "ie8");
+ prop.addDefinedValue(prop.getRootCondition(), "opera");
+ }
+
+ {
+ BindingProperty prop = props.createBinding("stackTraces");
+ prop.addDefinedValue(prop.getRootCondition(), "false");
+ prop.addDefinedValue(prop.getRootCondition(), "true");
+ // <set-property name="stackTraces" value="false" />
+ prop.setAllowedValues(prop.getRootCondition(), "false");
+
+ /*
+ * <set-property name="stackTraces" value="true,false"> <when user.agent
+ * is ie6 or ie 8> </set-property>
+ */
+ ConditionAny cond = new ConditionAny();
+ cond.getConditions().add(new ConditionWhenPropertyIs("user.agent", "ie6"));
+ cond.getConditions().add(new ConditionWhenPropertyIs("user.agent", "ie8"));
+ prop.setAllowedValues(cond, "true", "false");
+ }
+
+ validateTwoDimensionPerm(props);
+ }
+
+ public void testTwoDimensionPermWithExtension() {
+ // This is what you'd get with a conditional <extend-property>
+ ModuleDef md = new ModuleDef("testTwoDimensionsWithConditions");
+ Properties props = md.getProperties();
+
+ {
+ BindingProperty prop = props.createBinding("user.agent");
+ prop.addDefinedValue(prop.getRootCondition(), "moz");
+ prop.addDefinedValue(prop.getRootCondition(), "ie6");
+ prop.addDefinedValue(prop.getRootCondition(), "ie8");
+ prop.addDefinedValue(prop.getRootCondition(), "opera");
+ }
+
+ {
+ BindingProperty prop = props.createBinding("stackTraces");
+ prop.addDefinedValue(prop.getRootCondition(), "false");
+
+ ConditionAny cond = new ConditionAny();
+ cond.getConditions().add(new ConditionWhenPropertyIs("user.agent", "ie6"));
+ cond.getConditions().add(new ConditionWhenPropertyIs("user.agent", "ie8"));
+
+ prop.addDefinedValue(cond, "true");
+ }
+
+ validateTwoDimensionPerm(props);
+ }
+
+ public void testTwoDimensionPermWithRestriction() {
+ // This is what you'd get with a conditional <set-property value="false">
+ ModuleDef md = new ModuleDef("testTwoDimensionsWithRestriction");
+ Properties props = md.getProperties();
+
+ {
+ BindingProperty prop = props.createBinding("user.agent");
+ prop.addDefinedValue(prop.getRootCondition(), "moz");
+ prop.addDefinedValue(prop.getRootCondition(), "ie6");
+ prop.addDefinedValue(prop.getRootCondition(), "ie8");
+ prop.addDefinedValue(prop.getRootCondition(), "opera");
+ }
+
+ {
+ BindingProperty prop = props.createBinding("stackTraces");
+ prop.addDefinedValue(prop.getRootCondition(), "false");
+ prop.addDefinedValue(prop.getRootCondition(), "true");
+
+ ConditionAny cond = new ConditionAny();
+ cond.getConditions().add(new ConditionWhenPropertyIs("user.agent", "moz"));
+ cond.getConditions().add(
+ new ConditionWhenPropertyIs("user.agent", "opera"));
+
+ prop.setAllowedValues(cond, "false");
+ }
+
+ validateTwoDimensionPerm(props);
+ }
+
+ private void validateTwoDimensionPerm(Properties props) {
+ PropertyPermutations perms = new PropertyPermutations(props);
+
+ assertEquals(6, perms.size());
+
+ // Order is alphabetical in dependency order
+ String[] perm;
+ Iterator<String[]> iter = perms.iterator();
+
+ assertTrue(iter.hasNext());
+ perm = iter.next();
+ assertEquals("ie6", perm[0]);
+ assertEquals("false", perm[1]);
+
+ assertTrue(iter.hasNext());
+ perm = iter.next();
+ assertEquals("ie6", perm[0]);
+ assertEquals("true", perm[1]);
+
+ assertTrue(iter.hasNext());
+ perm = iter.next();
+ assertEquals("ie8", perm[0]);
+ assertEquals("false", perm[1]);
+
+ assertTrue(iter.hasNext());
+ perm = iter.next();
+ assertEquals("ie8", perm[0]);
+ assertEquals("true", perm[1]);
+
+ assertTrue(iter.hasNext());
+ perm = iter.next();
+ assertEquals("moz", perm[0]);
+ assertEquals("false", perm[1]);
+
+ assertTrue(iter.hasNext());
+ perm = iter.next();
+ assertEquals("opera", perm[0]);
+ assertEquals("false", perm[1]);
+
+ assertFalse(iter.hasNext());
+ }
}
diff --git a/user/src/com/google/gwt/junit/JUnitShell.java b/user/src/com/google/gwt/junit/JUnitShell.java
index 9ab5c7a..54a6c58 100644
--- a/user/src/com/google/gwt/junit/JUnitShell.java
+++ b/user/src/com/google/gwt/junit/JUnitShell.java
@@ -706,7 +706,9 @@
Properties props = module.getProperties();
Property userAgent = props.find("user.agent");
if (userAgent instanceof BindingProperty) {
- ((BindingProperty) userAgent).setAllowedValues(userAgents);
+ BindingProperty bindingProperty = (BindingProperty) userAgent;
+ bindingProperty.setAllowedValues(bindingProperty.getRootCondition(),
+ userAgents);
}
}
super.compile(getTopLogger(), module);
diff --git a/user/test/com/google/gwt/dev/cfg/PropertyTest.gwt.xml b/user/test/com/google/gwt/dev/cfg/PropertyTest.gwt.xml
index 5f61ad5..00695f5 100644
--- a/user/test/com/google/gwt/dev/cfg/PropertyTest.gwt.xml
+++ b/user/test/com/google/gwt/dev/cfg/PropertyTest.gwt.xml
@@ -20,7 +20,30 @@
<define-property name="restricted1s" values="a,b,c,d,e,f" />
<set-property name="restricted1s" value="d,e,f" />
<set-property name="restricted1s" value="a" />
-
+
+ <define-property name="conditional" values="a,b,c" />
+ <set-property name="conditional" value="a,b">
+ <when-property-is name="restricted" value="a" />
+ </set-property>
+ <set-property name="conditional" value="c">
+ <when-property-is name="restricted" value="c" />
+ </set-property>
+
+ <define-property name="derived" values="a,b,c" />
+ <set-property name="derived" value="a" />
+ <set-property name="derived" value="b" >
+ <when-property-is name="restricted" value="b" />
+ </set-property>
+ <set-property name="derived" value="c">
+ <when-property-is name="restricted" value="c" />
+ </set-property>
+
+ <define-property name="reset" values="a,b,c" />
+ <set-property name="reset" value="a,b" >
+ <when-property-is name="restricted" value="a" />
+ </set-property>
+ <set-property name="reset" value="a" />
+
<set-configuration-property name="configProperty" value="Hello World!" />
<set-configuration-property name="configRedefined" value="foo" />
diff --git a/user/test/com/google/gwt/dev/cfg/PropertyTest.java b/user/test/com/google/gwt/dev/cfg/PropertyTest.java
index bc85804..892427c 100644
--- a/user/test/com/google/gwt/dev/cfg/PropertyTest.java
+++ b/user/test/com/google/gwt/dev/cfg/PropertyTest.java
@@ -23,6 +23,8 @@
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Set;
/**
* Checks the behaviors of ModuleDefLoader and Properties.
@@ -50,9 +52,11 @@
{
BindingProperty restricted = (BindingProperty) p.find("restricted");
assertNotNull(restricted);
- assertEquals(3, restricted.getAllowedValues().length);
- assertEquals(Arrays.asList("a", "b", "c"),
- Arrays.asList(restricted.getAllowedValues()));
+ assertEquals(3,
+ restricted.getAllowedValues(restricted.getRootCondition()).length);
+ assertEquals(
+ Arrays.asList("a", "b", "c"),
+ Arrays.asList(restricted.getAllowedValues(restricted.getRootCondition())));
assertTrue(restricted.isDefinedValue("d"));
assertFalse(restricted.isAllowedValue("d"));
}
@@ -61,8 +65,30 @@
BindingProperty restricted1s = (BindingProperty) p.find("restricted1s");
assertNotNull(restricted1s);
assertTrue(restricted1s.isAllowedValue("a"));
- assertEquals(1, restricted1s.getAllowedValues().length);
- assertEquals("a", restricted1s.getAllowedValues()[0]);
+ assertEquals(1,
+ restricted1s.getAllowedValues(restricted1s.getRootCondition()).length);
+ assertEquals("a",
+ restricted1s.getAllowedValues(restricted1s.getRootCondition())[0]);
+ }
+
+ {
+ BindingProperty conditional = (BindingProperty) p.find("conditional");
+ assertNotNull(conditional);
+ assertFalse(conditional.isDerived());
+
+ Set<String> required = conditional.getRequiredProperties();
+ assertEquals(required.size(), 1);
+ assertTrue(required.contains("restricted"));
+
+ assertEquals(3, conditional.getConditionalValues().size());
+ Iterator<Condition> it = conditional.getConditionalValues().keySet().iterator();
+
+ assertEquals(Arrays.asList("a", "b", "c"),
+ Arrays.asList(conditional.getAllowedValues(it.next())));
+ assertEquals(Arrays.asList("a", "b"),
+ Arrays.asList(conditional.getAllowedValues(it.next())));
+ assertEquals(Arrays.asList("c"),
+ Arrays.asList(conditional.getAllowedValues(it.next())));
}
{
@@ -75,6 +101,31 @@
Property configRedefined = p.find("configRedefined");
assertEquals("bar", ((ConfigurationProperty) configRedefined).getValue());
}
+
+ {
+ BindingProperty derived = (BindingProperty) p.find("derived");
+ assertNotNull(derived);
+ assertTrue(derived.isDerived());
+
+ Set<String> required = derived.getRequiredProperties();
+ assertEquals(required.size(), 1);
+ assertTrue(required.contains("restricted"));
+
+ assertEquals(3, derived.getConditionalValues().size());
+ }
+
+ {
+ BindingProperty reset = (BindingProperty) p.find("reset");
+ assertNotNull(reset);
+ assertTrue(reset.isDerived());
+
+ Set<String> required = reset.getRequiredProperties();
+ assertEquals(0, required.size());
+
+ assertEquals(1, reset.getConditionalValues().size());
+ assertSame(reset.getRootCondition(),
+ reset.getConditionalValues().keySet().iterator().next());
+ }
}
public void testModuleInvalidOverride() {