Skip to content

Commit fd6227c

Browse files
author
Yaniv Inbar
committed
[Issue 3] Add support for enums
[Issue 8] Support for Java generic types (for example <T>) [Issue 189] Support JSON null http://codereview.appspot.com/4437045/
1 parent 65ca6b5 commit fd6227c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+3783
-857
lines changed

google-api-client-googleapis/src/main/java/com/google/api/client/googleapis/GoogleUrl.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
package com.google.api.client.googleapis;
1616

1717
import com.google.api.client.http.GenericUrl;
18-
import com.google.api.client.util.DataUtil;
18+
import com.google.api.client.util.Data;
1919
import com.google.api.client.util.Key;
2020
import com.google.api.client.util.escape.CharEscapers;
2121
import com.google.common.annotations.VisibleForTesting;
@@ -98,7 +98,7 @@ public static GoogleUrl create(String encodedServerUrl, String pathTemplate, Obj
9898
GoogleUrl url = new GoogleUrl(encodedServerUrl);
9999

100100
HashMap<String, Object> requestMap = new HashMap<String, Object>();
101-
for (Map.Entry<String, Object> entry : DataUtil.mapOf(parameters).entrySet()) {
101+
for (Map.Entry<String, Object> entry : Data.mapOf(parameters).entrySet()) {
102102
Object value = entry.getValue();
103103
if (value != null) {
104104
requestMap.put(entry.getKey(), value);
@@ -113,7 +113,7 @@ public static GoogleUrl create(String encodedServerUrl, String pathTemplate, Obj
113113
/**
114114
* Expands templates in a URI.
115115
*
116-
* @param pathUri Uri component. It may contain one or more sequences of the form "{name}", where
116+
* @param pathUri URI component. It may contain one or more sequences of the form "{name}", where
117117
* "name" must be a key in variableMap
118118
* @param variableMap map of request variable names to values. Any names which are found in
119119
* pathUri are removed from the map during processing

google-api-client-googleapis/src/main/java/com/google/api/client/googleapis/auth/AuthKeyValueParser.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.google.api.client.util.ClassInfo;
2020
import com.google.api.client.util.FieldInfo;
2121
import com.google.api.client.util.GenericData;
22+
import com.google.api.client.util.Types;
2223

2324
import java.io.BufferedReader;
2425
import java.io.IOException;
@@ -43,7 +44,7 @@ public String getContentType() {
4344
}
4445

4546
public <T> T parse(HttpResponse response, Class<T> dataClass) throws IOException {
46-
T newInstance = ClassInfo.newInstance(dataClass);
47+
T newInstance = Types.newInstance(dataClass);
4748
ClassInfo classInfo = ClassInfo.of(dataClass);
4849
response.disableContentLogging = true;
4950
InputStream content = response.getContent();

google-api-client-googleapis/src/main/java/com/google/api/client/googleapis/json/JsonMultiKindFeedParser.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.google.api.client.json.JsonParser;
2020
import com.google.api.client.util.ClassInfo;
2121
import com.google.api.client.util.FieldInfo;
22+
import com.google.api.client.util.Types;
2223

2324
import java.io.IOException;
2425
import java.lang.reflect.Field;
@@ -50,7 +51,7 @@ public JsonMultiKindFeedParser(JsonParser parser, Class<T> feedClass, Class<?>..
5051
if (field == null) {
5152
throw new IllegalArgumentException("missing kind field for " + itemClass.getName());
5253
}
53-
Object item = ClassInfo.newInstance(itemClass);
54+
Object item = Types.newInstance(itemClass);
5455
String kind = (String) FieldInfo.getFieldValue(field, item);
5556
if (kind == null) {
5657
throw new IllegalArgumentException(

google-api-client-googleapis/src/main/java/com/google/api/client/googleapis/xml/atom/GoogleAtom.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616

1717
import com.google.api.client.util.ArrayMap;
1818
import com.google.api.client.util.ClassInfo;
19-
import com.google.api.client.util.DataUtil;
19+
import com.google.api.client.util.Data;
2020
import com.google.api.client.util.FieldInfo;
2121
import com.google.api.client.util.GenericData;
22+
import com.google.api.client.util.Types;
2223

2324
import java.util.Collection;
24-
import java.util.HashSet;
2525
import java.util.Map;
2626
import java.util.TreeSet;
2727

@@ -77,24 +77,24 @@ private static void appendFieldsFor(
7777
"cannot specify field mask for a Map or Collection class: " + dataClass);
7878
}
7979
ClassInfo classInfo = ClassInfo.of(dataClass);
80-
for (String name : new TreeSet<String>(classInfo.getKeyNames())) {
80+
for (String name : new TreeSet<String>(classInfo.getNames())) {
8181
FieldInfo fieldInfo = classInfo.getFieldInfo(name);
82-
if (fieldInfo.isFinal) {
82+
if (fieldInfo.isFinal()) {
8383
continue;
8484
}
8585
if (++numFields[0] != 1) {
8686
fieldsBuf.append(',');
8787
}
8888
fieldsBuf.append(name);
8989
// TODO(yanivi): handle Java arrays?
90-
Class<?> fieldClass = fieldInfo.type;
90+
Class<?> fieldClass = fieldInfo.getType();
9191
if (Collection.class.isAssignableFrom(fieldClass)) {
9292
// TODO(yanivi): handle Java collection of Java collection or Java map?
93-
fieldClass = ClassInfo.getCollectionParameter(fieldInfo.field);
93+
fieldClass = (Class<?>) Types.getIterableParameter(fieldInfo.getField().getGenericType());
9494
}
9595
// TODO(yanivi): implement support for map when server implements support for *:*
9696
if (fieldClass != null) {
97-
if (fieldInfo.isPrimitive) {
97+
if (fieldInfo.isPrimitive()) {
9898
if (name.charAt(0) != '@' && !name.equals("text()")) {
9999
// TODO(yanivi): wait for bug fix from server to support text() -- already fixed???
100100
// buf.append("/text()");
@@ -160,9 +160,9 @@ public static ArrayMap<String, Object> computePatch(Object patched, Object origi
160160
private static ArrayMap<String, Object> computePatchInternal(
161161
FieldsMask fieldsMask, Object patchedObject, Object originalObject) {
162162
ArrayMap<String, Object> result = ArrayMap.create();
163-
Map<String, Object> patchedMap = DataUtil.mapOf(patchedObject);
164-
Map<String, Object> originalMap = DataUtil.mapOf(originalObject);
165-
HashSet<String> fieldNames = new HashSet<String>();
163+
Map<String, Object> patchedMap = Data.mapOf(patchedObject);
164+
Map<String, Object> originalMap = Data.mapOf(originalObject);
165+
TreeSet<String> fieldNames = new TreeSet<String>();
166166
fieldNames.addAll(patchedMap.keySet());
167167
fieldNames.addAll(originalMap.keySet());
168168
for (String name : fieldNames) {
@@ -172,7 +172,7 @@ private static ArrayMap<String, Object> computePatchInternal(
172172
continue;
173173
}
174174
Class<?> type = originalValue == null ? patchedValue.getClass() : originalValue.getClass();
175-
if (FieldInfo.isPrimitive(type)) {
175+
if (Data.isPrimitive(type)) {
176176
if (originalValue != null && originalValue.equals(patchedValue)) {
177177
continue;
178178
}
@@ -211,7 +211,7 @@ private static ArrayMap<String, Object> computePatchInternal(
211211
} else {
212212
if (originalValue == null) { // TODO(yanivi): test
213213
fieldsMask.append(name);
214-
result.add(name, DataUtil.mapOf(patchedValue));
214+
result.add(name, Data.mapOf(patchedValue));
215215
} else if (patchedValue == null) { // TODO(yanivi): test
216216
fieldsMask.append(name);
217217
} else {

google-api-client-googleapis/src/main/java/com/google/api/client/googleapis/xml/atom/MultiKindFeedParser.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import com.google.api.client.http.HttpResponse;
1818
import com.google.api.client.util.ClassInfo;
1919
import com.google.api.client.util.FieldInfo;
20+
import com.google.api.client.util.Types;
2021
import com.google.api.client.xml.Xml;
2122
import com.google.api.client.xml.XmlNamespaceDictionary;
2223
import com.google.api.client.xml.atom.AbstractAtomFeedParser;
@@ -51,7 +52,7 @@ public void setEntryClasses(Class<?>... entryClasses) {
5152
if (field == null) {
5253
throw new IllegalArgumentException("missing @gd:kind field for " + entryClass.getName());
5354
}
54-
Object entry = ClassInfo.newInstance(entryClass);
55+
Object entry = Types.newInstance(entryClass);
5556
String kind = (String) FieldInfo.getFieldValue(field, entry);
5657
if (kind == null) {
5758
throw new IllegalArgumentException(
@@ -69,7 +70,7 @@ protected Object parseEntryInternal() throws IOException, XmlPullParserException
6970
if (entryClass == null) {
7071
throw new IllegalArgumentException("unrecognized kind: " + kind);
7172
}
72-
Object result = ClassInfo.newInstance(entryClass);
73+
Object result = Types.newInstance(entryClass);
7374
Xml.parseElement(parser, result, namespaceDictionary, null);
7475
return result;
7576
}

google-api-client/src/main/java/com/google/api/client/http/HttpHeaders.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ public Map<String, Collection<Object>> canonicalMap() {
180180
*/
181181
static HashMap<String, String> getFieldNameMap(Class<? extends HttpHeaders> headersClass) {
182182
HashMap<String, String> fieldNameMap = new HashMap<String, String>();
183-
for (String keyName : ClassInfo.of(headersClass).getKeyNames()) {
183+
for (String keyName : ClassInfo.of(headersClass).getNames()) {
184184
fieldNameMap.put(keyName.toLowerCase(), keyName);
185185
}
186186
return fieldNameMap;

google-api-client/src/main/java/com/google/api/client/http/HttpRequest.java

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414

1515
package com.google.api.client.http;
1616

17+
import com.google.api.client.util.Data;
18+
import com.google.api.client.util.FieldInfo;
1719
import com.google.api.client.util.Strings;
20+
import com.google.api.client.util.Types;
1821
import com.google.common.base.Preconditions;
1922

2023
import java.io.IOException;
21-
import java.util.Collection;
2224
import java.util.HashSet;
2325
import java.util.Map;
2426
import java.util.logging.Level;
@@ -247,27 +249,23 @@ public HttpResponse execute() throws IOException {
247249
logbuf.append(method).append(' ').append(urlString).append(Strings.LINE_SEPARATOR);
248250
}
249251
// add to user agent
250-
HttpHeaders headers = this.headers;
251252
if (headers.userAgent == null) {
252253
headers.userAgent = USER_AGENT_SUFFIX;
253254
} else {
254255
headers.userAgent += " " + USER_AGENT_SUFFIX;
255256
}
256257
// headers
257258
HashSet<String> headerNames = new HashSet<String>();
258-
for (Map.Entry<String, Object> headerEntry : this.headers.entrySet()) {
259+
for (Map.Entry<String, Object> headerEntry : headers.entrySet()) {
259260
String name = headerEntry.getKey();
260261
String lowerCase = name.toLowerCase();
261262
Preconditions.checkArgument(headerNames.add(lowerCase),
262263
"multiple headers of the same name (headers are case insensitive): %s", lowerCase);
263264
Object value = headerEntry.getValue();
264265
if (value != null) {
265-
if (value instanceof Collection<?>) {
266-
for (Object repeatedValue : (Collection<?>) value) {
267-
addHeader(logger, logbuf, lowLevelHttpRequest, name, repeatedValue);
268-
}
269-
} else if (value.getClass().isArray()) {
270-
for (Object repeatedValue : (Object[]) value) {
266+
Class<? extends Object> valueClass = value.getClass();
267+
if (value instanceof Iterable<?> || valueClass.isArray()) {
268+
for (Object repeatedValue : Types.iterableOf(value)) {
271269
addHeader(logger, logbuf, lowLevelHttpRequest, name, repeatedValue);
272270
}
273271
} else {
@@ -344,7 +342,14 @@ public HttpResponse execute() throws IOException {
344342

345343
private static void addHeader(Logger logger, StringBuilder logbuf,
346344
LowLevelHttpRequest lowLevelHttpRequest, String name, Object value) {
347-
String stringValue = value.toString();
345+
// ignore nulls
346+
if (value == null || Data.isNull(value)) {
347+
return;
348+
}
349+
// compute value
350+
String stringValue =
351+
value instanceof Enum<?> ? FieldInfo.of((Enum<?>) value).getName() : value.toString();
352+
// log header
348353
if (logbuf != null) {
349354
logbuf.append(name).append(": ");
350355
if ("Authorization".equals(name) && !logger.isLoggable(Level.ALL)) {
@@ -354,6 +359,7 @@ private static void addHeader(Logger logger, StringBuilder logbuf,
354359
}
355360
logbuf.append(Strings.LINE_SEPARATOR);
356361
}
362+
// add header
357363
lowLevelHttpRequest.addHeader(name, stringValue);
358364
}
359365
}

google-api-client/src/main/java/com/google/api/client/http/HttpResponse.java

Lines changed: 33 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,23 @@
1414

1515
package com.google.api.client.http;
1616

17-
import com.google.api.client.util.ArrayMap;
17+
import com.google.api.client.util.ArrayValueMap;
1818
import com.google.api.client.util.ClassInfo;
19+
import com.google.api.client.util.Data;
1920
import com.google.api.client.util.FieldInfo;
2021
import com.google.api.client.util.Strings;
22+
import com.google.api.client.util.Types;
2123

2224
import java.io.ByteArrayInputStream;
2325
import java.io.ByteArrayOutputStream;
2426
import java.io.IOException;
2527
import java.io.InputStream;
26-
import java.lang.reflect.Array;
28+
import java.lang.reflect.Type;
2729
import java.util.ArrayList;
30+
import java.util.Arrays;
2831
import java.util.Collection;
2932
import java.util.HashMap;
30-
import java.util.Map;
33+
import java.util.List;
3134
import java.util.logging.Level;
3235
import java.util.logging.Logger;
3336
import java.util.zip.GZIPInputStream;
@@ -96,23 +99,10 @@ public final class HttpResponse {
9699
*/
97100
public boolean disableContentLogging;
98101

99-
/**
100-
* Stores the array values used during
101-
* {@link HttpResponse#HttpResponse(HttpRequest, LowLevelHttpResponse)}.
102-
*/
103-
static class ArrayValue {
104-
105-
/** Array component type. */
106-
Class<?> componentType;
107-
108-
/** Values to be stored in an array. */
109-
ArrayList<Object> values = new ArrayList<Object>();
110-
}
111-
112102
HttpResponse(HttpRequest request, LowLevelHttpResponse response) {
113103
this.request = request;
114-
this.transport = request.transport;
115-
this.headers = request.headers;
104+
transport = request.transport;
105+
headers = request.headers;
116106
this.response = response;
117107
contentLength = response.getContentLength();
118108
contentType = response.getContentType();
@@ -142,9 +132,10 @@ static class ArrayValue {
142132
// headers
143133
int size = response.getHeaderCount();
144134
Class<? extends HttpHeaders> headersClass = headers.getClass();
135+
List<Type> context = Arrays.<Type>asList(headersClass);
145136
ClassInfo classInfo = ClassInfo.of(headersClass);
146137
HashMap<String, String> fieldNameMap = HttpHeaders.getFieldNameMap(headersClass);
147-
Map<FieldInfo, ArrayValue> arrayValueMap = ArrayMap.create();
138+
ArrayValueMap arrayValueMap = new ArrayValueMap(headers);
148139
for (int i = 0; i < size; i++) {
149140
String headerName = response.getHeaderName(i);
150141
String headerValue = response.getHeaderValue(i);
@@ -158,29 +149,28 @@ static class ArrayValue {
158149
// use field information if available
159150
FieldInfo fieldInfo = classInfo.getFieldInfo(fieldName);
160151
if (fieldInfo != null) {
161-
Class<?> type = fieldInfo.type;
162-
// collection is used for repeating headers of the same name
163-
if (ClassInfo.isAssignableToOrFrom(type, Collection.class)) {
164-
Collection<Object> collection = fieldInfo.getCollectionValue(headers);
152+
Type type = Data.resolveWildcardTypeOrTypeVariable(context, fieldInfo.getGenericType());
153+
// type is now class, parameterized type, or generic array type
154+
if (Types.isArray(type)) {
155+
// array that can handle repeating values
156+
Class<?> rawArrayComponentType =
157+
Types.getRawArrayComponentType(context, Types.getArrayComponentType(type));
158+
arrayValueMap.put(fieldInfo.getField(), rawArrayComponentType,
159+
parseValue(rawArrayComponentType, context, headerValue));
160+
} else if (Types.isAssignableToOrFrom(
161+
Types.getRawArrayComponentType(context, type), Iterable.class)) {
162+
// iterable that can handle repeating values
163+
@SuppressWarnings("unchecked")
164+
Collection<Object> collection = (Collection<Object>) fieldInfo.getValue(headers);
165165
if (collection == null) {
166-
collection = ClassInfo.newCollectionInstance(type);
166+
collection = Data.newCollectionInstance(type);
167167
fieldInfo.setValue(headers, collection);
168168
}
169-
// parse value based on collection type parameter
170-
Class<?> subFieldClass = ClassInfo.getCollectionParameter(fieldInfo.field);
171-
collection.add(FieldInfo.parsePrimitiveValue(subFieldClass, headerValue));
172-
} else if (type.isArray()) {
173-
Class<?> componentType = type.getComponentType();
174-
ArrayValue arrayValue = arrayValueMap.get(fieldInfo);
175-
if (arrayValue == null) {
176-
arrayValue = new ArrayValue();
177-
arrayValue.componentType = componentType;
178-
arrayValueMap.put(fieldInfo, arrayValue);
179-
}
180-
arrayValue.values.add(FieldInfo.parsePrimitiveValue(componentType, headerValue));
169+
Type subFieldType = type == Object.class ? null : Types.getIterableParameter(type);
170+
collection.add(parseValue(subFieldType, context, headerValue));
181171
} else {
182172
// parse value based on field type
183-
fieldInfo.setValue(headers, FieldInfo.parsePrimitiveValue(type, headerValue));
173+
fieldInfo.setValue(headers, parseValue(type, context, headerValue));
184174
}
185175
} else {
186176
// store header values in an array list
@@ -193,19 +183,18 @@ static class ArrayValue {
193183
listValue.add(headerValue);
194184
}
195185
}
196-
// write the array values
197-
for (Map.Entry<FieldInfo, ArrayValue> entry : arrayValueMap.entrySet()) {
198-
FieldInfo fieldInfo = entry.getKey();
199-
ArrayValue arrayValue = entry.getValue();
200-
fieldInfo.setValue(headers, arrayValue.values.toArray(
201-
(Object[]) Array.newInstance(arrayValue.componentType, arrayValue.values.size())));
202-
}
186+
arrayValueMap.setValues();
203187
// log from buffer
204188
if (loggable) {
205189
logger.config(logbuf.toString());
206190
}
207191
}
208192

193+
private static Object parseValue(Type valueType, List<Type> context, String value) {
194+
Type resolved = Data.resolveWildcardTypeOrTypeVariable(context, valueType);
195+
return Data.parsePrimitiveValue(resolved, value);
196+
}
197+
209198
/**
210199
* Returns the content of the HTTP response.
211200
* <p>

0 commit comments

Comments
 (0)