Revision: 9824
Author: gwt.mirror...@gmail.com
Date: Tue Mar  8 09:11:26 2011
Log: property fall back value evaluation scheme - enable fall back bindings.

Review at http://gwt-code-reviews.appspot.com/1369807

http://code.google.com/p/google-web-toolkit/source/detail?r=9824

Added:
/trunk/user/src/com/google/gwt/user/rebind/UserAgentPropertyGeneratorPredicate.java
Modified:
 /trunk/dev/core/src/com/google/gwt/core/ext/DefaultSelectionProperty.java
 /trunk/dev/core/src/com/google/gwt/core/ext/SelectionProperty.java
 /trunk/dev/core/src/com/google/gwt/dev/cfg/BindingProperty.java
 /trunk/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenPropertyIs.java
 /trunk/dev/core/src/com/google/gwt/dev/cfg/DeferredBindingQuery.java
 /trunk/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
 /trunk/dev/core/src/com/google/gwt/dev/cfg/Rule.java
 /trunk/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java
 /trunk/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java
 /trunk/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
 /trunk/dev/core/src/com/google/gwt/dev/util/xml/HandlerArgs.java
 /trunk/dev/core/src/com/google/gwt/dev/util/xml/HandlerParam.java
 /trunk/dev/core/test/com/google/gwt/dev/js/JsCoerceIntShiftTest.java
 /trunk/user/src/com/google/gwt/user/UserAgent.gwt.xml
 /trunk/user/src/com/google/gwt/user/rebind/UserAgentGenerator.java
 /trunk/user/src/com/google/gwt/user/rebind/UserAgentPropertyGenerator.java

=======================================
--- /dev/null
+++ /trunk/user/src/com/google/gwt/user/rebind/UserAgentPropertyGeneratorPredicate.java Tue Mar 8 09:11:26 2011
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2011 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.user.rebind;
+
+/**
+ * Represents a predicate expressed in Javascript
+ * returns the given user agent if predicate evaluates
+ * to true.
+ */
+public class UserAgentPropertyGeneratorPredicate {
+
+ private final StringSourceWriter predicateWriter = new StringSourceWriter();
+  private String userAgent;
+  private String returnValue;
+
+  public UserAgentPropertyGeneratorPredicate(String userAgent) {
+    assert userAgent != null;
+    assert userAgent.length() > 0;
+    this.userAgent = userAgent;
+  }
+
+  public UserAgentPropertyGeneratorPredicate getPredicateBlock() {
+    return this;
+  }
+
+  public String getReturnValue() {
+    return returnValue;
+  }
+
+  public String getUserAgent() {
+    return userAgent;
+  }
+
+  public UserAgentPropertyGeneratorPredicate indent() {
+    predicateWriter.indent();
+    return this;
+  }
+
+  public UserAgentPropertyGeneratorPredicate outdent() {
+    predicateWriter.outdent();
+    return this;
+  }
+
+  public UserAgentPropertyGeneratorPredicate println(String s) {
+    predicateWriter.println(s);
+    return this;
+  }
+
+  public UserAgentPropertyGeneratorPredicate returns(String s) {
+    returnValue = s;
+    return this;
+  }
+
+  @Override
+  public String toString() {
+    return predicateWriter.toString();
+  }
+}
=======================================
--- /trunk/dev/core/src/com/google/gwt/core/ext/DefaultSelectionProperty.java Mon Nov 16 13:28:11 2009 +++ /trunk/dev/core/src/com/google/gwt/core/ext/DefaultSelectionProperty.java Tue Mar 8 09:11:26 2011
@@ -15,6 +15,9 @@
  */
 package com.google.gwt.core.ext;

+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.SortedSet;

 /**
@@ -27,6 +30,7 @@
   private final String fallbackValue;
   private final String name;
   private final SortedSet<String> possibleValues;
+ private final Map<String, ? extends List<? extends Set<String>>> fallbackValueMap;

   /**
    * Construct a selection property.
@@ -40,6 +44,23 @@
    */
