Support upcasting Date, BigInteger, and BigDecimal types. git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/2.1@9339 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/tools/api-checker/config/gwt20_21userApi.conf b/tools/api-checker/config/gwt20_21userApi.conf index 697b3cc..54313db 100644 --- a/tools/api-checker/config/gwt20_21userApi.conf +++ b/tools/api-checker/config/gwt20_21userApi.conf
@@ -66,6 +66,7 @@ :**/server/**\ :**/tools/**\ :user/src/com/google/gwt/regexp/shared/**\ +:user/src/com/google/gwt/autobean/shared/ValueCodexHelper.java\ :user/src/com/google/gwt/autobean/shared/impl/StringQuoter.java\ :user/src/com/google/gwt/core/client/impl/WeakMapping.java\ :user/src/com/google/gwt/junit/*.java\
diff --git a/tools/api-checker/config/gwt21_22userApi.conf b/tools/api-checker/config/gwt21_22userApi.conf index 9664ee9..3e4f820 100644 --- a/tools/api-checker/config/gwt21_22userApi.conf +++ b/tools/api-checker/config/gwt21_22userApi.conf
@@ -73,6 +73,7 @@ :**/server/**\ :**/tools/**\ :user/src/com/google/gwt/regexp/shared/**\ +:user/src/com/google/gwt/autobean/shared/ValueCodexHelper.java\ :user/src/com/google/gwt/autobean/shared/impl/StringQuoter.java\ :user/src/com/google/gwt/core/client/impl/WeakMapping.java\ :user/src/com/google/gwt/junit/*.java\
diff --git a/user/src/com/google/gwt/autobean/shared/AutoBeanCodex.java b/user/src/com/google/gwt/autobean/shared/AutoBeanCodex.java index 9d228f4..2ae2f0e 100644 --- a/user/src/com/google/gwt/autobean/shared/AutoBeanCodex.java +++ b/user/src/com/google/gwt/autobean/shared/AutoBeanCodex.java
@@ -295,7 +295,8 @@ if (ValueCodex.canDecode(ctx.getElementType())) { for (Object element : collection) { - sb.append(",").append(encodeValue(element).getPayload()); + sb.append(",").append( + encodeValue(ctx.getElementType(), element).getPayload()); } } else { boolean isEncoded = Splittable.class.equals(ctx.getElementType()); @@ -330,15 +331,18 @@ return false; } - boolean isEncodedKey = Splittable.class.equals(ctx.getKeyType()); - boolean isEncodedValue = Splittable.class.equals(ctx.getValueType()); - boolean isValueKey = ValueCodex.canDecode(ctx.getKeyType()); - boolean isValueValue = ValueCodex.canDecode(ctx.getValueType()); + Class<?> keyType = ctx.getKeyType(); + Class<?> valueType = ctx.getValueType(); + boolean isEncodedKey = Splittable.class.equals(keyType); + boolean isEncodedValue = Splittable.class.equals(valueType); + boolean isValueKey = ValueCodex.canDecode(keyType); + boolean isValueValue = ValueCodex.canDecode(valueType); if (isValueKey) { - writeValueKeyMap(map, isEncodedValue, isValueValue); + writeValueKeyMap(map, keyType, valueType, isEncodedValue, isValueValue); } else { - writeObjectKeyMap(map, isEncodedKey, isEncodedValue, isValueValue); + writeObjectKeyMap(map, valueType, isEncodedKey, isEncodedValue, + isValueValue); } return false; @@ -377,7 +381,7 @@ // Special handling for enums if we have an obfuscation map Splittable split; - split = encodeValue(value); + split = encodeValue(type, value); sb.append(",\"").append(propertyName).append("\":").append( split.getPayload()); return false; @@ -409,12 +413,13 @@ * Encodes a value, with special handling for enums to allow the field name * to be overridden. */ - private Splittable encodeValue(Object value) { + private Splittable encodeValue(Class<?> expectedType, Object value) { Splittable split; if (value instanceof Enum<?> && enumMap != null) { - split = ValueCodex.encode(enumMap.getToken((Enum<?>) value)); + split = ValueCodex.encode(String.class, + enumMap.getToken((Enum<?>) value)); } else { - split = ValueCodex.encode(value); + split = ValueCodex.encode(expectedType, value); } return split; } @@ -429,8 +434,8 @@ * encoded as a list of two lists, since it's possible that two distinct * objects have the same encoded form. */ - private void writeObjectKeyMap(Map<?, ?> map, boolean isEncodedKey, - boolean isEncodedValue, boolean isValueValue) { + private void writeObjectKeyMap(Map<?, ?> map, Class<?> valueType, + boolean isEncodedKey, boolean isEncodedValue, boolean isValueValue) { StringBuilder keys = new StringBuilder(); StringBuilder values = new StringBuilder(); @@ -445,7 +450,8 @@ values.append(",").append( ((Splittable) entry.getValue()).getPayload()); } else if (isValueValue) { - values.append(",").append(encodeValue(entry.getValue()).getPayload()); + values.append(",").append( + encodeValue(valueType, entry.getValue()).getPayload()); } else { encodeToStringBuilder(values.append(","), entry.getValue()); } @@ -462,15 +468,15 @@ /** * Writes a map JSON literal where the keys are value types. */ - private void writeValueKeyMap(Map<?, ?> map, boolean isEncodedValue, - boolean isValueValue) { + private void writeValueKeyMap(Map<?, ?> map, Class<?> keyType, + Class<?> valueType, boolean isEncodedValue, boolean isValueValue) { for (Map.Entry<?, ?> entry : map.entrySet()) { - sb.append(",").append(encodeValue(entry.getKey()).getPayload()).append( + sb.append(",").append(encodeValue(keyType, entry.getKey()).getPayload()).append( ":"); if (isEncodedValue) { sb.append(((Splittable) entry.getValue()).getPayload()); } else if (isValueValue) { - sb.append(encodeValue(entry.getValue()).getPayload()); + sb.append(encodeValue(valueType, entry.getValue()).getPayload()); } else { encodeToStringBuilder(sb, entry.getValue()); }
diff --git a/user/src/com/google/gwt/autobean/shared/ValueCodex.java b/user/src/com/google/gwt/autobean/shared/ValueCodex.java index d4a452f..ca867eb 100644 --- a/user/src/com/google/gwt/autobean/shared/ValueCodex.java +++ b/user/src/com/google/gwt/autobean/shared/ValueCodex.java
@@ -20,9 +20,11 @@ import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; +import java.util.Set; /** * Provides unified encoding and decoding of value objects. @@ -31,6 +33,11 @@ enum Type { BIG_DECIMAL(BigDecimal.class) { @Override + public boolean canUpcast(Object value) { + return value instanceof BigDecimal; + } + + @Override public BigDecimal decode(Class<?> clazz, String value) { return new BigDecimal(value); } @@ -42,6 +49,11 @@ }, BIG_INTEGER(BigInteger.class) { @Override + public boolean canUpcast(Object value) { + return value instanceof BigInteger; + } + + @Override public BigInteger decode(Class<?> clazz, String value) { return new BigInteger(value); } @@ -71,6 +83,11 @@ }, DATE(Date.class) { @Override + public boolean canUpcast(Object value) { + return value instanceof Date; + } + + @Override public Date decode(Class<?> clazz, String value) { return new Date(Long.valueOf(value)); } @@ -158,6 +175,15 @@ this.defaultValue = defaultValue; } + /** + * Determines whether or not the Type can handle the given value via + * upcasting semantics. + */ + public boolean canUpcast(Object value) { + // Most value types are final, so this method is meaningless + return false; + } + public abstract Object decode(Class<?> clazz, String value); public Object getDefaultValue() { @@ -177,14 +203,18 @@ } } - private static Map<Class<?>, Type> typesByClass = new HashMap<Class<?>, Type>(); + private static final Set<Class<?>> ALL_VALUE_TYPES; + private static final Map<Class<?>, Type> TYPES_BY_CLASS; static { + Map<Class<?>, Type> temp = new HashMap<Class<?>, Type>(); for (Type t : Type.values()) { - typesByClass.put(t.getType(), t); + temp.put(t.getType(), t); if (t.getPrimitiveType() != null) { - typesByClass.put(t.getPrimitiveType(), t); + temp.put(t.getPrimitiveType(), t); } } + ALL_VALUE_TYPES = Collections.unmodifiableSet(temp.keySet()); + TYPES_BY_CLASS = Collections.unmodifiableMap(temp); } /** @@ -194,7 +224,11 @@ * @return {@code true} if the given object type can be decoded */ public static boolean canDecode(Class<?> clazz) { - return findType(clazz) != null; + if (findType(clazz) != null) { + return true; + } + // Use other platform-specific tests + return ValueCodexHelper.canDecode(clazz); } public static <T> T decode(Class<T> clazz, Splittable split) { @@ -212,12 +246,42 @@ return (T) getTypeOrDie(clazz).decode(clazz, string); } + /** + * Encode a value object when the wire format type is known. This method + * should be preferred over {@link #encode(Object)} when possible. + */ + public static Splittable encode(Class<?> clazz, Object obj) { + if (obj == null) { + return LazySplittable.NULL; + } + return new LazySplittable(getTypeOrDie(clazz).toJsonExpression(obj)); + } + public static Splittable encode(Object obj) { if (obj == null) { return LazySplittable.NULL; } - return new LazySplittable( - getTypeOrDie(obj.getClass()).toJsonExpression(obj)); + Type t = findType(obj.getClass()); + // Try upcasting + if (t == null) { + for (Type maybe : Type.values()) { + if (maybe.canUpcast(obj)) { + t = maybe; + break; + } + } + } + if (t == null) { + throw new UnsupportedOperationException(obj.getClass().getName()); + } + return new LazySplittable(t.toJsonExpression(obj)); + } + + /** + * Return all Value types that can be processed by the ValueCodex. + */ + public static Set<Class<?>> getAllValueTypes() { + return ALL_VALUE_TYPES; } /** @@ -235,13 +299,10 @@ * May return <code>null</code>. */ private static <T> Type findType(Class<T> clazz) { - Type type = typesByClass.get(clazz); - if (type == null) { - if (clazz.isEnum()) { - return Type.ENUM; - } + if (clazz.isEnum()) { + return Type.ENUM; } - return type; + return TYPES_BY_CLASS.get(clazz); } private static <T> Type getTypeOrDie(Class<T> clazz) {
diff --git a/user/src/com/google/gwt/autobean/shared/ValueCodexHelper.java b/user/src/com/google/gwt/autobean/shared/ValueCodexHelper.java new file mode 100644 index 0000000..c6f72c8 --- /dev/null +++ b/user/src/com/google/gwt/autobean/shared/ValueCodexHelper.java
@@ -0,0 +1,38 @@ +/* + * Copyright 2010 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.autobean.shared; + +import com.google.gwt.core.client.GWT; + +/** + * Provides reflection-based operation for server (JVM) implementation. There is + * a no-op super-source version for client (dev- and web-mode) code. + */ +class ValueCodexHelper { + /** + * Returns {@code true} if {@code clazz} is assignable to any of the value + * types. + */ + static boolean canDecode(Class<?> clazz) { + assert !GWT.isClient(); + for (Class<?> valueType : ValueCodex.getAllValueTypes()) { + if (valueType.isAssignableFrom(clazz)) { + return true; + } + } + return false; + } +}
diff --git a/user/src/com/google/gwt/editor/rebind/model/ModelUtils.java b/user/src/com/google/gwt/editor/rebind/model/ModelUtils.java index 583c49d..310b9dd 100644 --- a/user/src/com/google/gwt/editor/rebind/model/ModelUtils.java +++ b/user/src/com/google/gwt/editor/rebind/model/ModelUtils.java
@@ -15,15 +15,14 @@ */ package com.google.gwt.editor.rebind.model; +import com.google.gwt.autobean.shared.ValueCodex; import com.google.gwt.core.ext.typeinfo.JArrayType; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JParameterizedType; import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.core.ext.typeinfo.TypeOracle; -import java.util.Arrays; import java.util.Collections; -import java.util.Date; import java.util.HashSet; import java.util.Set; @@ -32,16 +31,12 @@ */ public class ModelUtils { - @SuppressWarnings("unchecked") - static final Set<Class<?>> VALUE_TYPES = Collections.unmodifiableSet(new HashSet<Class<?>>( - Arrays.asList(Boolean.class, Character.class, Class.class, Date.class, - Enum.class, Number.class, String.class, Void.class))); - static final Set<String> VALUE_TYPE_NAMES; static { - Set<String> names = new HashSet<String>(VALUE_TYPES.size()); - for (Class<?> clazz : VALUE_TYPES) { + Set<Class<?>> valueTypes = ValueCodex.getAllValueTypes(); + Set<String> names = new HashSet<String>(valueTypes.size()); + for (Class<?> clazz : valueTypes) { names.add(clazz.getName()); } VALUE_TYPE_NAMES = Collections.unmodifiableSet(names); @@ -110,11 +105,14 @@ if (classType == null) { return true; } + if (type.isEnum() != null) { + return true; + } for (String valueType : VALUE_TYPE_NAMES) { JClassType found = oracle.findType(valueType); // null check to accommodate limited mock CompilationStates - if (found != null && found.isAssignableFrom(classType)) { + if (found != null && found.equals(classType)) { return true; } }
diff --git a/user/src/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidator.java b/user/src/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidator.java index 2c8f005..f034b1c 100644 --- a/user/src/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidator.java +++ b/user/src/com/google/gwt/requestfactory/server/RequestFactoryInterfaceValidator.java
@@ -15,6 +15,7 @@ */ package com.google.gwt.requestfactory.server; +import com.google.gwt.autobean.shared.ValueCodex; import com.google.gwt.dev.asm.AnnotationVisitor; import com.google.gwt.dev.asm.ClassReader; import com.google.gwt.dev.asm.ClassVisitor; @@ -44,9 +45,7 @@ import java.io.InputStream; import java.lang.annotation.Annotation; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -517,10 +516,7 @@ } } - @SuppressWarnings("unchecked") - static final Set<Class<?>> VALUE_TYPES = Collections.unmodifiableSet(new HashSet<Class<?>>( - Arrays.asList(Boolean.class, Character.class, Class.class, Date.class, - Enum.class, Number.class, String.class, Void.class))); + static final Set<Class<?>> VALUE_TYPES = ValueCodex.getAllValueTypes(); public static void main(String[] args) { if (args.length == 0) { @@ -578,6 +574,10 @@ */ private final Type entityProxyIntf = Type.getType(EntityProxy.class); /** + * The type {@link Enum}. + */ + private final Type enumType = Type.getType(Enum.class); + /** * A placeholder type for client types that could not be resolved to a domain * type. */ @@ -1361,12 +1361,8 @@ return true; } logger = logger.setType(type); - List<