Begin to tighten the ExclusionStrategy code. This replaces named classes with anonymous classes wherever we have a single instance of a type.

This commit is contained in:
Jesse Wilson 2011-11-21 06:14:23 +00:00
parent e23973afec
commit 7def596775
10 changed files with 83 additions and 243 deletions

View File

@ -1,38 +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;
/**
* Strategy for excluding anonymous and local classes.
*
* @author Joel Leitch
*/
final class AnonymousAndLocalClassExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipField(FieldAttributes f) {
return isAnonymousOrLocal(f.getDeclaredClass());
}
public boolean shouldSkipClass(Class<?> clazz) {
return isAnonymousOrLocal(clazz);
}
private boolean isAnonymousOrLocal(Class<?> clazz) {
return !Enum.class.isAssignableFrom(clazz)
&& (clazz.isAnonymousClass() || clazz.isLocalClass());
}
}

View File

@ -1,38 +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;
import com.google.gson.annotations.Expose;
/**
* Excludes fields that do not have the {@link Expose} annotation
*
* @author Joel Leitch
*/
final class ExposeAnnotationDeserializationExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
public boolean shouldSkipField(FieldAttributes f) {
Expose annotation = f.getAnnotation(Expose.class);
if (annotation == null) {
return true;
}
return !annotation.deserialize();
}
}

View File

@ -1,38 +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;
import com.google.gson.annotations.Expose;
/**
* Excludes fields that do not have the {@link Expose} annotation
*
* @author Joel Leitch
*/
final class ExposeAnnotationSerializationExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
public boolean shouldSkipField(FieldAttributes f) {
Expose annotation = f.getAnnotation(Expose.class);
if (annotation == null) {
return true;
}
return !annotation.serialize();
}
}

View File