public DefaultSelectionProperty(String currentValue, String fallbackValue,
       String name, SortedSet<String> possibleValues) {
+    this(currentValue, fallbackValue, name, possibleValues, null);
+  }
+
+  /**
+   * Construct a selection property.
+   *
+   * @param currentValue current value of this property, must not be null
+   * @param fallbackValue the fallback value to use, must not be null
+   * @param name the name of this property, must not be null
+   * @param possibleValues the set of possible values, must not be null and
+   *     will be returned to callers, so a copy should be passed into this
+   *     ctor if the caller will use this set later
+   * @param map the map propertyValue to fallback values
+   */
+ public DefaultSelectionProperty(String currentValue, String fallbackValue,
+      String name, SortedSet<String> possibleValues,
+ Map<String, ? extends List<? extends Set<String>>> fallbackValueMap) {
     assert currentValue != null;
     assert fallbackValue != null;
     assert name != null;
@@ -48,6 +69,7 @@
     this.fallbackValue = fallbackValue;
     this.name = name;
     this.possibleValues = possibleValues;
+    this.fallbackValueMap = fallbackValueMap;
   }

   @Override
@@ -75,6 +97,10 @@
   public String getFallbackValue() {
     return fallbackValue;
   }
+
+  public List<? extends Set<String>> getFallbackValues(String value) {
+    return (null != fallbackValueMap) ? fallbackValueMap.get(value) : null;
+  }

   public String getName() {
     return name;
=======================================
--- /trunk/dev/core/src/com/google/gwt/core/ext/SelectionProperty.java Tue Jun 30 14:56:38 2009 +++ /trunk/dev/core/src/com/google/gwt/core/ext/SelectionProperty.java Tue Mar 8 09:11:26 2011
@@ -16,6 +16,8 @@

 package com.google.gwt.core.ext;

+import java.util.List;
+import java.util.Set;
 import java.util.SortedSet;

 /**
@@ -46,6 +48,14 @@
    */
   String getFallbackValue();

+  /**
+   * Returns the list of fall back values for a given value.
+   * @param value the property value
+   * @return the fall back list of values by increasing order
+   *         of preference.
+   */
+  List<? extends Set<String>> getFallbackValues(String value);
+
   /**
    * Returns the possible values for the property in sorted order.
    *
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/cfg/BindingProperty.java Mon Dec 20 11:48:24 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/cfg/BindingProperty.java Tue Mar 8 09:11:26 2011
@@ -28,11 +28,13 @@
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
+import java.util.Map.Entry;
 import java.util.regex.Pattern;

 /**
@@ -51,6 +53,8 @@
   private PropertyProvider provider;
   private Class<? extends PropertyProviderGenerator> providerGenerator;
   private String fallback;
+ private HashMap<String,LinkedList<LinkedHashSet<String>>> fallbackValueMap; + private HashMap<String,LinkedList<String>> fallbackValues = new HashMap<String,LinkedList<String>>();
   private final ConditionAll rootCondition = new ConditionAll();

   {
@@ -93,6 +97,20 @@
     }
     set.add(newValue);
   }
+
+  /**
+   * Adds fall back value to given property name.
+   * @param value the property value.
+   * @param fallbackValue the fall back value for given property value.
+   */
+  public void addFallbackValue(String value, String fallbackValue) {
+    LinkedList<String> values = fallbackValues.get(fallbackValue);
+    if (values == null) {
+      values = new LinkedList<String>();
+      fallbackValues.put(fallbackValue, values);
+    }
+    values.addFirst(value);
+  }

   /**
* Returns the set of allowed values in sorted order when a certain condition
@@ -145,6 +163,43 @@
     return fallback;
   }

+  /**
+   * Returns the map of values to fall back values. the list of fall
+   * back values is in decreasing order of preference.
+   * @return map of property value to fall back values.
+   */
+ public Map<String,? extends List<? extends Set<String>>> getFallbackValuesMap() {
+    if (fallbackValueMap == null) {
+ HashMap<String,LinkedList<LinkedHashSet<String>>> valuesMap = new HashMap<String,LinkedList<LinkedHashSet<String>>>();
+      // compute closure of fall back values preserving order
+ for (Entry<String, LinkedList<String>> e : fallbackValues.entrySet()) {
+        String from = e.getKey();
+ LinkedList<LinkedHashSet<String>> alternates = new LinkedList<LinkedHashSet<String>>();
+        valuesMap.put(from, alternates);
+        LinkedList<String> childList = fallbackValues.get(from);
+        LinkedHashSet<String> children = new LinkedHashSet<String>();
+        children.addAll(childList);
+        while (children != null && children.size() > 0)
+        {
+          alternates.add(children);
+          LinkedHashSet<String> newChildren = new LinkedHashSet<String>();
+          for (String child : children) {
+            childList = fallbackValues.get(child);
+            if (null == childList) {
+              continue;
+            }
+            for (String val : childList) {
+              newChildren.add(val);
+            }
+          }
+          children = newChildren;
+        }
+      }
+      fallbackValueMap = valuesMap;
+    }
+    return fallbackValueMap;
+  }
+
   public PropertyProvider getProvider() {
     return provider;
   }
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenPropertyIs.java Wed Aug 11 09:22:23 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/cfg/ConditionWhenPropertyIs.java Tue Mar 8 09:11:26 2011
@@ -23,6 +23,7 @@
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.dev.util.collect.Sets;

+import java.util.List;
 import java.util.Set;

 /**
@@ -69,6 +70,31 @@
       if (testValue.equals(value)) {
         return true;
       } else {
+        // no exact match was found, see if any fall back
+        // value would satisfy the condition
+        try {
+ SelectionProperty prop = propertyOracle.getSelectionProperty(logger,
+              propName);
+ List<? extends Set<String>> fallbackValues = prop.getFallbackValues(value);
+          if (fallbackValues != null && fallbackValues.size() > 0) {
+            logger.log(TreeLogger.DEBUG, "Property value '" + value + "'" +
+ " is the fallback of '" + fallbackValues.toString() + "'", null);
+            int cost = -1;
+            for (Set<String> values : fallbackValues) {
+              for (String fallbackValue : values) {
+                if (testValue.equals(fallbackValue)) {
+                  query.setFallbackEvaluationCost(cost);
+                  return false;
+                }
+              }
+              cost--;
+            }
+          }
+        }
+        catch (BadPropertyValueException e) {
+          // do nothing - currently, only selection
+          // properties support fall back values
+        }
         return false;
       }
     } catch (BadPropertyValueException e) {
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/cfg/DeferredBindingQuery.java Thu Mar 4 12:16:00 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/cfg/DeferredBindingQuery.java Tue Mar 8 09:11:26 2011
@@ -26,6 +26,7 @@
  * against such a query.
  */
 public class DeferredBindingQuery {
+  private int fallbackEvalCost = Integer.MAX_VALUE;
   private final Set<String> linkerNames;
   private final PropertyOracle propertyOracle;
   private final String testType;
@@ -54,6 +55,10 @@
     this.testType = testType;
   }

+  public int getFallbackEvaluationCost() {
+    return fallbackEvalCost;
+  }
+
   public Set<String> getLinkerNames() {
     return linkerNames;
   }
@@ -69,4 +74,8 @@
   public TypeOracle getTypeOracle() {
     return typeOracle;
   }
-}
+
+  public void setFallbackEvaluationCost(int cost) {
+    fallbackEvalCost = cost;
+  }
+}
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java Mon Dec 20 11:48:24 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java Tue Mar 8 09:11:26 2011
@@ -98,6 +98,9 @@
     @SuppressWarnings("unused") // referenced reflectively
     protected final String __extend_property_2_values = null;

