/* * Copyright 2015-2019 the original author or authors. * * 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 org.lastaflute.core.json.adapter; import java.io.IOException; import java.lang.reflect.Type; import java.util.Collection; import java.util.Collections; import java.util.Map; import org.lastaflute.core.json.JsonMappingOption; import com.google.gson.Gson; import com.google.gson.InstanceCreator; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; import com.google.gson.internal.ConstructorConstructor; import com.google.gson.internal.bind.CollectionTypeAdapterFactory; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; /** * @author jflute * @since 0.7.3 (2015/12/29 Tuesday) */ public interface CollectionGsonAdaptable { // to show property path in exception message // =================================================================================== // Type Adapter // ============ class WrappingCollectionTypeAdapterFactory implements TypeAdapterFactory { protected final CollectionTypeAdapterFactory embeddedFactory; protected final JsonMappingOption gsonOption; public WrappingCollectionTypeAdapterFactory(JsonMappingOption gsonOption) { this.embeddedFactory = createEmbeddedFactory(); this.gsonOption = gsonOption; } protected CollectionTypeAdapterFactory createEmbeddedFactory() { final Map<Type, InstanceCreator<?>> instanceCreators = prepareInstanceCreators(); final ConstructorConstructor constructor = createConstructorConstructor(instanceCreators); return newCollectionTypeAdapterFactory(constructor); } protected Map<Type, InstanceCreator<?>> prepareInstanceCreators() { return Collections.emptyMap(); } protected ConstructorConstructor createConstructorConstructor(Map<Type, InstanceCreator<?>> instanceCreators) { return new ConstructorConstructor(instanceCreators); } protected CollectionTypeAdapterFactory newCollectionTypeAdapterFactory(ConstructorConstructor constructor) { return new CollectionTypeAdapterFactory(constructor); } @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { final TypeAdapter<T> embedded = embeddedFactory.create(gson, typeToken); // null allowed when other types return embedded != null ? createWrappingTypeAdapterCollection(embedded) : null; } @SuppressWarnings("unchecked") protected <T> TypeAdapter<T> createWrappingTypeAdapterCollection(TypeAdapter<T> embedded) { return (TypeAdapter<T>) newWrappingTypeAdapterCollection(embedded, gsonOption); } protected <T> WrappingTypeAdapterCollection newWrappingTypeAdapterCollection(TypeAdapter<T> embedded, JsonMappingOption option) { return new WrappingTypeAdapterCollection(embedded, option); } } class WrappingTypeAdapterCollection extends TypeAdapter<Collection<?>> { protected final TypeAdapter<Collection<?>> embedded; protected final JsonMappingOption gsonOption; @SuppressWarnings("unchecked") public WrappingTypeAdapterCollection(TypeAdapter<?> embedded, JsonMappingOption gsonOption) { this.embedded = (TypeAdapter<Collection<?>>) embedded; this.gsonOption = gsonOption; } @Override public Collection<?> read(JsonReader in) throws IOException { if (gsonOption.isListNullToEmptyReading()) { if (in.peek() == JsonToken.NULL) { in.nextNull(); return Collections.emptyList(); } } return embedded.read(in); } @Override public void write(JsonWriter out, Collection<?> collection) throws IOException { if (gsonOption.isListNullToEmptyWriting()) { if (collection == null) { out.beginArray(); out.endArray(); return; // [] } } embedded.write(out, collection); } } // =================================================================================== // Creator // ======= default TypeAdapterFactory createCollectionTypeAdapterFactory() { return newWrappingCollectionTypeAdapterFactory(getGsonOption()); } default WrappingCollectionTypeAdapterFactory newWrappingCollectionTypeAdapterFactory(JsonMappingOption option) { return new WrappingCollectionTypeAdapterFactory(option); } // =================================================================================== // Accessor // ======== JsonMappingOption getGsonOption(); }