/*
* 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.smartandroid.sa.json.internal.bind;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import com.smartandroid.sa.json.internal.$Gson$Types;
import com.smartandroid.sa.json.reflect.TypeToken;
import com.smartandroid.sa.json.stream.JsonReader;
import com.smartandroid.sa.json.stream.JsonToken;
import com.smartandroid.sa.json.stream.JsonWriter;
/**
* Adapt a homogeneous collection of objects.
*/
public final class CollectionTypeAdapter<E> extends TypeAdapter<Collection<E>> {
public static final Factory FACTORY = new Factory() {
public <T> TypeAdapter<T> create(MiniGson context,
TypeToken<T> typeToken) {
Type type = typeToken.getType();
Class<? super T> rawType = typeToken.getRawType();
if (!Collection.class.isAssignableFrom(rawType)) {
return null;
}
Type elementType = $Gson$Types.getCollectionElementType(type,
rawType);
TypeAdapter<?> elementTypeAdapter = context.getAdapter(TypeToken
.get(elementType));
Class<?> constructorType;
if (rawType == List.class || rawType == Collection.class) {
constructorType = ArrayList.class;
} else if (rawType == Set.class) {
constructorType = LinkedHashSet.class;
} else {
constructorType = rawType;
}
Constructor<?> constructor;
try {
constructor = constructorType.getConstructor();
} catch (NoSuchMethodException e) {
return null;
}
@SuppressWarnings("unchecked")
// create() doesn't define a type parameter
TypeAdapter<T> result = new CollectionTypeAdapter(context,
elementType, elementTypeAdapter, constructor);
return result;
}
};
private final TypeAdapter<E> elementTypeAdapter;
private final Constructor<? extends Collection<E>> constructor;
public CollectionTypeAdapter(MiniGson context, Type elementType,
TypeAdapter<E> elementTypeAdapter,
Constructor<? extends Collection<E>> constructor) {
this.elementTypeAdapter = new TypeAdapterRuntimeTypeWrapper<E>(context,
elementTypeAdapter, elementType);
this.constructor = constructor;
}
public Collection<E> read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull(); // TODO: does this belong here?
return null;
}
Collection<E> collection = Reflection.newInstance(constructor);
reader.beginArray();
while (reader.hasNext()) {
E instance = elementTypeAdapter.read(reader);
collection.add(instance);
}
reader.endArray();
return collection;
}
public void write(JsonWriter writer, Collection<E> collection)
throws IOException {
if (collection == null) {
writer.nullValue(); // TODO: better policy here?
return;
}
writer.beginArray();
for (E element : collection) {
elementTypeAdapter.write(writer, element);
}
writer.endArray();
}
}