1 /*
2  * Copyright (C) 2011 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.bind;
18
19 import com.google.gson.Gson;
20 import com.google.gson.TypeAdapter;
21 import com.google.gson.TypeAdapterFactory;
22 import com.google.gson.internal.$Gson$Types;
23 import com.google.gson.internal.ConstructorConstructor;
24 import com.google.gson.internal.ObjectConstructor;
25 import com.google.gson.reflect.TypeToken;
26 import com.google.gson.stream.JsonReader;
27 import com.google.gson.stream.JsonToken;
28 import com.google.gson.stream.JsonWriter;
29 import java.io.IOException;
30 import java.lang.reflect.Type;
31 import java.util.Collection;
32
33 /**
34  * Adapt a homogeneous collection of objects.
35  */

36 public final class CollectionTypeAdapterFactory implements TypeAdapterFactory {
37   private final ConstructorConstructor constructorConstructor;
38
39   public CollectionTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
40     this.constructorConstructor = constructorConstructor;
41   }
42
43   @Override
44   public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
45     Type type = typeToken.getType();
46
47     Class<? super T> rawType = typeToken.getRawType();
48     if (!Collection.class.isAssignableFrom(rawType)) {
49       return null;
50     }
51
52     Type elementType = $Gson$Types.getCollectionElementType(type, rawType);
53     TypeAdapter<?> elementTypeAdapter = gson.getAdapter(TypeToken.get(elementType));
54     ObjectConstructor<T> constructor = constructorConstructor.get(typeToken);
55
56     @SuppressWarnings({"unchecked""rawtypes"}) // create() doesn't define a type parameter
57     TypeAdapter<T> result = new Adapter(gson, elementType, elementTypeAdapter, constructor);
58     return result;
59   }
60
61   private static final class Adapter<E> extends TypeAdapter<Collection<E>> {
62     private final TypeAdapter<E> elementTypeAdapter;
63     private final ObjectConstructor<? extends Collection<E>> constructor;
64
65     public Adapter(Gson context, Type elementType,
66         TypeAdapter<E> elementTypeAdapter,
67         ObjectConstructor<? extends Collection<E>> constructor) {
68       this.elementTypeAdapter =
69           new TypeAdapterRuntimeTypeWrapper<E>(context, elementTypeAdapter, elementType);
70       this.constructor = constructor;
71     }
72
73     @Override public Collection<E> read(JsonReader in) throws IOException {
74       if (in.peek() == JsonToken.NULL) {
75         in.nextNull();
76         return null;
77       }
78
79       Collection<E> collection = constructor.construct();
80       in.beginArray();
81       while (in.hasNext()) {
82         E instance = elementTypeAdapter.read(in);
83         collection.add(instance);
84       }
85       in.endArray();
86       return collection;
87     }
88
89     @Override public void write(JsonWriter out, Collection<E> collection) throws IOException {
90       if (collection == null) {
91         out.nullValue();
92         return;
93       }
94
95       out.beginArray();
96       for (E element : collection) {
97         elementTypeAdapter.write(out, element);
98       }
99       out.endArray();
100     }
101   }
102 }
103