+ @SuppressWarnings("unused") // referenced reflectively ($ means optional)
+    protected final String __extend_property_3_fallback_value$ = null;
+
     @SuppressWarnings("unused") // referenced reflectively
     protected final String __generate_with_1_class = null;

@@ -441,11 +444,25 @@

     @SuppressWarnings("unused") // called reflectively
     protected Schema __extend_property_begin(BindingProperty property,
-        PropertyValue[] values) {
+        PropertyValue[] values, PropertyFallbackValue fallbackValue)
+        throws UnableToCompleteException {
       for (int i = 0; i < values.length; i++) {
property.addDefinedValue(property.getRootCondition(), values[i].token);
       }

+      // validate fallback-property value if present
+      if (fallbackValue != null) {
+        if (!property.isDefinedValue(fallbackValue.token)) {
+        logger.log(TreeLogger.ERROR, "The property " + property.name
+            + " fallback-value was not defined");
+        throw new UnableToCompleteException();
+        }
+        // add fallback map to the property
+        for (int i = 0; i < values.length; i++) {
+          property.addFallbackValue(values[i].token, fallbackValue.token);
+        }
+      }
+
       // No children.
       return null;
     }
@@ -1184,6 +1201,14 @@
     }
   }

+  private static class PropertyFallbackValue {
+    public final String token;
+
+    public PropertyFallbackValue(String token) {
+      this.token = token;
+    }
+  }
+
   /**
* Converts a comma-separated string into an array of property value tokens.
    */
