Deprecate the FieldNamingStrategy interface and replace it with FieldNamingStrategy2. This is the first step to help make it easy to cache field annotations across all instances of a class, etc.

This commit is contained in:
Joel Leitch 2010-01-09 22:43:27 +00:00
parent 7079799890
commit e3af076ff2
25 changed files with 258 additions and 87 deletions

View File

@ -18,6 +18,7 @@ package com.google.gson;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Collection;
/** /**
* Converts the field name that uses camel-case define word separation into separate words that * Converts the field name that uses camel-case define word separation into separate words that
@ -56,7 +57,8 @@ final class CamelCaseSeparatorNamingPolicy extends RecursiveFieldNamingPolicy {
} }
@Override @Override
protected String translateName(String target, Type fieldType, Annotation[] annnotations) { protected String translateName(String target, Type fieldType,
Collection<Annotation> annnotations) {
StringBuilder translation = new StringBuilder(); StringBuilder translation = new StringBuilder();
for (int i = 0; i < target.length(); i++) { for (int i = 0; i < target.length(); i++) {
char character = target.charAt(i); char character = target.charAt(i);

View File

@ -13,9 +13,8 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.gson;
import java.lang.reflect.Field; package com.google.gson;
/** /**
* Exception class to indicate a circular reference error. * Exception class to indicate a circular reference error.
@ -33,7 +32,7 @@ final class CircularReferenceException extends RuntimeException {
this.offendingNode = offendingNode; this.offendingNode = offendingNode;
} }
public IllegalStateException createDetailedException(Field offendingField) { public IllegalStateException createDetailedException(FieldAttributes offendingField) {
StringBuilder msg = new StringBuilder(getMessage()); StringBuilder msg = new StringBuilder(getMessage());
if (offendingField != null) { if (offendingField != null) {
msg.append("\n ").append("Offending field: ").append(offendingField.getName() + "\n"); msg.append("\n ").append("Offending field: ").append(offendingField.getName() + "\n");

View File

@ -18,6 +18,7 @@ package com.google.gson;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Collection;
/** /**
* Performs numerous field naming translations wrapped up as one object. * Performs numerous field naming translations wrapped up as one object.
@ -36,7 +37,7 @@ abstract class CompositionFieldNamingPolicy extends RecursiveFieldNamingPolicy {
} }
@Override @Override
protected String translateName(String target, Type fieldType, Annotation[] annotations) { protected String translateName(String target, Type fieldType, Collection<Annotation> annotations) {
for (RecursiveFieldNamingPolicy policy : fieldPolicies) { for (RecursiveFieldNamingPolicy policy : fieldPolicies) {
target = policy.translateName(target, fieldType, annotations); target = policy.translateName(target, fieldType, annotations);
} }

View File

@ -19,6 +19,9 @@ package com.google.gson;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
/** /**
* A data object that stores attributes of a field. * A data object that stores attributes of a field.
@ -35,11 +38,11 @@ public final class FieldAttributes {
private final Class<?> declaredType; private final Class<?> declaredType;
private final boolean isSynthetic; private final boolean isSynthetic;
private final int modifiers; private final int modifiers;
private final String name;
// Fields used for lazy initialization // Fields used for lazy initialization
private String name;
private Type genericType; private Type genericType;
private Annotation[] annotations; private Collection<Annotation> annotations;
/** /**
* Constructs a Field Attributes object from the {@code f}. * Constructs a Field Attributes object from the {@code f}.
@ -49,6 +52,7 @@ public final class FieldAttributes {
FieldAttributes(final Field f) { FieldAttributes(final Field f) {
Preconditions.checkNotNull(f); Preconditions.checkNotNull(f);
field = f; field = f;
name = field.getName();
declaredType = f.getType(); declaredType = f.getType();
isSynthetic = f.isSynthetic(); isSynthetic = f.isSynthetic();
modifiers = field.getModifiers(); modifiers = field.getModifiers();
@ -58,9 +62,6 @@ public final class FieldAttributes {
* @return the name of the field * @return the name of the field
*/ */
public String getName() { public String getName() {
if (name == null) {
name = field.getName();
}
return name; return name;
} }
@ -115,10 +116,21 @@ public final class FieldAttributes {
* @return the annotation instance if it is bound to the field; otherwise {@code null} * @return the annotation instance if it is bound to the field; otherwise {@code null}
*/ */
public <T extends Annotation> T getAnnotation(Class<T> annotation) { public <T extends Annotation> T getAnnotation(Class<T> annotation) {
return getAnnotationFromArray(getAnnotations(), annotation);
}
/**
* Return the annotations that are present on this field.
*
* @return an array of all the annotations set on the field
* @since 1.4
*/
public Collection<Annotation> getAnnotations() {
if (annotations == null) { if (annotations == null) {
annotations = field.getAnnotations(); annotations = Collections.unmodifiableCollection(
Arrays.asList(field.getAnnotations()));
} }
return getAnnotationFromArray(annotations, annotation); return annotations;
} }
/** /**
@ -135,6 +147,28 @@ public final class FieldAttributes {
return (modifiers & modifier) != 0; return (modifiers & modifier) != 0;
} }
/**
* This is exposed internally only for the removing synthetic fields from the JSON output.
*
* @return true if the field is synthetic; otherwise false
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
void set(Object instance, Object value) throws IllegalAccessException {
field.set(instance, value);
}
/**
* This is exposed internally only for the removing synthetic fields from the JSON output.
*
* @return true if the field is synthetic; otherwise false
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
Object get(Object instance) throws IllegalAccessException {
return field.get(instance);
}
/** /**
* This is exposed internally only for the removing synthetic fields from the JSON output. * This is exposed internally only for the removing synthetic fields from the JSON output.
* *
@ -143,10 +177,18 @@ public final class FieldAttributes {
boolean isSynthetic() { boolean isSynthetic() {
return isSynthetic; return isSynthetic;
} }
/**
* @deprecated remove this when {@link FieldNamingStrategy} is deleted.
*/
@Deprecated
Field getFieldObject() {
return field;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static <T extends Annotation> T getAnnotationFromArray( private static <T extends Annotation> T getAnnotationFromArray(
Annotation[] annotations, Class<T> annotation) { Collection<Annotation> annotations, Class<T> annotation) {
for (Annotation a : annotations) { for (Annotation a : annotations) {
if (a.annotationType() == annotation) { if (a.annotationType() == annotation) {
return (T) a; return (T) a;

View File

@ -72,13 +72,13 @@ public enum FieldNamingPolicy {
*/ */
LOWER_CASE_WITH_DASHES(new LowerCamelCaseSeparatorNamingPolicy("-")); LOWER_CASE_WITH_DASHES(new LowerCamelCaseSeparatorNamingPolicy("-"));
private final FieldNamingStrategy namingPolicy; private final FieldNamingStrategy2 namingPolicy;
private FieldNamingPolicy(FieldNamingStrategy namingPolicy) { private FieldNamingPolicy(FieldNamingStrategy2 namingPolicy) {
this.namingPolicy = namingPolicy; this.namingPolicy = namingPolicy;
} }
FieldNamingStrategy getFieldNamingPolicy() { FieldNamingStrategy2 getFieldNamingPolicy() {
return namingPolicy; return namingPolicy;
} }
} }

View File

@ -26,7 +26,9 @@ import java.lang.reflect.Field;
* @author Inderjeet Singh * @author Inderjeet Singh
* @author Joel Leitch * @author Joel Leitch
* @since 1.3 * @since 1.3
* @deprecated use {@link FieldNamingStrategy2} instead
*/ */
@Deprecated
public interface FieldNamingStrategy { public interface FieldNamingStrategy {
/** /**

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 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.gson;
/**
* The new mechanism for providing custom field naming in Gson. This allows the client code
* to translate field names into a particular convention that is not supported as a normal
* Java field declaration rules. For example, Java does not support "-" characters in a
* field name.
*
* @author Inderjeet Singh
* @author Joel Leitch
* @since 1.4
*/
public interface FieldNamingStrategy2 {
/**
* Translates the field name into its JSON field name representation.
*
* @param f the field that is being translated
* @return the translated field name.
* @since 1.3
*/
public String translateName(FieldAttributes f);
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 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.gson;
/**
* Adapts the old "deprecated" {@link FieldNamingStrategy} to the new {@link FieldNamingStrategy2}
* type.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
class FieldNamingStrategy2Adapter implements FieldNamingStrategy2 {
private final FieldNamingStrategy adaptee;
public FieldNamingStrategy2Adapter(FieldNamingStrategy adaptee) {
Preconditions.checkNotNull(adaptee);
this.adaptee = adaptee;
}
public String translateName(FieldAttributes f) {
return adaptee.translateName(f.getFieldObject());
}
}

View File

@ -86,7 +86,7 @@ public final class Gson {
static final ModifierBasedExclusionStrategy DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY = static final ModifierBasedExclusionStrategy DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY =
new ModifierBasedExclusionStrategy(new int[] { Modifier.TRANSIENT, Modifier.STATIC }); new ModifierBasedExclusionStrategy(new int[] { Modifier.TRANSIENT, Modifier.STATIC });
static final JsonFormatter DEFAULT_JSON_FORMATTER = new JsonCompactFormatter(); static final JsonFormatter DEFAULT_JSON_FORMATTER = new JsonCompactFormatter();
static final FieldNamingStrategy DEFAULT_NAMING_POLICY = static final FieldNamingStrategy2 DEFAULT_NAMING_POLICY =
new SerializedNameAnnotationInterceptingNamingPolicy(new JavaFieldNamingPolicy()); new SerializedNameAnnotationInterceptingNamingPolicy(new JavaFieldNamingPolicy());
private static final ExclusionStrategy DEFAULT_EXCLUSION_STRATEGY = private static final ExclusionStrategy DEFAULT_EXCLUSION_STRATEGY =
@ -98,7 +98,7 @@ public final class Gson {
private final ExclusionStrategy deserializationStrategy; private final ExclusionStrategy deserializationStrategy;
private final FieldNamingStrategy fieldNamingPolicy; private final FieldNamingStrategy2 fieldNamingPolicy;
private final MappedObjectConstructor objectConstructor; private final MappedObjectConstructor objectConstructor;
/** Map containing Type or Class objects as keys */ /** Map containing Type or Class objects as keys */
@ -154,7 +154,7 @@ public final class Gson {
} }
Gson(ExclusionStrategy serializationStrategy, ExclusionStrategy deserializationStrategy, Gson(ExclusionStrategy serializationStrategy, ExclusionStrategy deserializationStrategy,
FieldNamingStrategy fieldNamingPolicy, MappedObjectConstructor objectConstructor, FieldNamingStrategy2 fieldNamingPolicy, MappedObjectConstructor objectConstructor,
JsonFormatter formatter, boolean serializeNulls, JsonFormatter formatter, boolean serializeNulls,
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers, ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers,
ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers, ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers,

View File

@ -69,7 +69,7 @@ public final class GsonBuilder {
private boolean serializeInnerClasses; private boolean serializeInnerClasses;
private boolean excludeFieldsWithoutExposeAnnotation; private boolean excludeFieldsWithoutExposeAnnotation;
private LongSerializationPolicy longSerializationPolicy; private LongSerializationPolicy longSerializationPolicy;
private FieldNamingStrategy fieldNamingPolicy; private FieldNamingStrategy2 fieldNamingPolicy;
private final ParameterizedTypeHandlerMap<InstanceCreator<?>> instanceCreators; private final ParameterizedTypeHandlerMap<InstanceCreator<?>> instanceCreators;
private final ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers; private final ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers;
private final ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers; private final ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers;
@ -220,8 +220,22 @@ public final class GsonBuilder {
* @param fieldNamingStrategy the actual naming strategy to apply to the fields * @param fieldNamingStrategy the actual naming strategy to apply to the fields
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.3 * @since 1.3
* @deprecated use {@link #setFieldNamingStrategy(FieldNamingStrategy2)} instead.
*/ */
@Deprecated
public GsonBuilder setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy) { public GsonBuilder setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy) {
return setFieldNamingStrategy(new FieldNamingStrategy2Adapter(fieldNamingStrategy));
}
/**
* Configures Gson to apply a specific naming policy strategy to an object's field during
* serialization and deserialization.
*
* @param fieldNamingStrategy the actual naming strategy to apply to the fields
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since 1.4
*/
public GsonBuilder setFieldNamingStrategy(FieldNamingStrategy2 fieldNamingStrategy) {
this.fieldNamingPolicy = this.fieldNamingPolicy =
new SerializedNameAnnotationInterceptingNamingPolicy(fieldNamingStrategy); new SerializedNameAnnotationInterceptingNamingPolicy(fieldNamingStrategy);
return this; return this;

View File

@ -18,6 +18,7 @@ package com.google.gson;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Collection;
/** /**
* A simple implementation of the {@link FieldNamingStrategy} interface such that it does not * A simple implementation of the {@link FieldNamingStrategy} interface such that it does not
@ -44,7 +45,7 @@ import java.lang.reflect.Type;
class JavaFieldNamingPolicy extends RecursiveFieldNamingPolicy { class JavaFieldNamingPolicy extends RecursiveFieldNamingPolicy {
@Override @Override
protected String translateName(String target, Type fieldType, Annotation[] annotations) { protected String translateName(String target, Type fieldType, Collection<Annotation> annotations) {
return target; return target;
} }
} }

View File

@ -17,7 +17,6 @@
package com.google.gson; package com.google.gson;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Type; import java.lang.reflect.Type;
/** /**
@ -91,17 +90,17 @@ final class JsonArrayDeserializationVisitor<T> extends JsonDeserializationVisito
throw new JsonParseException("Expecting array but found object: " + node); throw new JsonParseException("Expecting array but found object: " + node);
} }
public void visitArrayField(Field f, Type typeOfF, Object obj) { public void visitArrayField(FieldAttributes f, Type typeOfF, Object obj) {
throw new JsonParseException("Expecting array but found array field " + f.getName() + ": " throw new JsonParseException("Expecting array but found array field " + f.getName() + ": "
+ obj); + obj);
} }
public void visitObjectField(Field f, Type typeOfF, Object obj) { public void visitObjectField(FieldAttributes f, Type typeOfF, Object obj) {
throw new JsonParseException("Expecting array but found object field " + f.getName() + ": " throw new JsonParseException("Expecting array but found object field " + f.getName() + ": "
+ obj); + obj);
} }
public boolean visitFieldUsingCustomHandler(Field f, Type actualTypeOfField, Object parent) { public boolean visitFieldUsingCustomHandler(FieldAttributes f, Type actualTypeOfField, Object parent) {
throw new JsonParseException("Expecting array but found field " + f.getName() + ": " throw new JsonParseException("Expecting array but found field " + f.getName() + ": "
+ parent); + parent);
} }

View File

@ -16,7 +16,6 @@
package com.google.gson; package com.google.gson;
import java.lang.reflect.Field;
import java.lang.reflect.Type; import java.lang.reflect.Type;
/** /**
@ -50,7 +49,7 @@ final class JsonObjectDeserializationVisitor<T> extends JsonDeserializationVisit
throw new JsonParseException("Expecting object but found array: " + array); throw new JsonParseException("Expecting object but found array: " + array);
} }
public void visitObjectField(Field f, Type typeOfF, Object obj) { public void visitObjectField(FieldAttributes f, Type typeOfF, Object obj) {
try { try {
if (!json.isJsonObject()) { if (!json.isJsonObject()) {
throw new JsonParseException("Expecting object found: " + json); throw new JsonParseException("Expecting object found: " + json);
@ -69,7 +68,7 @@ final class JsonObjectDeserializationVisitor<T> extends JsonDeserializationVisit
} }
} }
public void visitArrayField(Field f, Type typeOfF, Object obj) { public void visitArrayField(FieldAttributes f, Type typeOfF, Object obj) {
try { try {
if (!json.isJsonObject()) { if (!json.isJsonObject()) {
throw new JsonParseException("Expecting object found: " + json); throw new JsonParseException("Expecting object found: " + json);
@ -88,12 +87,12 @@ final class JsonObjectDeserializationVisitor<T> extends JsonDeserializationVisit
} }
} }
private String getFieldName(Field f) { private String getFieldName(FieldAttributes f) {
FieldNamingStrategy namingPolicy = factory.getFieldNamingPolicy(); FieldNamingStrategy2 namingPolicy = factory.getFieldNamingPolicy();
return namingPolicy.translateName(f); return namingPolicy.translateName(f);
} }
public boolean visitFieldUsingCustomHandler(Field f, Type declaredTypeOfField, Object parent) { public boolean visitFieldUsingCustomHandler(FieldAttributes f, Type declaredTypeOfField, Object parent) {
try { try {
String fName = getFieldName(f); String fName = getFieldName(f);
if (!json.isJsonObject()) { if (!json.isJsonObject()) {

View File

@ -17,7 +17,6 @@
package com.google.gson; package com.google.gson;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Type; import java.lang.reflect.Type;
/** /**
@ -84,7 +83,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
} }
} }
public void visitArrayField(Field f, Type typeOfF, Object obj) { public void visitArrayField(FieldAttributes f, Type typeOfF, Object obj) {
try { try {
if (isFieldNull(f, obj)) { if (isFieldNull(f, obj)) {
if (serializeNulls) { if (serializeNulls) {
@ -99,7 +98,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
} }
} }
public void visitObjectField(Field f, Type typeOfF, Object obj) { public void visitObjectField(FieldAttributes f, Type typeOfF, Object obj) {
try { try {
if (isFieldNull(f, obj)) { if (isFieldNull(f, obj)) {
if (serializeNulls) { if (serializeNulls) {
@ -122,13 +121,13 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
assignToRoot(json); assignToRoot(json);
} }
private void addAsChildOfObject(Field f, ObjectTypePair fieldValuePair) { private void addAsChildOfObject(FieldAttributes f, ObjectTypePair fieldValuePair) {
JsonElement childElement = getJsonElementForChild(fieldValuePair); JsonElement childElement = getJsonElementForChild(fieldValuePair);
addChildAsElement(f, childElement); addChildAsElement(f, childElement);
} }
private void addChildAsElement(Field f, JsonElement childElement) { private void addChildAsElement(FieldAttributes f, JsonElement childElement) {
FieldNamingStrategy namingPolicy = factory.getFieldNamingPolicy(); FieldNamingStrategy2 namingPolicy = factory.getFieldNamingPolicy();
root.getAsJsonObject().add(namingPolicy.translateName(f), childElement); root.getAsJsonObject().add(namingPolicy.translateName(f), childElement);
} }
@ -191,7 +190,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
} }
} }
public boolean visitFieldUsingCustomHandler(Field f, Type declaredTypeOfField, Object parent) { public boolean visitFieldUsingCustomHandler(FieldAttributes f, Type declaredTypeOfField, Object parent) {
try { try {
Preconditions.checkState(root.isJsonObject()); Preconditions.checkState(root.isJsonObject());
Object obj = f.get(parent); Object obj = f.get(parent);
@ -221,11 +220,11 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
root = newRoot; root = newRoot;
} }
private boolean isFieldNull(Field f, Object obj) { private boolean isFieldNull(FieldAttributes f, Object obj) {
return getFieldValue(f, obj) == null; return getFieldValue(f, obj) == null;
} }
private Object getFieldValue(Field f, Object obj) { private Object getFieldValue(FieldAttributes f, Object obj) {
try { try {
return f.get(obj); return f.get(obj);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {

View File

@ -18,12 +18,13 @@ package com.google.gson;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Collection;
/** /**
* A {@link FieldNamingStrategy} that ensures the JSON field names consist of only * A {@link FieldNamingStrategy} that ensures the JSON field names consist of only
* lower case letters. * lower case letters.
* *
*<p>The following is an example:</p> * <p>The following is an example:</p>
* <pre> * <pre>
* class IntWrapper { * class IntWrapper {
* public int integerField = 0; * public int integerField = 0;
@ -36,12 +37,14 @@ import java.lang.reflect.Type;
* assert("integerfield".equals(translatedFieldName)); * assert("integerfield".equals(translatedFieldName));
* </pre> * </pre>
* *
* @author Inderjeet Singh
* @author Joel Leitch * @author Joel Leitch
*/ */
class LowerCaseNamingPolicy extends RecursiveFieldNamingPolicy { class LowerCaseNamingPolicy extends RecursiveFieldNamingPolicy {
@Override @Override
protected String translateName(String target, Type fieldType, Annotation[] annotations) { protected String translateName(String target, Type fieldType,
Collection<Annotation> annotations) {
return target.toLowerCase(); return target.toLowerCase();
} }
} }

View File

@ -18,6 +18,7 @@ package com.google.gson;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Collection;
/** /**
* A {@link FieldNamingStrategy} that ensures the JSON field names begins with * A {@link FieldNamingStrategy} that ensures the JSON field names begins with
@ -67,7 +68,8 @@ class ModifyFirstLetterNamingPolicy extends RecursiveFieldNamingPolicy {
} }
@Override @Override
protected String translateName(String target, Type fieldType, Annotation[] annotations) { protected String translateName(String target, Type fieldType,
Collection<Annotation> annotations) {
StringBuilder fieldNameBuilder = new StringBuilder(); StringBuilder fieldNameBuilder = new StringBuilder();
int index = 0; int index = 0;
char firstCharacter = target.charAt(index); char firstCharacter = target.charAt(index);

View File

@ -21,8 +21,9 @@ import java.lang.reflect.Field;
import java.lang.reflect.Type; import java.lang.reflect.Type;
/** /**
* Provides ability to apply a visitor to an object and all of its fields recursively. * Provides ability to apply a visitor to an object and all of its fields
* * recursively.
*
* @author Inderjeet Singh * @author Inderjeet Singh
* @author Joel Leitch * @author Joel Leitch
*/ */
@ -30,10 +31,12 @@ final class ObjectNavigator {
public interface Visitor { public interface Visitor {
public void start(ObjectTypePair node); public void start(ObjectTypePair node);
public void end(ObjectTypePair node); public void end(ObjectTypePair node);
/** /**
* This is called before the object navigator starts visiting the current object * This is called before the object navigator starts visiting the current
* object
*/ */
void startVisitingObject(Object node); void startVisitingObject(Object node);
@ -45,23 +48,26 @@ final class ObjectNavigator {
/** /**
* This is called to visit an object field of the current object * This is called to visit an object field of the current object
*/ */
void visitObjectField(Field f, Type typeOfF, Object obj); void visitObjectField(FieldAttributes f, Type typeOfF, Object obj);
/** /**
* This is called to visit an array field of the current object * This is called to visit an array field of the current object
*/ */
void visitArrayField(Field f, Type typeOfF, Object obj); void visitArrayField(FieldAttributes f, Type typeOfF, Object obj);
/** /**
* This is called to visit an object using a custom handler * This is called to visit an object using a custom handler
*
* @return true if a custom handler exists, false otherwise * @return true if a custom handler exists, false otherwise
*/ */
public boolean visitUsingCustomHandler(ObjectTypePair objTypePair); public boolean visitUsingCustomHandler(ObjectTypePair objTypePair);
/** /**
* This is called to visit a field of the current object using a custom handler * This is called to visit a field of the current object using a custom
* handler
*/ */
public boolean visitFieldUsingCustomHandler(Field f, Type actualTypeOfField, Object parent); public boolean visitFieldUsingCustomHandler(FieldAttributes f, Type actualTypeOfField,
Object parent);
/** /**
* Retrieve the current target * Retrieve the current target
@ -75,9 +81,11 @@ final class ObjectNavigator {
private final ObjectTypePair objTypePair; private final ObjectTypePair objTypePair;
/** /**
* @param objTypePair The object,type (fully genericized) being navigated * @param objTypePair
* @param exclusionStrategy the concrete strategy object to be used to * The object,type (fully genericized) being navigated
* filter out fields of an object. * @param exclusionStrategy
* the concrete strategy object to be used to filter out fields of an
* object.
*/ */
ObjectNavigator(ObjectTypePair objTypePair, ExclusionStrategy exclusionStrategy) { ObjectNavigator(ObjectTypePair objTypePair, ExclusionStrategy exclusionStrategy) {
Preconditions.checkNotNull(exclusionStrategy); Preconditions.checkNotNull(exclusionStrategy);
@ -87,8 +95,8 @@ final class ObjectNavigator {
} }
/** /**
* Navigate all the fields of the specified object. * Navigate all the fields of the specified object. If a field is null, it
* If a field is null, it does not get visited. * does not get visited.
*/ */
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
TypeInfo objTypeInfo = new TypeInfo(objTypePair.type); TypeInfo objTypeInfo = new TypeInfo(objTypePair.type);
@ -110,15 +118,15 @@ final class ObjectNavigator {
} else if (objTypeInfo.getActualType() == Object.class } else if (objTypeInfo.getActualType() == Object.class
&& isPrimitiveOrString(objectToVisit)) { && isPrimitiveOrString(objectToVisit)) {
// TODO(Joel): this is only used for deserialization of "primitives" // TODO(Joel): this is only used for deserialization of "primitives"
// we should rethink this!!! // we should rethink this!!!
visitor.visitPrimitive(objectToVisit); visitor.visitPrimitive(objectToVisit);
objectToVisit = visitor.getTarget(); objectToVisit = visitor.getTarget();
} else { } else {
visitor.startVisitingObject(objectToVisit); visitor.startVisitingObject(objectToVisit);
ObjectTypePair currObjTypePair = objTypePair.toMoreSpecificType(); ObjectTypePair currObjTypePair = objTypePair.toMoreSpecificType();
Class<?> topLevelClass = new TypeInfo(currObjTypePair.type).getRawClass(); Class<?> topLevelClass = new TypeInfo(currObjTypePair.type).getRawClass();
for (Class<?> curr = topLevelClass; curr != null && !curr.equals(Object.class); for (Class<?> curr = topLevelClass; curr != null && !curr.equals(Object.class); curr =
curr = curr.getSuperclass()) { curr.getSuperclass()) {
if (!curr.isSynthetic()) { if (!curr.isSynthetic()) {
navigateClassFields(objectToVisit, curr, visitor); navigateClassFields(objectToVisit, curr, visitor);
} }
@ -148,12 +156,12 @@ final class ObjectNavigator {
TypeInfo fieldTypeInfo = TypeInfoFactory.getTypeInfoForField(f, objTypePair.type); TypeInfo fieldTypeInfo = TypeInfoFactory.getTypeInfoForField(f, objTypePair.type);
Type declaredTypeOfField = fieldTypeInfo.getActualType(); Type declaredTypeOfField = fieldTypeInfo.getActualType();
boolean visitedWithCustomHandler = boolean visitedWithCustomHandler =
visitor.visitFieldUsingCustomHandler(f, declaredTypeOfField, obj); visitor.visitFieldUsingCustomHandler(fieldAttributes, declaredTypeOfField, obj);
if (!visitedWithCustomHandler) { if (!visitedWithCustomHandler) {
if (fieldTypeInfo.isArray()) { if (fieldTypeInfo.isArray()) {
visitor.visitArrayField(f, declaredTypeOfField, obj); visitor.visitArrayField(fieldAttributes, declaredTypeOfField, obj);
} else { } else {
visitor.visitObjectField(f, declaredTypeOfField, obj); visitor.visitObjectField(fieldAttributes, declaredTypeOfField, obj);
} }
} }
} }

View File

@ -25,7 +25,7 @@ package com.google.gson;
*/ */
final class ObjectNavigatorFactory { final class ObjectNavigatorFactory {
private final ExclusionStrategy strategy; private final ExclusionStrategy strategy;
private final FieldNamingStrategy fieldNamingPolicy; private final FieldNamingStrategy2 fieldNamingPolicy;
/** /**
* Creates a factory object that will be able to create new * Creates a factory object that will be able to create new
@ -36,7 +36,7 @@ final class ObjectNavigatorFactory {
* @param fieldNamingPolicy the naming policy that should be applied to field * @param fieldNamingPolicy the naming policy that should be applied to field
* names * names
*/ */
public ObjectNavigatorFactory(ExclusionStrategy strategy, FieldNamingStrategy fieldNamingPolicy) { public ObjectNavigatorFactory(ExclusionStrategy strategy, FieldNamingStrategy2 fieldNamingPolicy) {
Preconditions.checkNotNull(fieldNamingPolicy); Preconditions.checkNotNull(fieldNamingPolicy);
this.strategy = (strategy == null ? new NullExclusionStrategy() : strategy); this.strategy = (strategy == null ? new NullExclusionStrategy() : strategy);
this.fieldNamingPolicy = fieldNamingPolicy; this.fieldNamingPolicy = fieldNamingPolicy;
@ -55,7 +55,7 @@ final class ObjectNavigatorFactory {
return new ObjectNavigator(objTypePair, strategy); return new ObjectNavigator(objTypePair, strategy);
} }
FieldNamingStrategy getFieldNamingPolicy() { FieldNamingStrategy2 getFieldNamingPolicy() {
return fieldNamingPolicy; return fieldNamingPolicy;
} }
} }

View File

@ -13,8 +13,18 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.gson; package com.google.gson;
/**
* A simple object that holds onto a pair of object references, first and second.
*
* @author Inderjeet Singh
* @author Joel Leitch
*
* @param <FIRST>
* @param <SECOND>
*/
final class Pair<FIRST, SECOND> { final class Pair<FIRST, SECOND> {
final FIRST first; final FIRST first;
@ -24,4 +34,24 @@ final class Pair<FIRST, SECOND> {
this.first = first; this.first = first;
this.second = second; this.second = second;
} }
}
@Override
public int hashCode() {
return 17 * ((first != null) ? first.hashCode() : 0)
+ 17 * ((second != null) ? second.hashCode() : 0);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Pair<?, ?>)) {
return false;
}
Pair<?, ?> that = (Pair<?, ?>) o;
return equal(this.first, that.first) && equal(this.second, that.second);
}
private static boolean equal(Object a, Object b) {
return a == b || (a != null && a.equals(b));
}
}

View File

@ -17,8 +17,8 @@
package com.google.gson; package com.google.gson;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Collection;
/** /**
* A mechanism for providing custom field naming in Gson. This allows the client code to translate * A mechanism for providing custom field naming in Gson. This allows the client code to translate
@ -27,11 +27,11 @@ import java.lang.reflect.Type;
* *
* @author Joel Leitch * @author Joel Leitch
*/ */
abstract class RecursiveFieldNamingPolicy implements FieldNamingStrategy { abstract class RecursiveFieldNamingPolicy implements FieldNamingStrategy2 {
public final String translateName(Field f) { public final String translateName(FieldAttributes f) {
Preconditions.checkNotNull(f); Preconditions.checkNotNull(f);
return translateName(f.getName(), f.getGenericType(), f.getAnnotations()); return translateName(f.getName(), f.getDeclaredType(), f.getAnnotations());
} }
/** /**
@ -42,5 +42,5 @@ abstract class RecursiveFieldNamingPolicy implements FieldNamingStrategy {
* @param annotations the annotations set on the field * @param annotations the annotations set on the field
* @return the translated field name * @return the translated field name
*/ */
protected abstract String translateName(String target, Type fieldType, Annotation[] annotations); protected abstract String translateName(String target, Type fieldType, Collection<Annotation> annotations);
} }

View File

@ -18,8 +18,6 @@ package com.google.gson;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import java.lang.reflect.Field;
/** /**
* A {@link FieldNamingStrategy} that acts as a chain of responsibility. If the * A {@link FieldNamingStrategy} that acts as a chain of responsibility. If the
* {@link com.google.gson.annotations.SerializedName} annotation is applied to a field then this * {@link com.google.gson.annotations.SerializedName} annotation is applied to a field then this
@ -33,15 +31,15 @@ import java.lang.reflect.Field;
* *
* @author Joel Leitch * @author Joel Leitch
*/ */
final class SerializedNameAnnotationInterceptingNamingPolicy implements FieldNamingStrategy { final class SerializedNameAnnotationInterceptingNamingPolicy implements FieldNamingStrategy2 {
private static final JsonFieldNameValidator fieldNameValidator = new JsonFieldNameValidator(); private static final JsonFieldNameValidator fieldNameValidator = new JsonFieldNameValidator();
private final FieldNamingStrategy delegate; private final FieldNamingStrategy2 delegate;
public SerializedNameAnnotationInterceptingNamingPolicy(FieldNamingStrategy delegate) { public SerializedNameAnnotationInterceptingNamingPolicy(FieldNamingStrategy2 delegate) {
this.delegate = delegate; this.delegate = delegate;
} }
public String translateName(Field f) { public String translateName(FieldAttributes f) {
Preconditions.checkNotNull(f); Preconditions.checkNotNull(f);
SerializedName serializedName = f.getAnnotation(SerializedName.class); SerializedName serializedName = f.getAnnotation(SerializedName.class);
if (serializedName != null) { if (serializedName != null) {

View File

@ -18,6 +18,7 @@ package com.google.gson;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Collection;
/** /**
* A {@link FieldNamingStrategy} that ensures the JSON field names consist of only * A {@link FieldNamingStrategy} that ensures the JSON field names consist of only
@ -41,7 +42,7 @@ import java.lang.reflect.Type;
class UpperCaseNamingPolicy extends RecursiveFieldNamingPolicy { class UpperCaseNamingPolicy extends RecursiveFieldNamingPolicy {
@Override @Override
protected String translateName(String target, Type fieldType, Annotation[] annotations) { protected String translateName(String target, Type fieldType, Collection<Annotation> annotations) {
return target.toUpperCase(); return target.toUpperCase();
} }
} }

View File

@ -18,8 +18,6 @@ package com.google.gson;
import junit.framework.TestCase; import junit.framework.TestCase;
import java.lang.reflect.Field;
/** /**
* Tests for the {@link JavaFieldNamingPolicy} class. * Tests for the {@link JavaFieldNamingPolicy} class.
* *
@ -36,13 +34,13 @@ public class JavaFieldNamingPolicyTest extends TestCase {
} }
public void testFieldNamingPolicy() throws Exception { public void testFieldNamingPolicy() throws Exception {
Field f = String.class.getFields()[0]; FieldAttributes f = new FieldAttributes(String.class.getFields()[0]);
assertEquals(f.getName(), namingPolicy.translateName(f)); assertEquals(f.getName(), namingPolicy.translateName(f));
} }
public void testNullField() throws Exception { public void testNullField() throws Exception {
try { try {
namingPolicy.translateName((Field) null); namingPolicy.translateName((FieldAttributes) null);
fail("Should have thrown an exception"); fail("Should have thrown an exception");
} catch (IllegalArgumentException expected) { } } catch (IllegalArgumentException expected) { }
} }

View File

@ -16,11 +16,9 @@
package com.google.gson; package com.google.gson;
import com.google.gson.annotations.SerializedName;
import junit.framework.TestCase; import junit.framework.TestCase;
import java.lang.reflect.Field; import com.google.gson.annotations.SerializedName;
/** /**
* Unit tests for the {@link SerializedNameAnnotationInterceptingNamingPolicy} class. * Unit tests for the {@link SerializedNameAnnotationInterceptingNamingPolicy} class.
@ -40,7 +38,7 @@ public class SerializedNameAnnotationInterceptingNamingPolicyTest extends TestCa
public void testFieldWithAnnotation() throws Exception { public void testFieldWithAnnotation() throws Exception {
String fieldName = "fieldWithAnnotation"; String fieldName = "fieldWithAnnotation";
Field f = SomeObject.class.getField(fieldName); FieldAttributes f = new FieldAttributes(SomeObject.class.getField(fieldName));
assertFalse(ANNOTATED_FIELD_NAME.equals(fieldName)); assertFalse(ANNOTATED_FIELD_NAME.equals(fieldName));
assertEquals(ANNOTATED_FIELD_NAME, policy.translateName(f)); assertEquals(ANNOTATED_FIELD_NAME, policy.translateName(f));
@ -48,7 +46,7 @@ public class SerializedNameAnnotationInterceptingNamingPolicyTest extends TestCa
public void testFieldWithoutAnnotation() throws Exception { public void testFieldWithoutAnnotation() throws Exception {
String fieldName = "fieldWithoutAnnotation"; String fieldName = "fieldWithoutAnnotation";
Field f = SomeObject.class.getField(fieldName); FieldAttributes f = new FieldAttributes(SomeObject.class.getField(fieldName));
assertEquals(fieldName, policy.translateName(f)); assertEquals(fieldName, policy.translateName(f));
} }

View File

@ -426,7 +426,6 @@ public class CustomTypeAdaptersTest extends TestCase {
} }
private static class DataHolder { private static class DataHolder {
@SuppressWarnings("unused")
final String data; final String data;
// For use by Gson // For use by Gson