Rename GsonExclusionStrategy to Excluder. The new class is its own factory, which simplifies its caller in GsonBuilder. It no longer implements ExclusionStrategy, which allows the callers to pass in a boolean for serialize/deserialize. This allows us to use one excluder for both code paths. The delegate ExclusionStrategy instances might end up not being shared so it has two lists internally.

This commit is contained in:
Jesse Wilson 2011-11-22 07:37:13 +00:00
parent fed332906d
commit aa2f61b7d8
11 changed files with 324 additions and 450 deletions

View File

@ -16,13 +16,12 @@
package com.google.gson;
import com.google.gson.internal.Pair;
import com.google.gson.internal.$Gson$Preconditions;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
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.
@ -35,22 +34,7 @@ import java.util.Collections;
* @since 1.4
*/
public final class FieldAttributes {
private static final String MAX_CACHE_PROPERTY_NAME =
"com.google.gson.annotation_cache_size_hint";
private static final LruCache<Pair<Class<?>, String>, Collection<Annotation>> ANNOTATION_CACHE
= new LruCache<Pair<Class<?>,String>, Collection<Annotation>>(getMaxCacheSize());
private final Class<?> declaringClazz;
private final Field field;
private final Class<?> declaredType;
private final boolean isSynthetic;
private final int modifiers;
private final String name;
// Fields used for lazy initialization
private Type genericType;
private Collection<Annotation> annotations;
/**
* Constructs a Field Attributes object from the {@code f}.
@ -58,37 +42,22 @@ public final class FieldAttributes {
* @param f the field to pull attributes from
*/
public FieldAttributes(Field f) {
this.declaringClazz = f.getDeclaringClass();
this.name = f.getName();
this.declaredType = f.getType();
this.isSynthetic = f.isSynthetic();
this.modifiers = f.getModifiers();
$Gson$Preconditions.checkNotNull(f);
this.field = f;
}
private static int getMaxCacheSize() {
final int defaultMaxCacheSize = 2000;
try {
String propertyValue = System.getProperty(
MAX_CACHE_PROPERTY_NAME, String.valueOf(defaultMaxCacheSize));
return Integer.parseInt(propertyValue);
} catch (NumberFormatException e) {
return defaultMaxCacheSize;
}
}
/**
* @return the declaring class that contains this field
*/
public Class<?> getDeclaringClass() {
return declaringClazz;
return field.getDeclaringClass();
}
/**
* @return the name of the field
*/
public String getName() {
return name;
return field.getName();
}
/**
@ -108,10 +77,7 @@ public final class FieldAttributes {
* @return the specific type declared for this field
*/
public Type getDeclaredType() {
if (genericType == null) {
genericType = field.getGenericType();
}
return genericType;
return field.getGenericType();
}
/**
@ -131,7 +97,7 @@ public final class FieldAttributes {
* @return the specific class object that was declared for the field
*/
public Class<?> getDeclaredClass() {
return declaredType;
return field.getType();
}
/**
@ -142,7 +108,7 @@ public final class FieldAttributes {
* @return the annotation instance if it is bound to the field; otherwise {@code null}
*/
public <T extends Annotation> T getAnnotation(Class<T> annotation) {
return getAnnotationFromArray(getAnnotations(), annotation);
return field.getAnnotation(annotation);
}
/**
@ -152,17 +118,7 @@ public final class FieldAttributes {
* @since 1.4
*/
public Collection<Annotation> getAnnotations() {
if (annotations == null) {
Pair<Class<?>, String> key = new Pair<Class<?>, String>(declaringClazz, name);
Collection<Annotation> cachedValue = ANNOTATION_CACHE.get(key);
if (cachedValue == null) {
cachedValue = Collections.unmodifiableCollection(
Arrays.asList(field.getAnnotations()));
ANNOTATION_CACHE.put(key, cachedValue);
}
annotations = cachedValue;
}
return annotations;
return Arrays.asList(field.getAnnotations());
}
/**
@ -176,7 +132,7 @@ public final class FieldAttributes {
* @see java.lang.reflect.Modifier
*/
public boolean hasModifier(int modifier) {
return (modifiers & modifier) != 0;
return (field.getModifiers() & modifier) != 0;
}
/**
@ -196,7 +152,7 @@ public final class FieldAttributes {
* @return true if the field is synthetic; otherwise false
*/
boolean isSynthetic() {
return isSynthetic;
return field.isSynthetic();
}
@SuppressWarnings("unchecked")

View File

@ -17,6 +17,7 @@
package com.google.gson;
import com.google.gson.internal.ConstructorConstructor;
import com.google.gson.internal.Excluder;
import com.google.gson.internal.Primitives;
import com.google.gson.internal.Streams;
import com.google.gson.internal.TypeMap;
@ -25,7 +26,6 @@ import com.google.gson.internal.bind.BigDecimalTypeAdapter;
import com.google.gson.internal.bind.BigIntegerTypeAdapter;
import com.google.gson.internal.bind.CollectionTypeAdapterFactory;
import com.google.gson.internal.bind.DateTypeAdapter;
import com.google.gson.internal.bind.ExcludedTypeAdapterFactory;
import com.google.gson.internal.bind.JsonElementReader;
import com.google.gson.internal.bind.JsonElementWriter;
import com.google.gson.internal.bind.MapTypeAdapterFactory;
@ -45,7 +45,6 @@ import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
@ -103,10 +102,6 @@ public final class Gson {
static final boolean DEFAULT_JSON_NON_EXECUTABLE = false;
private static final ExclusionStrategy DEFAULT_EXCLUSION_STRATEGY = new GsonExclusionStrategy(
GsonExclusionStrategy.IGNORE_VERSIONS, Modifier.TRANSIENT | Modifier.STATIC,
true, true, true, false, false);
private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n";
/**
@ -188,15 +183,13 @@ public final class Gson {
*/
@SuppressWarnings("unchecked")
public Gson() {
this(DEFAULT_EXCLUSION_STRATEGY, DEFAULT_EXCLUSION_STRATEGY, FieldNamingPolicy.IDENTITY,
this(Excluder.DEFAULT, FieldNamingPolicy.IDENTITY,
EMPTY_MAP, false, EMPTY_MAP, EMPTY_MAP, false, DEFAULT_JSON_NON_EXECUTABLE, true,
false, false, LongSerializationPolicy.DEFAULT,
Collections.<TypeAdapter.Factory>emptyList());
}
Gson(final ExclusionStrategy deserializationExclusionStrategy,
final ExclusionStrategy serializationExclusionStrategy,
final FieldNamingStrategy fieldNamingPolicy,
Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingPolicy,
final TypeMap<InstanceCreator<?>> instanceCreators, boolean serializeNulls,
final TypeMap<JsonSerializer<?>> serializers,
final TypeMap<JsonDeserializer<?>> deserializers,
@ -213,8 +206,7 @@ public final class Gson {
this.prettyPrinting = prettyPrinting;
TypeAdapter.Factory reflectiveTypeAdapterFactory = new ReflectiveTypeAdapterFactory(
constructorConstructor, fieldNamingPolicy, serializationExclusionStrategy,
deserializationExclusionStrategy);
constructorConstructor, fieldNamingPolicy, excluder);
ConstructorConstructor constructorConstructor = new ConstructorConstructor();
List<TypeAdapter.Factory> factories = new ArrayList<TypeAdapter.Factory>();
@ -229,8 +221,7 @@ public final class Gson {
doubleAdapter(serializeSpecialFloatingPointValues)));
factories.add(TypeAdapters.newFactory(float.class, Float.class,
floatAdapter(serializeSpecialFloatingPointValues)));
factories.add(new ExcludedTypeAdapterFactory(
serializationExclusionStrategy, deserializationExclusionStrategy));
factories.add(excluder);
factories.add(TypeAdapters.NUMBER_FACTORY);
factories.add(TypeAdapters.CHARACTER_FACTORY);
factories.add(TypeAdapters.STRING_BUILDER_FACTORY);

View File

@ -17,11 +17,11 @@
package com.google.gson;
import com.google.gson.internal.$Gson$Preconditions;
import com.google.gson.internal.Excluder;
import com.google.gson.internal.Primitives;
import com.google.gson.internal.TypeMap;
import com.google.gson.internal.bind.TypeAdapters;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.sql.Timestamp;
import java.text.DateFormat;
@ -63,12 +63,7 @@ import java.util.List;
* @author Joel Leitch
*/
public final class GsonBuilder {
private ExclusionStrategy serializeExclusionStrategy;
private ExclusionStrategy deserializeExclusionStrategy;
private int modifiers = Modifier.TRANSIENT | Modifier.STATIC;
private double ignoreVersionsAfter = GsonExclusionStrategy.IGNORE_VERSIONS;
private boolean serializeInnerClasses = true;
private boolean excludeFieldsWithoutExposeAnnotation = false;
private Excluder excluder = Excluder.DEFAULT;
private LongSerializationPolicy longSerializationPolicy;
private FieldNamingStrategy fieldNamingPolicy;
@ -142,7 +137,7 @@ public final class GsonBuilder {
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/
public GsonBuilder setVersion(double ignoreVersionsAfter) {
this.ignoreVersionsAfter = ignoreVersionsAfter;
excluder = excluder.withVersion(ignoreVersionsAfter);
return this;
}
@ -158,10 +153,7 @@ public final class GsonBuilder {
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/
public GsonBuilder excludeFieldsWithModifiers(int... modifiers) {
this.modifiers = 0;
for (int modifier : modifiers) {
this.modifiers |= modifier;
}
excluder = excluder.withModifiers(modifiers);
return this;
}
@ -186,7 +178,7 @@ public final class GsonBuilder {
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
*/
public GsonBuilder excludeFieldsWithoutExposeAnnotation() {
excludeFieldsWithoutExposeAnnotation = true;
excluder = excluder.excludeFieldsWithoutExposeAnnotation();
return this;
}
@ -290,7 +282,7 @@ public final class GsonBuilder {
* @since 1.3
*/
public GsonBuilder disableInnerClassSerialization() {
serializeInnerClasses = false;
excluder = excluder.disableInnerClassSerialization();
return this;
}
@ -345,8 +337,7 @@ public final class GsonBuilder {
*/
public GsonBuilder setExclusionStrategies(ExclusionStrategy... strategies) {
for (ExclusionStrategy strategy : strategies) {
addSerializationExclusionStrategy(strategy);
addDeserializationExclusionStrategy(strategy);
excluder = excluder.withExclusionStrategy(strategy, true, true);
}
return this;
}
@ -364,7 +355,7 @@ public final class GsonBuilder {
* @since 1.7
*/
public GsonBuilder addSerializationExclusionStrategy(ExclusionStrategy strategy) {
serializeExclusionStrategy = combine(serializeExclusionStrategy, strategy);
excluder = excluder.withExclusionStrategy(strategy, true, false);
return this;
}
@ -381,7 +372,7 @@ public final class GsonBuilder {
* @since 1.7
*/
public GsonBuilder addDeserializationExclusionStrategy(ExclusionStrategy strategy) {
deserializeExclusionStrategy = combine(deserializeExclusionStrategy, strategy);
excluder = excluder.withExclusionStrategy(strategy, false, true);
return this;
}
@ -600,27 +591,6 @@ public final class GsonBuilder {
return this;
}
/**
* Unions two exclusion strategies. If the first is null, this returns the
* second.
*/
private static ExclusionStrategy combine(final ExclusionStrategy a, final ExclusionStrategy b) {
if (b == null) {
throw new IllegalArgumentException();
}
if (a == null) {
return b;
}
return new ExclusionStrategy() {
public boolean shouldSkipField(FieldAttributes f) {
return a.shouldSkipField(f) || b.shouldSkipField(f);
}
public boolean shouldSkipClass(Class<?> clazz) {
return a.shouldSkipClass(clazz) || b.shouldSkipClass(clazz);
}
};
}
/**
* Creates a {@link Gson} instance based on the current configuration. This method is free of
* side-effects to this {@code GsonBuilder} instance and hence can be called multiple times.
@ -630,17 +600,10 @@ public final class GsonBuilder {
public Gson create() {
addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, serializers, deserializers);
ExclusionStrategy deserializeExclusionStrategy = combine(this.deserializeExclusionStrategy,
new GsonExclusionStrategy(ignoreVersionsAfter, modifiers, true,
true, serializeInnerClasses, false, excludeFieldsWithoutExposeAnnotation));
ExclusionStrategy serializeExclusionStrategy = combine(this.serializeExclusionStrategy,
new GsonExclusionStrategy(ignoreVersionsAfter, modifiers, true, true,
serializeInnerClasses, excludeFieldsWithoutExposeAnnotation, false));
return new Gson(deserializeExclusionStrategy, serializeExclusionStrategy, fieldNamingPolicy,
instanceCreators.copyOf().makeUnmodifiable(), serializeNulls,
serializers.copyOf().makeUnmodifiable(), deserializers.copyOf().makeUnmodifiable(),
complexMapKeySerialization, generateNonExecutableJson, escapeHtmlChars, prettyPrinting,
return new Gson(excluder, fieldNamingPolicy, instanceCreators.copyOf().makeUnmodifiable(),
serializeNulls, serializers.copyOf().makeUnmodifiable(),
deserializers.copyOf().makeUnmodifiable(), complexMapKeySerialization,
generateNonExecutableJson, escapeHtmlChars, prettyPrinting,
serializeSpecialFloatingPointValues, longSerializationPolicy, typeAdapterFactories);
}

View File

@ -1,132 +0,0 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.Since;
import com.google.gson.annotations.Until;
import java.lang.reflect.Modifier;
/**
* A configurable exclusion strategy. This strategy supports version attributes
* {@link Since} and {@link Until}, modifiers, synthetic fields, anonymous and
* local classes, inner classes, and fields with the {@link Expose} annotation.
*
* @author Joel Leitch
* @author Jesse Wilson
*/
final class GsonExclusionStrategy implements ExclusionStrategy {
static final double IGNORE_VERSIONS = -1D;
private final double version;
private final int modifiers;
private final boolean excludeSyntheticFields;
private final boolean excludeAnonymousAndLocal;
private final boolean serializeInnerClasses;
private final boolean requireExposeOnSerialize;
private final boolean requireExposeOnDeserialize;
GsonExclusionStrategy(double version, int modifiers, boolean excludeSyntheticFields,
boolean excludeAnonymousAndLocal, boolean serializeInnerClasses,
boolean requireExposeOnSerialize, boolean requireExposeOnDeserialize) {
this.version = version;
this.modifiers = modifiers;
this.excludeSyntheticFields = excludeSyntheticFields;
this.excludeAnonymousAndLocal = excludeAnonymousAndLocal;
this.serializeInnerClasses = serializeInnerClasses;
this.requireExposeOnSerialize = requireExposeOnSerialize;
this.requireExposeOnDeserialize = requireExposeOnDeserialize;
}
public boolean shouldSkipField(FieldAttributes f) {
if (f.hasModifier(modifiers)) {
return true;
}
if (version != GsonExclusionStrategy.IGNORE_VERSIONS
&& !isValidVersion(f.getAnnotation(Since.class), f.getAnnotation(Until.class))) {
return true;
}
if (excludeSyntheticFields && f.isSynthetic()) {
return true;
}
if (requireExposeOnSerialize || requireExposeOnDeserialize) {
Expose annotation = f.getAnnotation(Expose.class);
if (annotation == null
|| requireExposeOnSerialize && !annotation.serialize()
|| requireExposeOnDeserialize && !annotation.deserialize()) {
return true;
}
}
if (!serializeInnerClasses && isInnerClass(f.getDeclaredClass())) {
return true;
}
if (excludeAnonymousAndLocal && isAnonymousOrLocal(f.getDeclaredClass())) {
return true;
}
return false;
}
public boolean shouldSkipClass(Class<?> clazz) {
if (version != GsonExclusionStrategy.IGNORE_VERSIONS
&& !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class))) {
return true;
}
if (!serializeInnerClasses && isInnerClass(clazz)) {
return true;
}
if (excludeAnonymousAndLocal && isAnonymousOrLocal(clazz)) {
return true;
}
return false;
}
private boolean isAnonymousOrLocal(Class<?> clazz) {
return !Enum.class.isAssignableFrom(clazz)
&& (clazz.isAnonymousClass() || clazz.isLocalClass());
}
private boolean isInnerClass(Class<?> clazz) {
return clazz.isMemberClass() && !isStatic(clazz);
}
private boolean isStatic(Class<?> clazz) {
return (clazz.getModifiers() & Modifier.STATIC) != 0;
}
private boolean isValidVersion(Since since, Until until) {
return isValidSince(since) && isValidUntil(until);
}
private boolean isValidSince(Since annotation) {
if (annotation != null) {
double annotationVersion = annotation.value();
if (annotationVersion > version) {
return false;
}
}
return true;
}
private boolean isValidUntil(Until annotation) {
if (annotation != null) {
double annotationVersion = annotation.value();
if (annotationVersion <= version) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,250 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson.internal;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.Since;
import com.google.gson.annotations.Until;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* This class selects which fields and types to omit. It is configurable,
* supporting version attributes {@link Since} and {@link Until}, modifiers,
* synthetic fields, anonymous and local classes, inner classes, and fields with
* the {@link Expose} annotation.
*
* <p>This class is a type adapter factory; types that are excluded will be
* adapted to null. It may delegate to another type adapter if only one
* direction is excluded.
*
* @author Joel Leitch
* @author Jesse Wilson
*/
public final class Excluder implements TypeAdapter.Factory, Cloneable {
private static final double IGNORE_VERSIONS = -1.0d;
public static final Excluder DEFAULT = new Excluder();
private double version = IGNORE_VERSIONS;
private int modifiers = Modifier.TRANSIENT | Modifier.STATIC;
private boolean serializeInnerClasses = true;
private boolean requireExpose;
private List<ExclusionStrategy> serializationStrategies = Collections.emptyList();
private List<ExclusionStrategy> deserializationStrategies = Collections.emptyList();
@Override protected Excluder clone() {
try {
return (Excluder) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
public Excluder withVersion(double ignoreVersionsAfter) {
Excluder result = clone();
result.version = ignoreVersionsAfter;
return result;
}
public Excluder withModifiers(int... modifiers) {
Excluder result = clone();
result.modifiers = 0;
for (int modifier : modifiers) {
result.modifiers |= modifier;
}
return result;
}
public Excluder disableInnerClassSerialization() {
Excluder result = clone();
result.serializeInnerClasses = false;
return result;
}
public Excluder excludeFieldsWithoutExposeAnnotation() {
Excluder result = clone();
result.requireExpose = true;
return result;
}
public Excluder withExclusionStrategy(ExclusionStrategy exclusionStrategy,
boolean serialization, boolean deserialization) {
Excluder result = clone();
if (serialization) {
result.serializationStrategies = new ArrayList<ExclusionStrategy>(serializationStrategies);
result.serializationStrategies.add(exclusionStrategy);
}
if (deserialization) {
result.deserializationStrategies
= new ArrayList<ExclusionStrategy>(deserializationStrategies);
result.deserializationStrategies.add(exclusionStrategy);
}
return result;
}
public <T> TypeAdapter<T> create(final Gson context, final TypeToken<T> type) {
Class<?> rawType = type.getRawType();
final boolean skipSerialize = excludeClass(rawType, true);
final boolean skipDeserialize = excludeClass(rawType, false);
if (!skipSerialize && !skipDeserialize) {
return null;
}
return new TypeAdapter<T>() {
/** The delegate is lazily created because it may not be needed, and creating it may fail. */
private TypeAdapter<T> delegate;
@Override public T read(JsonReader reader) throws IOException {
if (skipDeserialize) {
reader.skipValue();
return null;
}
return delegate().read(reader);
}
@Override public void write(JsonWriter writer, T value) throws IOException {
if (skipSerialize) {
writer.nullValue();
return;
}
delegate().write(writer, value);
}
private TypeAdapter<T> delegate() {
TypeAdapter<T> d = delegate;
return d != null
? d
: (delegate = context.getNextAdapter(Excluder.this, type));
}
};
}
public boolean excludeField(Field field, boolean serialize) {
if ((modifiers & field.getModifiers()) != 0) {
return true;
}
if (version != Excluder.IGNORE_VERSIONS
&& !isValidVersion(field.getAnnotation(Since.class), field.getAnnotation(Until.class))) {
return true;
}
if (field.isSynthetic()) {
return true;
}
if (requireExpose) {
Expose annotation = field.getAnnotation(Expose.class);
if (annotation == null || (serialize ? !annotation.serialize() : !annotation.deserialize())) {
return true;
}
}
if (!serializeInnerClasses && isInnerClass(field.getType())) {
return true;
}
if (isAnonymousOrLocal(field.getType())) {
return true;
}
List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies;
if (!list.isEmpty()) {
FieldAttributes fieldAttributes = new FieldAttributes(field);
for (ExclusionStrategy exclusionStrategy : list) {
if (exclusionStrategy.shouldSkipField(fieldAttributes)) {
return true;
}
}
}
return false;
}
public boolean excludeClass(Class<?> clazz, boolean serialize) {
if (version != Excluder.IGNORE_VERSIONS
&& !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class))) {
return true;
}
if (!serializeInnerClasses && isInnerClass(clazz)) {
return true;
}
if (isAnonymousOrLocal(clazz)) {
return true;
}
List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies;
for (ExclusionStrategy exclusionStrategy : list) {
if (exclusionStrategy.shouldSkipClass(clazz)) {
return true;
}
}
return false;
}
private boolean isAnonymousOrLocal(Class<?> clazz) {
return !Enum.class.isAssignableFrom(clazz)
&& (clazz.isAnonymousClass() || clazz.isLocalClass());
}
private boolean isInnerClass(Class<?> clazz) {
return clazz.isMemberClass() && !isStatic(clazz);
}
private boolean isStatic(Class<?> clazz) {
return (clazz.getModifiers() & Modifier.STATIC) != 0;
}
private boolean isValidVersion(Since since, Until until) {
return isValidSince(since) && isValidUntil(until);
}
private boolean isValidSince(Since annotation) {
if (annotation != null) {
double annotationVersion = annotation.value();
if (annotationVersion > version) {
return false;
}
}
return true;
}
private boolean isValidUntil(Until annotation) {
if (annotation != null) {
double annotationVersion = annotation.value();
if (annotationVersion <= version) {
return false;
}
}
return true;
}
}

View File

@ -1,78 +0,0 @@
/*
* Copyright (C) 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.gson.internal.bind;
import com.google.gson.ExclusionStrategy;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
/**
* This type adapter skips values using an exclusion strategy. It may delegate
* to another type adapter if only one direction is excluded.
*/
public final class ExcludedTypeAdapterFactory implements TypeAdapter.Factory {
private final ExclusionStrategy serializationExclusionStrategy;
private final ExclusionStrategy deserializationExclusionStrategy;
public ExcludedTypeAdapterFactory(ExclusionStrategy serializationExclusionStrategy,
ExclusionStrategy deserializationExclusionStrategy) {
this.serializationExclusionStrategy = serializationExclusionStrategy;
this.deserializationExclusionStrategy = deserializationExclusionStrategy;
}
public <T> TypeAdapter<T> create(final Gson context, final TypeToken<T> type) {
Class<?> rawType = type.getRawType();
final boolean skipSerialize = serializationExclusionStrategy.shouldSkipClass(rawType);
final boolean skipDeserialize = deserializationExclusionStrategy.shouldSkipClass(rawType);
if (!skipSerialize && !skipDeserialize) {
return null;
}
return new TypeAdapter<T>() {
/** The delegate is lazily created because it may not be needed, and creating it may fail. */
private TypeAdapter<T> delegate;
@Override public T read(JsonReader reader) throws IOException {
if (skipDeserialize) {
reader.skipValue();
return null;
}
return delegate().read(reader);
}
@Override public void write(JsonWriter writer, T value) throws IOException {
if (skipSerialize) {
writer.nullValue();
return;
}
delegate().write(writer, value);
}
private TypeAdapter<T> delegate() {
TypeAdapter<T> d = delegate;
return d != null
? d
: (delegate = context.getNextAdapter(ExcludedTypeAdapterFactory.this, type));
}
};
}
}

View File

@ -16,8 +16,6 @@
package com.google.gson.internal.bind;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.FieldNamingStrategy;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
@ -25,6 +23,7 @@ import com.google.gson.TypeAdapter;
import com.google.gson.annotations.SerializedName;
import com.google.gson.internal.$Gson$Types;
import com.google.gson.internal.ConstructorConstructor;
import com.google.gson.internal.Excluder;
import com.google.gson.internal.ObjectConstructor;
import com.google.gson.internal.Primitives;
import com.google.gson.reflect.TypeToken;
@ -44,26 +43,17 @@ import java.util.Map;
public final class ReflectiveTypeAdapterFactory implements TypeAdapter.Factory {
private final ConstructorConstructor constructorConstructor;
private final FieldNamingStrategy fieldNamingPolicy;
private final ExclusionStrategy serializationExclusionStrategy;
private final ExclusionStrategy deserializationExclusionStrategy;
private final Excluder excluder;
public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor,
FieldNamingStrategy fieldNamingPolicy, ExclusionStrategy serializationExclusionStrategy,
ExclusionStrategy deserializationExclusionStrategy) {
FieldNamingStrategy fieldNamingPolicy, Excluder excluder) {
this.constructorConstructor = constructorConstructor;
this.fieldNamingPolicy = fieldNamingPolicy;
this.serializationExclusionStrategy = serializationExclusionStrategy;
this.deserializationExclusionStrategy = deserializationExclusionStrategy;
this.excluder = excluder;
}
public boolean serializeField(Field f) {
return !serializationExclusionStrategy.shouldSkipClass(f.getType())
&& !serializationExclusionStrategy.shouldSkipField(new FieldAttributes(f));
}
private boolean deserializeField(Field f) {
return !deserializationExclusionStrategy.shouldSkipClass(f.getType())
&& !deserializationExclusionStrategy.shouldSkipField(new FieldAttributes(f));
public boolean excludeField(Field f, boolean serialize) {
return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize);
}
private String getFieldName(Field f) {
@ -119,8 +109,8 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapter.Factory {
Field[] fields = raw.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
for (Field field : fields) {
boolean serialize = serializeField(field);
boolean deserialize = deserializeField(field);
boolean serialize = excludeField(field, true);
boolean deserialize = excludeField(field, false);
if (!serialize && !deserialize) {
continue;
}

View File

@ -18,6 +18,7 @@ package com.google.gson;
import com.google.gson.annotations.Expose;
import com.google.gson.internal.Excluder;
import junit.framework.TestCase;
import java.lang.reflect.Field;
@ -28,49 +29,45 @@ import java.lang.reflect.Field;
* @author Joel Leitch
*/
public class ExposeAnnotationExclusionStrategyTest extends TestCase {
private ExclusionStrategy serializationStrategy = new GsonExclusionStrategy(
GsonExclusionStrategy.IGNORE_VERSIONS, 0, true, true, true, true, false);
private ExclusionStrategy deserializationStrategy = new GsonExclusionStrategy(
GsonExclusionStrategy.IGNORE_VERSIONS, 0, true, true, true, false, true);
private Excluder excluder = Excluder.DEFAULT.excludeFieldsWithoutExposeAnnotation();
public void testNeverSkipClasses() throws Exception {
assertFalse(deserializationStrategy.shouldSkipClass(MockObject.class));
assertFalse(serializationStrategy.shouldSkipClass(MockObject.class));
assertFalse(excluder.excludeClass(MockObject.class, true));
assertFalse(excluder.excludeClass(MockObject.class, false));
}
public void testSkipNonAnnotatedFields() throws Exception {
FieldAttributes f = createFieldAttributes("hiddenField");
assertTrue(deserializationStrategy.shouldSkipField(f));
assertTrue(serializationStrategy.shouldSkipField(f));
Field f = createFieldAttributes("hiddenField");
assertTrue(excluder.excludeField(f, true));
assertTrue(excluder.excludeField(f, false));
}
public void testSkipExplicitlySkippedFields() throws Exception {
FieldAttributes f = createFieldAttributes("explicitlyHiddenField");
assertTrue(deserializationStrategy.shouldSkipField(f));
assertTrue(serializationStrategy.shouldSkipField(f));
Field f = createFieldAttributes("explicitlyHiddenField");
assertTrue(excluder.excludeField(f, true));
assertTrue(excluder.excludeField(f, false));
}
public void testNeverSkipExposedAnnotatedFields() throws Exception {
FieldAttributes f = createFieldAttributes("exposedField");
assertFalse(deserializationStrategy.shouldSkipField(f));
assertFalse(serializationStrategy.shouldSkipField(f));
Field f = createFieldAttributes("exposedField");
assertFalse(excluder.excludeField(f, true));
assertFalse(excluder.excludeField(f, false));
}
public void testNeverSkipExplicitlyExposedAnnotatedFields() throws Exception {
FieldAttributes f = createFieldAttributes("explicitlyExposedField");
assertFalse(deserializationStrategy.shouldSkipField(f));
assertFalse(serializationStrategy.shouldSkipField(f));
Field f = createFieldAttributes("explicitlyExposedField");
assertFalse(excluder.excludeField(f, true));
assertFalse(excluder.excludeField(f, false));
}
public void testDifferentSerializeAndDeserializeField() throws Exception {
FieldAttributes f = createFieldAttributes("explicitlyDifferentModeField");
assertTrue(deserializationStrategy.shouldSkipField(f));
assertFalse(serializationStrategy.shouldSkipField(f));
Field f = createFieldAttributes("explicitlyDifferentModeField");
assertFalse(excluder.excludeField(f, true));
assertTrue(excluder.excludeField(f, false));
}
private static FieldAttributes createFieldAttributes(String fieldName) throws Exception {
Field f = MockObject.class.getField(fieldName);
return new FieldAttributes(f);
private static Field createFieldAttributes(String fieldName) throws Exception {
return MockObject.class.getField(fieldName);
}
@SuppressWarnings("unused")

View File

@ -1,48 +0,0 @@
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson;
import com.google.gson.common.TestTypes.ClassWithNoFields;
import java.lang.reflect.Modifier;
import java.util.Collections;
import junit.framework.TestCase;
/**
* Functional tests for Gson that depend on some internal package-protected elements of
* com.google.gson package and hence must be placed in the same package. We should make every
* attempt to migrate tests out of this class.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public class FunctionWithInternalDependenciesTest extends TestCase {
@SuppressWarnings("unchecked")
public void testAnonymousLocalClassesSerialization() throws Exception {
ExclusionStrategy exclusionStrategy = new GsonExclusionStrategy(
GsonExclusionStrategy.IGNORE_VERSIONS, Modifier.TRANSIENT | Modifier.STATIC,
true, false, true, false, false);
Gson gson = new Gson(exclusionStrategy, exclusionStrategy, FieldNamingPolicy.IDENTITY,
Gson.EMPTY_MAP, false, Gson.EMPTY_MAP, Gson.EMPTY_MAP, false,
Gson.DEFAULT_JSON_NON_EXECUTABLE,
true, false, false, LongSerializationPolicy.DEFAULT,
Collections.<TypeAdapter.Factory>emptyList());
assertEquals("{}", gson.toJson(new ClassWithNoFields() {
// empty anonymous class
}));
}
}

View File

@ -16,6 +16,7 @@
package com.google.gson;
import com.google.gson.internal.Excluder;
import java.lang.reflect.Field;
import junit.framework.TestCase;
@ -27,27 +28,26 @@ import junit.framework.TestCase;
public class InnerClassExclusionStrategyTest extends TestCase {
public InnerClass innerClass = new InnerClass();
public StaticNestedClass staticNestedClass = new StaticNestedClass();
private ExclusionStrategy strategy = new GsonExclusionStrategy(
GsonExclusionStrategy.IGNORE_VERSIONS, 0, true, false, false, false, false);
private Excluder excluder = Excluder.DEFAULT.disableInnerClassSerialization();
public void testExcludeInnerClassObject() throws Exception {
Class<?> clazz = innerClass.getClass();
assertTrue(strategy.shouldSkipClass(clazz));
assertTrue(excluder.excludeClass(clazz, true));
}
public void testExcludeInnerClassField() throws Exception {
Field f = getClass().getField("innerClass");
assertTrue(strategy.shouldSkipField(new FieldAttributes(f)));
assertTrue(excluder.excludeField(f, true));
}
public void testIncludeStaticNestedClassObject() throws Exception {
Class<?> clazz = staticNestedClass.getClass();
assertFalse(strategy.shouldSkipClass(clazz));
assertFalse(excluder.excludeClass(clazz, true));
}
public void testIncludeStaticNestedClassField() throws Exception {
Field f = getClass().getField("staticNestedClass");
assertFalse(strategy.shouldSkipField(new FieldAttributes(f)));
assertFalse(excluder.excludeField(f, true));
}
class InnerClass {

View File

@ -17,11 +17,11 @@
package com.google.gson;
import com.google.gson.annotations.Since;
import java.lang.reflect.Field;
import com.google.gson.internal.Excluder;
import junit.framework.TestCase;
/**
* Unit tests for the {@link GsonExclusionStrategy} class.
* Unit tests for the {@link Excluder} class.
*
* @author Joel Leitch
*/
@ -29,36 +29,21 @@ public class VersionExclusionStrategyTest extends TestCase {
private static final double VERSION = 5.0D;
public void testClassAndFieldAreAtSameVersion() throws Exception {
Class<MockObject> clazz = MockObject.class;
Field f = clazz.getField("someField");
GsonExclusionStrategy strategy = new GsonExclusionStrategy(VERSION, 0,
true, true, true, false, false);
assertFalse(strategy.shouldSkipClass(clazz));
FieldAttributes fieldAttributes = new FieldAttributes(f);
assertFalse(strategy.shouldSkipField(fieldAttributes));
Excluder excluder = Excluder.DEFAULT.withVersion(VERSION);
assertFalse(excluder.excludeClass(MockObject.class, true));
assertFalse(excluder.excludeField(MockObject.class.getField("someField"), true));
}
public void testClassAndFieldAreBehindInVersion() throws Exception {
Class<MockObject> clazz = MockObject.class;
Field f = clazz.getField("someField");
GsonExclusionStrategy strategy = new GsonExclusionStrategy(VERSION + 1, 0,
true, true, true, false, false);
assertFalse(strategy.shouldSkipClass(clazz));
FieldAttributes fieldAttributes = new FieldAttributes(f);
assertFalse(strategy.shouldSkipField(fieldAttributes));
Excluder excluder = Excluder.DEFAULT.withVersion(VERSION + 1);
assertFalse(excluder.excludeClass(MockObject.class, true));
assertFalse(excluder.excludeField(MockObject.class.getField("someField"), true));
}
public void testClassAndFieldAreAheadInVersion() throws Exception {
Class<MockObject> clazz = MockObject.class;
Field f = clazz.getField("someField");
GsonExclusionStrategy strategy = new GsonExclusionStrategy(VERSION - 1, 0,
true, true, true, false, false);
assertTrue(strategy.shouldSkipClass(clazz));
FieldAttributes fieldAttributes = new FieldAttributes(f);
assertTrue(strategy.shouldSkipField(fieldAttributes));
Excluder excluder = Excluder.DEFAULT.withVersion(VERSION - 1);
assertTrue(excluder.excludeClass(MockObject.class, true));
assertTrue(excluder.excludeField(MockObject.class.getField("someField"), true));
}
@Since(VERSION)