@@ -1235,6 +1260,24 @@
     }
   }

+  /**
+ * Converts a string into a property fallback value, validating it in the process.
+   */
+ private final class PropertyFallbackValueAttrCvt extends AttributeConverter {
+    @Override
+    public Object convertToArg(Schema schema, int line, String elem,
+        String attr, String value) throws UnableToCompleteException {
+      String token = value.trim();
+      if (Util.isValidJavaIdent(token)) {
+        return new PropertyFallbackValue(token);
+      } else {
+        Messages.PROPERTY_VALUE_INVALID.log(logger, token, null);
+        throw new UnableToCompleteException();
+      }
+    }
+  }
+
+
   /**
* Converts a comma-separated string into an array of property value tokens.
    */
@@ -1351,6 +1394,7 @@
private final PropertyNameAttrCvt propNameAttrCvt = new PropertyNameAttrCvt(); private final PropertyValueArrayAttrCvt propValueArrayAttrCvt = new PropertyValueArrayAttrCvt(); private final PropertyValueAttrCvt propValueAttrCvt = new PropertyValueAttrCvt(); + private final PropertyFallbackValueAttrCvt propFallbackValueAttrCvt = new PropertyFallbackValueAttrCvt(); private final PropertyValueGlobArrayAttrCvt propValueGlobArrayAttrCvt = new PropertyValueGlobArrayAttrCvt(); private final PropertyValueGlobAttrCvt propValueGlobAttrCvt = new PropertyValueGlobAttrCvt();

@@ -1371,6 +1415,7 @@
     registerAttributeConverter(ConfigurationProperty.class,
         configurationPropAttrCvt);
     registerAttributeConverter(PropertyValue.class, propValueAttrCvt);
