1 /*
2  * Copyright (C) 2014 Square, 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 package com.squareup.moshi;
17
18 import java.io.IOException;
19 import java.lang.annotation.Annotation;
20 import java.lang.reflect.Type;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.LinkedHashSet;
24 import java.util.List;
25 import java.util.Set;
26 import javax.annotation.Nullable;
27
28 /** Converts collection types to JSON arrays containing their converted contents. */
29 abstract class CollectionJsonAdapter<C extends Collection<T>, T> extends JsonAdapter<C> {
30   public static final JsonAdapter.Factory FACTORY = new JsonAdapter.Factory() {
31     @Override public @Nullable JsonAdapter<?> create(
32         Type type, Set<? extends Annotation> annotations, Moshi moshi) {
33       Class<?> rawType = Types.getRawType(type);
34       if (!annotations.isEmpty()) return null;
35       if (rawType == List.class || rawType == Collection.class) {
36         return newArrayListAdapter(type, moshi).nullSafe();
37       } else if (rawType == Set.class) {
38         return newLinkedHashSetAdapter(type, moshi).nullSafe();
39       }
40       return null;
41     }
42   };
43
44   private final JsonAdapter<T> elementAdapter;
45
46   private CollectionJsonAdapter(JsonAdapter<T> elementAdapter) {
47     this.elementAdapter = elementAdapter;
48   }
49
50   static <T> JsonAdapter<Collection<T>> newArrayListAdapter(Type type, Moshi moshi) {
51     Type elementType = Types.collectionElementType(type, Collection.class);
52     JsonAdapter<T> elementAdapter = moshi.adapter(elementType);
53     return new CollectionJsonAdapter<Collection<T>, T>(elementAdapter) {
54       @Override Collection<T> newCollection() {
55         return new ArrayList<>();
56       }
57     };
58   }
59
60   static <T> JsonAdapter<Set<T>> newLinkedHashSetAdapter(Type type, Moshi moshi) {
61     Type elementType = Types.collectionElementType(type, Collection.class);
62     JsonAdapter<T> elementAdapter = moshi.adapter(elementType);
63     return new CollectionJsonAdapter<Set<T>, T>(elementAdapter) {
64       @Override Set<T> newCollection() {
65         return new LinkedHashSet<>();
66       }
67     };
68   }
69
70   abstract C newCollection();
71
72   @Override public C fromJson(JsonReader reader) throws IOException {
73     C result = newCollection();
74     reader.beginArray();
75     while (reader.hasNext()) {
76       result.add(elementAdapter.fromJson(reader));
77     }
78     reader.endArray();
79     return result;
80   }
81
82   @Override public void toJson(JsonWriter writer, C value) throws IOException {
83     writer.beginArray();
84     for (T element : value) {
85       elementAdapter.toJson(writer, element);
86     }
87     writer.endArray();
88   }
89
90   @Override public String toString() {
91     return elementAdapter + ".collection()";
92   }
93 }
94