/* * 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.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import com.smartandroid.sa.json.reflect.TypeToken; import com.smartandroid.sa.json.stream.JsonReader; import com.smartandroid.sa.json.stream.JsonWriter; /** * A basic binding between JSON and Java objects. */ public final class MiniGson { /** * This thread local guards against reentrant calls to getAdapter(). In * certain object graphs, creating an adapter for a type may recursively * require an adapter for the same type! Without intervention, the recursive * lookup would stack overflow. We cheat by returning a proxy type adapter. * The proxy is wired up once the initial adapter has been created. */ private final ThreadLocal<Map<TypeToken<?>, FutureTypeAdapter<?>>> calls = new ThreadLocal<Map<TypeToken<?>, FutureTypeAdapter<?>>>() { @Override protected Map<TypeToken<?>, FutureTypeAdapter<?>> initialValue() { return new HashMap<TypeToken<?>, FutureTypeAdapter<?>>(); } }; private final List<TypeAdapter.Factory> factories; private MiniGson(Builder builder) { List<TypeAdapter.Factory> factories = new ArrayList<TypeAdapter.Factory>(); if (builder.addDefaultFactories) { factories.add(TypeAdapters.BOOLEAN_FACTORY); factories.add(TypeAdapters.INTEGER_FACTORY); factories.add(TypeAdapters.DOUBLE_FACTORY); factories.add(TypeAdapters.FLOAT_FACTORY); factories.add(TypeAdapters.LONG_FACTORY); factories.add(TypeAdapters.STRING_FACTORY); } factories.addAll(builder.factories); if (builder.addDefaultFactories) { factories.add(CollectionTypeAdapter.FACTORY); factories.add(StringToValueMapTypeAdapter.FACTORY); factories.add(ArrayTypeAdapter.FACTORY); factories.add(ReflectiveTypeAdapter.FACTORY); } this.factories = Collections.unmodifiableList(factories); } /** * Returns the type adapter for {@code} type. * * @throws IllegalArgumentException * if this GSON cannot serialize and deserialize {@code type}. */ public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) { // TODO: create a cache! Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get(); @SuppressWarnings("unchecked") // the key and value type parameters always agree FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls .get(type); if (ongoingCall != null) { return ongoingCall; } FutureTypeAdapter<T> call = new FutureTypeAdapter<T>(); threadCalls.put(type, call); try { for (TypeAdapter.Factory factory : factories) { TypeAdapter<T> candidate = factory.create(this, type); if (candidate != null) { call.setDelegate(candidate); return candidate; } } throw new IllegalArgumentException( "This MiniGSON cannot serialize " + type); } finally { threadCalls.remove(type); } } static class FutureTypeAdapter<T> extends TypeAdapter<T> { private TypeAdapter<T> delegate; public void setDelegate(TypeAdapter<T> typeAdapter) { if (delegate != null) { throw new AssertionError(); } delegate = typeAdapter; } @Override public T read(JsonReader reader) throws IOException { if (delegate == null) { throw new IllegalStateException(); } return delegate.read(reader); } @Override public void write(JsonWriter writer, T value) throws IOException { if (delegate == null) { throw new IllegalStateException(); } delegate.write(writer, value); } } /** * Returns the type adapter for {@code} type. * * @throws IllegalArgumentException * if this GSON cannot serialize and deserialize {@code type}. */ public <T> TypeAdapter<T> getAdapter(Class<T> type) { return getAdapter(TypeToken.get(type)); } /** * Returns the type adapters of this context in order of precedence. */ public List<TypeAdapter.Factory> getFactories() { return factories; } public static final class Builder { private final List<TypeAdapter.Factory> factories = new ArrayList<TypeAdapter.Factory>(); boolean addDefaultFactories = true; public Builder factory(TypeAdapter.Factory factory) { factories.add(factory); return this; } public Builder withoutDefaultFactories() { this.addDefaultFactories = false; return this; } public <T> Builder typeAdapter(final Class<T> type, final TypeAdapter<T> typeAdapter) { factories.add(TypeAdapters.newFactory(type, typeAdapter)); return this; } public <T> Builder typeAdapter(TypeToken<T> type, TypeAdapter<T> typeAdapter) { factories.add(TypeAdapters.newFactory(type, typeAdapter)); return this; } public <T> Builder typeHierarchyAdapter(TypeToken<T> type, TypeAdapter<T> typeAdapter) { factories.add(TypeAdapters.newTypeHierarchyFactory(type, typeAdapter)); return this; } public MiniGson build() { return new MiniGson(this); } } }