+ registerAttributeConverter(PropertyFallbackValue.class, propFallbackValueAttrCvt); registerAttributeConverter(PropertyValue[].class, propValueArrayAttrCvt); registerAttributeConverter(PropertyValueGlob.class, propValueGlobAttrCvt);
     registerAttributeConverter(PropertyValueGlob[].class,
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/cfg/Rule.java Mon Dec 20 10:49:11 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/cfg/Rule.java Tue Mar 8 09:11:26 2011
@@ -24,19 +24,35 @@
  * Abstract base class for various kinds of deferred binding rules.
  */
 public abstract class Rule {
-
+
+  private int fallbackEvalCost = Integer.MAX_VALUE;
   private final ConditionAll rootCondition = new ConditionAll();

+  /**
+   * Returns the cost of evaluation fallback binding values.
+   * when isApplicable() is true, this value is meaningless
+   * when isApplicable() is false, [1,Integer.MAX_VALUE-1] means
+   * a relative scale of cost, where cost c is better than cost c+k
+   * Integer.MAX_VALUE implies no match.
+   * @return cost of evaluating fallback values.
+   */
+  public int getFallbackEvaluationCost() {
+    return fallbackEvalCost;
+  }
+
   public ConditionAll getRootCondition() {
     return rootCondition;
   }
-
+
   public boolean isApplicable(TreeLogger logger,
       StandardGeneratorContext context, String typeName)
       throws UnableToCompleteException {
-    return rootCondition.isTrue(logger, new DeferredBindingQuery(
+    DeferredBindingQuery query = new DeferredBindingQuery(
         context.getPropertyOracle(), context.getActiveLinkerNames(),
-        context.getTypeOracle(), typeName));
+        context.getTypeOracle(), typeName);
+    boolean result = rootCondition.isTrue(logger, query);
+    fallbackEvalCost = query.getFallbackEvaluationCost();
+    return result;
   }

   public abstract RebindResult realize(TreeLogger logger,
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java Thu Mar 25 12:00:47 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java Tue Mar 8 09:11:26 2011
@@ -22,6 +22,9 @@
 import com.google.gwt.core.ext.TreeLogger;

 import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.TreeSet;

 /**
@@ -152,7 +155,7 @@
           possibleValues.add(v);
         }
return new DefaultSelectionProperty(value, prop.getFallback(), name,
-            possibleValues);
+ possibleValues, (Map<String, List<Set<String>>>) prop.getFallbackValuesMap());
       }
     }

=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java Thu Dec 16 11:33:51 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java Tue Mar 8 09:11:26 2011
@@ -135,7 +135,8 @@
       for (String v : cprop.getDefinedValues()) {
         possibleValues.add(v);
       }
- return new DefaultSelectionProperty(value, fallback, name, possibleValues); + return new DefaultSelectionProperty(value, fallback, name, possibleValues,
+          cprop.getFallbackValuesMap());
     } else {
       throw new BadPropertyValueException(propertyName);
     }
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java Wed Feb 9 07:49:06 2011 +++ /trunk/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java Tue Mar 8 09:11:26 2011
@@ -116,6 +116,8 @@
             "No rules are defined, so no substitution can occur", null);
         return null;
       }
+
+      Rule minCostRuleSoFar = null;

       for (Iterator<Rule> iter = rules.iterator(); iter.hasNext();) {
         Rule rule = iter.next();
@@ -132,7 +134,7 @@
           if (!usedRules.contains(rule)) {
             usedRules.add(rule);
             Messages.TRACE_RULE_MATCHED.log(logger, null);
-
+
             return rule;
           } else {
             // We are skipping this rule because it has already been used
@@ -141,11 +143,35 @@
           }
         } else {
           Messages.TRACE_RULE_DID_NOT_MATCH.log(logger, null);
+
+          // keep track of fallback partial matches
+          if (minCostRuleSoFar == null) {
+            minCostRuleSoFar = rule;
+          }
+          assert rule.getFallbackEvaluationCost() != 0;
+ // if we found a better match, keep that as the best candidate so far + if (rule.getFallbackEvaluationCost() <= minCostRuleSoFar.getFallbackEvaluationCost()) { + logger.log(TreeLogger.DEBUG, "Found better fallback match for " + rule);
+            minCostRuleSoFar = rule;
+          }
         }
       }

+      // if we reach this point, it means we did not find an exact match
+      // and we may have a partial match based on fall back values
+      assert minCostRuleSoFar != null;
+ if (minCostRuleSoFar.getFallbackEvaluationCost() < Integer.MAX_VALUE) { + logger.log(TreeLogger.WARN, "Could not find an exact match rule. Using 'closest' rule " + + minCostRuleSoFar + " based on fall back values. You may need to implement a specific " + + "binding in case the fall back behavior does not replace the missing binding");
+        if (!usedRules.contains(minCostRuleSoFar)) {
+          usedRules.add(minCostRuleSoFar);
+ logger.log(TreeLogger.DEBUG, "No exact match was found, using closest match rule " + minCostRuleSoFar);
+          return minCostRuleSoFar;
+        }
+      }
+
       // No matching rule for this type.
-      //
       return null;
     }

@@ -161,14 +187,14 @@
       if (!genCtx.isGeneratorResultCachingEnabled()) {
         return resultTypeName;
       }
-
+
       RebindStatus status = newResult.getResultStatus();
       switch (status) {

         case USE_EXISTING:
           // in this case, no newly generated or cached types are needed
           break;
-
+
         case USE_ALL_NEW_WITH_NO_CACHING:
           /*
* in this case, new artifacts have been generated, but no need to
@@ -176,7 +202,7 @@
            * advantage of caching).
            */
           break;
-
+
         case USE_ALL_NEW:
           // use all new results, add a new cache entry
cachedResult = new CachedRebindResult(newResult.getReturnedTypeName(),
@@ -184,18 +210,18 @@
               System.currentTimeMillis(), newResult.getClientDataMap());
           rebindCachePut(rule, typeName, cachedResult);
           break;
-
+
         case USE_ALL_CACHED:
           // use all cached results
           assert (cachedResult != null);
-
+
           genCtx.commitArtifactsFromCache(logger);
           genCtx.addGeneratedUnitsFromCache();
-
+
           // use cached type name
           resultTypeName = cachedResult.getReturnedTypeName();
           break;
-
+
         case USE_PARTIAL_CACHED:
           /*
            * Add cached generated units marked for reuse to the context.
@@ -203,7 +229,7 @@
            * as GeneratedUnits.
            */
           genCtx.addGeneratedUnitsMarkedForReuseFromCache();
-
+
           /*
            * Create a new cache entry using the composite set of new and
            * reused cached results currently in genCtx.
@@ -267,14 +293,14 @@
   public void setRebindCache(RebindCache cache) {
     this.rebindCache = cache;
   }
-
+
   private CachedRebindResult rebindCacheGet(Rule rule, String typeName) {
     if (rebindCache != null) {
       return rebindCache.get(rule, typeName);
     }
     return null;
   }
-
+
private void rebindCachePut(Rule rule, String typeName, CachedRebindResult result) {
     if (rebindCache != null) {
       rebindCache.put(rule, typeName, result);
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/util/xml/HandlerArgs.java Wed Dec 27 13:19:22 2006 +++ /trunk/dev/core/src/com/google/gwt/dev/util/xml/HandlerArgs.java Tue Mar 8 09:11:26 2011
@@ -63,6 +63,8 @@
AttributeConverter converter = schema.getAttributeConverter(handlerParams[i].getParamType()); return converter.convertToArg(schema, lineNumber, elemName, attrNames[i],
           value);
+    } else if (handlerParams[i].isOptional()) {
+        return null;
     } else {
       return new NullPointerException("Argument " + i + " was null");
     }
@@ -77,7 +79,7 @@
   }

   public boolean isArgSet(int i) {
-    if (argValues[i] != null) {
+    if (argValues[i] != null || handlerParams[i].isOptional()) {
       return true;
     } else {
       return false;
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/util/xml/HandlerParam.java Fri Mar 19 08:12:41 2010 +++ /trunk/dev/core/src/com/google/gwt/dev/util/xml/HandlerParam.java Tue Mar 8 09:11:26 2011
@@ -81,11 +81,17 @@
   private final Class<?> paramType;

   private final Field metaField;
+
+  private final boolean isOptional;

   private final String normalizedAttrName;

   private HandlerParam(Class<?> paramType, Field metaField,
       String normalizedAttrName) {
+    this.isOptional = normalizedAttrName.endsWith("$");
+    if (isOptional) {
+ normalizedAttrName = normalizedAttrName.substring(0, normalizedAttrName.length() - 1);
+    }
     this.paramType = paramType;
     this.metaField = metaField;
     this.normalizedAttrName = normalizedAttrName;
@@ -116,4 +122,8 @@
   public Class<?> getParamType() {
     return paramType;
   }
-}
+
+  public boolean isOptional() {
+    return isOptional;
+  }
+}
=======================================
--- /trunk/dev/core/test/com/google/gwt/dev/js/JsCoerceIntShiftTest.java Tue Jun 22 11:13:30 2010 +++ /trunk/dev/core/test/com/google/gwt/dev/js/JsCoerceIntShiftTest.java Tue Mar 8 09:11:26 2011
@@ -52,7 +52,8 @@
       } else {
         SortedSet<String> valueSet = new TreeSet<String>();
         valueSet.add(value);
-        return new DefaultSelectionProperty(value, value, value, valueSet);
+        return new DefaultSelectionProperty(value, value, value, valueSet,
+            null /* fallbackValueMap */);
       }
     }

