• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
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 /**
38  * This class selects which fields and types to omit. It is configurable,
39  * supporting version attributes {@link Since} and {@link Until}, modifiers,
40  * synthetic fields, anonymous and local classes, inner classes, and fields with
41  * the {@link Expose} annotation.
42  *
43  * <p>This class is a type adapter factory; types that are excluded will be
44  * adapted to null. It may delegate to another type adapter if only one
45  * direction is excluded.
46  *
47  * @author Joel Leitch
48  * @author Jesse Wilson
49  */
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 
clone()61   @Override protected Excluder clone() {
62     try {
63       return (Excluder) super.clone();
64     } catch (CloneNotSupportedException e) {
65       throw new AssertionError(e);
66     }
67   }
68 
withVersion(double ignoreVersionsAfter)69   public Excluder withVersion(double ignoreVersionsAfter) {
70     Excluder result = clone();
71     result.version = ignoreVersionsAfter;
72     return result;
73   }
74 
withModifiers(int... modifiers)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 
disableInnerClassSerialization()84   public Excluder disableInnerClassSerialization() {
85     Excluder result = clone();
86     result.serializeInnerClasses = false;
87     return result;
88   }
89 
excludeFieldsWithoutExposeAnnotation()90   public Excluder excludeFieldsWithoutExposeAnnotation() {
91     Excluder result = clone();
92     result.requireExpose = true;
93     return result;
94   }
95 
withExclusionStrategy(ExclusionStrategy exclusionStrategy, boolean serialization, boolean deserialization)96   public Excluder withExclusionStrategy(ExclusionStrategy exclusionStrategy,
97       boolean serialization, boolean deserialization) {
98     Excluder result = clone();
99     if (serialization) {
100       result.serializationStrategies = new ArrayList<>(serializationStrategies);
101       result.serializationStrategies.add(exclusionStrategy);
102     }
103     if (deserialization) {
104       result.deserializationStrategies = new ArrayList<>(deserializationStrategies);
105       result.deserializationStrategies.add(exclusionStrategy);
106     }
107     return result;
108   }
109 
create(final Gson gson, final TypeToken<T> type)110   @Override public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
111     Class<?> rawType = type.getRawType();
112     boolean excludeClass = excludeClassChecks(rawType);
113 
114     final boolean skipSerialize = excludeClass || excludeClassInStrategy(rawType, true);
115     final boolean skipDeserialize = excludeClass ||  excludeClassInStrategy(rawType, false);
116 
117     if (!skipSerialize && !skipDeserialize) {
118       return null;
119     }
120 
121     return new TypeAdapter<T>() {
122       /** The delegate is lazily created because it may not be needed, and creating it may fail. */
123       private TypeAdapter<T> delegate;
124 
125       @Override public T read(JsonReader in) throws IOException {
126         if (skipDeserialize) {
127           in.skipValue();
128           return null;
129         }
130         return delegate().read(in);
131       }
132 
133       @Override public void write(JsonWriter out, T value) throws IOException {
134         if (skipSerialize) {
135           out.nullValue();
136           return;
137         }
138         delegate().write(out, value);
139       }
140 
141       private TypeAdapter<T> delegate() {
142         TypeAdapter<T> d = delegate;
143         return d != null
144             ? d
145             : (delegate = gson.getDelegateAdapter(Excluder.this, type));
146       }
147     };
148   }
149 
excludeField(Field field, boolean serialize)150   public boolean excludeField(Field field, boolean serialize) {
151     if ((modifiers & field.getModifiers()) != 0) {
152       return true;
153     }
154 
155     if (version != Excluder.IGNORE_VERSIONS
156         && !isValidVersion(field.getAnnotation(Since.class), field.getAnnotation(Until.class))) {
157       return true;
158     }
159 
160     if (field.isSynthetic()) {
161       return true;
162     }
163 
164     if (requireExpose) {
165       Expose annotation = field.getAnnotation(Expose.class);
166       if (annotation == null || (serialize ? !annotation.serialize() : !annotation.deserialize())) {
167         return true;
168       }
169     }
170 
171     if (!serializeInnerClasses && isInnerClass(field.getType())) {
172       return true;
173     }
174 
175     if (isAnonymousOrNonStaticLocal(field.getType())) {
176       return true;
177     }
178 
179     List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies;
180     if (!list.isEmpty()) {
181       FieldAttributes fieldAttributes = new FieldAttributes(field);
182       for (ExclusionStrategy exclusionStrategy : list) {
183         if (exclusionStrategy.shouldSkipField(fieldAttributes)) {
184           return true;
185         }
186       }
187     }
188 
189     return false;
190   }
191 
excludeClassChecks(Class<?> clazz)192   private boolean excludeClassChecks(Class<?> clazz) {
193       if (version != Excluder.IGNORE_VERSIONS && !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class))) {
194           return true;
195       }
196 
197       if (!serializeInnerClasses && isInnerClass(clazz)) {
198           return true;
199       }
200 
201       return isAnonymousOrNonStaticLocal(clazz);
202   }
203 
excludeClass(Class<?> clazz, boolean serialize)204   public boolean excludeClass(Class<?> clazz, boolean serialize) {
205       return excludeClassChecks(clazz) ||
206               excludeClassInStrategy(clazz, serialize);
207   }
208 
excludeClassInStrategy(Class<?> clazz, boolean serialize)209   private boolean excludeClassInStrategy(Class<?> clazz, boolean serialize) {
210       List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies;
211       for (ExclusionStrategy exclusionStrategy : list) {
212           if (exclusionStrategy.shouldSkipClass(clazz)) {
213               return true;
214           }
215       }
216       return false;
217   }
218 
isAnonymousOrNonStaticLocal(Class<?> clazz)219   private boolean isAnonymousOrNonStaticLocal(Class<?> clazz) {
220     return !Enum.class.isAssignableFrom(clazz) && !isStatic(clazz)
221         && (clazz.isAnonymousClass() || clazz.isLocalClass());
222   }
223 
isInnerClass(Class<?> clazz)224   private boolean isInnerClass(Class<?> clazz) {
225     return clazz.isMemberClass() && !isStatic(clazz);
226   }
227 
isStatic(Class<?> clazz)228   private boolean isStatic(Class<?> clazz) {
229     return (clazz.getModifiers() & Modifier.STATIC) != 0;
230   }
231 
isValidVersion(Since since, Until until)232   private boolean isValidVersion(Since since, Until until) {
233     return isValidSince(since) && isValidUntil(until);
234   }
235 
isValidSince(Since annotation)236   private boolean isValidSince(Since annotation) {
237     if (annotation != null) {
238       double annotationVersion = annotation.value();
239       return version >= annotationVersion;
240     }
241     return true;
242   }
243 
isValidUntil(Until annotation)244   private boolean isValidUntil(Until annotation) {
245     if (annotation != null) {
246       double annotationVersion = annotation.value();
247       return version < annotationVersion;
248     }
249     return true;
250   }
251 }
252