@ -45,7 +45,6 @@ import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.io.StringWriter; import java.io.StringWriter;
import java.io.Writer; import java.io.Writer;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
@ -104,14 +103,6 @@ public final class Gson {
static final boolean DEFAULT_JSON_NON_EXECUTABLE = false; static final boolean DEFAULT_JSON_NON_EXECUTABLE = false;
// Default instances of plug-ins
static final AnonymousAndLocalClassExclusionStrategy DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY =
new AnonymousAndLocalClassExclusionStrategy();
static final SyntheticFieldExclusionStrategy DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY =
new SyntheticFieldExclusionStrategy(true);
static final ModifierBasedExclusionStrategy DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY =
new ModifierBasedExclusionStrategy(Modifier.TRANSIENT, Modifier.STATIC);
private static final ExclusionStrategy DEFAULT_EXCLUSION_STRATEGY = createExclusionStrategy(); private static final ExclusionStrategy DEFAULT_EXCLUSION_STRATEGY = createExclusionStrategy();
private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n"; private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n";
@ -352,9 +343,9 @@ public final class Gson {
private static ExclusionStrategy createExclusionStrategy() { private static ExclusionStrategy createExclusionStrategy() {
List<ExclusionStrategy> strategies = new LinkedList<ExclusionStrategy>(); List<ExclusionStrategy> strategies = new LinkedList<ExclusionStrategy>();
strategies.add(DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY); strategies.add(GsonBuilder.EXCLUDE_ANONYMOUS_AND_LOCAL);
strategies.add(DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY); strategies.add(GsonBuilder.EXCLUDE_SYNTHETIC_FIELDS);
strategies.add(DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY); strategies.add(GsonBuilder.EXCLUDE_TRANSIENT_AND_STATIC);
return new DisjunctionExclusionStrategy(strategies); return new DisjunctionExclusionStrategy(strategies);
} }

View File

@ -17,11 +17,13 @@
package com.google.gson; package com.google.gson;
import com.google.gson.DefaultTypeAdapters.DefaultDateTypeAdapter; import com.google.gson.DefaultTypeAdapters.DefaultDateTypeAdapter;
import com.google.gson.annotations.Expose;
import com.google.gson.internal.$Gson$Preconditions; import com.google.gson.internal.$Gson$Preconditions;
import com.google.gson.internal.Primitives; import com.google.gson.internal.Primitives;
import com.google.gson.internal.TypeMap; import com.google.gson.internal.TypeMap;
import com.google.gson.internal.bind.TypeAdapters; import com.google.gson.internal.bind.TypeAdapters;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.text.DateFormat; import java.text.DateFormat;
@ -67,14 +69,68 @@ import java.util.Set;
* @author Joel Leitch * @author Joel Leitch
*/ */
public final class GsonBuilder { public final class GsonBuilder {
private static final InnerClassExclusionStrategy innerClassExclusionStrategy = /** Strategy for excluding inner classes. */
new InnerClassExclusionStrategy(); static final ExclusionStrategy EXCLUDE_INNER_CLASSES = new ExclusionStrategy() {
private static final ExposeAnnotationDeserializationExclusionStrategy public boolean shouldSkipField(FieldAttributes f) {
exposeAnnotationDeserializationExclusionStrategy = return isInnerClass(f.getDeclaredClass());
new ExposeAnnotationDeserializationExclusionStrategy(); }
private static final ExposeAnnotationSerializationExclusionStrategy public boolean shouldSkipClass(Class<?> clazz) {
exposeAnnotationSerializationExclusionStrategy = return isInnerClass(clazz);
new ExposeAnnotationSerializationExclusionStrategy(); }
private boolean isInnerClass(Class<?> clazz) {
return clazz.isMemberClass() && !isStatic(clazz);
}
private boolean isStatic(Class<?> clazz) {
return (clazz.getModifiers() & Modifier.STATIC) != 0;
}
};
/** Excludes fields that do not have the {@link Expose} annotation */
static final ExclusionStrategy REQUIRE_EXPOSE_DESERIALIZE = new ExclusionStrategy() {
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
public boolean shouldSkipField(FieldAttributes f) {
Expose annotation = f.getAnnotation(Expose.class);
return annotation == null || !annotation.deserialize();
}
};
/** Excludes fields that do not have the {@link Expose} annotation */
static final ExclusionStrategy REQUIRE_EXPOSE_SERIALIZE = new ExclusionStrategy() {
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
public boolean shouldSkipField(FieldAttributes f) {
Expose annotation = f.getAnnotation(Expose.class);
return annotation == null || !annotation.serialize();
}
};
static final ExclusionStrategy EXCLUDE_ANONYMOUS_AND_LOCAL = new ExclusionStrategy() {
public boolean shouldSkipField(FieldAttributes f) {
return isAnonymousOrLocal(f.getDeclaredClass());
}
public boolean shouldSkipClass(Class<?> clazz) {
return isAnonymousOrLocal(clazz);
}
private boolean isAnonymousOrLocal(Class<?> clazz) {
return !Enum.class.isAssignableFrom(clazz)
&& (clazz.isAnonymousClass() || clazz.isLocalClass());
}
};
static final ExclusionStrategy EXCLUDE_SYNTHETIC_FIELDS = new ExclusionStrategy() {
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
public boolean shouldSkipField(FieldAttributes f) {
return f.isSynthetic();
}
};
static final ModifierBasedExclusionStrategy EXCLUDE_TRANSIENT_AND_STATIC
= new ModifierBasedExclusionStrategy(Modifier.TRANSIENT, Modifier.STATIC);
private final Set<ExclusionStrategy> serializeExclusionStrategies = private final Set<ExclusionStrategy> serializeExclusionStrategies =
new HashSet<ExclusionStrategy>(); new HashSet<ExclusionStrategy>();
@ -110,17 +166,17 @@ public final class GsonBuilder {
*/ */
public GsonBuilder() { public GsonBuilder() {
// add default exclusion strategies // add default exclusion strategies
deserializeExclusionStrategies.add(Gson.DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY); deserializeExclusionStrategies.add(EXCLUDE_ANONYMOUS_AND_LOCAL);
deserializeExclusionStrategies.add(Gson.DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY); deserializeExclusionStrategies.add(EXCLUDE_SYNTHETIC_FIELDS);
serializeExclusionStrategies.add(Gson.DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY); serializeExclusionStrategies.add(EXCLUDE_ANONYMOUS_AND_LOCAL);
serializeExclusionStrategies.add(Gson.DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY); serializeExclusionStrategies.add(EXCLUDE_SYNTHETIC_FIELDS);
// setup default values // setup default values
ignoreVersionsAfter = VersionExclusionStrategy.IGNORE_VERSIONS; ignoreVersionsAfter = VersionExclusionStrategy.IGNORE_VERSIONS;
serializeInnerClasses = true; serializeInnerClasses = true;
prettyPrinting = false; prettyPrinting = false;
escapeHtmlChars = true; escapeHtmlChars = true;
modifierBasedExclusionStrategy = Gson.DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY; modifierBasedExclusionStrategy = EXCLUDE_TRANSIENT_AND_STATIC;
excludeFieldsWithoutExposeAnnotation = false; excludeFieldsWithoutExposeAnnotation = false;
longSerializationPolicy = LongSerializationPolicy.DEFAULT; longSerializationPolicy = LongSerializationPolicy.DEFAULT;
fieldNamingPolicy = FieldNamingPolicy.IDENTITY; fieldNamingPolicy = FieldNamingPolicy.IDENTITY;
@ -636,8 +692,8 @@ public final class GsonBuilder {
serializationStrategies.add(modifierBasedExclusionStrategy); serializationStrategies.add(modifierBasedExclusionStrategy);
if (!serializeInnerClasses) { if (!serializeInnerClasses) {
deserializationStrategies.add(innerClassExclusionStrategy); deserializationStrategies.add(EXCLUDE_INNER_CLASSES);
serializationStrategies.add(innerClassExclusionStrategy); serializationStrategies.add(EXCLUDE_INNER_CLASSES);
} }
if (ignoreVersionsAfter != VersionExclusionStrategy.IGNORE_VERSIONS) { if (ignoreVersionsAfter != VersionExclusionStrategy.IGNORE_VERSIONS) {
VersionExclusionStrategy versionExclusionStrategy = VersionExclusionStrategy versionExclusionStrategy =
@ -646,8 +702,8 @@ public final class GsonBuilder {
serializationStrategies.add(versionExclusionStrategy); serializationStrategies.add(versionExclusionStrategy);
} }
if (excludeFieldsWithoutExposeAnnotation) { if (excludeFieldsWithoutExposeAnnotation) {
deserializationStrategies.add(exposeAnnotationDeserializationExclusionStrategy); deserializationStrategies.add(REQUIRE_EXPOSE_DESERIALIZE);
serializationStrategies.add(exposeAnnotationSerializationExclusionStrategy); serializationStrategies.add(REQUIRE_EXPOSE_SERIALIZE);
} }
addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, serializers, deserializers); addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, serializers, deserializers);

View File

@ -1,43 +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 java.lang.reflect.Modifier;
/**
* Strategy for excluding inner classes.
*
* @author Joel Leitch
*/
final class InnerClassExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipField(FieldAttributes f) {
return isInnerClass(f.getDeclaredClass());
}
public boolean shouldSkipClass(Class<?> clazz) {
return isInnerClass(clazz);
}
private boolean isInnerClass(Class<?> clazz) {
return clazz.isMemberClass() && !isStatic(clazz);
}
private boolean isStatic(Class<?> clazz) {
return (clazz.getModifiers() & Modifier.STATIC) != 0;
}
}

View File

@ -1,43 +0,0 @@
/*
* Copyright (C) 2009 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;
/**
* A data object that stores attributes of a field.
*
* <p>This class is immutable; therefore, it can be safely shared across threads.
*
* @author Inderjeet Singh
* @author Joel Leitch
*
* @since 1.4
*/
final class SyntheticFieldExclusionStrategy implements ExclusionStrategy {
private final boolean skipSyntheticFields;
SyntheticFieldExclusionStrategy(boolean skipSyntheticFields) {
this.skipSyntheticFields = skipSyntheticFields;
}
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
public boolean shouldSkipField(FieldAttributes f) {
return skipSyntheticFields && f.isSynthetic();
}
}

View File

@ -23,20 +23,13 @@ import junit.framework.TestCase;
import java.lang.reflect.Field; import java.lang.reflect.Field;
/** /**
* Unit tests for the {@link ExposeAnnotationSerializationExclusionStrategy} class. * Unit tests for GsonBuilder.REQUIRE_EXPOSE_DESERIALIZE.
* *
* @author Joel Leitch * @author Joel Leitch
*/ */
public class ExposeAnnotationExclusionStrategyTest extends TestCase { public class ExposeAnnotationExclusionStrategyTest extends TestCase {
private ExposeAnnotationDeserializationExclusionStrategy deserializationStrategy; private ExclusionStrategy deserializationStrategy = GsonBuilder.REQUIRE_EXPOSE_DESERIALIZE;
private ExposeAnnotationSerializationExclusionStrategy serializationStrategy; private ExclusionStrategy serializationStrategy = GsonBuilder.REQUIRE_EXPOSE_SERIALIZE;
@Override
protected void setUp() throws Exception {
super.setUp();
deserializationStrategy = new ExposeAnnotationDeserializationExclusionStrategy();
serializationStrategy = new ExposeAnnotationSerializationExclusionStrategy();
}
public void testNeverSkipClasses() throws Exception { public void testNeverSkipClasses() throws Exception {
assertFalse(deserializationStrategy.shouldSkipClass(MockObject.class)); assertFalse(deserializationStrategy.shouldSkipClass(MockObject.class));

View File

@ -35,7 +35,7 @@ public class FunctionWithInternalDependenciesTest extends TestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void testAnonymousLocalClassesSerialization() throws Exception { public void testAnonymousLocalClassesSerialization() throws Exception {
LinkedList<ExclusionStrategy> strategies = new LinkedList<ExclusionStrategy>(); LinkedList<ExclusionStrategy> strategies = new LinkedList<ExclusionStrategy>();
strategies.add(new SyntheticFieldExclusionStrategy(true)); strategies.add(GsonBuilder.EXCLUDE_SYNTHETIC_FIELDS);
strategies.add(new ModifierBasedExclusionStrategy(Modifier.TRANSIENT, Modifier.STATIC)); strategies.add(new ModifierBasedExclusionStrategy(Modifier.TRANSIENT, Modifier.STATIC));
ExclusionStrategy exclusionStrategy = new DisjunctionExclusionStrategy(strategies); ExclusionStrategy exclusionStrategy = new DisjunctionExclusionStrategy(strategies);
Gson gson = new Gson(exclusionStrategy, exclusionStrategy, FieldNamingPolicy.IDENTITY, Gson gson = new Gson(exclusionStrategy, exclusionStrategy, FieldNamingPolicy.IDENTITY,

View File

@ -21,7 +21,7 @@ import junit.framework.TestCase;
import java.lang.reflect.Field; import java.lang.reflect.Field;
/** /**
* Unit test for the {@link InnerClassExclusionStrategy} class. * Unit test for GsonBuilder.EXCLUDE_INNER_CLASSES.
* *
* @author Joel Leitch * @author Joel Leitch
*/ */
@ -29,14 +29,14 @@ public class InnerClassExclusionStrategyTest extends TestCase {
public InnerClass innerClass; public InnerClass innerClass;
public StaticNestedClass staticNestedClass; public StaticNestedClass staticNestedClass;
private InnerClassExclusionStrategy strategy; private ExclusionStrategy strategy;
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
innerClass = new InnerClass(); innerClass = new InnerClass();
staticNestedClass = new StaticNestedClass(); staticNestedClass = new StaticNestedClass();
strategy = new InnerClassExclusionStrategy(); strategy = GsonBuilder.EXCLUDE_INNER_CLASSES;
} }
public void testExcludeInnerClassObject() throws Exception { public void testExcludeInnerClassObject() throws Exception {