=======================================
--- /trunk/user/src/com/google/gwt/user/UserAgent.gwt.xml Tue Feb 8 16:15:58 2011 +++ /trunk/user/src/com/google/gwt/user/UserAgent.gwt.xml Tue Mar 8 09:11:26 2011
@@ -19,8 +19,11 @@
 <module>

   <!-- Browser-sensitive code should use the 'user.agent' property -->
- <define-property name="user.agent" values="ie6,ie8,gecko1_8,safari,opera"/>
-
+  <define-property name="user.agent" values="ie6" />
+  <extend-property name="user.agent" values="ie8" />
+  <extend-property name="user.agent" values="gecko1_8" />
+  <extend-property name="user.agent" values="safari" />
+  <extend-property name="user.agent" values="opera" />
<property-provider name="user.agent" generator="com.google.gwt.user.rebind.UserAgentPropertyGenerator"/>

<!-- Set to false to avoid runtime warnings for mismatched runtime and -->
=======================================
--- /trunk/user/src/com/google/gwt/user/rebind/UserAgentGenerator.java Tue Feb 8 16:15:58 2011 +++ /trunk/user/src/com/google/gwt/user/rebind/UserAgentGenerator.java Tue Mar 8 09:11:26 2011
@@ -80,10 +80,11 @@
     }

     String userAgentValue;
