1
16
17 package com.google.gson.internal;
18
19 import com.google.gson.ExclusionStrategy;
20 import com.google.gson.FieldAttributes;
21 import com.google.gson.Gson;
22 import com.google.gson.TypeAdapter;
23 import com.google.gson.TypeAdapterFactory;
24 import com.google.gson.annotations.Expose;
25 import com.google.gson.annotations.Since;
26 import com.google.gson.annotations.Until;
27 import com.google.gson.reflect.TypeToken;
28 import com.google.gson.stream.JsonReader;
29 import com.google.gson.stream.JsonWriter;
30 import java.io.IOException;
31 import java.lang.reflect.Field;
32 import java.lang.reflect.Modifier;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.List;
36
37
50 public final class Excluder implements TypeAdapterFactory, Cloneable {
51 private static final double IGNORE_VERSIONS = -1.0d;
52 public static final Excluder DEFAULT = new Excluder();
53
54 private double version = IGNORE_VERSIONS;
55 private int modifiers = Modifier.TRANSIENT | Modifier.STATIC;
56 private boolean serializeInnerClasses = true;
57 private boolean requireExpose;
58 private List<ExclusionStrategy> serializationStrategies = Collections.emptyList();
59 private List<ExclusionStrategy> deserializationStrategies = Collections.emptyList();
60
61 @Override protected Excluder clone() {
62 try {
63 return (Excluder) super.clone();
64 } catch (CloneNotSupportedException e) {
65 throw new AssertionError(e);
66 }
67 }
68
69 public Excluder withVersion(double ignoreVersionsAfter) {
70 Excluder result = clone();
71 result.version = ignoreVersionsAfter;
72 return result;
73 }
74
75 public Excluder withModifiers(int... modifiers) {
76 Excluder result = clone();
77 result.modifiers = 0;
78 for (int modifier : modifiers) {
79 result.modifiers |= modifier;
80 }
81 return result;
82 }
83
84 public Excluder disableInnerClassSerialization() {
85 Excluder result = clone();
86 result.serializeInnerClasses = false;
87 return result;
88 }
89
90 public Excluder excludeFieldsWithoutExposeAnnotation() {
91 Excluder result = clone();
92 result.requireExpose = true;
93 return result;
94 }
95
96 public Excluder withExclusionStrategy(ExclusionStrategy exclusionStrategy,
97 boolean serialization, boolean deserialization) {
98 Excluder result = clone();
99 if (serialization) {
100 result.serializationStrategies = new ArrayList<ExclusionStrategy>(serializationStrategies);
101 result.serializationStrategies.add(exclusionStrategy);
102 }
103 if (deserialization) {
104 result.deserializationStrategies
105 = new ArrayList<ExclusionStrategy>(deserializationStrategies);
106 result.deserializationStrategies.add(exclusionStrategy);
107 }
108 return result;
109 }
110
111 public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
112 Class<?> rawType = type.getRawType();
113 boolean excludeClass = excludeClassChecks(rawType);
114
115 final boolean skipSerialize = excludeClass || excludeClassInStrategy(rawType, true);
116 final boolean skipDeserialize = excludeClass || excludeClassInStrategy(rawType, false);
117
118 if (!skipSerialize && !skipDeserialize) {
119 return null;
120 }
121
122 return new TypeAdapter<T>() {
123
124 private TypeAdapter<T> delegate;
125
126 @Override public T read(JsonReader in) throws IOException {
127 if (skipDeserialize) {
128 in.skipValue();
129 return null;
130 }
131 return delegate().read(in);
132 }
133
134 @Override public void write(JsonWriter out, T value) throws IOException {
135 if (skipSerialize) {
136 out.nullValue();
137 return;
138 }
139 delegate().write(out, value);
140 }
141
142 private TypeAdapter<T> delegate() {
143 TypeAdapter<T> d = delegate;
144 return d != null
145 ? d
146 : (delegate = gson.getDelegateAdapter(Excluder.this, type));
147 }
148 };
149 }
150
151 public boolean excludeField(Field field, boolean serialize) {
152 if ((modifiers & field.getModifiers()) != 0) {
153 return true;
154 }
155
156 if (version != Excluder.IGNORE_VERSIONS
157 && !isValidVersion(field.getAnnotation(Since.class), field.getAnnotation(Until.class))) {
158 return true;
159 }
160
161 if (field.isSynthetic()) {
162 return true;
163 }
164
165 if (requireExpose) {
166 Expose annotation = field.getAnnotation(Expose.class);
167 if (annotation == null || (serialize ? !annotation.serialize() : !annotation.deserialize())) {
168 return true;
169 }
170 }
171
172 if (!serializeInnerClasses && isInnerClass(field.getType())) {
173 return true;
174 }
175
176 if (isAnonymousOrLocal(field.getType())) {
177 return true;
178 }
179
180 List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies;
181 if (!list.isEmpty()) {
182 FieldAttributes fieldAttributes = new FieldAttributes(field);
183 for (ExclusionStrategy exclusionStrategy : list) {
184 if (exclusionStrategy.shouldSkipField(fieldAttributes)) {
185 return true;
186 }
187 }
188 }
189
190 return false;
191 }
192
193 private boolean excludeClassChecks(Class<?> clazz) {
194 if (version != Excluder.IGNORE_VERSIONS && !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class))) {
195 return true;
196 }
197
198 if (!serializeInnerClasses && isInnerClass(clazz)) {
199 return true;
200 }
201
202 if (isAnonymousOrLocal(clazz)) {
203 return true;
204 }
205
206 return false;
207 }
208
209 public boolean excludeClass(Class<?> clazz, boolean serialize) {
210 return excludeClassChecks(clazz) ||
211 excludeClassInStrategy(clazz, serialize);
212 }
213
214 private boolean excludeClassInStrategy(Class<?> clazz, boolean serialize) {
215 List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies;
216 for (ExclusionStrategy exclusionStrategy : list) {
217 if (exclusionStrategy.shouldSkipClass(clazz)) {
218 return true;
219 }
220 }
221 return false;
222 }
223
224 private boolean isAnonymousOrLocal(Class<?> clazz) {
225 return !Enum.class.isAssignableFrom(clazz)
226 && (clazz.isAnonymousClass() || clazz.isLocalClass());
227 }
228
229 private boolean isInnerClass(Class<?> clazz) {
230 return clazz.isMemberClass() && !isStatic(clazz);
231 }
232
233 private boolean isStatic(Class<?> clazz) {
234 return (clazz.getModifiers() & Modifier.STATIC) != 0;
235 }
236
237 private boolean isValidVersion(Since since, Until until) {
238 return isValidSince(since) && isValidUntil(until);
239 }
240
241 private boolean isValidSince(Since annotation) {
242 if (annotation != null) {
243 double annotationVersion = annotation.value();
244 if (annotationVersion > version) {
245 return false;
246 }
247 }
248 return true;
249 }
250
251 private boolean isValidUntil(Until annotation) {
252 if (annotation != null) {
253 double annotationVersion = annotation.value();
254 if (annotationVersion <= version) {
255 return false;
256 }
257 }
258 return true;
259 }
260 }
261