mirror of https://github.com/apache/jclouds.git
Merge pull request #1210 from jclouds/gson-typetoken
Gson internals refactor
This commit is contained in:
commit
ad70dae25c
|
@ -26,12 +26,10 @@ import javax.inject.Inject;
|
|||
import org.jclouds.date.DateService;
|
||||
import org.jclouds.json.config.GsonModule.DateAdapter;
|
||||
import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
|
||||
import org.jclouds.json.internal.IgnoreNullIterableTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.IterableTypeAdapterFactory;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
|
@ -47,15 +45,14 @@ public class CloudStackParserModule extends AbstractModule {
|
|||
@Override
|
||||
protected void configure() {
|
||||
bind(DateAdapter.class).to(CloudStackDateAdapter.class);
|
||||
bind(IgnoreNullIterableTypeAdapterFactory.class).to(CommaDelimitedOKIgnoreNullIterableTypeAdapterFactory.class);
|
||||
bind(IterableTypeAdapterFactory.class).to(CommaDelimitedOKIterableTypeAdapterFactory.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data adapter for the date formats used by CloudStack.
|
||||
*
|
||||
* Essentially this is a workaround for the CloudStack getUsage() API call returning a corrupted
|
||||
* form of ISO-8601 dates, which have an unexpected pair of apostrophes, like
|
||||
* 2011-12-12'T'00:00:00+00:00
|
||||
* Essentially this is a workaround for the CloudStack getUsage() API call returning a corrupted form of ISO-8601
|
||||
* dates, which have an unexpected pair of apostrophes, like 2011-12-12'T'00:00:00+00:00
|
||||
*
|
||||
* @author Richard Downer
|
||||
*/
|
||||
|
@ -77,39 +74,38 @@ public class CloudStackParserModule extends AbstractModule {
|
|||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public static class CommaDelimitedOKIgnoreNullIterableTypeAdapterFactory extends IgnoreNullIterableTypeAdapterFactory {
|
||||
public static class CommaDelimitedOKIterableTypeAdapterFactory extends IterableTypeAdapterFactory {
|
||||
|
||||
@Override
|
||||
protected <E> TypeAdapter<Iterable<E>> newIterableAdapter(final TypeAdapter<E> elementAdapter) {
|
||||
return new TypeAdapter<Iterable<E>>() {
|
||||
public void write(JsonWriter out, Iterable<E> value) throws IOException {
|
||||
out.beginArray();
|
||||
for (E element : value) {
|
||||
elementAdapter.write(out, element);
|
||||
}
|
||||
out.endArray();
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <E, I> TypeAdapter<I> newAdapter(TypeAdapter<E> elementAdapter) {
|
||||
return (TypeAdapter<I>) new Adapter<E>(elementAdapter);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Iterable<E> read(JsonReader in) throws IOException {
|
||||
// HACK as cloudstack changed a field from String to Set!
|
||||
if (in.peek() == JsonToken.STRING) {
|
||||
String val = Strings.emptyToNull(in.nextString());
|
||||
return (Iterable<E>) (val != null ? Splitter.on(',').split(val) : ImmutableSet.of());
|
||||
} else {
|
||||
Builder<E> builder = ImmutableList.<E> builder();
|
||||
in.beginArray();
|
||||
while (in.hasNext()) {
|
||||
E element = elementAdapter.read(in);
|
||||
if (element != null)
|
||||
builder.add(element);
|
||||
}
|
||||
in.endArray();
|
||||
return builder.build();
|
||||
}
|
||||
public static final class Adapter<E> extends TypeAdapter<Iterable<E>> {
|
||||
|
||||
private final IterableTypeAdapterFactory.IterableTypeAdapter<E> delegate;
|
||||
|
||||
public Adapter(TypeAdapter<E> elementAdapter) {
|
||||
this.delegate = new IterableTypeAdapterFactory.IterableTypeAdapter<E>(elementAdapter);
|
||||
nullSafe();
|
||||
}
|
||||
|
||||
public void write(JsonWriter out, Iterable<E> value) throws IOException {
|
||||
this.delegate.write(out, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Iterable<E> read(JsonReader in) throws IOException {
|
||||
// HACK as cloudstack changed a field from String to Set!
|
||||
if (in.peek() == JsonToken.STRING) {
|
||||
String val = Strings.emptyToNull(in.nextString());
|
||||
return (Iterable<E>) (val != null ? Splitter.on(',').split(val) : ImmutableSet.of());
|
||||
} else {
|
||||
return delegate.read(in);
|
||||
}
|
||||
}.nullSafe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,37 +21,30 @@ package org.jclouds.openstack.keystone.v2_0.config;
|
|||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jclouds.json.config.GsonModule;
|
||||
import org.jclouds.json.config.GsonModule.DateAdapter;
|
||||
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.SetTypeAdapterFactory;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.common.collect.ImmutableSet.Builder;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
/**
|
||||
* @author Adam Lowe
|
||||
*/
|
||||
public class KeystoneParserModule extends AbstractModule {
|
||||
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class);
|
||||
bind(new TypeLiteral<Set<TypeAdapterFactory>>() {
|
||||
}).toInstance(ImmutableSet.<TypeAdapterFactory>of(new SetTypeAdapterFactory()));
|
||||
bind(SetTypeAdapterFactory.class).to(ValuesSetTypeAdapterFactory.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,61 +53,49 @@ public class KeystoneParserModule extends AbstractModule {
|
|||
* <p/>
|
||||
* Treats [A,B,C] and {"values"=[A,B,C], "someotherstuff"=...} as the same Set
|
||||
*/
|
||||
public static class SetTypeAdapterFactory implements TypeAdapterFactory {
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
Type type = typeToken.getType();
|
||||
if (typeToken.getRawType() != Set.class || !(type instanceof ParameterizedType)) {
|
||||
return null;
|
||||
}
|
||||
public static class ValuesSetTypeAdapterFactory extends SetTypeAdapterFactory {
|
||||
|
||||
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
|
||||
return TypeAdapter.class.cast(newSetAdapter(elementAdapter));
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <E, I> TypeAdapter<I> newAdapter(TypeAdapter<E> elementAdapter) {
|
||||
return (TypeAdapter<I>) new Adapter<E>(elementAdapter);
|
||||
}
|
||||
|
||||
private <E> TypeAdapter<Set<E>> newSetAdapter(final TypeAdapter<E> elementAdapter) {
|
||||
return new TypeAdapter<Set<E>>() {
|
||||
public void write(JsonWriter out, Set<E> value) throws IOException {
|
||||
out.beginArray();
|
||||
for (E element : value) {
|
||||
elementAdapter.write(out, element);
|
||||
}
|
||||
out.endArray();
|
||||
}
|
||||
public static final class Adapter<E> extends TypeAdapter<Set<E>> {
|
||||
|
||||
public Set<E> read(JsonReader in) throws IOException {
|
||||
Set<E> result = Sets.newLinkedHashSet();
|
||||
if (in.peek() == JsonToken.BEGIN_OBJECT) {
|
||||
boolean foundValues = false;
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
String name = in.nextName();
|
||||
if (Objects.equal("values", name)) {
|
||||
foundValues = true;
|
||||
readArray(in, result);
|
||||
} else {
|
||||
in.skipValue();
|
||||
}
|
||||
}
|
||||
checkState(foundValues, "Expected BEGIN_ARRAY or the object to contain an array called 'values'");
|
||||
in.endObject();
|
||||
} else {
|
||||
readArray(in, result);
|
||||
}
|
||||
private final SetTypeAdapterFactory.SetTypeAdapter<E> delegate;
|
||||
|
||||
return result;
|
||||
}
|
||||
public Adapter(TypeAdapter<E> elementAdapter) {
|
||||
this.delegate = new SetTypeAdapterFactory.SetTypeAdapter<E>(elementAdapter);
|
||||
nullSafe();
|
||||
}
|
||||
|
||||
private void readArray(JsonReader in, Set<E> result) throws IOException {
|
||||
in.beginArray();
|
||||
public void write(JsonWriter out, Set<E> value) throws IOException {
|
||||
this.delegate.write(out, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<E> read(JsonReader in) throws IOException {
|
||||
if (in.peek() == JsonToken.BEGIN_OBJECT) {
|
||||
Builder<E> builder = ImmutableSet.<E>builder();
|
||||
boolean foundValues = false;
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
E element = elementAdapter.read(in);
|
||||
result.add(element);
|
||||
String name = in.nextName();
|
||||
if (Objects.equal("values", name)) {
|
||||
foundValues = true;
|
||||
builder.addAll(delegate.read(in));
|
||||
} else {
|
||||
in.skipValue();
|
||||
}
|
||||
}
|
||||
in.endArray();
|
||||
checkState(foundValues, "Expected BEGIN_ARRAY or the object to contain an array called 'values'");
|
||||
in.endObject();
|
||||
return builder.build();
|
||||
} else {
|
||||
return delegate.read(in);
|
||||
}
|
||||
}.nullSafe();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,10 +44,6 @@ import com.google.common.collect.Iterables;
|
|||
*/
|
||||
@Beta
|
||||
public class PaginatedCollection<T> extends IterableWithMarker<T> {
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public static final PaginatedCollection EMPTY = new PaginatedCollection(ImmutableSet.of(), ImmutableSet.of());
|
||||
|
||||
private Iterable<T> resources;
|
||||
private Iterable<Link> links;
|
||||
|
||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
|||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
public class ParseTenants extends ParseJson<Tenants<? extends Tenant>> {
|
||||
static class Tenants<T extends Tenant> extends PaginatedCollection<T> {
|
||||
public class ParseTenants extends ParseJson<Tenants> {
|
||||
static class Tenants extends PaginatedCollection<Tenant> {
|
||||
|
||||
@ConstructorProperties({ "tenants", "tenants_links" })
|
||||
protected Tenants(Iterable<T> tenants, Iterable<Link> tenants_links) {
|
||||
protected Tenants(Iterable<Tenant> tenants, Iterable<Link> tenants_links) {
|
||||
super(tenants, tenants_links);
|
||||
}
|
||||
|
||||
|
@ -60,8 +60,7 @@ public class ParseTenants extends ParseJson<Tenants<? extends Tenant>> {
|
|||
|
||||
@Inject
|
||||
public ParseTenants(Json json) {
|
||||
super(json, new TypeLiteral<Tenants<? extends Tenant>>() {
|
||||
});
|
||||
super(json, TypeLiteral.get(Tenants.class));
|
||||
}
|
||||
|
||||
public static class ToPagedIterable extends CallerArg0ToPagedIterable<Tenant, ToPagedIterable> {
|
||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
|||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
public class ParseUsers extends ParseJson<Users<? extends User>> {
|
||||
static class Users<T extends User> extends PaginatedCollection<T> {
|
||||
public class ParseUsers extends ParseJson<Users> {
|
||||
static class Users extends PaginatedCollection<User> {
|
||||
|
||||
@ConstructorProperties({ "users", "users_links" })
|
||||
protected Users(Iterable<T> users, Iterable<Link> users_links) {
|
||||
protected Users(Iterable<User> users, Iterable<Link> users_links) {
|
||||
super(users, users_links);
|
||||
}
|
||||
|
||||
|
@ -60,8 +60,7 @@ public class ParseUsers extends ParseJson<Users<? extends User>> {
|
|||
|
||||
@Inject
|
||||
public ParseUsers(Json json) {
|
||||
super(json, new TypeLiteral<Users<? extends User>>() {
|
||||
});
|
||||
super(json, TypeLiteral.get(Users.class));
|
||||
}
|
||||
|
||||
public static class ToPagedIterable extends CallerArg0ToPagedIterable<User, ToPagedIterable> {
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds licenses this file
|
||||
* to you 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.jclouds.openstack.keystone.v2_0.functions.internal;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jclouds.json.config.GsonModule;
|
||||
import org.jclouds.openstack.keystone.v2_0.config.KeystoneParserModule;
|
||||
import org.jclouds.openstack.keystone.v2_0.domain.User;
|
||||
import org.jclouds.openstack.keystone.v2_0.functions.internal.ParseUsers.Users;
|
||||
import org.jclouds.util.Strings2;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.inject.Guice;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(testName = "ParseUsersTest")
|
||||
public class ParseUsersTest {
|
||||
|
||||
private Gson gson = Guice.createInjector(new GsonModule(), new KeystoneParserModule()).getInstance(Gson.class);
|
||||
private Type usersMapType = new TypeToken<Map<String, Set<? extends User>>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}.getType();
|
||||
|
||||
Set<User> expectedUsers = ImmutableSet.of(
|
||||
User.builder().name("nova").id("e021dfd758eb44a89f1c57c8ef3be8e2").build(),
|
||||
User.builder().name("glance").id("3f6c1c9ba993495ead7d2eb2192e284f").build(),
|
||||
User.builder().name("demo").id("667b2e1420604df8b67cd8ea57d4ee64").build(),
|
||||
User.builder().name("admin").id("2b9b606181634ae9ac86fd95a8bc2cde").build());
|
||||
|
||||
public void testParseUsersInMap() throws JsonSyntaxException, IOException {
|
||||
String json = Strings2.toStringAndClose(getClass().getResourceAsStream("/user_list.json"));
|
||||
Map<String, Set<? extends User>> users = gson.fromJson(json, usersMapType);
|
||||
assertEquals(users.get("users"), expectedUsers);
|
||||
}
|
||||
|
||||
public void testParseUsers() throws JsonSyntaxException, IOException {
|
||||
String json = Strings2.toStringAndClose(getClass().getResourceAsStream("/user_list.json"));
|
||||
Users users = gson.fromJson(json, Users.class);
|
||||
assertEquals(users.toSet(), expectedUsers);
|
||||
}
|
||||
}
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
|||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
public class ParseFlavorDetails extends ParseJson<Flavors<? extends Flavor>> {
|
||||
static class Flavors<T extends Flavor> extends PaginatedCollection<T> {
|
||||
public class ParseFlavorDetails extends ParseJson<Flavors> {
|
||||
static class Flavors extends PaginatedCollection<Flavor> {
|
||||
|
||||
@ConstructorProperties({ "flavors", "flavors_links" })
|
||||
protected Flavors(Iterable<T> flavors, Iterable<Link> flavors_links) {
|
||||
protected Flavors(Iterable<Flavor> flavors, Iterable<Link> flavors_links) {
|
||||
super(flavors, flavors_links);
|
||||
}
|
||||
|
||||
|
@ -60,8 +60,7 @@ public class ParseFlavorDetails extends ParseJson<Flavors<? extends Flavor>> {
|
|||
|
||||
@Inject
|
||||
public ParseFlavorDetails(Json json) {
|
||||
super(json, new TypeLiteral<Flavors<? extends Flavor>>() {
|
||||
});
|
||||
super(json, TypeLiteral.get(Flavors.class));
|
||||
}
|
||||
|
||||
public static class ToPagedIterable extends CallerArg0ToPagedIterable<Flavor, ToPagedIterable> {
|
||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
|||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
public class ParseFlavors extends ParseJson<Flavors<? extends Resource>> {
|
||||
static class Flavors<T extends Resource> extends PaginatedCollection<T> {
|
||||
public class ParseFlavors extends ParseJson<Flavors> {
|
||||
static class Flavors extends PaginatedCollection<Resource> {
|
||||
|
||||
@ConstructorProperties({ "flavors", "flavors_links" })
|
||||
protected Flavors(Iterable<T> flavors, Iterable<Link> flavors_links) {
|
||||
protected Flavors(Iterable<Resource> flavors, Iterable<Link> flavors_links) {
|
||||
super(flavors, flavors_links);
|
||||
}
|
||||
|
||||
|
@ -60,8 +60,7 @@ public class ParseFlavors extends ParseJson<Flavors<? extends Resource>> {
|
|||
|
||||
@Inject
|
||||
public ParseFlavors(Json json) {
|
||||
super(json, new TypeLiteral<Flavors<? extends Resource>>() {
|
||||
});
|
||||
super(json, TypeLiteral.get(Flavors.class));
|
||||
}
|
||||
|
||||
public static class ToPagedIterable extends CallerArg0ToPagedIterable<Resource, ToPagedIterable> {
|
||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
|||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
public class ParseImageDetails extends ParseJson<Images<? extends Image>> {
|
||||
static class Images<T extends Image> extends PaginatedCollection<T> {
|
||||
public class ParseImageDetails extends ParseJson<Images> {
|
||||
static class Images extends PaginatedCollection<Image> {
|
||||
|
||||
@ConstructorProperties({ "images", "images_links" })
|
||||
protected Images(Iterable<T> images, Iterable<Link> images_links) {
|
||||
protected Images(Iterable<Image> images, Iterable<Link> images_links) {
|
||||
super(images, images_links);
|
||||
}
|
||||
|
||||
|
@ -60,8 +60,7 @@ public class ParseImageDetails extends ParseJson<Images<? extends Image>> {
|
|||
|
||||
@Inject
|
||||
public ParseImageDetails(Json json) {
|
||||
super(json, new TypeLiteral<Images<? extends Image>>() {
|
||||
});
|
||||
super(json, TypeLiteral.get(Images.class));
|
||||
}
|
||||
|
||||
public static class ToPagedIterable extends CallerArg0ToPagedIterable<Image, ToPagedIterable> {
|
||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
|||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
public class ParseImages extends ParseJson<Images<? extends Resource>> {
|
||||
static class Images<T extends Resource> extends PaginatedCollection<T> {
|
||||
public class ParseImages extends ParseJson<Images> {
|
||||
static class Images extends PaginatedCollection<Resource> {
|
||||
|
||||
@ConstructorProperties({ "images", "images_links" })
|
||||
protected Images(Iterable<T> images, Iterable<Link> images_links) {
|
||||
protected Images(Iterable<Resource> images, Iterable<Link> images_links) {
|
||||
super(images, images_links);
|
||||
}
|
||||
|
||||
|
@ -60,8 +60,7 @@ public class ParseImages extends ParseJson<Images<? extends Resource>> {
|
|||
|
||||
@Inject
|
||||
public ParseImages(Json json) {
|
||||
super(json, new TypeLiteral<Images<? extends Resource>>() {
|
||||
});
|
||||
super(json, TypeLiteral.get(Images.class));
|
||||
}
|
||||
|
||||
public static class ToPagedIterable extends CallerArg0ToPagedIterable<Resource, ToPagedIterable> {
|
||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
|||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
public class ParseServerDetails extends ParseJson<Servers<? extends Server>> {
|
||||
static class Servers<T extends Server> extends PaginatedCollection<T> {
|
||||
public class ParseServerDetails extends ParseJson<Servers> {
|
||||
static class Servers extends PaginatedCollection<Server> {
|
||||
|
||||
@ConstructorProperties({ "servers", "servers_links" })
|
||||
protected Servers(Iterable<T> servers, Iterable<Link> servers_links) {
|
||||
protected Servers(Iterable<Server> servers, Iterable<Link> servers_links) {
|
||||
super(servers, servers_links);
|
||||
}
|
||||
|
||||
|
@ -60,8 +60,7 @@ public class ParseServerDetails extends ParseJson<Servers<? extends Server>> {
|
|||
|
||||
@Inject
|
||||
public ParseServerDetails(Json json) {
|
||||
super(json, new TypeLiteral<Servers<? extends Server>>() {
|
||||
});
|
||||
super(json, TypeLiteral.get(Servers.class));
|
||||
}
|
||||
|
||||
public static class ToPagedIterable extends CallerArg0ToPagedIterable<Server, ToPagedIterable> {
|
||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
|||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
public class ParseServers extends ParseJson<Servers<? extends Resource>> {
|
||||
static class Servers<T extends Resource> extends PaginatedCollection<T> {
|
||||
public class ParseServers extends ParseJson<Servers> {
|
||||
static class Servers extends PaginatedCollection<Resource> {
|
||||
|
||||
@ConstructorProperties({ "servers", "servers_links" })
|
||||
protected Servers(Iterable<T> servers, Iterable<Link> servers_links) {
|
||||
protected Servers(Iterable<Resource> servers, Iterable<Link> servers_links) {
|
||||
super(servers, servers_links);
|
||||
}
|
||||
|
||||
|
@ -60,8 +60,7 @@ public class ParseServers extends ParseJson<Servers<? extends Resource>> {
|
|||
|
||||
@Inject
|
||||
public ParseServers(Json json) {
|
||||
super(json, new TypeLiteral<Servers<? extends Resource>>() {
|
||||
});
|
||||
super(json, TypeLiteral.get(Servers.class));
|
||||
}
|
||||
|
||||
public static class ToPagedIterable extends CallerArg0ToPagedIterable<Resource, ToPagedIterable> {
|
||||
|
|
|
@ -53,8 +53,7 @@ public class FilterStringsBoundToInjectorByName implements Function<Predicate<St
|
|||
|
||||
@Override
|
||||
public Map<String, String> apply(Predicate<String> filter) {
|
||||
List<Binding<String>> stringBindings = injector.findBindingsByType(new TypeLiteral<String>() {
|
||||
});
|
||||
List<Binding<String>> stringBindings = injector.findBindingsByType(TypeLiteral.get(String.class));
|
||||
Iterable<Binding<String>> annotatedWithName = Iterables.filter(stringBindings, new Predicate<Binding<String>>() {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -40,15 +40,16 @@ import org.jclouds.json.Json;
|
|||
import org.jclouds.json.internal.DeserializationConstructorAndReflectiveTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.EnumTypeAdapterThatReturnsFromValue;
|
||||
import org.jclouds.json.internal.GsonWrapper;
|
||||
import org.jclouds.json.internal.IgnoreNullFluentIterableTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.IgnoreNullIterableTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.IgnoreNullMapTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.IgnoreNullMultimapTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.IgnoreNullSetTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.NamingStrategies.AnnotationConstructorNamingStrategy;
|
||||
import org.jclouds.json.internal.NamingStrategies.AnnotationOrNameFieldNamingStrategy;
|
||||
import org.jclouds.json.internal.NamingStrategies.ExtractNamed;
|
||||
import org.jclouds.json.internal.NamingStrategies.ExtractSerializedName;
|
||||
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.CollectionTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.FluentIterableTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.IterableTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.MapTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.MultimapTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.SetTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.NullHackJsonLiteralAdapter;
|
||||
import org.jclouds.json.internal.OptionalTypeAdapterFactory;
|
||||
|
||||
|
@ -84,14 +85,13 @@ public class GsonModule extends AbstractModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
Gson provideGson(TypeAdapter<JsonBall> jsonAdapter, DateAdapter adapter, ByteListAdapter byteListAdapter,
|
||||
ByteArrayAdapter byteArrayAdapter, PropertiesAdapter propertiesAdapter, JsonAdapterBindings bindings,
|
||||
OptionalTypeAdapterFactory optional, IgnoreNullSetTypeAdapterFactory set,
|
||||
IgnoreNullMapTypeAdapterFactory map, IgnoreNullMultimapTypeAdapterFactory multimap,
|
||||
IgnoreNullIterableTypeAdapterFactory iterable, IgnoreNullFluentIterableTypeAdapterFactory fluentIterable)
|
||||
throws Exception {
|
||||
ByteArrayAdapter byteArrayAdapter, PropertiesAdapter propertiesAdapter, JsonAdapterBindings bindings,
|
||||
OptionalTypeAdapterFactory optional, SetTypeAdapterFactory set, MapTypeAdapterFactory map,
|
||||
MultimapTypeAdapterFactory multimap, IterableTypeAdapterFactory iterable,
|
||||
CollectionTypeAdapterFactory collection, FluentIterableTypeAdapterFactory fluentIterable) throws Exception {
|
||||
|
||||
FieldNamingStrategy serializationPolicy = new AnnotationOrNameFieldNamingStrategy(new ExtractSerializedName(),
|
||||
new ExtractNamed());
|
||||
FieldNamingStrategy serializationPolicy = new AnnotationOrNameFieldNamingStrategy(ImmutableSet.of(
|
||||
new ExtractSerializedName(), new ExtractNamed()));
|
||||
|
||||
GsonBuilder builder = new GsonBuilder().setFieldNamingStrategy(serializationPolicy);
|
||||
|
||||
|
@ -104,18 +104,17 @@ public class GsonModule extends AbstractModule {
|
|||
builder.registerTypeAdapter(JsonBall.class, jsonAdapter.nullSafe());
|
||||
builder.registerTypeAdapterFactory(optional);
|
||||
builder.registerTypeAdapterFactory(iterable);
|
||||
builder.registerTypeAdapterFactory(collection);
|
||||
builder.registerTypeAdapterFactory(set);
|
||||
builder.registerTypeAdapterFactory(map);
|
||||
builder.registerTypeAdapterFactory(multimap);
|
||||
builder.registerTypeAdapterFactory(fluentIterable);
|
||||
|
||||
AnnotationConstructorNamingStrategy deserializationPolicy =
|
||||
new AnnotationConstructorNamingStrategy(
|
||||
ImmutableSet.of(ConstructorProperties.class, Inject.class), ImmutableSet.of(new ExtractNamed()));
|
||||
AnnotationConstructorNamingStrategy deserializationPolicy = new AnnotationConstructorNamingStrategy(
|
||||
ImmutableSet.of(ConstructorProperties.class, Inject.class), ImmutableSet.of(new ExtractNamed()));
|
||||
|
||||
builder.registerTypeAdapterFactory(
|
||||
new DeserializationConstructorAndReflectiveTypeAdapterFactory(new ConstructorConstructor(),
|
||||
serializationPolicy, Excluder.DEFAULT, deserializationPolicy));
|
||||
builder.registerTypeAdapterFactory(new DeserializationConstructorAndReflectiveTypeAdapterFactory(
|
||||
new ConstructorConstructor(), serializationPolicy, Excluder.DEFAULT, deserializationPolicy));
|
||||
|
||||
// complicated (serializers/deserializers as they need context to operate)
|
||||
builder.registerTypeHierarchyAdapter(Enum.class, new EnumTypeAdapterThatReturnsFromValue());
|
||||
|
|
|
@ -20,22 +20,25 @@ package org.jclouds.json.internal;
|
|||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.jclouds.reflect.Reflection2.typeToken;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jclouds.json.internal.NamingStrategies.ConstructorFieldNamingStrategy;
|
||||
import org.jclouds.json.internal.NamingStrategies.AnnotationConstructorNamingStrategy;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMap.Builder;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.reflect.Parameter;
|
||||
import com.google.gson.FieldNamingStrategy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.internal.$Gson$Types;
|
||||
import com.google.gson.internal.ConstructorConstructor;
|
||||
import com.google.gson.internal.Excluder;
|
||||
import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory;
|
||||
|
@ -49,21 +52,20 @@ import com.google.gson.stream.JsonWriter;
|
|||
* <p/>
|
||||
* <ul>
|
||||
* <li>Deserialization</li>
|
||||
* If there's an annotation designating a parameterized constructor, invoke that for fields
|
||||
* correlating to named parameter annotations. Otherwise, use {@link ConstructorConstructor}, and
|
||||
* set fields via reflection.
|
||||
* If there's an annotation designating a parameterized constructor, invoke that for fields correlating to named
|
||||
* parameter annotations. Otherwise, use {@link ConstructorConstructor}, and set fields via reflection.
|
||||
* <p/>
|
||||
* Notes: primitive constructor params are set to the Java defaults (0 or false) if not present; and
|
||||
* the empty object ({}) is treated as a null if the constructor for the object throws an NPE.
|
||||
* <li>Serialization</li>
|
||||
* Serialize based on reflective access to fields, delegating to ReflectiveTypeAdaptor.
|
||||
* Notes: primitive constructor params are set to the Java defaults (0 or false) if not present; and the empty object
|
||||
* ({}) is treated as a null if the constructor for the object throws an NPE.
|
||||
* <li>Serialization</li> Serialize based on reflective access to fields, delegating to ReflectiveTypeAdaptor.
|
||||
* </ul>
|
||||
* <h3>Example: Using javax inject to select a constructor and corresponding named parameters</h3>
|
||||
* <p/>
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
*
|
||||
* import NamingStrategies.*;
|
||||
*
|
||||
*
|
||||
* serializationStrategy = new AnnotationOrNameFieldNamingStrategy(
|
||||
* new ExtractSerializedName(), new ExtractNamed());
|
||||
*
|
||||
|
@ -73,19 +75,20 @@ import com.google.gson.stream.JsonWriter;
|
|||
*
|
||||
* factory = new DeserializationConstructorAndReflectiveTypeAdapterFactory(new ConstructorConstructor(),
|
||||
* serializationStrategy, Excluder.DEFAULT, deserializationStrategy);
|
||||
*
|
||||
*
|
||||
* gson = new GsonBuilder(serializationStrategy).registerTypeAdapterFactory(factory).create();
|
||||
*
|
||||
*
|
||||
* </pre>
|
||||
* <p/>
|
||||
* The above would work fine on the following class, which has no gson-specific annotations:
|
||||
* <p/>
|
||||
*
|
||||
* <pre>
|
||||
* private static class ImmutableAndVerifiedInCtor {
|
||||
* final int foo;
|
||||
* @Named("_bar")
|
||||
* final int bar;
|
||||
*
|
||||
*
|
||||
* @Inject
|
||||
* ImmutableAndVerifiedInCtor(@Named("foo") int foo, @Named("_bar") int bar) {
|
||||
* if (foo < 0)
|
||||
|
@ -97,54 +100,47 @@ import com.google.gson.stream.JsonWriter;
|
|||
* </pre>
|
||||
* <p/>
|
||||
* <br/>
|
||||
*
|
||||
*
|
||||
* @author Adrian Cole
|
||||
* @author Adam Lowe
|
||||
*/
|
||||
public final class DeserializationConstructorAndReflectiveTypeAdapterFactory implements TypeAdapterFactory {
|
||||
private final ConstructorFieldNamingStrategy constructorFieldNamingPolicy;
|
||||
private final AnnotationConstructorNamingStrategy constructorFieldNamingPolicy;
|
||||
private final ReflectiveTypeAdapterFactory delegateFactory;
|
||||
|
||||
/**
|
||||
* @param constructorConstructor passed through to delegate ReflectiveTypeAdapterFactory for serialization
|
||||
* @param serializationFieldNamingPolicy passed through to delegate ReflectiveTypeAdapterFactory for serialization
|
||||
* @param excluder passed through to delegate ReflectiveTypeAdapterFactory for serialization
|
||||
* @param deserializationFieldNamingPolicy
|
||||
* determines which constructor to use and how to determine field names for
|
||||
* deserialization
|
||||
* @see ReflectiveTypeAdapterFactory
|
||||
*/
|
||||
public DeserializationConstructorAndReflectiveTypeAdapterFactory(
|
||||
ConstructorConstructor constructorConstructor,
|
||||
FieldNamingStrategy serializationFieldNamingPolicy,
|
||||
Excluder excluder,
|
||||
ConstructorFieldNamingStrategy deserializationFieldNamingPolicy) {
|
||||
this.constructorFieldNamingPolicy = checkNotNull(deserializationFieldNamingPolicy, "deserializationFieldNamingPolicy");
|
||||
this.delegateFactory = new ReflectiveTypeAdapterFactory(constructorConstructor, checkNotNull(serializationFieldNamingPolicy, "fieldNamingPolicy"), checkNotNull(excluder, "excluder"));
|
||||
public DeserializationConstructorAndReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor,
|
||||
FieldNamingStrategy serializationFieldNamingPolicy, Excluder excluder,
|
||||
AnnotationConstructorNamingStrategy deserializationFieldNamingPolicy) {
|
||||
this.constructorFieldNamingPolicy = checkNotNull(deserializationFieldNamingPolicy,
|
||||
"deserializationFieldNamingPolicy");
|
||||
this.delegateFactory = new ReflectiveTypeAdapterFactory(constructorConstructor, checkNotNull(
|
||||
serializationFieldNamingPolicy, "fieldNamingPolicy"), checkNotNull(excluder, "excluder"));
|
||||
}
|
||||
|
||||
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
|
||||
Class<? super T> raw = type.getRawType();
|
||||
Constructor<? super T> deserializationCtor = constructorFieldNamingPolicy.getDeserializationConstructor(raw);
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
||||
com.google.common.reflect.TypeToken<T> token = typeToken(type.getType());
|
||||
Invokable<T, T> deserializationCtor = constructorFieldNamingPolicy.getDeserializer(token);
|
||||
|
||||
if (deserializationCtor == null) {
|
||||
return null; // allow GSON to choose the correct Adapter (can't simply return delegateFactory.create())
|
||||
} else {
|
||||
deserializationCtor.setAccessible(true);
|
||||
return new DeserializeWithParameterizedConstructorSerializeWithDelegate<T>(delegateFactory.create(gson, type), deserializationCtor,
|
||||
getParameterReaders(gson, type, deserializationCtor));
|
||||
return new DeserializeIntoParameterizedConstructor<T>(delegateFactory.create(gson, type), deserializationCtor,
|
||||
getParameterReaders(gson, deserializationCtor));
|
||||
}
|
||||
}
|
||||
|
||||
private final class DeserializeWithParameterizedConstructorSerializeWithDelegate<T> extends TypeAdapter<T> {
|
||||
private final Constructor<? super T> parameterizedCtor;
|
||||
private final class DeserializeIntoParameterizedConstructor<T> extends TypeAdapter<T> {
|
||||
private final TypeAdapter<T> serializer;
|
||||
private final Invokable<T, T> parameterizedCtor;
|
||||
private final Map<String, ParameterReader<?>> parameterReaders;
|
||||
private final TypeAdapter<T> delegate;
|
||||
|
||||
private DeserializeWithParameterizedConstructorSerializeWithDelegate(TypeAdapter<T> delegate,
|
||||
Constructor<? super T> parameterizedCtor, Map<String, ParameterReader<?>> parameterReaders) {
|
||||
this.delegate = delegate;
|
||||
this.parameterizedCtor = parameterizedCtor;
|
||||
private DeserializeIntoParameterizedConstructor(TypeAdapter<T> serializer, Invokable<T, T> deserializationCtor,
|
||||
Map<String, ParameterReader<?>> parameterReaders) {
|
||||
this.serializer = serializer;
|
||||
this.parameterizedCtor = deserializationCtor;
|
||||
this.parameterReaders = parameterReaders;
|
||||
}
|
||||
|
||||
|
@ -155,16 +151,16 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactory imp
|
|||
return null;
|
||||
}
|
||||
|
||||
Class<?>[] paramTypes = parameterizedCtor.getParameterTypes();
|
||||
Object[] ctorParams = new Object[paramTypes.length];
|
||||
List<Parameter> params = parameterizedCtor.getParameters();
|
||||
Object[] values = new Object[params.size()];
|
||||
boolean empty = true;
|
||||
|
||||
// Set all primitive constructor params to defaults
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
if (paramTypes[i] == boolean.class) {
|
||||
ctorParams[i] = Boolean.FALSE;
|
||||
} else if (paramTypes[i].isPrimitive()) {
|
||||
ctorParams[i] = 0;
|
||||
for (Parameter param : params) {
|
||||
if (param.getType().getRawType() == boolean.class) {
|
||||
values[param.hashCode()] = Boolean.FALSE;
|
||||
} else if (param.getType().getRawType().isPrimitive()) {
|
||||
values[param.hashCode()] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,26 +174,27 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactory imp
|
|||
in.skipValue();
|
||||
} else {
|
||||
Object value = parameter.read(in);
|
||||
if (value != null) ctorParams[parameter.index] = value;
|
||||
if (value != null)
|
||||
values[parameter.position] = value;
|
||||
}
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
throw new JsonSyntaxException(e);
|
||||
}
|
||||
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
if (paramTypes[i].isPrimitive()) {
|
||||
checkArgument(ctorParams[i] != null, "Primitive param[" + i + "] in constructor " + parameterizedCtor
|
||||
+ " cannot be absent!");
|
||||
for (Parameter param : params) {
|
||||
if (param.getType().getRawType().isPrimitive()) {
|
||||
checkArgument(values[param.hashCode()] != null, "Primitive param[" + param.hashCode()
|
||||
+ "] in constructor " + parameterizedCtor + " cannot be absent!");
|
||||
}
|
||||
}
|
||||
in.endObject();
|
||||
|
||||
try {
|
||||
return newInstance(ctorParams);
|
||||
return newInstance(values);
|
||||
} catch (NullPointerException ex) {
|
||||
// If {} was found and constructor threw NPE, we treat the field as null
|
||||
if (empty && paramTypes.length > 0) {
|
||||
if (empty && values.length > 0) {
|
||||
return null;
|
||||
}
|
||||
throw ex;
|
||||
|
@ -209,15 +206,12 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactory imp
|
|||
*/
|
||||
@Override
|
||||
public void write(JsonWriter out, T value) throws IOException {
|
||||
delegate.write(out, value);
|
||||
serializer.write(out, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private T newInstance(Object[] ctorParams) throws AssertionError {
|
||||
try {
|
||||
return (T) parameterizedCtor.newInstance(ctorParams);
|
||||
} catch (InstantiationException e) {
|
||||
throw new AssertionError(e);
|
||||
return (T) parameterizedCtor.invoke(null, ctorParams);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
|
@ -226,43 +220,79 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactory imp
|
|||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(serializer, parameterizedCtor, parameterReaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
DeserializeIntoParameterizedConstructor<?> that = DeserializeIntoParameterizedConstructor.class.cast(obj);
|
||||
return Objects.equal(this.serializer, that.serializer)
|
||||
&& Objects.equal(this.parameterizedCtor, that.parameterizedCtor)
|
||||
&& Objects.equal(this.parameterReaders, that.parameterReaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Objects.toStringHelper(this).add("parameterizedCtor", parameterizedCtor)
|
||||
.add("parameterReaders", parameterReaders).add("serializer", serializer).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// logic borrowed from ReflectiveTypeAdapterFactory
|
||||
static class ParameterReader<T> {
|
||||
final String name;
|
||||
final int index;
|
||||
final int position;
|
||||
final TypeAdapter<T> typeAdapter;
|
||||
|
||||
ParameterReader(String name, int index, TypeAdapter<T> typeAdapter) {
|
||||
ParameterReader(int position, String name, TypeAdapter<T> typeAdapter) {
|
||||
this.name = name;
|
||||
this.index = index;
|
||||
this.position = position;
|
||||
this.typeAdapter = typeAdapter;
|
||||
}
|
||||
|
||||
public Object read(JsonReader reader) throws IOException {
|
||||
return typeAdapter.read(reader);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, ParameterReader<?>> getParameterReaders(Gson context, TypeToken<?> declaring, Constructor<?> constructor) {
|
||||
Map<String, ParameterReader<?>> result = Maps.newLinkedHashMap();
|
||||
|
||||
for (int index = 0; index < constructor.getGenericParameterTypes().length; index++) {
|
||||
Type parameterType = getTypeOfConstructorParameter(declaring, constructor, index);
|
||||
TypeAdapter<?> adapter = context.getAdapter(TypeToken.get(parameterType));
|
||||
String parameterName = constructorFieldNamingPolicy.translateName(constructor, index);
|
||||
checkArgument(parameterName != null, constructor + " parameter " + 0 + " failed to be named by " + constructorFieldNamingPolicy);
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
ParameterReader<?> parameterReader = new ParameterReader(parameterName, index, adapter);
|
||||
ParameterReader<?> previous = result.put(parameterReader.name, parameterReader);
|
||||
checkArgument(previous == null, constructor + " declares multiple JSON parameters named " + parameterReader.name);
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof ParameterReader) {
|
||||
ParameterReader<?> that = ParameterReader.class.cast(obj);
|
||||
return position == that.position && name.equals(that.name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(position, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return typeAdapter + " arg" + position;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Type getTypeOfConstructorParameter(TypeToken<?> declaring, Constructor<?> constructor, int index) {
|
||||
Type genericParameter = constructor.getGenericParameterTypes()[index];
|
||||
return $Gson$Types.resolve(declaring.getType(), declaring.getRawType(), genericParameter);
|
||||
private <T> Map<String, ParameterReader<?>> getParameterReaders(Gson context, Invokable<T, T> deserializationCtor) {
|
||||
Builder<String, ParameterReader<?>> result = ImmutableMap.builder();
|
||||
for (Parameter param : deserializationCtor.getParameters()) {
|
||||
TypeAdapter<?> adapter = context.getAdapter(TypeToken.get(param.getType().getType()));
|
||||
String parameterName = constructorFieldNamingPolicy.translateName(deserializationCtor, param.hashCode());
|
||||
checkArgument(parameterName != null, deserializationCtor + " parameter " + 0 + " failed to be named by "
|
||||
+ constructorFieldNamingPolicy);
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
ParameterReader<?> parameterReader = new ParameterReader(param.hashCode(), parameterName, adapter);
|
||||
result.put(parameterReader.name, parameterReader);
|
||||
}
|
||||
return result.build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,13 +18,11 @@
|
|||
*/
|
||||
package org.jclouds.json.internal;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import static org.jclouds.reflect.Reflection2.method;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
|
@ -48,32 +46,11 @@ public class EnumTypeAdapterThatReturnsFromValue<T extends Enum<T>> implements J
|
|||
return (T) Enum.valueOf((Class<T>) classOfT, json.getAsString());
|
||||
} catch (IllegalArgumentException e) {
|
||||
try {
|
||||
Method converter = classToConvert.get((Class<?>) classOfT);
|
||||
Invokable<?, Object> converter = method((Class<?>) classOfT, "fromValue", String.class);
|
||||
return (T) converter.invoke(null, json.getAsString());
|
||||
} catch (Exception e1) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final LoadingCache<Class<?>, Method> classToConvert = CacheBuilder.newBuilder()
|
||||
.build(new CacheLoader<Class<?>, Method>() {
|
||||
|
||||
@Override
|
||||
public Method load(Class<?> from) throws ExecutionException {
|
||||
try {
|
||||
Method method = from.getMethod("fromValue", String.class);
|
||||
method.setAccessible(true);
|
||||
return method;
|
||||
} catch (Exception e) {
|
||||
throw new ExecutionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return EnumTypeAdapterThatReturnsFromValue.class.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds licenses this file
|
||||
* to you 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.jclouds.json.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
/**
|
||||
* Eliminates null values when deserializing FluentIterables
|
||||
* <p/>
|
||||
* Treats [null] as the empty set; [A, null] as [A]; etc.
|
||||
*
|
||||
* @author Adam Lowe
|
||||
*/
|
||||
public class IgnoreNullFluentIterableTypeAdapterFactory implements TypeAdapterFactory {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
Type type = typeToken.getType();
|
||||
if (typeToken.getRawType() != FluentIterable.class || !(type instanceof ParameterizedType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
|
||||
return (TypeAdapter<T>) newFluentIterableAdapter(elementAdapter);
|
||||
}
|
||||
|
||||
protected <E> TypeAdapter<FluentIterable<E>> newFluentIterableAdapter(final TypeAdapter<E> elementAdapter) {
|
||||
return new TypeAdapter<FluentIterable<E>>() {
|
||||
public void write(JsonWriter out, FluentIterable<E> value) throws IOException {
|
||||
out.beginArray();
|
||||
for (E element : value) {
|
||||
elementAdapter.write(out, element);
|
||||
}
|
||||
out.endArray();
|
||||
}
|
||||
|
||||
public FluentIterable<E> read(JsonReader in) throws IOException {
|
||||
in.beginArray();
|
||||
Builder<E> builder = ImmutableList.<E>builder();
|
||||
while (in.hasNext()) {
|
||||
E element = elementAdapter.read(in);
|
||||
if (element != null) builder.add(element);
|
||||
}
|
||||
in.endArray();
|
||||
return FluentIterable.from(builder.build());
|
||||
}
|
||||
}.nullSafe();
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds licenses this file
|
||||
* to you 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.jclouds.json.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
/**
|
||||
* Eliminates null values when deserializing Iterables
|
||||
* <p/>
|
||||
* Treats [null] as the empty set; [A, null] as [A]; etc.
|
||||
*
|
||||
* @author Adam Lowe
|
||||
*/
|
||||
public class IgnoreNullIterableTypeAdapterFactory implements TypeAdapterFactory {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
Type type = typeToken.getType();
|
||||
if (typeToken.getRawType() != Iterable.class || !(type instanceof ParameterizedType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
|
||||
return (TypeAdapter<T>) newIterableAdapter(elementAdapter);
|
||||
}
|
||||
|
||||
protected <E> TypeAdapter<Iterable<E>> newIterableAdapter(final TypeAdapter<E> elementAdapter) {
|
||||
return new TypeAdapter<Iterable<E>>() {
|
||||
public void write(JsonWriter out, Iterable<E> value) throws IOException {
|
||||
out.beginArray();
|
||||
for (E element : value) {
|
||||
elementAdapter.write(out, element);
|
||||
}
|
||||
out.endArray();
|
||||
}
|
||||
|
||||
public Iterable<E> read(JsonReader in) throws IOException {
|
||||
in.beginArray();
|
||||
Builder<E> builder = ImmutableList.<E>builder();
|
||||
while (in.hasNext()) {
|
||||
E element = elementAdapter.read(in);
|
||||
if (element != null) builder.add(element);
|
||||
}
|
||||
in.endArray();
|
||||
return builder.build();
|
||||
}
|
||||
}.nullSafe();
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds licenses this file
|
||||
* to you 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.jclouds.json.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.internal.JsonReaderInternalAccess;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
/**
|
||||
* Eliminates null values when deserializing Maps
|
||||
* <p/>
|
||||
* Treats {"a":null} as the empty map; {"a":1, "b":null} as {"a":1}; etc.
|
||||
*
|
||||
* @author Adam Lowe
|
||||
*/
|
||||
public class IgnoreNullMapTypeAdapterFactory implements TypeAdapterFactory {
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
Type type = typeToken.getType();
|
||||
if (typeToken.getRawType() != Map.class || !(type instanceof ParameterizedType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Type keyType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
Type valueType = ((ParameterizedType) type).getActualTypeArguments()[1];
|
||||
TypeAdapter<?> keyAdapter = gson.getAdapter(TypeToken.get(keyType));
|
||||
TypeAdapter<?> valueAdapter = gson.getAdapter(TypeToken.get(valueType));
|
||||
return (TypeAdapter<T>) newMapAdapter(keyAdapter, valueAdapter);
|
||||
}
|
||||
|
||||
protected <K,V> TypeAdapter<Map<K, V>> newMapAdapter(final TypeAdapter<K> keyAdapter, final TypeAdapter<V> valueAdapter) {
|
||||
return new TypeAdapter<Map<K, V>>() {
|
||||
public void write(JsonWriter out, Map<K, V> value) throws IOException {
|
||||
out.beginObject();
|
||||
for (Map.Entry<K, V> element : value.entrySet()) {
|
||||
out.name(String.valueOf(element.getKey()));
|
||||
valueAdapter.write(out, element.getValue());
|
||||
}
|
||||
out.endObject();
|
||||
}
|
||||
|
||||
public Map<K, V> read(JsonReader in) throws IOException {
|
||||
Map<K, V> result = Maps.newLinkedHashMap();
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
|
||||
K name = keyAdapter.read(in);
|
||||
V value = valueAdapter.read(in);
|
||||
if (value != null) result.put(name, value);
|
||||
}
|
||||
in.endObject();
|
||||
return result;
|
||||
}
|
||||
}.nullSafe();
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds licenses this file
|
||||
* to you 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.jclouds.json.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.internal.JsonReaderInternalAccess;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
/**
|
||||
* Parses Multimaps to/from json - strips out any null values when deserializing
|
||||
*
|
||||
* @author Adam Lowe
|
||||
*/
|
||||
public class IgnoreNullMultimapTypeAdapterFactory implements TypeAdapterFactory {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
Type type = typeToken.getType();
|
||||
if ((typeToken.getRawType() != Multimap.class) || !(type instanceof ParameterizedType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Type keyType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
Type valueType = ((ParameterizedType) type).getActualTypeArguments()[1];
|
||||
TypeAdapter<?> keyAdapter = gson.getAdapter(TypeToken.get(keyType));
|
||||
TypeAdapter<?> valueAdapter = gson.getAdapter(TypeToken.get(valueType));
|
||||
return (TypeAdapter<T>) newMultimapAdapter(keyAdapter, valueAdapter);
|
||||
}
|
||||
|
||||
protected <K,V> TypeAdapter<Multimap<K, V>> newMultimapAdapter(final TypeAdapter<K> keyAdapter, final TypeAdapter<V> valueAdapter) {
|
||||
return new TypeAdapter<Multimap<K, V>>() {
|
||||
public void write(JsonWriter out, Multimap<K, V> map) throws IOException {
|
||||
out.beginObject();
|
||||
for (K key : map.keySet()) {
|
||||
out.name(String.valueOf(key));
|
||||
out.beginArray();
|
||||
for (V value : map.get(key)) {
|
||||
valueAdapter.write(out, value);
|
||||
}
|
||||
out.endArray();
|
||||
}
|
||||
out.endObject();
|
||||
}
|
||||
|
||||
public Multimap<K, V> read(JsonReader in) throws IOException {
|
||||
ImmutableMultimap.Builder<K, V> result = ImmutableListMultimap.builder();
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
|
||||
K name = keyAdapter.read(in);
|
||||
in.beginArray();
|
||||
while (in.hasNext()) {
|
||||
V value = valueAdapter.read(in);
|
||||
if (value != null) result.put(name, value);
|
||||
}
|
||||
in.endArray();
|
||||
}
|
||||
in.endObject();
|
||||
return result.build();
|
||||
}
|
||||
}.nullSafe();
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds licenses this file
|
||||
* to you 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.jclouds.json.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
/**
|
||||
* Eliminates null values when deserializing Sets.
|
||||
* <p/>
|
||||
* Treats [null] as the empty set; [A, null] as [A]; etc.
|
||||
*
|
||||
* @author Adam Lowe
|
||||
*/
|
||||
public class IgnoreNullSetTypeAdapterFactory implements TypeAdapterFactory {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
|
||||
Type type = typeToken.getType();
|
||||
if (typeToken.getRawType() != Set.class || !(type instanceof ParameterizedType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
|
||||
return (TypeAdapter<T>) newSetAdapter(elementAdapter);
|
||||
}
|
||||
|
||||
protected <E> TypeAdapter<Set<E>> newSetAdapter(final TypeAdapter<E> elementAdapter) {
|
||||
return new TypeAdapter<Set<E>>() {
|
||||
public void write(JsonWriter out, Set<E> value) throws IOException {
|
||||
out.beginArray();
|
||||
for (E element : value) {
|
||||
elementAdapter.write(out, element);
|
||||
}
|
||||
out.endArray();
|
||||
}
|
||||
|
||||
public Set<E> read(JsonReader in) throws IOException {
|
||||
ImmutableSet.Builder<E> result = ImmutableSet.<E> builder();
|
||||
in.beginArray();
|
||||
while (in.hasNext()) {
|
||||
E element = elementAdapter.read(in);
|
||||
if (element != null)
|
||||
result.add(element);
|
||||
}
|
||||
in.endArray();
|
||||
return result.build();
|
||||
}
|
||||
}.nullSafe();
|
||||
}
|
||||
}
|
|
@ -20,49 +20,74 @@ package org.jclouds.json.internal;
|
|||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Predicates.in;
|
||||
import static com.google.common.collect.Iterables.transform;
|
||||
import static com.google.common.collect.Iterables.tryFind;
|
||||
import static org.jclouds.reflect.Reflection2.constructors;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Named;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.FieldNamingStrategy;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* NamingStrategies used for JSON deserialization using GSON
|
||||
*
|
||||
*
|
||||
* @author Adrian Cole
|
||||
* @author Adam Lowe
|
||||
*/
|
||||
public class NamingStrategies {
|
||||
/**
|
||||
* Specifies how to extract the name from an annotation for use in determining the serialized
|
||||
* name.
|
||||
*
|
||||
* Specifies how to extract the name from an annotation for use in determining the serialized name.
|
||||
*
|
||||
* @see com.google.gson.annotations.SerializedName
|
||||
* @see ExtractSerializedName
|
||||
*/
|
||||
public abstract static class NameExtractor<A extends Annotation> {
|
||||
public abstract static class NameExtractor<A extends Annotation> implements Function<Annotation, String>,
|
||||
Supplier<Predicate<Annotation>> {
|
||||
protected final Class<A> annotationType;
|
||||
protected final Predicate<Annotation> predicate;
|
||||
|
||||
protected NameExtractor(Class<A> annotationType) {
|
||||
protected NameExtractor(final Class<A> annotationType) {
|
||||
this.annotationType = checkNotNull(annotationType, "annotationType");
|
||||
this.predicate = new Predicate<Annotation>() {
|
||||
public boolean apply(Annotation input) {
|
||||
return input.getClass().equals(annotationType);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public abstract String extractName(A in);
|
||||
@SuppressWarnings("unchecked")
|
||||
public Class<Annotation> annotationType() {
|
||||
return (Class<Annotation>) annotationType;
|
||||
}
|
||||
|
||||
public Class<A> annotationType() {
|
||||
return annotationType;
|
||||
@Override
|
||||
public String apply(Annotation in) {
|
||||
return extractName(annotationType.cast(in));
|
||||
}
|
||||
|
||||
protected abstract String extractName(A cast);
|
||||
|
||||
@Override
|
||||
public Predicate<Annotation> get() {
|
||||
return predicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,7 +115,6 @@ public class NamingStrategies {
|
|||
super(SerializedName.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String extractName(SerializedName in) {
|
||||
return checkNotNull(in, "input annotation").value();
|
||||
}
|
||||
|
@ -108,22 +132,22 @@ public class NamingStrategies {
|
|||
}
|
||||
|
||||
public abstract static class AnnotationBasedNamingStrategy {
|
||||
protected final Map<Class<? extends Annotation>, ? extends NameExtractor> annotationToNameExtractor;
|
||||
private String forToString;
|
||||
protected final Map<Class<? extends Annotation>, ? extends NameExtractor<?>> annotationToNameExtractor;
|
||||
protected final String forToString;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public AnnotationBasedNamingStrategy(Iterable<? extends NameExtractor> extractors) {
|
||||
public AnnotationBasedNamingStrategy(Iterable<? extends NameExtractor<?>> extractors) {
|
||||
checkNotNull(extractors, "means to extract names by annotations");
|
||||
|
||||
this.annotationToNameExtractor = Maps.uniqueIndex(extractors, new Function<NameExtractor, Class<? extends Annotation>>() {
|
||||
this.annotationToNameExtractor = Maps.uniqueIndex(extractors,
|
||||
new Function<NameExtractor<?>, Class<? extends Annotation>>() {
|
||||
@Override
|
||||
public Class<? extends Annotation> apply(NameExtractor<?> input) {
|
||||
return input.annotationType();
|
||||
}
|
||||
});
|
||||
this.forToString = Joiner.on(",").join(transform(extractors, new Function<NameExtractor<?>, String>() {
|
||||
@Override
|
||||
public Class<? extends Annotation> apply(NameExtractor input) {
|
||||
return input.annotationType();
|
||||
}
|
||||
});
|
||||
this.forToString = Joiner.on(",").join(Iterables.transform(extractors, new Function<NameExtractor, String>() {
|
||||
@Override
|
||||
public String apply(NameExtractor input) {
|
||||
public String apply(NameExtractor<?> input) {
|
||||
return input.annotationType().getName();
|
||||
}
|
||||
}));
|
||||
|
@ -138,32 +162,30 @@ public class NamingStrategies {
|
|||
/**
|
||||
* Definition of field naming policy for annotation-based field
|
||||
*/
|
||||
public static class AnnotationFieldNamingStrategy extends AnnotationBasedNamingStrategy implements FieldNamingStrategy {
|
||||
public static class AnnotationFieldNamingStrategy extends AnnotationBasedNamingStrategy implements
|
||||
FieldNamingStrategy {
|
||||
|
||||
public AnnotationFieldNamingStrategy(Iterable<? extends NameExtractor> extractors) {
|
||||
public AnnotationFieldNamingStrategy(Iterable<? extends NameExtractor<?>> extractors) {
|
||||
super(extractors);
|
||||
checkArgument(extractors.iterator().hasNext(), "you must supply at least one name extractor, for example: "
|
||||
+ ExtractSerializedName.class.getSimpleName());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public String translateName(Field f) {
|
||||
for (Annotation annotation : f.getAnnotations()) {
|
||||
if (annotationToNameExtractor.containsKey(annotation.annotationType())) {
|
||||
return annotationToNameExtractor.get(annotation.annotationType()).extractName(annotation);
|
||||
return annotationToNameExtractor.get(annotation.annotationType()).apply(annotation);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class AnnotationOrNameFieldNamingStrategy extends AnnotationFieldNamingStrategy implements FieldNamingStrategy {
|
||||
public AnnotationOrNameFieldNamingStrategy(NameExtractor... extractors) {
|
||||
this(ImmutableSet.copyOf(extractors));
|
||||
}
|
||||
public static class AnnotationOrNameFieldNamingStrategy extends AnnotationFieldNamingStrategy implements
|
||||
FieldNamingStrategy {
|
||||
|
||||
public AnnotationOrNameFieldNamingStrategy(Iterable<? extends NameExtractor> extractors) {
|
||||
public AnnotationOrNameFieldNamingStrategy(Iterable<? extends NameExtractor<?>> extractors) {
|
||||
super(extractors);
|
||||
}
|
||||
|
||||
|
@ -174,37 +196,42 @@ public class NamingStrategies {
|
|||
}
|
||||
}
|
||||
|
||||
public static interface ConstructorFieldNamingStrategy {
|
||||
public String translateName(Constructor<?> c, int index);
|
||||
|
||||
public <T> Constructor<? super T> getDeserializationConstructor(Class<?> raw);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines field naming from constructor annotations
|
||||
*/
|
||||
public static class AnnotationConstructorNamingStrategy extends AnnotationBasedNamingStrategy implements ConstructorFieldNamingStrategy {
|
||||
private final Set<Class<? extends Annotation>> markers;
|
||||
public final static class AnnotationConstructorNamingStrategy extends AnnotationBasedNamingStrategy {
|
||||
private final Predicate<Invokable<?, ?>> hasMarker;
|
||||
private final Collection<? extends Class<? extends Annotation>> markers;
|
||||
|
||||
public AnnotationConstructorNamingStrategy(Iterable<? extends Class<? extends Annotation>> markers, Iterable<? extends NameExtractor> extractors) {
|
||||
public AnnotationConstructorNamingStrategy(Collection<? extends Class<? extends Annotation>> markers,
|
||||
Iterable<? extends NameExtractor<?>> extractors) {
|
||||
super(extractors);
|
||||
this.markers = ImmutableSet.copyOf(checkNotNull(markers, "you must supply at least one annotation to mark deserialization constructors"));
|
||||
this.markers = checkNotNull(markers,
|
||||
"you must supply at least one annotation to mark deserialization constructors");
|
||||
this.hasMarker = hasAnnotationIn(markers);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Constructor<? super T> getDeserializationConstructor(Class<?> raw) {
|
||||
for (Constructor<?> ctor : raw.getDeclaredConstructors())
|
||||
for (Class<? extends Annotation> deserializationCtorAnnotation : markers)
|
||||
if (ctor.isAnnotationPresent(deserializationCtorAnnotation))
|
||||
return (Constructor<T>) ctor;
|
||||
|
||||
return null;
|
||||
private static Predicate<Invokable<?, ?>> hasAnnotationIn(
|
||||
final Collection<? extends Class<? extends Annotation>> markers) {
|
||||
return new Predicate<Invokable<?, ?>>() {
|
||||
public boolean apply(Invokable<?, ?> input) {
|
||||
return FluentIterable.from(Arrays.asList(input.getAnnotations()))
|
||||
.transform(new Function<Annotation, Class<? extends Annotation>>() {
|
||||
public Class<? extends Annotation> apply(Annotation input) {
|
||||
return input.annotationType();
|
||||
}
|
||||
}).anyMatch(in(markers));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public String translateName(Constructor<?> c, int index) {
|
||||
@VisibleForTesting
|
||||
<T> Invokable<T, T> getDeserializer(TypeToken<T> token) {
|
||||
return tryFind(constructors(token), hasMarker).orNull();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
<T> String translateName(Invokable<T, T> c, int index) {
|
||||
String name = null;
|
||||
|
||||
if (markers.contains(ConstructorProperties.class) && c.getAnnotation(ConstructorProperties.class) != null) {
|
||||
|
@ -214,9 +241,9 @@ public class NamingStrategies {
|
|||
}
|
||||
}
|
||||
|
||||
for (Annotation annotation : c.getParameterAnnotations()[index]) {
|
||||
for (Annotation annotation : c.getParameters().get(index).getAnnotations()) {
|
||||
if (annotationToNameExtractor.containsKey(annotation.annotationType())) {
|
||||
name = annotationToNameExtractor.get(annotation.annotationType()).extractName(annotation);
|
||||
name = annotationToNameExtractor.get(annotation.annotationType()).apply(annotation);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,440 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds licenses this file
|
||||
* to you 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.jclouds.json.internal;
|
||||
|
||||
import static com.google.common.base.Objects.equal;
|
||||
import static com.google.common.base.Objects.toStringHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.internal.JsonReaderInternalAccess;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
/**
|
||||
* Eliminates null values when deserializing Collections, Maps, and Multimaps
|
||||
* <p/>
|
||||
* Treats [null] as the empty set; [A, null] as [A]; etc.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public class NullFilteringTypeAdapterFactories {
|
||||
private NullFilteringTypeAdapterFactories() {
|
||||
}
|
||||
|
||||
static <T> TypeToken<?> resolve(TypeToken<T> ownerType, Type param) {
|
||||
return TypeToken.get(com.google.gson.internal.$Gson$Types.resolve(ownerType.getType(), ownerType.getRawType(),
|
||||
param));
|
||||
}
|
||||
|
||||
public static class IterableTypeAdapterFactory implements TypeAdapterFactory {
|
||||
|
||||
protected final Class<?> declaring;
|
||||
|
||||
public IterableTypeAdapterFactory() {
|
||||
this(Iterable.class);
|
||||
}
|
||||
|
||||
protected IterableTypeAdapterFactory(Class<?> declaring) {
|
||||
this.declaring = declaring;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> ownerType) {
|
||||
Type type = ownerType.getType();
|
||||
if (ownerType.getRawType() != declaring || !(type instanceof ParameterizedType))
|
||||
return null;
|
||||
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
|
||||
return (TypeAdapter<T>) newAdapter(elementAdapter);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <E, I> TypeAdapter<I> newAdapter(TypeAdapter<E> elementAdapter) {
|
||||
return (TypeAdapter<I>) new IterableTypeAdapter<E>(elementAdapter);
|
||||
}
|
||||
|
||||
public static final class IterableTypeAdapter<E> extends TypeAdapter<Iterable<E>> {
|
||||
|
||||
private final TypeAdapter<E> elementAdapter;
|
||||
|
||||
public IterableTypeAdapter(TypeAdapter<E> elementAdapter) {
|
||||
this.elementAdapter = elementAdapter;
|
||||
nullSafe();
|
||||
}
|
||||
|
||||
public void write(JsonWriter out, Iterable<E> value) throws IOException {
|
||||
if (value == null) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
out.beginArray();
|
||||
for (E element : value)
|
||||
elementAdapter.write(out, element);
|
||||
out.endArray();
|
||||
}
|
||||
|
||||
public Iterable<E> read(JsonReader in) throws IOException {
|
||||
return readAndBuild(in, ImmutableList.<E> builder());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <C extends Iterable<E>, B extends ImmutableCollection.Builder<E>> C readAndBuild(JsonReader in,
|
||||
B builder) throws IOException {
|
||||
in.beginArray();
|
||||
while (in.hasNext()) {
|
||||
E element = elementAdapter.read(in);
|
||||
if (element != null)
|
||||
builder.add(element);
|
||||
}
|
||||
in.endArray();
|
||||
return (C) builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return elementAdapter.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
IterableTypeAdapter<?> that = IterableTypeAdapter.class.cast(obj);
|
||||
return equal(this.elementAdapter, that.elementAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toStringHelper(this).add("elementAdapter", elementAdapter).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class CollectionTypeAdapterFactory extends IterableTypeAdapterFactory {
|
||||
public CollectionTypeAdapterFactory() {
|
||||
super(Collection.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <E, I> TypeAdapter<I> newAdapter(TypeAdapter<E> elementAdapter) {
|
||||
return (TypeAdapter<I>) new CollectionTypeAdapter<E>(elementAdapter);
|
||||
}
|
||||
|
||||
public static final class CollectionTypeAdapter<E> extends TypeAdapter<Collection<E>> {
|
||||
|
||||
private final IterableTypeAdapterFactory.IterableTypeAdapter<E> delegate;
|
||||
|
||||
public CollectionTypeAdapter(TypeAdapter<E> elementAdapter) {
|
||||
this.delegate = new IterableTypeAdapterFactory.IterableTypeAdapter<E>(elementAdapter);
|
||||
nullSafe();
|
||||
}
|
||||
|
||||
public void write(JsonWriter out, Collection<E> value) throws IOException {
|
||||
this.delegate.write(out, value);
|
||||
}
|
||||
|
||||
public Collection<E> read(JsonReader in) throws IOException {
|
||||
return delegate.readAndBuild(in, ImmutableList.<E> builder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
CollectionTypeAdapter<?> that = CollectionTypeAdapter.class.cast(obj);
|
||||
return equal(this.delegate, that.delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toStringHelper(this).add("elementAdapter", delegate.elementAdapter).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class SetTypeAdapterFactory extends IterableTypeAdapterFactory {
|
||||
public SetTypeAdapterFactory() {
|
||||
super(Set.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <E, I> TypeAdapter<I> newAdapter(TypeAdapter<E> elementAdapter) {
|
||||
return (TypeAdapter<I>) new SetTypeAdapter<E>(elementAdapter);
|
||||
}
|
||||
|
||||
public static final class SetTypeAdapter<E> extends TypeAdapter<Set<E>> {
|
||||
|
||||
private final IterableTypeAdapterFactory.IterableTypeAdapter<E> delegate;
|
||||
|
||||
public SetTypeAdapter(TypeAdapter<E> elementAdapter) {
|
||||
this.delegate = new IterableTypeAdapterFactory.IterableTypeAdapter<E>(elementAdapter);
|
||||
nullSafe();
|
||||
}
|
||||
|
||||
public void write(JsonWriter out, Set<E> value) throws IOException {
|
||||
this.delegate.write(out, value);
|
||||
}
|
||||
|
||||
public Set<E> read(JsonReader in) throws IOException {
|
||||
return delegate.readAndBuild(in, ImmutableSet.<E> builder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
SetTypeAdapter<?> that = SetTypeAdapter.class.cast(obj);
|
||||
return equal(this.delegate, that.delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toStringHelper(this).add("elementAdapter", delegate.elementAdapter).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class FluentIterableTypeAdapterFactory extends IterableTypeAdapterFactory {
|
||||
public FluentIterableTypeAdapterFactory() {
|
||||
super(FluentIterable.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <E, I> TypeAdapter<I> newAdapter(TypeAdapter<E> elementAdapter) {
|
||||
return (TypeAdapter<I>) new FluentIterableTypeAdapter<E>(elementAdapter);
|
||||
}
|
||||
|
||||
public static final class FluentIterableTypeAdapter<E> extends TypeAdapter<FluentIterable<E>> {
|
||||
|
||||
private final IterableTypeAdapterFactory.IterableTypeAdapter<E> delegate;
|
||||
|
||||
public FluentIterableTypeAdapter(TypeAdapter<E> elementAdapter) {
|
||||
this.delegate = new IterableTypeAdapterFactory.IterableTypeAdapter<E>(elementAdapter);
|
||||
nullSafe();
|
||||
}
|
||||
|
||||
public void write(JsonWriter out, FluentIterable<E> value) throws IOException {
|
||||
this.delegate.write(out, value.toList());
|
||||
}
|
||||
|
||||
public FluentIterable<E> read(JsonReader in) throws IOException {
|
||||
return FluentIterable.from(delegate.read(in));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
FluentIterableTypeAdapter<?> that = FluentIterableTypeAdapter.class.cast(obj);
|
||||
return equal(this.delegate, that.delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toStringHelper(this).add("elementAdapter", delegate.elementAdapter).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class MapTypeAdapterFactory implements TypeAdapterFactory {
|
||||
|
||||
protected final Class<?> declaring;
|
||||
|
||||
public MapTypeAdapterFactory() {
|
||||
this(Map.class);
|
||||
}
|
||||
|
||||
protected MapTypeAdapterFactory(Class<?> declaring) {
|
||||
this.declaring = declaring;
|
||||
}
|
||||
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> ownerType) {
|
||||
Type type = ownerType.getType();
|
||||
if (ownerType.getRawType() != declaring || !(type instanceof ParameterizedType))
|
||||
return null;
|
||||
Type keyType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
Type valueType = ((ParameterizedType) type).getActualTypeArguments()[1];
|
||||
TypeAdapter<?> keyAdapter = gson.getAdapter(TypeToken.get(keyType));
|
||||
TypeAdapter<?> valueAdapter = gson.getAdapter(TypeToken.get(valueType));
|
||||
return newAdapter(keyAdapter, valueAdapter);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <K, V, T> TypeAdapter<T> newAdapter(TypeAdapter<K> keyAdapter, TypeAdapter<V> valueAdapter) {
|
||||
return (TypeAdapter<T>) new MapTypeAdapter<K, V>(keyAdapter, valueAdapter);
|
||||
}
|
||||
|
||||
public static final class MapTypeAdapter<K, V> extends TypeAdapter<Map<K, V>> {
|
||||
|
||||
protected final TypeAdapter<K> keyAdapter;
|
||||
protected final TypeAdapter<V> valueAdapter;
|
||||
|
||||
protected MapTypeAdapter(TypeAdapter<K> keyAdapter, TypeAdapter<V> valueAdapter) {
|
||||
this.keyAdapter = keyAdapter;
|
||||
this.valueAdapter = valueAdapter;
|
||||
nullSafe();
|
||||
}
|
||||
|
||||
public void write(JsonWriter out, Map<K, V> value) throws IOException {
|
||||
if (value == null) {
|
||||
out.nullValue();
|
||||
return;
|
||||
}
|
||||
out.beginObject();
|
||||
for (Map.Entry<K, V> element : value.entrySet()) {
|
||||
out.name(String.valueOf(element.getKey()));
|
||||
valueAdapter.write(out, element.getValue());
|
||||
}
|
||||
out.endObject();
|
||||
}
|
||||
|
||||
public Map<K, V> read(JsonReader in) throws IOException {
|
||||
ImmutableMap.Builder<K, V> result = ImmutableMap.builder();
|
||||
in.beginObject();
|
||||
while (in.hasNext()) {
|
||||
JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
|
||||
K name = keyAdapter.read(in);
|
||||
V value = valueAdapter.read(in);
|
||||
if (value != null)
|
||||
result.put(name, value);
|
||||
}
|
||||
in.endObject();
|
||||
return result.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(keyAdapter, valueAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
MapTypeAdapter<?, ?> that = MapTypeAdapter.class.cast(obj);
|
||||
return equal(this.keyAdapter, that.keyAdapter) && equal(this.valueAdapter, that.valueAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toStringHelper(this).add("keyAdapter", keyAdapter).add("valueAdapter", valueAdapter).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class MultimapTypeAdapterFactory extends MapTypeAdapterFactory {
|
||||
|
||||
public MultimapTypeAdapterFactory() {
|
||||
super(Multimap.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected <K, V, T> TypeAdapter<T> newAdapter(TypeAdapter<K> keyAdapter, TypeAdapter<V> valueAdapter) {
|
||||
return (TypeAdapter<T>) new MultimapTypeAdapter<K, V>(keyAdapter, valueAdapter);
|
||||
}
|
||||
|
||||
public static final class MultimapTypeAdapter<K, V> extends TypeAdapter<Multimap<K, V>> {
|
||||
|
||||
private final MapTypeAdapterFactory.MapTypeAdapter<K, Collection<V>> delegate;
|
||||
|
||||
public MultimapTypeAdapter(TypeAdapter<K> keyAdapter, TypeAdapter<V> valueAdapter) {
|
||||
this.delegate = new MapTypeAdapterFactory.MapTypeAdapter<K, Collection<V>>(keyAdapter,
|
||||
new CollectionTypeAdapterFactory.CollectionTypeAdapter<V>(valueAdapter));
|
||||
nullSafe();
|
||||
}
|
||||
|
||||
public void write(JsonWriter out, Multimap<K, V> value) throws IOException {
|
||||
this.delegate.write(out, value.asMap());
|
||||
}
|
||||
|
||||
public Multimap<K, V> read(JsonReader in) throws IOException {
|
||||
ImmutableMultimap.Builder<K, V> builder = ImmutableMultimap.<K, V> builder();
|
||||
for (Entry<K, Collection<V>> entry : delegate.read(in).entrySet())
|
||||
builder.putAll(entry.getKey(), entry.getValue());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return delegate.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
MultimapTypeAdapter<?, ?> that = MultimapTypeAdapter.class.cast(obj);
|
||||
return equal(this.delegate, that.delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toStringHelper(this).add("keyAdapter", delegate.keyAdapter)
|
||||
.add("valueAdapter", delegate.valueAdapter).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,19 +19,18 @@
|
|||
package org.jclouds.lifecycle.config;
|
||||
|
||||
import static com.google.common.base.Throwables.propagate;
|
||||
import static com.google.common.collect.Sets.newHashSet;
|
||||
import static com.google.common.collect.Iterables.filter;
|
||||
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
|
||||
import static com.google.inject.matcher.Matchers.any;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.jclouds.Constants.PROPERTY_IO_WORKER_THREADS;
|
||||
import static org.jclouds.Constants.PROPERTY_SCHEDULER_THREADS;
|
||||
import static org.jclouds.Constants.PROPERTY_USER_THREADS;
|
||||
import static org.jclouds.reflect.Reflection2.methods;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
@ -40,6 +39,8 @@ import javax.inject.Named;
|
|||
|
||||
import org.jclouds.lifecycle.Closer;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.util.concurrent.ExecutionList;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import com.google.inject.AbstractModule;
|
||||
|
@ -51,15 +52,15 @@ import com.google.inject.spi.TypeEncounter;
|
|||
import com.google.inject.spi.TypeListener;
|
||||
|
||||
/**
|
||||
* This associates java lifecycle annotations with guice hooks. For example, we invoke
|
||||
* {@link PostConstruct} after injection, and Associate {@link PreDestroy} with a global
|
||||
* {@link Closer} object.
|
||||
* This associates java lifecycle annotations with guice hooks. For example, we invoke {@link PostConstruct} after
|
||||
* injection, and Associate {@link PreDestroy} with a global {@link Closer} object.
|
||||
*
|
||||
* <h3>Important</h3> Make sure you create your injector with {@link Stage#PRODUCTION} and execute
|
||||
* the bound {@link ExecutionList} prior to using any other objects.
|
||||
* <h3>Important</h3> Make sure you create your injector with {@link Stage#PRODUCTION} and execute the bound
|
||||
* {@link ExecutionList} prior to using any other objects.
|
||||
*
|
||||
* <p/>
|
||||
* Ex.
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* </pre>
|
||||
|
@ -89,7 +90,7 @@ public class LifeCycleModule extends AbstractModule {
|
|||
ioExecutor.shutdownNow();
|
||||
// ScheduledExecutor is defined in an optional module
|
||||
if (scheduledExecutor != null)
|
||||
scheduledExecutor.shutdownNow();
|
||||
scheduledExecutor.shutdownNow();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -103,67 +104,57 @@ public class LifeCycleModule extends AbstractModule {
|
|||
bind(ExecutionList.class).toInstance(list);
|
||||
}
|
||||
|
||||
private static final Predicate<Invokable<?, ?>> isPreDestroy = new Predicate<Invokable<?, ?>>() {
|
||||
public boolean apply(Invokable<?, ?> in) {
|
||||
return in.isAnnotationPresent(PreDestroy.class);
|
||||
}
|
||||
};
|
||||
|
||||
private static final Predicate<Invokable<?, ?>> isPostConstruct = new Predicate<Invokable<?, ?>>() {
|
||||
public boolean apply(Invokable<?, ?> in) {
|
||||
return in.isAnnotationPresent(PostConstruct.class);
|
||||
}
|
||||
};
|
||||
|
||||
protected void bindPostInjectionInvoke(final Closer closer, final ExecutionList list) {
|
||||
bindListener(any(), new TypeListener() {
|
||||
public <I> void hear(TypeLiteral<I> injectableType, TypeEncounter<I> encounter) {
|
||||
Set<Method> methods = newHashSet();
|
||||
Class<? super I> type = injectableType.getRawType();
|
||||
while (type != null) {
|
||||
methods.addAll(asList(type.getDeclaredMethods()));
|
||||
type = type.getSuperclass();
|
||||
}
|
||||
for (final Method method : methods) {
|
||||
invokePostConstructMethodAfterInjection(encounter, method);
|
||||
associatePreDestroyWithCloser(closer, encounter, method);
|
||||
}
|
||||
}
|
||||
|
||||
private <I> void associatePreDestroyWithCloser(final Closer closer, TypeEncounter<I> encounter,
|
||||
final Method method) {
|
||||
PreDestroy preDestroy = method.getAnnotation(PreDestroy.class);
|
||||
if (preDestroy != null) {
|
||||
encounter.register(new InjectionListener<I>() {
|
||||
public void afterInjection(final I injectee) {
|
||||
closer.addToClose(new Closeable() {
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
method.invoke(injectee);
|
||||
} catch (InvocationTargetException ie) {
|
||||
Throwable e = ie.getTargetException();
|
||||
throw new IOException(e.getMessage());
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private <I> void invokePostConstructMethodAfterInjection(TypeEncounter<I> encounter, final Method method) {
|
||||
PostConstruct postConstruct = method.getAnnotation(PostConstruct.class);
|
||||
if (postConstruct != null) {
|
||||
Collection<? extends Invokable<? super I, Object>> methods = methods(injectableType.getRawType());
|
||||
for (final Invokable<? super I, Object> method : filter(methods, isPostConstruct)) {
|
||||
encounter.register(new InjectionListener<I>() {
|
||||
public void afterInjection(final I injectee) {
|
||||
list.add(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
method.invoke(injectee);
|
||||
} catch (InvocationTargetException ie) {
|
||||
Throwable e = ie.getTargetException();
|
||||
throw propagate(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw propagate(e);
|
||||
}
|
||||
invokeOnInjectee(method, injectee);
|
||||
}
|
||||
|
||||
}, sameThreadExecutor());
|
||||
}
|
||||
});
|
||||
}
|
||||
for (final Invokable<? super I, Object> method : filter(methods, isPreDestroy)) {
|
||||
encounter.register(new InjectionListener<I>() {
|
||||
public void afterInjection(final I injectee) {
|
||||
closer.addToClose(new Closeable() {
|
||||
public void close() throws IOException {
|
||||
invokeOnInjectee(method, injectee);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private static <I> void invokeOnInjectee(Invokable<? super I, Object> method, I injectee) {
|
||||
try {
|
||||
method.invoke(injectee);
|
||||
} catch (InvocationTargetException ie) {
|
||||
throw propagate(ie.getTargetException());
|
||||
} catch (IllegalAccessException e) {
|
||||
throw propagate(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,23 +19,33 @@
|
|||
package org.jclouds.reflect;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.collect.Iterables.toArray;
|
||||
import static com.google.common.base.Throwables.propagate;
|
||||
import static com.google.common.collect.Iterables.tryFind;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMap.Builder;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.reflect.Parameter;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
|
||||
/**
|
||||
* Utilities that allow access to {@link Invokable}s with {@link Invokable#getOwnerType() owner types}.
|
||||
|
@ -43,14 +53,50 @@ import com.google.common.reflect.TypeToken;
|
|||
* @since 1.6
|
||||
*/
|
||||
@Beta
|
||||
public final class Reflection2 {
|
||||
public class Reflection2 {
|
||||
|
||||
/**
|
||||
* gets a {@link TypeToken} for the given type.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> TypeToken<T> typeToken(Type in) {
|
||||
return (TypeToken<T>) get(typeTokenForType, checkNotNull(in, "class"));
|
||||
}
|
||||
|
||||
/**
|
||||
* gets a {@link TypeToken} for the given class.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> TypeToken<T> typeToken(Class<T> in) {
|
||||
return (TypeToken<T>) typeTokenForClass.apply(checkNotNull(in, "class"));
|
||||
return (TypeToken<T>) get(typeTokenForClass, checkNotNull(in, "class"));
|
||||
}
|
||||
|
||||
/**
|
||||
* returns an {@link Invokable} object that reflects a constructor present in the {@link TypeToken} type.
|
||||
*
|
||||
* @param ownerType
|
||||
* corresponds to {@link Invokable#getOwnerType()}
|
||||
* @param parameterTypes
|
||||
* corresponds to {@link Constructor#getParameterTypes()}
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* if the constructor doesn't exist or a security exception occurred
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Invokable<T, T> constructor(Class<T> ownerType, Class<?>... parameterTypes) {
|
||||
return (Invokable<T, T>) get(constructorForParams, new TypeTokenAndParameterTypes(typeToken(ownerType),
|
||||
parameterTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* return all constructors present in the class as {@link Invokable}s.
|
||||
*
|
||||
* @param ownerType
|
||||
* corresponds to {@link Invokable#getOwnerType()}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Collection<Invokable<T, T>> constructors(TypeToken<T> ownerType) {
|
||||
return Collection.class.cast(get(constructorsForTypeToken, ownerType));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,7 +109,7 @@ public final class Reflection2 {
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T, R> Invokable<T, R> method(TypeToken<T> ownerType, Method method) {
|
||||
return (Invokable<T, R>) methods.apply(new TypeTokenAndMethod(ownerType, method));
|
||||
return (Invokable<T, R>) method(ownerType.getRawType(), method.getName(), method.getParameterTypes());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,7 +125,7 @@ public final class Reflection2 {
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T, R> Invokable<T, R> method(Class<T> ownerType, String name, Class<?>... parameterTypes) {
|
||||
return (Invokable<T, R>) methodForArgs.apply(new TypeTokenNameAndParameterTypes(typeToken(ownerType), name,
|
||||
return (Invokable<T, R>) get(methodForParams, new TypeTokenNameAndParameterTypes(typeToken(ownerType), name,
|
||||
parameterTypes));
|
||||
}
|
||||
|
||||
|
@ -91,51 +137,65 @@ public final class Reflection2 {
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Collection<Invokable<T, Object>> methods(Class<T> ownerType) {
|
||||
return Collection.class.cast(methodsForTypeToken.apply(typeToken(ownerType)).values());
|
||||
return Collection.class.cast(get(methodsForTypeToken, typeToken(ownerType)));
|
||||
}
|
||||
|
||||
private static final LoadingCache<TypeTokenAndMethod, Invokable<?, ?>> methods = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<TypeTokenAndMethod, Invokable<?, ?>>() {
|
||||
public Invokable<?, ?> load(TypeTokenAndMethod key) {
|
||||
return key.type.method(key.method);
|
||||
/**
|
||||
* this gets all declared constructors, not just public ones. makes them accessible, as well.
|
||||
*/
|
||||
private static LoadingCache<TypeToken<?>, Set<Invokable<?, ?>>> constructorsForTypeToken = CacheBuilder
|
||||
.newBuilder().build(new CacheLoader<TypeToken<?>, Set<Invokable<?, ?>>>() {
|
||||
public Set<Invokable<?, ?>> load(TypeToken<?> key) {
|
||||
ImmutableSet.Builder<Invokable<?, ?>> builder = ImmutableSet.<Invokable<?, ?>> builder();
|
||||
for (Constructor<?> ctor : key.getRawType().getDeclaredConstructors()) {
|
||||
ctor.setAccessible(true);
|
||||
builder.add(key.constructor(ctor));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
});
|
||||
|
||||
private static class TypeTokenAndMethod {
|
||||
|
||||
protected final TypeToken<?> type;
|
||||
protected final Method method;
|
||||
|
||||
public TypeTokenAndMethod(TypeToken<?> type, Method method) {
|
||||
this.type = checkNotNull(type, "type");
|
||||
this.method = checkNotNull(method, "method");
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(type, method);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
TypeTokenAndMethod that = TypeTokenAndMethod.class.cast(obj);
|
||||
return Objects.equal(this.type, that.type) && Objects.equal(this.method, that.method);
|
||||
}
|
||||
protected static List<Class<?>> toClasses(ImmutableList<Parameter> params) {
|
||||
return Lists.transform(params, new Function<Parameter, Class<?>>() {
|
||||
public Class<?> apply(Parameter input) {
|
||||
return input.getType().getRawType();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static final LoadingCache<Class<?>, TypeToken<?>> typeTokenForClass = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<Class<?>, TypeToken<?>>() {
|
||||
public TypeToken<?> load(final Class<?> key) {
|
||||
private static LoadingCache<Type, TypeToken<?>> typeTokenForType = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<Type, TypeToken<?>>() {
|
||||
public TypeToken<?> load(Type key) {
|
||||
return TypeToken.of(key);
|
||||
}
|
||||
});
|
||||
|
||||
private static LoadingCache<Class<?>, TypeToken<?>> typeTokenForClass = CacheBuilder.newBuilder().build(
|
||||
new CacheLoader<Class<?>, TypeToken<?>>() {
|
||||
public TypeToken<?> load(Class<?> key) {
|
||||
return TypeToken.of(key);
|
||||
}
|
||||
});
|
||||
|
||||
private static LoadingCache<TypeTokenAndParameterTypes, Invokable<?, ?>> constructorForParams = CacheBuilder
|
||||
.newBuilder().build(new CacheLoader<TypeTokenAndParameterTypes, Invokable<?, ?>>() {
|
||||
public Invokable<?, ?> load(final TypeTokenAndParameterTypes key) {
|
||||
Set<Invokable<?, ?>> constructors = get(constructorsForTypeToken, key.type);
|
||||
Optional<Invokable<?, ?>> constructor = tryFind(constructors, new Predicate<Invokable<?, ?>>() {
|
||||
public boolean apply(Invokable<?, ?> input) {
|
||||
return Objects.equal(toClasses(input.getParameters()), key.parameterTypes);
|
||||
}
|
||||
});
|
||||
if (constructor.isPresent())
|
||||
return constructor.get();
|
||||
throw new IllegalArgumentException("no such constructor " + key.toString() + "in: " + constructors);
|
||||
}
|
||||
});
|
||||
|
||||
private static class TypeTokenAndParameterTypes {
|
||||
|
||||
protected final TypeToken<?> type;
|
||||
protected final List<Class<?>> parameterTypes;
|
||||
protected TypeToken<?> type;
|
||||
protected List<Class<?>> parameterTypes;
|
||||
|
||||
public TypeTokenAndParameterTypes(TypeToken<?> type, Class<?>... parameterTypes) {
|
||||
this.type = checkNotNull(type, "type");
|
||||
|
@ -160,23 +220,25 @@ public final class Reflection2 {
|
|||
}
|
||||
}
|
||||
|
||||
private static final LoadingCache<TypeTokenNameAndParameterTypes, Invokable<?, ?>> methodForArgs = CacheBuilder
|
||||
private static LoadingCache<TypeTokenNameAndParameterTypes, Invokable<?, ?>> methodForParams = CacheBuilder
|
||||
.newBuilder().build(new CacheLoader<TypeTokenNameAndParameterTypes, Invokable<?, ?>>() {
|
||||
public Invokable<?, ?> load(final TypeTokenNameAndParameterTypes key) {
|
||||
try {
|
||||
Method method = key.type.getRawType().getMethod(key.name, toArray(key.parameterTypes, Class.class));
|
||||
return methods.apply(new TypeTokenAndMethod(key.type, method));
|
||||
} catch (SecurityException e) {
|
||||
throw new IllegalArgumentException(e.getMessage() + " getting method " + key.toString(), e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new IllegalArgumentException("no such method " + key.toString(), e);
|
||||
}
|
||||
Set<Invokable<?, ?>> methods = get(methodsForTypeToken, key.type);
|
||||
Optional<Invokable<?, ?>> method = tryFind(methods, new Predicate<Invokable<?, ?>>() {
|
||||
public boolean apply(Invokable<?, ?> input) {
|
||||
return Objects.equal(input.getName(), key.name)
|
||||
&& Objects.equal(toClasses(input.getParameters()), key.parameterTypes);
|
||||
}
|
||||
});
|
||||
if (method.isPresent())
|
||||
return method.get();
|
||||
throw new IllegalArgumentException("no such method " + key.toString() + "in: " + methods);
|
||||
}
|
||||
});
|
||||
|
||||
private static class TypeTokenNameAndParameterTypes extends TypeTokenAndParameterTypes {
|
||||
|
||||
private final String name;
|
||||
private String name;
|
||||
|
||||
public TypeTokenNameAndParameterTypes(TypeToken<?> type, String name, Class<?>... parameterTypes) {
|
||||
super(type, parameterTypes);
|
||||
|
@ -201,14 +263,36 @@ public final class Reflection2 {
|
|||
}
|
||||
}
|
||||
|
||||
private static final LoadingCache<TypeToken<?>, Map<Method, Invokable<?, ?>>> methodsForTypeToken = CacheBuilder
|
||||
.newBuilder().build(new CacheLoader<TypeToken<?>, Map<Method, Invokable<?, ?>>>() {
|
||||
public Map<Method, Invokable<?, ?>> load(final TypeToken<?> key) {
|
||||
Builder<Method, Invokable<?, ?>> builder = ImmutableMap.<Method, Invokable<?, ?>> builder();
|
||||
for (Method method : key.getRawType().getMethods())
|
||||
builder.put(method, method(key, method));
|
||||
/**
|
||||
* this gets all declared methods, not just public ones. makes them accessible. Does not include Object methods.
|
||||
*/
|
||||
private static LoadingCache<TypeToken<?>, Set<Invokable<?, ?>>> methodsForTypeToken = CacheBuilder
|
||||
.newBuilder().build(new CacheLoader<TypeToken<?>, Set<Invokable<?, ?>>>() {
|
||||
public Set<Invokable<?, ?>> load(TypeToken<?> key) {
|
||||
ImmutableSet.Builder<Invokable<?, ?>> builder = ImmutableSet.<Invokable<?, ?>> builder();
|
||||
for (TypeToken<?> token : key.getTypes()) {
|
||||
Class<?> raw = token.getRawType();
|
||||
if (raw == Object.class)
|
||||
continue;
|
||||
for (Method method : raw.getDeclaredMethods()) {
|
||||
method.setAccessible(true);
|
||||
builder.add(key.method(method));
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* ensures that exceptions are not doubly-wrapped
|
||||
*/
|
||||
private static <K, V> V get(LoadingCache<K, V> cache, K key) {
|
||||
try {
|
||||
return cache.get(key);
|
||||
} catch (UncheckedExecutionException e) {
|
||||
throw propagate(e.getCause());
|
||||
} catch (ExecutionException e) {
|
||||
throw propagate(e.getCause());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,6 @@ package org.jclouds.rest.config;
|
|||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Throwables.propagate;
|
||||
import static com.google.common.collect.Iterables.toArray;
|
||||
import static com.google.common.collect.Iterables.transform;
|
||||
import static com.google.common.collect.Maps.transformValues;
|
||||
|
@ -32,11 +31,9 @@ import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
|
|||
import static org.jclouds.util.Maps2.transformKeys;
|
||||
import static org.jclouds.util.Predicates2.startsWith;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.Proxy;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.inject.Named;
|
||||
|
@ -47,7 +44,6 @@ import org.jclouds.http.functions.config.SaxParserModule;
|
|||
import org.jclouds.internal.FilterStringsBoundToInjectorByName;
|
||||
import org.jclouds.json.config.GsonModule;
|
||||
import org.jclouds.location.config.LocationModule;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import org.jclouds.proxy.ProxyForURI;
|
||||
import org.jclouds.rest.AuthorizationException;
|
||||
import org.jclouds.rest.HttpAsyncClient;
|
||||
|
@ -55,13 +51,14 @@ import org.jclouds.rest.HttpClient;
|
|||
import org.jclouds.rest.binders.BindToJsonPayloadWrappedWith;
|
||||
import org.jclouds.rest.internal.BlockOnFuture;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.reflect.Parameter;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
|
@ -80,8 +77,6 @@ public class RestModule extends AbstractModule {
|
|||
this(ImmutableMap.<Class<?>, Class<?>> of());
|
||||
}
|
||||
|
||||
private static final Set<Method> objectMethods = ImmutableSet.copyOf(Object.class.getMethods());
|
||||
|
||||
public RestModule(Map<Class<?>, Class<?>> sync2Async) {
|
||||
this.sync2Async = sync2Async;
|
||||
}
|
||||
|
@ -92,6 +87,11 @@ public class RestModule extends AbstractModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
protected Cache<Invokable<?, ?>, Invokable<?, ?>> seedKnownSync2AsyncInvokables() {
|
||||
return seedKnownSync2AsyncInvokables(sync2Async);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static Cache<Invokable<?, ?>, Invokable<?, ?>> seedKnownSync2AsyncInvokables(Map<Class<?>, Class<?>> sync2Async) {
|
||||
Cache<Invokable<?, ?>, Invokable<?, ?>> sync2AsyncBuilder = CacheBuilder.newBuilder().build();
|
||||
putInvokables(HttpClient.class, HttpAsyncClient.class, sync2AsyncBuilder);
|
||||
for (Class<?> s : sync2Async.keySet()) {
|
||||
|
@ -103,18 +103,10 @@ public class RestModule extends AbstractModule {
|
|||
// accessible for ClientProvider
|
||||
public static void putInvokables(Class<?> sync, Class<?> async, Cache<Invokable<?, ?>, Invokable<?, ?>> cache) {
|
||||
for (Invokable<?, ?> invoked : methods(sync)) {
|
||||
if (!objectMethods.contains(invoked)) {
|
||||
try {
|
||||
Invokable<?, ?> delegatedMethod = method(async, invoked.getName(), getParameterTypes(invoked));
|
||||
checkArgument(delegatedMethod.getExceptionTypes().equals(invoked.getExceptionTypes()),
|
||||
"invoked %s has different typed exceptions than delegated invoked %s", invoked, delegatedMethod);
|
||||
invoked.setAccessible(true);
|
||||
delegatedMethod.setAccessible(true);
|
||||
cache.put(invoked, delegatedMethod);
|
||||
} catch (SecurityException e) {
|
||||
throw propagate(e);
|
||||
}
|
||||
}
|
||||
Invokable<?, ?> delegatedMethod = method(async, invoked.getName(), getParameterTypes(invoked));
|
||||
checkArgument(delegatedMethod.getExceptionTypes().equals(invoked.getExceptionTypes()),
|
||||
"invoked %s has different typed exceptions than target %s", invoked, delegatedMethod);
|
||||
cache.put(invoked, delegatedMethod);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,14 +19,13 @@
|
|||
package org.jclouds.rest.config;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.inject.name.Names.named;
|
||||
|
||||
import org.jclouds.reflect.Invocation;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
/**
|
||||
* Allows the provider to supply a value set in a threadlocal.
|
||||
|
@ -55,8 +54,7 @@ public class SetCaller {
|
|||
}
|
||||
}
|
||||
|
||||
private static final Key<Invocation> CALLER_INVOCATION = Key.get(new TypeLiteral<Invocation>() {
|
||||
}, Names.named("caller"));
|
||||
private static final Key<Invocation> CALLER_INVOCATION = Key.get(Invocation.class, named("caller"));
|
||||
|
||||
class CallerInvocationProvider implements Provider<Invocation> {
|
||||
@Override
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
package org.jclouds.json.internal;
|
||||
|
||||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
|
@ -54,6 +55,7 @@ import com.google.gson.reflect.TypeToken;
|
|||
* @author Adam Lowe
|
||||
*/
|
||||
@Test(testName = "DeserializationConstructorTypeAdapterFactoryTest")
|
||||
@SuppressWarnings("unused")
|
||||
public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest {
|
||||
|
||||
Gson gson = new Gson();
|
||||
|
@ -61,13 +63,10 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
|||
DeserializationConstructorAndReflectiveTypeAdapterFactory parameterizedCtorFactory = parameterizedCtorFactory();
|
||||
|
||||
static DeserializationConstructorAndReflectiveTypeAdapterFactory parameterizedCtorFactory() {
|
||||
FieldNamingStrategy serializationPolicy = new AnnotationOrNameFieldNamingStrategy(
|
||||
ImmutableSet.of(new ExtractSerializedName(), new ExtractNamed())
|
||||
);
|
||||
NamingStrategies.AnnotationConstructorNamingStrategy deserializationPolicy =
|
||||
new NamingStrategies.AnnotationConstructorNamingStrategy(
|
||||
ImmutableSet.of(ConstructorProperties.class, Inject.class),
|
||||
ImmutableSet.of(new ExtractNamed()));
|
||||
FieldNamingStrategy serializationPolicy = new AnnotationOrNameFieldNamingStrategy(ImmutableSet.of(
|
||||
new ExtractSerializedName(), new ExtractNamed()));
|
||||
NamingStrategies.AnnotationConstructorNamingStrategy deserializationPolicy = new NamingStrategies.AnnotationConstructorNamingStrategy(
|
||||
ImmutableSet.of(ConstructorProperties.class, Inject.class), ImmutableSet.of(new ExtractNamed()));
|
||||
|
||||
return new DeserializationConstructorAndReflectiveTypeAdapterFactory(new ConstructorConstructor(),
|
||||
serializationPolicy, Excluder.DEFAULT, deserializationPolicy);
|
||||
|
@ -83,25 +82,11 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
|||
|
||||
private DefaultConstructor() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (obj == this)
|
||||
return true;
|
||||
DefaultConstructor other = DefaultConstructor.class.cast(obj);
|
||||
if (bar != other.bar)
|
||||
return false;
|
||||
if (foo != other.foo)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void testRejectsIfNoConstuctorMarked() throws IOException {
|
||||
TypeAdapter<DefaultConstructor> adapter = parameterizedCtorFactory.create(gson, TypeToken.get(DefaultConstructor.class));
|
||||
TypeAdapter<DefaultConstructor> adapter = parameterizedCtorFactory.create(gson,
|
||||
TypeToken.get(DefaultConstructor.class));
|
||||
assertNull(adapter);
|
||||
}
|
||||
|
||||
|
@ -114,16 +99,10 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = ".* parameter 0 failed to be named by AnnotationBasedNamingStrategy requiring one of javax.inject.Named")
|
||||
public void testSerializedNameRequiredOnAllParameters() {
|
||||
try {
|
||||
parameterizedCtorFactory.create(gson, TypeToken
|
||||
.get(WithDeserializationConstructorButWithoutSerializedName.class));
|
||||
fail();
|
||||
} catch (IllegalArgumentException actual) {
|
||||
assertEquals(actual.getMessage(),
|
||||
"org.jclouds.json.internal.DeserializationConstructorAndReflectiveTypeAdapterFactoryTest$WithDeserializationConstructorButWithoutSerializedName(int)" +
|
||||
" parameter 0 failed to be named by AnnotationBasedNamingStrategy requiring one of javax.inject.Named");
|
||||
}
|
||||
parameterizedCtorFactory
|
||||
.create(gson, TypeToken.get(WithDeserializationConstructorButWithoutSerializedName.class));
|
||||
}
|
||||
|
||||
private static class DuplicateSerializedNames {
|
||||
|
@ -137,15 +116,9 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "duplicate key: foo")
|
||||
public void testNoDuplicateSerializedNamesRequiredOnAllParameters() {
|
||||
try {
|
||||
parameterizedCtorFactory.create(gson, TypeToken.get(DuplicateSerializedNames.class));
|
||||
fail();
|
||||
} catch (IllegalArgumentException actual) {
|
||||
assertEquals(actual.getMessage(),
|
||||
"org.jclouds.json.internal.DeserializationConstructorAndReflectiveTypeAdapterFactoryTest$DuplicateSerializedNames(int,int)" +
|
||||
" declares multiple JSON parameters named foo");
|
||||
}
|
||||
parameterizedCtorFactory.create(gson, TypeToken.get(DuplicateSerializedNames.class));
|
||||
}
|
||||
|
||||
private static class ValidatedConstructor {
|
||||
|
@ -160,34 +133,18 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
|||
this.bar = bar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (obj == this)
|
||||
return true;
|
||||
ValidatedConstructor other = ValidatedConstructor.class.cast(obj);
|
||||
if (bar != other.bar)
|
||||
return false;
|
||||
if (foo != other.foo)
|
||||
return false;
|
||||
return true;
|
||||
return other != null && Objects.equal(foo, other.foo) && Objects.equal(bar, other.bar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { return "ValidatedConstructor[foo=" + foo + ",bar=" + bar + "]"; }
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "negative!")
|
||||
public void testValidatedConstructor() throws IOException {
|
||||
TypeAdapter<ValidatedConstructor> adapter = parameterizedCtorFactory.create(gson, TypeToken
|
||||
.get(ValidatedConstructor.class));
|
||||
TypeAdapter<ValidatedConstructor> adapter = parameterizedCtorFactory.create(gson,
|
||||
TypeToken.get(ValidatedConstructor.class));
|
||||
assertEquals(new ValidatedConstructor(0, 1), adapter.fromJson("{\"foo\":0,\"bar\":1}"));
|
||||
try {
|
||||
adapter.fromJson("{\"foo\":-1,\"bar\":1}");
|
||||
fail();
|
||||
} catch (IllegalArgumentException expected) {
|
||||
assertEquals("negative!", expected.getMessage());
|
||||
}
|
||||
adapter.fromJson("{\"foo\":-1,\"bar\":1}");
|
||||
}
|
||||
|
||||
private static class GenericParamsCopiedIn {
|
||||
|
@ -199,12 +156,11 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
|||
this.foo = Lists.newArrayList(foo);
|
||||
this.bar = Maps.newHashMap(bar);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void testGenericParamsCopiedIn() throws IOException {
|
||||
TypeAdapter<GenericParamsCopiedIn> adapter = parameterizedCtorFactory.create(gson, TypeToken
|
||||
.get(GenericParamsCopiedIn.class));
|
||||
TypeAdapter<GenericParamsCopiedIn> adapter = parameterizedCtorFactory.create(gson,
|
||||
TypeToken.get(GenericParamsCopiedIn.class));
|
||||
List<String> inputFoo = Lists.newArrayList();
|
||||
inputFoo.add("one");
|
||||
Map<String, String> inputBar = Maps.newHashMap();
|
||||
|
@ -222,29 +178,27 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
|||
@Named("_bar")
|
||||
final int bar;
|
||||
|
||||
@ConstructorProperties({"foo", "_bar"})
|
||||
@ConstructorProperties({ "foo", "_bar" })
|
||||
RenamedFields(int foo, int bar) {
|
||||
if (foo < 0)
|
||||
throw new IllegalArgumentException("negative!");
|
||||
this.foo = foo;
|
||||
this.bar = bar;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (obj == this)
|
||||
return true;
|
||||
RenamedFields other = RenamedFields.class.cast(obj);
|
||||
if (bar != other.bar)
|
||||
return false;
|
||||
if (foo != other.foo)
|
||||
return false;
|
||||
return true;
|
||||
return other != null && Objects.equal(foo, other.foo) && Objects.equal(bar, other.bar);
|
||||
}
|
||||
}
|
||||
|
||||
public void testCanOverrideDefault() throws IOException {
|
||||
Gson gson = new GsonBuilder().registerTypeAdapterFactory(parameterizedCtorFactory).create();
|
||||
|
||||
assertEquals(new RenamedFields(0, 1), gson.fromJson("{\"foo\":0,\"_bar\":1}", RenamedFields.class));
|
||||
assertEquals(gson.toJson(new RenamedFields(0, 1)), "{\"foo\":0,\"_bar\":1}");
|
||||
}
|
||||
|
||||
public void testRenamedFields() throws IOException {
|
||||
TypeAdapter<RenamedFields> adapter = parameterizedCtorFactory.create(gson, TypeToken.get(RenamedFields.class));
|
||||
assertEquals(new RenamedFields(0, 1), adapter.fromJson("{\"foo\":0,\"_bar\":1}"));
|
||||
|
@ -255,44 +209,38 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
|||
final ValidatedConstructor x;
|
||||
final ValidatedConstructor y;
|
||||
|
||||
@ConstructorProperties({"x", "y"})
|
||||
@ConstructorProperties({ "x", "y" })
|
||||
ComposedObjects(ValidatedConstructor x, ValidatedConstructor y) {
|
||||
this.x = checkNotNull(x);
|
||||
this.y = checkNotNull(y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
ComposedObjects other = ComposedObjects.class.cast(obj);
|
||||
return other != null && Objects.equal(x, other.x) && Objects.equal(y, other.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { return "ComposedObjects[x=" + x.toString() + ";y=" + y.toString() + "]"; }
|
||||
}
|
||||
|
||||
public void checkSimpleComposedObject() throws IOException {
|
||||
ValidatedConstructor x = new ValidatedConstructor(0,1);
|
||||
ValidatedConstructor y = new ValidatedConstructor(1,2);
|
||||
TypeAdapter<ComposedObjects> adapter = parameterizedCtorFactory.create(gson, TypeToken.get(ComposedObjects.class));
|
||||
assertEquals(new ComposedObjects(x, y), adapter.fromJson("{\"x\":{\"foo\":0,\"bar\":1},\"y\":{\"foo\":1,\"bar\":2}}"));
|
||||
|
||||
public void checkSimpleComposedObject() throws IOException {
|
||||
ValidatedConstructor x = new ValidatedConstructor(0, 1);
|
||||
ValidatedConstructor y = new ValidatedConstructor(1, 2);
|
||||
TypeAdapter<ComposedObjects> adapter = parameterizedCtorFactory
|
||||
.create(gson, TypeToken.get(ComposedObjects.class));
|
||||
assertEquals(new ComposedObjects(x, y),
|
||||
adapter.fromJson("{\"x\":{\"foo\":0,\"bar\":1},\"y\":{\"foo\":1,\"bar\":2}}"));
|
||||
}
|
||||
|
||||
public void testEmptyObjectIsNull() throws IOException {
|
||||
TypeAdapter<ComposedObjects> adapter = parameterizedCtorFactory.create(gson, TypeToken.get(ComposedObjects.class));
|
||||
TypeAdapter<ComposedObjects> adapter = parameterizedCtorFactory
|
||||
.create(gson, TypeToken.get(ComposedObjects.class));
|
||||
assertNull(adapter.fromJson("{}"));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
public void testPartialObjectStillThrows() throws IOException {
|
||||
TypeAdapter<ComposedObjects> adapter = parameterizedCtorFactory.create(gson, TypeToken.get(ComposedObjects.class));
|
||||
TypeAdapter<ComposedObjects> adapter = parameterizedCtorFactory
|
||||
.create(gson, TypeToken.get(ComposedObjects.class));
|
||||
assertNull(adapter.fromJson("{\"x\":{\"foo\":0,\"bar\":1}}"));
|
||||
}
|
||||
|
||||
public void testCanOverrideDefault() throws IOException {
|
||||
Gson gson = new GsonBuilder().registerTypeAdapterFactory(parameterizedCtorFactory).create();
|
||||
|
||||
assertEquals(new RenamedFields(0, 1), gson.fromJson("{\"foo\":0,\"_bar\":1}", RenamedFields.class));
|
||||
assertEquals(gson.toJson(new RenamedFields(0, 1)), "{\"foo\":0,\"_bar\":1}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,13 +18,13 @@ package org.jclouds.json.internal;
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import static org.jclouds.reflect.Reflection2.typeToken;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
import static org.testng.Assert.assertNull;
|
||||
import static org.testng.Assert.fail;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
@ -32,13 +32,13 @@ import javax.inject.Named;
|
|||
import org.jclouds.json.internal.NamingStrategies.AnnotationConstructorNamingStrategy;
|
||||
import org.jclouds.json.internal.NamingStrategies.AnnotationFieldNamingStrategy;
|
||||
import org.jclouds.json.internal.NamingStrategies.AnnotationOrNameFieldNamingStrategy;
|
||||
import org.jclouds.json.internal.NamingStrategies.ConstructorFieldNamingStrategy;
|
||||
import org.jclouds.json.internal.NamingStrategies.ExtractNamed;
|
||||
import org.jclouds.json.internal.NamingStrategies.ExtractSerializedName;
|
||||
import org.jclouds.json.internal.NamingStrategies.NameExtractor;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.gson.FieldNamingStrategy;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
|
@ -51,6 +51,7 @@ public final class NamingStrategiesTest {
|
|||
private static class SimpleTest {
|
||||
@SerializedName("aardvark")
|
||||
private String a;
|
||||
@SuppressWarnings("unused")
|
||||
private String b;
|
||||
@Named("cat")
|
||||
private String c;
|
||||
|
@ -75,7 +76,7 @@ public final class NamingStrategiesTest {
|
|||
|
||||
|
||||
public void testExtractSerializedName() throws Exception {
|
||||
NameExtractor extractor = new ExtractSerializedName();
|
||||
NameExtractor<SerializedName> extractor = new ExtractSerializedName();
|
||||
assertEquals(extractor.extractName(SimpleTest.class.getDeclaredField("a").getAnnotation(SerializedName.class)),
|
||||
"aardvark");
|
||||
try {
|
||||
|
@ -96,7 +97,7 @@ public final class NamingStrategiesTest {
|
|||
}
|
||||
|
||||
public void testExtractNamed() throws Exception {
|
||||
NameExtractor extractor = new ExtractNamed();
|
||||
NameExtractor<Named> extractor = new ExtractNamed();
|
||||
try {
|
||||
extractor.extractName(SimpleTest.class.getDeclaredField("a").getAnnotation(Named.class));
|
||||
} catch (NullPointerException e) {
|
||||
|
@ -131,12 +132,12 @@ public final class NamingStrategiesTest {
|
|||
}
|
||||
|
||||
public void testAnnotationConstructorFieldNamingStrategyCPAndNamed() throws Exception {
|
||||
ConstructorFieldNamingStrategy strategy = new AnnotationConstructorNamingStrategy(
|
||||
AnnotationConstructorNamingStrategy strategy = new AnnotationConstructorNamingStrategy(
|
||||
ImmutableSet.of(ConstructorProperties.class), ImmutableSet.of(new ExtractNamed()));
|
||||
|
||||
Constructor<? super SimpleTest> constructor = strategy.getDeserializationConstructor(SimpleTest.class);
|
||||
Invokable<SimpleTest, SimpleTest> constructor = strategy.getDeserializer(typeToken(SimpleTest.class));
|
||||
assertNotNull(constructor);
|
||||
assertEquals(constructor.getParameterTypes().length, 4);
|
||||
assertEquals(constructor.getParameters().size(), 4);
|
||||
|
||||
assertEquals(strategy.translateName(constructor, 0), "aardvark");
|
||||
assertEquals(strategy.translateName(constructor, 1), "bat");
|
||||
|
@ -144,9 +145,9 @@ public final class NamingStrategiesTest {
|
|||
// Note: @Named overrides the ConstructorProperties setting
|
||||
assertEquals(strategy.translateName(constructor, 3), "dingo");
|
||||
|
||||
Constructor<? super MixedConstructorTest> mixedCtor = strategy.getDeserializationConstructor(MixedConstructorTest.class);
|
||||
Invokable<MixedConstructorTest, MixedConstructorTest> mixedCtor = strategy.getDeserializer(typeToken(MixedConstructorTest.class));
|
||||
assertNotNull(mixedCtor);
|
||||
assertEquals(mixedCtor.getParameterTypes().length, 4);
|
||||
assertEquals(mixedCtor.getParameters().size(), 4);
|
||||
|
||||
assertEquals(strategy.translateName(mixedCtor, 0), "aardvark");
|
||||
assertEquals(strategy.translateName(mixedCtor, 1), "bat");
|
||||
|
@ -155,21 +156,21 @@ public final class NamingStrategiesTest {
|
|||
}
|
||||
|
||||
public void testAnnotationConstructorFieldNamingStrategyCP() throws Exception {
|
||||
ConstructorFieldNamingStrategy strategy = new AnnotationConstructorNamingStrategy(
|
||||
ImmutableSet.of(ConstructorProperties.class), ImmutableSet.<NameExtractor>of());
|
||||
AnnotationConstructorNamingStrategy strategy = new AnnotationConstructorNamingStrategy(
|
||||
ImmutableSet.of(ConstructorProperties.class), ImmutableSet.<NameExtractor<?>>of());
|
||||
|
||||
Constructor<? super SimpleTest> constructor = strategy.getDeserializationConstructor(SimpleTest.class);
|
||||
Invokable<SimpleTest, SimpleTest> constructor = strategy.getDeserializer(typeToken(SimpleTest.class));
|
||||
assertNotNull(constructor);
|
||||
assertEquals(constructor.getParameterTypes().length, 4);
|
||||
assertEquals(constructor.getParameters().size(), 4);
|
||||
|
||||
assertEquals(strategy.translateName(constructor, 0), "aardvark");
|
||||
assertEquals(strategy.translateName(constructor, 1), "bat");
|
||||
assertEquals(strategy.translateName(constructor, 2), "coyote");
|
||||
assertEquals(strategy.translateName(constructor, 3), "dog");
|
||||
|
||||
Constructor<? super MixedConstructorTest> mixedCtor = strategy.getDeserializationConstructor(MixedConstructorTest.class);
|
||||
Invokable<MixedConstructorTest, MixedConstructorTest> mixedCtor = strategy.getDeserializer(typeToken(MixedConstructorTest.class));
|
||||
assertNotNull(mixedCtor);
|
||||
assertEquals(mixedCtor.getParameterTypes().length, 4);
|
||||
assertEquals(mixedCtor.getParameters().size(), 4);
|
||||
|
||||
assertEquals(strategy.translateName(mixedCtor, 0), "thiscanbeoverriddenbyNamed");
|
||||
assertNull(strategy.translateName(mixedCtor, 1));
|
||||
|
@ -178,21 +179,21 @@ public final class NamingStrategiesTest {
|
|||
}
|
||||
|
||||
public void testAnnotationConstructorFieldNamingStrategyInject() throws Exception {
|
||||
ConstructorFieldNamingStrategy strategy = new AnnotationConstructorNamingStrategy(
|
||||
AnnotationConstructorNamingStrategy strategy = new AnnotationConstructorNamingStrategy(
|
||||
ImmutableSet.of(Inject.class), ImmutableSet.of(new ExtractNamed()));
|
||||
|
||||
Constructor<? super SimpleTest> constructor = strategy.getDeserializationConstructor(SimpleTest.class);
|
||||
Invokable<SimpleTest, SimpleTest> constructor = strategy.getDeserializer(typeToken(SimpleTest.class));
|
||||
assertNotNull(constructor);
|
||||
assertEquals(constructor.getParameterTypes().length, 5);
|
||||
assertEquals(constructor.getParameters().size(), 5);
|
||||
|
||||
assertEquals(strategy.translateName(constructor, 0), "aa");
|
||||
assertEquals(strategy.translateName(constructor, 1), "bb");
|
||||
assertEquals(strategy.translateName(constructor, 2), "cc");
|
||||
assertEquals(strategy.translateName(constructor, 3), "dd");
|
||||
|
||||
Constructor<? super MixedConstructorTest> mixedCtor = strategy.getDeserializationConstructor(MixedConstructorTest.class);
|
||||
Invokable<MixedConstructorTest, MixedConstructorTest> mixedCtor = strategy.getDeserializer(typeToken(MixedConstructorTest.class));
|
||||
assertNotNull(mixedCtor);
|
||||
assertEquals(mixedCtor.getParameterTypes().length, 4);
|
||||
assertEquals(mixedCtor.getParameters().size(), 4);
|
||||
|
||||
assertEquals(strategy.translateName(mixedCtor, 0), "aardvark");
|
||||
assertEquals(strategy.translateName(mixedCtor, 1), "bat");
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds licenses this file
|
||||
* to you 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.jclouds.json.internal;
|
||||
|
||||
import static com.google.common.base.Objects.equal;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.CollectionTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.FluentIterableTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.IterableTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.MapTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.MultimapTypeAdapterFactory;
|
||||
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.SetTypeAdapterFactory;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(testName = "NullFilteringTypeAdapterFactoriesTest")
|
||||
public class NullFilteringTypeAdapterFactoriesTest {
|
||||
private static class Resource {
|
||||
private final String id;
|
||||
private final String name;
|
||||
|
||||
private Resource(String id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(id, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
Resource that = Resource.class.cast(obj);
|
||||
return equal(this.id, that.id) && equal(this.name, that.name);
|
||||
}
|
||||
}
|
||||
|
||||
private Gson fluentIterable = new GsonBuilder().registerTypeAdapterFactory(new FluentIterableTypeAdapterFactory())
|
||||
.create();
|
||||
private Type fluentIterableType = new TypeToken<FluentIterable<String>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}.getType();
|
||||
private Type fluentIterableResourceType = new TypeToken<FluentIterable<Resource>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}.getType();
|
||||
|
||||
public void testFluentIterable() {
|
||||
FluentIterable<String> noNulls = fluentIterable.fromJson("[\"value\",\"a test string!\"]", fluentIterableType);
|
||||
assertEquals(noNulls.toList(), ImmutableList.of("value", "a test string!"));
|
||||
FluentIterable<String> withNull = fluentIterable.fromJson("[null,\"a test string!\"]", fluentIterableType);
|
||||
assertEquals(withNull.toList(), ImmutableList.of("a test string!"));
|
||||
FluentIterable<String> withDupes = fluentIterable.fromJson("[\"value\",\"value\"]", fluentIterableType);
|
||||
assertEquals(withDupes.toList(), ImmutableList.of("value", "value"));
|
||||
FluentIterable<Resource> resources = fluentIterable.fromJson(
|
||||
"[{\"id\":\"i-foo\",\"name\":\"foo\"},{\"id\":\"i-bar\",\"name\":\"bar\"}]", fluentIterableResourceType);
|
||||
assertEquals(resources.toList(), ImmutableList.of(new Resource("i-foo", "foo"), new Resource("i-bar", "bar")));
|
||||
}
|
||||
|
||||
private Gson collection = new GsonBuilder().registerTypeAdapterFactory(new CollectionTypeAdapterFactory()).create();
|
||||
private Type collectionType = new TypeToken<Collection<String>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}.getType();
|
||||
private Type collectionResourceType = new TypeToken<Collection<Resource>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}.getType();
|
||||
|
||||
public void testCollection() {
|
||||
Collection<String> noNulls = collection.fromJson("[\"value\",\"a test string!\"]", collectionType);
|
||||
assertEquals(noNulls, ImmutableList.of("value", "a test string!"));
|
||||
Collection<String> withNull = collection.fromJson("[null,\"a test string!\"]", collectionType);
|
||||
assertEquals(withNull, ImmutableList.of("a test string!"));
|
||||
Collection<String> withDupes = collection.fromJson("[\"value\",\"value\"]", collectionType);
|
||||
assertEquals(withDupes, ImmutableList.of("value", "value"));
|
||||
Collection<Resource> resources = collection.fromJson(
|
||||
"[{\"id\":\"i-foo\",\"name\":\"foo\"},{\"id\":\"i-bar\",\"name\":\"bar\"}]", collectionResourceType);
|
||||
assertEquals(resources, ImmutableList.of(new Resource("i-foo", "foo"), new Resource("i-bar", "bar")));
|
||||
}
|
||||
|
||||
private Gson iterable = new GsonBuilder().registerTypeAdapterFactory(new IterableTypeAdapterFactory()).create();
|
||||
private Type iterableType = new TypeToken<Iterable<String>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}.getType();
|
||||
private Type iterableResourceType = new TypeToken<Iterable<Resource>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}.getType();
|
||||
|
||||
public void testIterable() {
|
||||
Iterable<String> noNulls = iterable.fromJson("[\"value\",\"a test string!\"]", iterableType);
|
||||
assertEquals(noNulls, ImmutableList.of("value", "a test string!"));
|
||||
Iterable<String> withNull = iterable.fromJson("[null,\"a test string!\"]", iterableType);
|
||||
assertEquals(withNull, ImmutableList.of("a test string!"));
|
||||
Iterable<String> withDupes = iterable.fromJson("[\"value\",\"value\"]", iterableType);
|
||||
assertEquals(withDupes, ImmutableList.of("value", "value"));
|
||||
Iterable<Resource> resources = iterable.fromJson(
|
||||
"[{\"id\":\"i-foo\",\"name\":\"foo\"},{\"id\":\"i-bar\",\"name\":\"bar\"}]", iterableResourceType);
|
||||
assertEquals(resources, ImmutableList.of(new Resource("i-foo", "foo"), new Resource("i-bar", "bar")));
|
||||
}
|
||||
|
||||
private Type iterableWildcardExtendsResourceType = new TypeToken<Iterable<? extends Resource>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}.getType();
|
||||
|
||||
public void testWildcardExtends() {
|
||||
Iterable<? extends Resource> wildcardExtendsResources = iterable.fromJson(
|
||||
"[{\"id\":\"i-foo\",\"name\":\"foo\"},{\"id\":\"i-bar\",\"name\":\"bar\"}]",
|
||||
iterableWildcardExtendsResourceType);
|
||||
assertEquals(wildcardExtendsResources,
|
||||
ImmutableList.of(new Resource("i-foo", "foo"), new Resource("i-bar", "bar")));
|
||||
}
|
||||
|
||||
private Gson set = new GsonBuilder().registerTypeAdapterFactory(new SetTypeAdapterFactory()).create();
|
||||
private Type setType = new TypeToken<Set<String>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}.getType();
|
||||
private Type setResourceType = new TypeToken<Set<Resource>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}.getType();
|
||||
|
||||
public void testSet() {
|
||||
Set<String> noNulls = set.fromJson("[\"value\",\"a test string!\"]", setType);
|
||||
assertEquals(noNulls, ImmutableSet.of("value", "a test string!"));
|
||||
Set<String> withNull = set.fromJson("[null,\"a test string!\"]", setType);
|
||||
assertEquals(withNull, ImmutableSet.of("a test string!"));
|
||||
Set<String> withDupes = set.fromJson("[\"value\",\"value\"]", setType);
|
||||
assertEquals(withDupes, ImmutableSet.of("value"));
|
||||
Set<Resource> resources = set.fromJson(
|
||||
"[{\"id\":\"i-foo\",\"name\":\"foo\"},{\"id\":\"i-bar\",\"name\":\"bar\"}]", setResourceType);
|
||||
assertEquals(resources, ImmutableSet.of(new Resource("i-foo", "foo"), new Resource("i-bar", "bar")));
|
||||
}
|
||||
|
||||
private Gson map = new GsonBuilder().registerTypeAdapterFactory(new MapTypeAdapterFactory()).create();
|
||||
private Type mapType = new TypeToken<Map<String, String>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}.getType();
|
||||
private Type mapResourceType = new TypeToken<Map<String, Resource>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}.getType();
|
||||
|
||||
public void testMap() {
|
||||
Map<String, String> noNulls = map.fromJson("{\"value\":\"a test string!\"}", mapType);
|
||||
assertEquals(noNulls, ImmutableMap.of("value", "a test string!"));
|
||||
Map<String, String> withNull = map.fromJson("{\"value\":null}", mapType);
|
||||
assertEquals(withNull, ImmutableMap.of());
|
||||
Map<String, String> withEmpty = map.fromJson("{\"value\":\"\"}", mapType);
|
||||
assertEquals(withEmpty, ImmutableMap.of("value", ""));
|
||||
Map<String, Resource> resources = map.fromJson(
|
||||
"{\"i-foo\":{\"id\":\"i-foo\",\"name\":\"foo\"},\"i-bar\":{\"id\":\"i-bar\",\"name\":\"bar\"}}",
|
||||
mapResourceType);
|
||||
assertEquals(resources,
|
||||
ImmutableMap.of("i-foo", new Resource("i-foo", "foo"), "i-bar", new Resource("i-bar", "bar")));
|
||||
}
|
||||
|
||||
private Gson multimap = new GsonBuilder().registerTypeAdapterFactory(new MultimapTypeAdapterFactory()).create();
|
||||
private Type multimapType = new TypeToken<Multimap<String, String>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}.getType();
|
||||
private Type multimapResourceType = new TypeToken<Multimap<String, Resource>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}.getType();
|
||||
|
||||
public void testMultimap() {
|
||||
Multimap<String, String> noNulls = multimap.fromJson("{\"value\":[\"a test string!\"]}", multimapType);
|
||||
assertEquals(noNulls, ImmutableMultimap.of("value", "a test string!"));
|
||||
Multimap<String, String> withNull = multimap.fromJson("{\"value\":[null]}", multimapType);
|
||||
assertEquals(withNull, ImmutableMultimap.of());
|
||||
Multimap<String, String> withEmpty = multimap.fromJson("{\"value\":[\"\"]}", multimapType);
|
||||
assertEquals(withEmpty, ImmutableMultimap.of("value", ""));
|
||||
Multimap<String, String> withDupes = multimap.fromJson("{\"key\":[\"value\",\"value\"]}", multimapType);
|
||||
assertEquals(withDupes.get("key"), ImmutableList.of("value", "value"));
|
||||
Multimap<String, Resource> resources = multimap.fromJson(
|
||||
"{\"i-foo\":[{\"id\":\"i-foo\",\"name\":\"foo\"}],\"i-bar\":[{\"id\":\"i-bar\",\"name\":\"bar\"}]}",
|
||||
multimapResourceType);
|
||||
assertEquals(resources,
|
||||
ImmutableMultimap.of("i-foo", new Resource("i-foo", "foo"), "i-bar", new Resource("i-bar", "bar")));
|
||||
Multimap<String, Resource> resourceDupes = multimap.fromJson(
|
||||
"{\"i-foo\":[{\"id\":\"i-foo\",\"name\":\"foo\"},{\"id\":\"i-bar\",\"name\":\"bar\"}]}",
|
||||
multimapResourceType);
|
||||
assertEquals(resourceDupes.get("i-foo"),
|
||||
ImmutableList.of(new Resource("i-foo", "foo"), new Resource("i-bar", "bar")));
|
||||
}
|
||||
}
|
|
@ -42,7 +42,7 @@ public class OptionalTypeAdapterFactoryTest {
|
|||
* Simple type with an Optional field
|
||||
*/
|
||||
static class SimpleBean {
|
||||
private Optional<String> value;
|
||||
private final Optional<String> value;
|
||||
private final String someOtherValue;
|
||||
|
||||
public SimpleBean(Optional<String> value, String someOtherValue) {
|
||||
|
@ -51,6 +51,7 @@ public class OptionalTypeAdapterFactoryTest {
|
|||
}
|
||||
|
||||
// Required to ensure GSON doesn't initialize our Optional to null!
|
||||
@SuppressWarnings("unused")
|
||||
private SimpleBean() {
|
||||
this.value = Optional.absent();
|
||||
this.someOtherValue = null;
|
||||
|
@ -64,22 +65,9 @@ public class OptionalTypeAdapterFactoryTest {
|
|||
return someOtherValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(value, someOtherValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object that) {
|
||||
if (that == null || that.getClass() != getClass())
|
||||
return false;
|
||||
SimpleBean other = (SimpleBean) that;
|
||||
return Objects.equal(value, other.value) && Objects.equal(someOtherValue, other.someOtherValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Objects.toStringHelper("SimpleBean").add("value", value).add("someOtherValue", someOtherValue).toString();
|
||||
public boolean equals(Object other) {
|
||||
SimpleBean that = SimpleBean.class.cast(other);
|
||||
return Objects.equal(value, that.value) && Objects.equal(someOtherValue, that.someOtherValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,12 +19,15 @@
|
|||
package org.jclouds.reflect;
|
||||
|
||||
import static com.google.common.base.Functions.toStringFunction;
|
||||
import static org.jclouds.reflect.Reflection2.constructors;
|
||||
import static org.jclouds.reflect.Reflection2.method;
|
||||
import static org.jclouds.reflect.Reflection2.methods;
|
||||
import static org.jclouds.reflect.Reflection2.typeToken;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
|
@ -32,7 +35,9 @@ import com.google.common.base.Function;
|
|||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.reflect.Parameter;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -41,7 +46,31 @@ import com.google.common.reflect.TypeToken;
|
|||
@Test
|
||||
public class Reflection2Test {
|
||||
|
||||
public void testTypeToken() {
|
||||
/**
|
||||
* useful when converting to and from type literals from other libraries such as guice and gson.
|
||||
*/
|
||||
public void testTypeTokenForType() {
|
||||
TypeLiteral<Set<String>> guice = new TypeLiteral<Set<String>>() {
|
||||
};
|
||||
|
||||
assertEquals(typeToken(guice.getType()), new TypeToken<Set<String>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
});
|
||||
}
|
||||
|
||||
public void testConstructors() {
|
||||
Set<String> ctorParams = FluentIterable.from(constructors(TypeToken.of(HashSet.class)))
|
||||
.transform(new Function<Invokable<?, ?>, Iterable<Parameter>>() {
|
||||
public Iterable<Parameter> apply(Invokable<?, ?> input) {
|
||||
return input.getParameters();
|
||||
}
|
||||
}).transform(toStringFunction()).toSet();
|
||||
|
||||
assertEquals(ctorParams, ImmutableSet.of("[]", "[java.util.Collection<? extends E> arg0]",
|
||||
"[int arg0, float arg1]", "[int arg0]", "[int arg0, float arg1, boolean arg2]"));
|
||||
}
|
||||
|
||||
public void testTypeTokenForClass() {
|
||||
assertEquals(typeToken(String.class), TypeToken.of(String.class));
|
||||
}
|
||||
|
||||
|
@ -65,15 +94,28 @@ public class Reflection2Test {
|
|||
assertEquals(methodInSuper.getParameters().get(0).getType().getRawType(), Object.class);
|
||||
}
|
||||
|
||||
public void testMethods() {
|
||||
Set<String> methodNames = FluentIterable.from(methods(Set.class))
|
||||
.transform(new Function<Invokable<?, ?>, String>() {
|
||||
public String apply(Invokable<?, ?> input) {
|
||||
return input.getName();
|
||||
}
|
||||
}).transform(toStringFunction()).toSet();
|
||||
ImmutableSet<String> setMethods = ImmutableSet.of("add", "equals", "hashCode", "clear", "isEmpty", "contains",
|
||||
"addAll", "size", "toArray", "iterator", "remove", "removeAll", "containsAll", "retainAll");
|
||||
|
||||
assertEquals(methodNames, ImmutableSet.of("add", "equals", "hashCode", "clear", "isEmpty", "contains", "addAll",
|
||||
"size", "toArray", "iterator", "remove", "removeAll", "containsAll", "retainAll"));
|
||||
public void testMethods() {
|
||||
Set<String> methodNames = FluentIterable.from(methods(Set.class)).transform(invokableToName)
|
||||
.transform(toStringFunction()).toSet();
|
||||
|
||||
assertEquals(methodNames, setMethods);
|
||||
}
|
||||
|
||||
public void testMethodsSubClass() {
|
||||
Set<String> methodNames = FluentIterable.from(methods(SortedSet.class)).transform(invokableToName)
|
||||
.transform(toStringFunction()).toSet();
|
||||
|
||||
assertEquals(methodNames,
|
||||
ImmutableSet.builder().add("comparator", "last", "first", "subSet", "headSet", "tailSet")
|
||||
.addAll(setMethods).build());
|
||||
}
|
||||
|
||||
static final Function<Invokable<?, ?>, String> invokableToName = new Function<Invokable<?, ?>, String>() {
|
||||
public String apply(Invokable<?, ?> input) {
|
||||
return input.getName();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
/**
|
||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||
* contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. jclouds licenses this file
|
||||
* to you 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.jclouds.rest.config;
|
||||
|
||||
import static com.google.common.base.Predicates.not;
|
||||
import static com.google.common.collect.Maps.filterEntries;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.jclouds.rest.HttpAsyncClient;
|
||||
import org.jclouds.rest.HttpClient;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.reflect.Invokable;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Test(groups = "unit")
|
||||
public class RestModuleTest {
|
||||
static interface Sync {
|
||||
String get();
|
||||
}
|
||||
|
||||
private static interface Async {
|
||||
ListenableFuture<String> get();
|
||||
}
|
||||
|
||||
public void testPutInvokablesWhenInterfacesMatch() {
|
||||
Cache<Invokable<?, ?>, Invokable<?, ?>> cache = CacheBuilder.newBuilder().build();
|
||||
RestModule.putInvokables(Sync.class, Async.class, cache);
|
||||
|
||||
assertEquals(cache.size(), 1);
|
||||
|
||||
Invokable<?, ?> sync = cache.asMap().keySet().iterator().next();
|
||||
assertEquals(sync.getOwnerType().getRawType(), Sync.class);
|
||||
assertEquals(sync.getName(), "get");
|
||||
assertEquals(sync.getReturnType(), TypeToken.of(String.class));
|
||||
|
||||
Invokable<?, ?> async = cache.getIfPresent(sync);
|
||||
assertEquals(async.getOwnerType().getRawType(), Async.class);
|
||||
assertEquals(async.getName(), "get");
|
||||
assertEquals(async.getReturnType(), new TypeToken<ListenableFuture<String>>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
});
|
||||
}
|
||||
|
||||
private static interface AsyncWithException {
|
||||
ListenableFuture<String> get() throws IOException;
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = ".* has different typed exceptions than target .*")
|
||||
public void testPutInvokablesWhenInterfacesMatchExceptExceptions() {
|
||||
Cache<Invokable<?, ?>, Invokable<?, ?>> cache = CacheBuilder.newBuilder().build();
|
||||
RestModule.putInvokables(Sync.class, AsyncWithException.class, cache);
|
||||
}
|
||||
|
||||
private static interface AsyncWithMisnamedMethod {
|
||||
ListenableFuture<String> got();
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "no such method .*")
|
||||
public void testPutInvokablesWhenTargetMethodNotFound() {
|
||||
Cache<Invokable<?, ?>, Invokable<?, ?>> cache = CacheBuilder.newBuilder().build();
|
||||
RestModule.putInvokables(Sync.class, AsyncWithMisnamedMethod.class, cache);
|
||||
}
|
||||
|
||||
static final Predicate<Entry<Invokable<?, ?>, Invokable<?, ?>>> isHttpInvokable = new Predicate<Map.Entry<Invokable<?, ?>, Invokable<?, ?>>>() {
|
||||
public boolean apply(Map.Entry<Invokable<?, ?>, Invokable<?, ?>> in) {
|
||||
return in.getKey().getOwnerType().getRawType().equals(HttpClient.class)
|
||||
&& in.getValue().getOwnerType().getRawType().equals(HttpAsyncClient.class);
|
||||
}
|
||||
};
|
||||
|
||||
public void testSeedKnownSync2AsyncIncludesHttpClientByDefault() {
|
||||
Map<Invokable<?, ?>, Invokable<?, ?>> cache = RestModule.seedKnownSync2AsyncInvokables(
|
||||
ImmutableMap.<Class<?>, Class<?>> of()).asMap();
|
||||
|
||||
assertEquals(cache.size(), 6);
|
||||
assertEquals(filterEntries(cache, isHttpInvokable), cache);
|
||||
}
|
||||
|
||||
public void testSeedKnownSync2AsyncInvokablesInterfacesMatch() {
|
||||
Map<Invokable<?, ?>, Invokable<?, ?>> cache = RestModule.seedKnownSync2AsyncInvokables(
|
||||
ImmutableMap.<Class<?>, Class<?>> of(Sync.class, Async.class)).asMap();
|
||||
|
||||
assertEquals(cache.size(), 7);
|
||||
|
||||
cache = filterEntries(cache, not(isHttpInvokable));
|
||||
|
||||
assertEquals(cache.size(), 1);
|
||||
}
|
||||
|
||||
}
|
|
@ -22,8 +22,9 @@ package org.jclouds.abiquo.domain;
|
|||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.collect.Iterables.concat;
|
||||
import static com.google.common.collect.Iterables.transform;
|
||||
import static org.jclouds.reflect.Reflection2.constructor;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
@ -44,6 +45,7 @@ import com.abiquo.server.core.task.TaskDto;
|
|||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.reflect.Invokable;
|
||||
|
||||
/**
|
||||
* This class is used to decorate transport objects with high level
|
||||
|
@ -103,13 +105,12 @@ public abstract class DomainWrapper<T extends SingleResourceTransportDto> {
|
|||
}
|
||||
|
||||
try {
|
||||
Constructor<W> cons = wrapperClass.getDeclaredConstructor(RestContext.class, target.getClass());
|
||||
if (!cons.isAccessible()) {
|
||||
cons.setAccessible(true);
|
||||
}
|
||||
return cons.newInstance(context, target);
|
||||
} catch (Exception ex) {
|
||||
throw new WrapperException(wrapperClass, target, ex);
|
||||
Invokable<W, W> cons = constructor(wrapperClass, RestContext.class, target.getClass());
|
||||
return cons.invoke(null, context, target);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new WrapperException(wrapperClass, target, e.getTargetException());
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new WrapperException(wrapperClass, target, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,8 +46,8 @@ public class ListPage<T> extends IterableWithMarker<T> {
|
|||
private final Iterable<T> items;
|
||||
|
||||
protected ListPage(Kind kind, String id, URI selfLink, String nextPageToken, Iterable<T> items) {
|
||||
this.kind = checkNotNull(kind, "kind of %id", id);
|
||||
this.id = checkNotNull(id, "id");
|
||||
this.kind = checkNotNull(kind, "kind of %id", id);
|
||||
this.selfLink = checkNotNull(selfLink, "selfLink of %id", id);
|
||||
this.nextPageToken = nextPageToken;
|
||||
this.items = items != null ? ImmutableSet.copyOf(items) : ImmutableSet.<T>of();
|
||||
|
@ -90,7 +90,7 @@ public class ListPage<T> extends IterableWithMarker<T> {
|
|||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null || getClass() != obj.getClass()) return false;
|
||||
ListPage that = ListPage.class.cast(obj);
|
||||
ListPage<?> that = ListPage.class.cast(obj);
|
||||
return equal(this.kind, that.kind)
|
||||
&& equal(this.id, that.id);
|
||||
}
|
||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
|||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
public class ParseImageDetails extends ParseJson<Images<? extends ImageDetails>> {
|
||||
static class Images<T extends ImageDetails> extends PaginatedCollection<T> {
|
||||
public class ParseImageDetails extends ParseJson<Images> {
|
||||
static class Images extends PaginatedCollection<ImageDetails> {
|
||||
|
||||
@ConstructorProperties({ "images", "images_links" })
|
||||
protected Images(Iterable<T> images, Iterable<Link> images_links) {
|
||||
protected Images(Iterable<ImageDetails> images, Iterable<Link> images_links) {
|
||||
super(images, images_links);
|
||||
}
|
||||
|
||||
|
@ -60,8 +60,7 @@ public class ParseImageDetails extends ParseJson<Images<? extends ImageDetails>>
|
|||
|
||||
@Inject
|
||||
public ParseImageDetails(Json json) {
|
||||
super(json, new TypeLiteral<Images<? extends ImageDetails>>() {
|
||||
});
|
||||
super(json, TypeLiteral.get(Images.class));
|
||||
}
|
||||
|
||||
public static class ToPagedIterable extends CallerArg0ToPagedIterable<ImageDetails, ToPagedIterable> {
|
||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
|||
*/
|
||||
@Beta
|
||||
@Singleton
|
||||
public class ParseImages extends ParseJson<Images<? extends Image>> {
|
||||
static class Images<T extends Image> extends PaginatedCollection<T> {
|
||||
public class ParseImages extends ParseJson<Images> {
|
||||
static class Images extends PaginatedCollection<Image> {
|
||||
|
||||
@ConstructorProperties({ "images", "images_links" })
|
||||
protected Images(Iterable<T> images, Iterable<Link> images_links) {
|
||||
protected Images(Iterable<Image> images, Iterable<Link> images_links) {
|
||||
super(images, images_links);
|
||||
}
|
||||
|
||||
|
@ -60,8 +60,7 @@ public class ParseImages extends ParseJson<Images<? extends Image>> {
|
|||
|
||||
@Inject
|
||||
public ParseImages(Json json) {
|
||||
super(json, new TypeLiteral<Images<? extends Image>>() {
|
||||
});
|
||||
super(json, TypeLiteral.get(Images.class));
|
||||
}
|
||||
|
||||
public static class ToPagedIterable extends CallerArg0ToPagedIterable<Image, ToPagedIterable> {
|
||||
|
|
Loading…
Reference in New Issue