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