+    SelectionProperty selectionProperty;
     try {
- SelectionProperty property = propertyOracle.getSelectionProperty(logger,
+      selectionProperty = propertyOracle.getSelectionProperty(logger,
           PROPERTY_USER_AGENT);
-      userAgentValue = property.getCurrentValue();
+      userAgentValue = selectionProperty.getCurrentValue();
     } catch (BadPropertyValueException e) {
       logger.log(TreeLogger.ERROR, "Unable to find value for '"
           + PROPERTY_USER_AGENT + "'", e);
@@ -113,7 +114,8 @@
       sw.println();
       sw.println("public native String getRuntimeValue() /*-{");
       sw.indent();
-      UserAgentPropertyGenerator.writeUserAgentPropertyJavaScript(sw);
+      UserAgentPropertyGenerator.writeUserAgentPropertyJavaScript(sw,
+          selectionProperty.getPossibleValues());
       sw.outdent();
       sw.println("}-*/;");
       sw.println();
=======================================
--- /trunk/user/src/com/google/gwt/user/rebind/UserAgentPropertyGenerator.java Tue Feb 8 16:15:58 2011 +++ /trunk/user/src/com/google/gwt/user/rebind/UserAgentPropertyGenerator.java Tue Mar 8 09:11:26 2011
@@ -39,6 +39,51 @@
private static final List<String> VALID_VALUES = Arrays.asList(new String[]{
       "ie6", "ie8", "gecko1_8", "safari", "opera"});

+  /**
+   * List of predicates to identify user agent.
+ * The order of evaluation is from top to bottom, i.e., the first matching
+   * predicate will have the associated ua token returned.
+   * ua is defined in an outer scope and is therefore visible in
+   * the predicate javascript fragment.
+   */
+  private static UserAgentPropertyGeneratorPredicate[] predicates =
+    new UserAgentPropertyGeneratorPredicate[] {
+
+      // opera
+      new UserAgentPropertyGeneratorPredicate("opera")
+      .getPredicateBlock()
+        .println("return (ua.indexOf('opera') != -1);")
+      .returns("'opera'"),
+
+      // webkit family
+      new UserAgentPropertyGeneratorPredicate("safari")
+      .getPredicateBlock()
+        .println("return (ua.indexOf('webkit') != -1);")
+      .returns("'safari'"),
+
+      // IE8
+      new UserAgentPropertyGeneratorPredicate("ie8")
+      .getPredicateBlock()
+ .println("return (ua.indexOf('msie') != -1 && ($doc.documentMode
= 8));")
+      .returns("'ie8'"),
+
+      // IE6
+      new UserAgentPropertyGeneratorPredicate("ie6")
+      .getPredicateBlock()
+        .println("var result = /msie ([0-9]+)\\.([0-9]+)/.exec(ua);")
+        .println("if (result && result.length == 3)")
+        .indent()
+          .println("return (makeVersion(result) >= 6000);")
+        .outdent()
+      .returns("'ie6'"),
+
+      // gecko family
+      new UserAgentPropertyGeneratorPredicate("gecko1_8")
+      .getPredicateBlock()
+        .println("return (ua.indexOf('gecko') != -1);")
+      .returns("'gecko1_8'"),
+  };
+
   /**
* Writes out the JavaScript function body for determining the value of the * <code>user.agent</code> selection property. This method is used to create
@@ -47,48 +92,29 @@
    * <code>user.agent</code> values listed here should be kept in sync with
    * {@link #VALID_VALUES} and <code>UserAgent.gwt.xml</code>.
    */
-  static void writeUserAgentPropertyJavaScript(SourceWriter body) {
+  static void writeUserAgentPropertyJavaScript(SourceWriter body,
+      SortedSet<String> possibleValues) {
+
+    // write preamble
     body.println("var ua = navigator.userAgent.toLowerCase();");
     body.println("var makeVersion = function(result) {");
     body.indent();
body.println("return (parseInt(result[1]) * 1000) + parseInt(result[2]);");
     body.outdent();
     body.println("};");
-    body.println("if (ua.indexOf('opera') != -1) {");
-    body.indent();
-    body.println("return 'opera';");
-    body.outdent();
-    body.println("} else if (ua.indexOf('webkit') != -1) {");
-    body.indent();
-    body.println("return 'safari';");
-    body.outdent();
-    body.println("} else if (ua.indexOf('msie') != -1) {");
-    body.indent();
-    body.println("if ($doc.documentMode >= 8) {");
-    body.indent();
-    body.println("return 'ie8';");
-    body.outdent();
-    body.println("} else {");
-    body.indent();
-    body.println("var result = /msie ([0-9]+)\\.([0-9]+)/.exec(ua);");
-    body.println("if (result && result.length == 3) {");
-    body.indent();
-    body.println("var v = makeVersion(result);");
-    body.println("if (v >= 6000) {");
-    body.indent();
-    body.println("return 'ie6';");
-    body.outdent();
-    body.println("}");
-    body.outdent();
-    body.println("}");
-    body.outdent();
-    body.println("}");
-    body.outdent();
-    body.println("} else if (ua.indexOf('gecko') != -1) {");
-    body.indent();
-    body.println("return 'gecko1_8';");
-    body.outdent();
-    body.println("}");
+
+    // write only selected user agents
+    for (int i = 0; i < predicates.length; i++) {
+      if (possibleValues.contains(predicates[i].getUserAgent())) {
+        body.println("if ((function() { ");
+        body.indent();
+        body.print(predicates[i].toString());
+        body.outdent();
+ body.println("})()) return " + predicates[i].getReturnValue() + ";");
+      }
+    }
+
+    // default return
     body.println("return 'unknown';");
   }

@@ -105,11 +131,14 @@
             + "\" value=\"false\"/> to suppress this warning message.");
       }
     }
-
+    // make sure that the # of ua in VALID_VALUES
+    // is the same of predicates. maybe should iterate
+    // to make sure each one has a match.
+    assert predicates.length == VALID_VALUES.size();
     StringSourceWriter body = new StringSourceWriter();
     body.println("{");
     body.indent();
-    writeUserAgentPropertyJavaScript(body);
+    writeUserAgentPropertyJavaScript(body, possibleValues);
     body.outdent();
     body.println("}");

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

Reply via email to