]>
Commit | Line | Data |
---|---|---|
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; | |
18 | ||
19 | import com.google.gson.internal.$Gson$Preconditions; | |
20 | import com.google.gson.internal.Streams; | |
21 | import com.google.gson.reflect.TypeToken; | |
22 | import com.google.gson.stream.JsonReader; | |
23 | import com.google.gson.stream.JsonWriter; | |
24 | import java.io.IOException; | |
25 | ||
26 | /** | |
27 | * Adapts a Gson 1.x tree-style adapter as a streaming TypeAdapter. Since the | |
28 | * tree adapter may be serialization-only or deserialization-only, this class | |
29 | * has a facility to lookup a delegate type adapter on demand. | |
30 | */ | |
31 | final class TreeTypeAdapter<T> extends TypeAdapter<T> { | |
32 | private final JsonSerializer<T> serializer; | |
33 | private final JsonDeserializer<T> deserializer; | |
34 | private final Gson gson; | |
35 | private final TypeToken<T> typeToken; | |
36 | private final TypeAdapterFactory skipPast; | |
37 | ||
38 | /** The delegate is lazily created because it may not be needed, and creating it may fail. */ | |
39 | private TypeAdapter<T> delegate; | |
40 | ||
41 | private TreeTypeAdapter(JsonSerializer<T> serializer, JsonDeserializer<T> deserializer, | |
42 | Gson gson, TypeToken<T> typeToken, TypeAdapterFactory skipPast) { | |
43 | this.serializer = serializer; | |
44 | this.deserializer = deserializer; | |
45 | this.gson = gson; | |
46 | this.typeToken = typeToken; | |
47 | this.skipPast = skipPast; | |
48 | } | |
49 | ||
50 | @Override public T read(JsonReader in) throws IOException { | |
51 | if (deserializer == null) { | |
52 | return delegate().read(in); | |
53 | } | |
54 | JsonElement value = Streams.parse(in); | |
55 | if (value.isJsonNull()) { | |
56 | return null; | |
57 | } | |
58 | return deserializer.deserialize(value, typeToken.getType(), gson.deserializationContext); | |
59 | } | |
60 | ||
61 | @Override public void write(JsonWriter out, T value) throws IOException { | |
62 | if (serializer == null) { | |
63 | delegate().write(out, value); | |
64 | return; | |
65 | } | |
66 | if (value == null) { | |
67 | out.nullValue(); | |
68 | return; | |
69 | } | |
70 | JsonElement tree = serializer.serialize(value, typeToken.getType(), gson.serializationContext); | |
71 | Streams.write(tree, out); | |
72 | } | |
73 | ||
74 | private TypeAdapter<T> delegate() { | |
75 | TypeAdapter<T> d = delegate; | |
76 | return d != null | |
77 | ? d | |
78 | : (delegate = gson.getDelegateAdapter(skipPast, typeToken)); | |
79 | } | |
80 | ||
81 | /** | |
82 | * Returns a new factory that will match each type against {@code exactType}. | |
83 | */ | |
84 | public static TypeAdapterFactory newFactory(TypeToken<?> exactType, Object typeAdapter) { | |
85 | return new SingleTypeFactory(typeAdapter, exactType, false, null); | |
86 | } | |
87 | ||
88 | /** | |
89 | * Returns a new factory that will match each type and its raw type against | |
90 | * {@code exactType}. | |
91 | */ | |
92 | public static TypeAdapterFactory newFactoryWithMatchRawType( | |
93 | TypeToken<?> exactType, Object typeAdapter) { | |
94 | // only bother matching raw types if exact type is a raw type | |
95 | boolean matchRawType = exactType.getType() == exactType.getRawType(); | |
96 | return new SingleTypeFactory(typeAdapter, exactType, matchRawType, null); | |
97 | } | |
98 | ||
99 | /** | |
100 | * Returns a new factory that will match each type's raw type for assignability | |
101 | * to {@code hierarchyType}. | |
102 | */ | |
103 | public static TypeAdapterFactory newTypeHierarchyFactory( | |
104 | Class<?> hierarchyType, Object typeAdapter) { | |
105 | return new SingleTypeFactory(typeAdapter, null, false, hierarchyType); | |
106 | } | |
107 | ||
108 | private static class SingleTypeFactory implements TypeAdapterFactory { | |
109 | private final TypeToken<?> exactType; | |
110 | private final boolean matchRawType; | |
111 | private final Class<?> hierarchyType; | |
112 | private final JsonSerializer<?> serializer; | |
113 | private final JsonDeserializer<?> deserializer; | |
114 | ||
115 | private SingleTypeFactory(Object typeAdapter, TypeToken<?> exactType, boolean matchRawType, | |
116 | Class<?> hierarchyType) { | |
117 | serializer = typeAdapter instanceof JsonSerializer | |
118 | ? (JsonSerializer<?>) typeAdapter | |
119 | : null; | |
120 | deserializer = typeAdapter instanceof JsonDeserializer | |
121 | ? (JsonDeserializer<?>) typeAdapter | |
122 | : null; | |
123 | $Gson$Preconditions.checkArgument(serializer != null || deserializer != null); | |
124 | this.exactType = exactType; | |
125 | this.matchRawType = matchRawType; | |
126 | this.hierarchyType = hierarchyType; | |
127 | } | |
128 | ||
129 | @SuppressWarnings("unchecked") // guarded by typeToken.equals() call | |
130 | public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { | |
131 | boolean matches = exactType != null | |
132 | ? exactType.equals(type) || matchRawType && exactType.getType() == type.getRawType() | |
133 | : hierarchyType.isAssignableFrom(type.getRawType()); | |
134 | return matches | |
135 | ? new TreeTypeAdapter<T>((JsonSerializer<T>) serializer, | |
136 | (JsonDeserializer<T>) deserializer, gson, type, this) | |
137 | : null; | |
138 | } | |
139 | } | |
140 | } |