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.date.DateService;
|
||||||
import org.jclouds.json.config.GsonModule.DateAdapter;
|
import org.jclouds.json.config.GsonModule.DateAdapter;
|
||||||
import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
|
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.Splitter;
|
||||||
import com.google.common.base.Strings;
|
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.common.collect.ImmutableSet;
|
||||||
import com.google.gson.TypeAdapter;
|
import com.google.gson.TypeAdapter;
|
||||||
import com.google.gson.stream.JsonReader;
|
import com.google.gson.stream.JsonReader;
|
||||||
|
@ -47,15 +45,14 @@ public class CloudStackParserModule extends AbstractModule {
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bind(DateAdapter.class).to(CloudStackDateAdapter.class);
|
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.
|
* Data adapter for the date formats used by CloudStack.
|
||||||
*
|
*
|
||||||
* Essentially this is a workaround for the CloudStack getUsage() API call returning a corrupted
|
* Essentially this is a workaround for the CloudStack getUsage() API call returning a corrupted form of ISO-8601
|
||||||
* form of ISO-8601 dates, which have an unexpected pair of apostrophes, like
|
* dates, which have an unexpected pair of apostrophes, like 2011-12-12'T'00:00:00+00:00
|
||||||
* 2011-12-12'T'00:00:00+00:00
|
|
||||||
*
|
*
|
||||||
* @author Richard Downer
|
* @author Richard Downer
|
||||||
*/
|
*/
|
||||||
|
@ -77,39 +74,38 @@ public class CloudStackParserModule extends AbstractModule {
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public static class CommaDelimitedOKIgnoreNullIterableTypeAdapterFactory extends IgnoreNullIterableTypeAdapterFactory {
|
public static class CommaDelimitedOKIterableTypeAdapterFactory extends IterableTypeAdapterFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected <E> TypeAdapter<Iterable<E>> newIterableAdapter(final TypeAdapter<E> elementAdapter) {
|
@SuppressWarnings("unchecked")
|
||||||
return new TypeAdapter<Iterable<E>>() {
|
protected <E, I> TypeAdapter<I> newAdapter(TypeAdapter<E> elementAdapter) {
|
||||||
public void write(JsonWriter out, Iterable<E> value) throws IOException {
|
return (TypeAdapter<I>) new Adapter<E>(elementAdapter);
|
||||||
out.beginArray();
|
}
|
||||||
for (E element : value) {
|
|
||||||
elementAdapter.write(out, element);
|
|
||||||
}
|
|
||||||
out.endArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
public static final class Adapter<E> extends TypeAdapter<Iterable<E>> {
|
||||||
public Iterable<E> read(JsonReader in) throws IOException {
|
|
||||||
// HACK as cloudstack changed a field from String to Set!
|
private final IterableTypeAdapterFactory.IterableTypeAdapter<E> delegate;
|
||||||
if (in.peek() == JsonToken.STRING) {
|
|
||||||
String val = Strings.emptyToNull(in.nextString());
|
public Adapter(TypeAdapter<E> elementAdapter) {
|
||||||
return (Iterable<E>) (val != null ? Splitter.on(',').split(val) : ImmutableSet.of());
|
this.delegate = new IterableTypeAdapterFactory.IterableTypeAdapter<E>(elementAdapter);
|
||||||
} else {
|
nullSafe();
|
||||||
Builder<E> builder = ImmutableList.<E> builder();
|
}
|
||||||
in.beginArray();
|
|
||||||
while (in.hasNext()) {
|
public void write(JsonWriter out, Iterable<E> value) throws IOException {
|
||||||
E element = elementAdapter.read(in);
|
this.delegate.write(out, value);
|
||||||
if (element != null)
|
}
|
||||||
builder.add(element);
|
|
||||||
}
|
@SuppressWarnings("unchecked")
|
||||||
in.endArray();
|
@Override
|
||||||
return builder.build();
|
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 static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.ParameterizedType;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.jclouds.json.config.GsonModule;
|
import org.jclouds.json.config.GsonModule;
|
||||||
import org.jclouds.json.config.GsonModule.DateAdapter;
|
import org.jclouds.json.config.GsonModule.DateAdapter;
|
||||||
|
import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.SetTypeAdapterFactory;
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.ImmutableSet.Builder;
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.TypeAdapter;
|
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.JsonReader;
|
||||||
import com.google.gson.stream.JsonToken;
|
import com.google.gson.stream.JsonToken;
|
||||||
import com.google.gson.stream.JsonWriter;
|
import com.google.gson.stream.JsonWriter;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.TypeLiteral;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Adam Lowe
|
* @author Adam Lowe
|
||||||
*/
|
*/
|
||||||
public class KeystoneParserModule extends AbstractModule {
|
public class KeystoneParserModule extends AbstractModule {
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bind(DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class);
|
bind(DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class);
|
||||||
bind(new TypeLiteral<Set<TypeAdapterFactory>>() {
|
bind(SetTypeAdapterFactory.class).to(ValuesSetTypeAdapterFactory.class);
|
||||||
}).toInstance(ImmutableSet.<TypeAdapterFactory>of(new SetTypeAdapterFactory()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,61 +53,49 @@ public class KeystoneParserModule extends AbstractModule {
|
||||||
* <p/>
|
* <p/>
|
||||||
* Treats [A,B,C] and {"values"=[A,B,C], "someotherstuff"=...} as the same Set
|
* Treats [A,B,C] and {"values"=[A,B,C], "someotherstuff"=...} as the same Set
|
||||||
*/
|
*/
|
||||||
public static class SetTypeAdapterFactory implements TypeAdapterFactory {
|
public static class ValuesSetTypeAdapterFactory extends SetTypeAdapterFactory {
|
||||||
@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];
|
@Override
|
||||||
TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
|
@SuppressWarnings("unchecked")
|
||||||
return TypeAdapter.class.cast(newSetAdapter(elementAdapter));
|
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) {
|
public static final class Adapter<E> extends TypeAdapter<Set<E>> {
|
||||||
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 {
|
private final SetTypeAdapterFactory.SetTypeAdapter<E> delegate;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
public void write(JsonWriter out, Set<E> value) throws IOException {
|
||||||
in.beginArray();
|
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()) {
|
while (in.hasNext()) {
|
||||||
E element = elementAdapter.read(in);
|
String name = in.nextName();
|
||||||
result.add(element);
|
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
|
@Beta
|
||||||
public class PaginatedCollection<T> extends IterableWithMarker<T> {
|
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<T> resources;
|
||||||
private Iterable<Link> links;
|
private Iterable<Link> links;
|
||||||
|
|
||||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
||||||
*/
|
*/
|
||||||
@Beta
|
@Beta
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ParseTenants extends ParseJson<Tenants<? extends Tenant>> {
|
public class ParseTenants extends ParseJson<Tenants> {
|
||||||
static class Tenants<T extends Tenant> extends PaginatedCollection<T> {
|
static class Tenants extends PaginatedCollection<Tenant> {
|
||||||
|
|
||||||
@ConstructorProperties({ "tenants", "tenants_links" })
|
@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);
|
super(tenants, tenants_links);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,8 +60,7 @@ public class ParseTenants extends ParseJson<Tenants<? extends Tenant>> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ParseTenants(Json json) {
|
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> {
|
public static class ToPagedIterable extends CallerArg0ToPagedIterable<Tenant, ToPagedIterable> {
|
||||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
||||||
*/
|
*/
|
||||||
@Beta
|
@Beta
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ParseUsers extends ParseJson<Users<? extends User>> {
|
public class ParseUsers extends ParseJson<Users> {
|
||||||
static class Users<T extends User> extends PaginatedCollection<T> {
|
static class Users extends PaginatedCollection<User> {
|
||||||
|
|
||||||
@ConstructorProperties({ "users", "users_links" })
|
@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);
|
super(users, users_links);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,8 +60,7 @@ public class ParseUsers extends ParseJson<Users<? extends User>> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ParseUsers(Json json) {
|
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> {
|
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
|
@Beta
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ParseFlavorDetails extends ParseJson<Flavors<? extends Flavor>> {
|
public class ParseFlavorDetails extends ParseJson<Flavors> {
|
||||||
static class Flavors<T extends Flavor> extends PaginatedCollection<T> {
|
static class Flavors extends PaginatedCollection<Flavor> {
|
||||||
|
|
||||||
@ConstructorProperties({ "flavors", "flavors_links" })
|
@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);
|
super(flavors, flavors_links);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,8 +60,7 @@ public class ParseFlavorDetails extends ParseJson<Flavors<? extends Flavor>> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ParseFlavorDetails(Json json) {
|
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> {
|
public static class ToPagedIterable extends CallerArg0ToPagedIterable<Flavor, ToPagedIterable> {
|
||||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
||||||
*/
|
*/
|
||||||
@Beta
|
@Beta
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ParseFlavors extends ParseJson<Flavors<? extends Resource>> {
|
public class ParseFlavors extends ParseJson<Flavors> {
|
||||||
static class Flavors<T extends Resource> extends PaginatedCollection<T> {
|
static class Flavors extends PaginatedCollection<Resource> {
|
||||||
|
|
||||||
@ConstructorProperties({ "flavors", "flavors_links" })
|
@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);
|
super(flavors, flavors_links);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,8 +60,7 @@ public class ParseFlavors extends ParseJson<Flavors<? extends Resource>> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ParseFlavors(Json json) {
|
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> {
|
public static class ToPagedIterable extends CallerArg0ToPagedIterable<Resource, ToPagedIterable> {
|
||||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
||||||
*/
|
*/
|
||||||
@Beta
|
@Beta
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ParseImageDetails extends ParseJson<Images<? extends Image>> {
|
public class ParseImageDetails extends ParseJson<Images> {
|
||||||
static class Images<T extends Image> extends PaginatedCollection<T> {
|
static class Images extends PaginatedCollection<Image> {
|
||||||
|
|
||||||
@ConstructorProperties({ "images", "images_links" })
|
@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);
|
super(images, images_links);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,8 +60,7 @@ public class ParseImageDetails extends ParseJson<Images<? extends Image>> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ParseImageDetails(Json json) {
|
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> {
|
public static class ToPagedIterable extends CallerArg0ToPagedIterable<Image, ToPagedIterable> {
|
||||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
||||||
*/
|
*/
|
||||||
@Beta
|
@Beta
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ParseImages extends ParseJson<Images<? extends Resource>> {
|
public class ParseImages extends ParseJson<Images> {
|
||||||
static class Images<T extends Resource> extends PaginatedCollection<T> {
|
static class Images extends PaginatedCollection<Resource> {
|
||||||
|
|
||||||
@ConstructorProperties({ "images", "images_links" })
|
@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);
|
super(images, images_links);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,8 +60,7 @@ public class ParseImages extends ParseJson<Images<? extends Resource>> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ParseImages(Json json) {
|
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> {
|
public static class ToPagedIterable extends CallerArg0ToPagedIterable<Resource, ToPagedIterable> {
|
||||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
||||||
*/
|
*/
|
||||||
@Beta
|
@Beta
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ParseServerDetails extends ParseJson<Servers<? extends Server>> {
|
public class ParseServerDetails extends ParseJson<Servers> {
|
||||||
static class Servers<T extends Server> extends PaginatedCollection<T> {
|
static class Servers extends PaginatedCollection<Server> {
|
||||||
|
|
||||||
@ConstructorProperties({ "servers", "servers_links" })
|
@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);
|
super(servers, servers_links);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,8 +60,7 @@ public class ParseServerDetails extends ParseJson<Servers<? extends Server>> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ParseServerDetails(Json json) {
|
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> {
|
public static class ToPagedIterable extends CallerArg0ToPagedIterable<Server, ToPagedIterable> {
|
||||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
||||||
*/
|
*/
|
||||||
@Beta
|
@Beta
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ParseServers extends ParseJson<Servers<? extends Resource>> {
|
public class ParseServers extends ParseJson<Servers> {
|
||||||
static class Servers<T extends Resource> extends PaginatedCollection<T> {
|
static class Servers extends PaginatedCollection<Resource> {
|
||||||
|
|
||||||
@ConstructorProperties({ "servers", "servers_links" })
|
@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);
|
super(servers, servers_links);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,8 +60,7 @@ public class ParseServers extends ParseJson<Servers<? extends Resource>> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ParseServers(Json json) {
|
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> {
|
public static class ToPagedIterable extends CallerArg0ToPagedIterable<Resource, ToPagedIterable> {
|
||||||
|
|
|
@ -53,8 +53,7 @@ public class FilterStringsBoundToInjectorByName implements Function<Predicate<St
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> apply(Predicate<String> filter) {
|
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>>() {
|
Iterable<Binding<String>> annotatedWithName = Iterables.filter(stringBindings, new Predicate<Binding<String>>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -40,15 +40,16 @@ import org.jclouds.json.Json;
|
||||||
import org.jclouds.json.internal.DeserializationConstructorAndReflectiveTypeAdapterFactory;
|
import org.jclouds.json.internal.DeserializationConstructorAndReflectiveTypeAdapterFactory;
|
||||||
import org.jclouds.json.internal.EnumTypeAdapterThatReturnsFromValue;
|
import org.jclouds.json.internal.EnumTypeAdapterThatReturnsFromValue;
|
||||||
import org.jclouds.json.internal.GsonWrapper;
|
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.AnnotationConstructorNamingStrategy;
|
||||||
import org.jclouds.json.internal.NamingStrategies.AnnotationOrNameFieldNamingStrategy;
|
import org.jclouds.json.internal.NamingStrategies.AnnotationOrNameFieldNamingStrategy;
|
||||||
import org.jclouds.json.internal.NamingStrategies.ExtractNamed;
|
import org.jclouds.json.internal.NamingStrategies.ExtractNamed;
|
||||||
import org.jclouds.json.internal.NamingStrategies.ExtractSerializedName;
|
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.NullHackJsonLiteralAdapter;
|
||||||
import org.jclouds.json.internal.OptionalTypeAdapterFactory;
|
import org.jclouds.json.internal.OptionalTypeAdapterFactory;
|
||||||
|
|
||||||
|
@ -84,14 +85,13 @@ public class GsonModule extends AbstractModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
Gson provideGson(TypeAdapter<JsonBall> jsonAdapter, DateAdapter adapter, ByteListAdapter byteListAdapter,
|
Gson provideGson(TypeAdapter<JsonBall> jsonAdapter, DateAdapter adapter, ByteListAdapter byteListAdapter,
|
||||||
ByteArrayAdapter byteArrayAdapter, PropertiesAdapter propertiesAdapter, JsonAdapterBindings bindings,
|
ByteArrayAdapter byteArrayAdapter, PropertiesAdapter propertiesAdapter, JsonAdapterBindings bindings,
|
||||||
OptionalTypeAdapterFactory optional, IgnoreNullSetTypeAdapterFactory set,
|
OptionalTypeAdapterFactory optional, SetTypeAdapterFactory set, MapTypeAdapterFactory map,
|
||||||
IgnoreNullMapTypeAdapterFactory map, IgnoreNullMultimapTypeAdapterFactory multimap,
|
MultimapTypeAdapterFactory multimap, IterableTypeAdapterFactory iterable,
|
||||||
IgnoreNullIterableTypeAdapterFactory iterable, IgnoreNullFluentIterableTypeAdapterFactory fluentIterable)
|
CollectionTypeAdapterFactory collection, FluentIterableTypeAdapterFactory fluentIterable) throws Exception {
|
||||||
throws Exception {
|
|
||||||
|
|
||||||
FieldNamingStrategy serializationPolicy = new AnnotationOrNameFieldNamingStrategy(new ExtractSerializedName(),
|
FieldNamingStrategy serializationPolicy = new AnnotationOrNameFieldNamingStrategy(ImmutableSet.of(
|
||||||
new ExtractNamed());
|
new ExtractSerializedName(), new ExtractNamed()));
|
||||||
|
|
||||||
GsonBuilder builder = new GsonBuilder().setFieldNamingStrategy(serializationPolicy);
|
GsonBuilder builder = new GsonBuilder().setFieldNamingStrategy(serializationPolicy);
|
||||||
|
|
||||||
|
@ -104,18 +104,17 @@ public class GsonModule extends AbstractModule {
|
||||||
builder.registerTypeAdapter(JsonBall.class, jsonAdapter.nullSafe());
|
builder.registerTypeAdapter(JsonBall.class, jsonAdapter.nullSafe());
|
||||||
builder.registerTypeAdapterFactory(optional);
|
builder.registerTypeAdapterFactory(optional);
|
||||||
builder.registerTypeAdapterFactory(iterable);
|
builder.registerTypeAdapterFactory(iterable);
|
||||||
|
builder.registerTypeAdapterFactory(collection);
|
||||||
builder.registerTypeAdapterFactory(set);
|
builder.registerTypeAdapterFactory(set);
|
||||||
builder.registerTypeAdapterFactory(map);
|
builder.registerTypeAdapterFactory(map);
|
||||||
builder.registerTypeAdapterFactory(multimap);
|
builder.registerTypeAdapterFactory(multimap);
|
||||||
builder.registerTypeAdapterFactory(fluentIterable);
|
builder.registerTypeAdapterFactory(fluentIterable);
|
||||||
|
|
||||||
AnnotationConstructorNamingStrategy deserializationPolicy =
|
AnnotationConstructorNamingStrategy deserializationPolicy = new AnnotationConstructorNamingStrategy(
|
||||||
new AnnotationConstructorNamingStrategy(
|
ImmutableSet.of(ConstructorProperties.class, Inject.class), ImmutableSet.of(new ExtractNamed()));
|
||||||
ImmutableSet.of(ConstructorProperties.class, Inject.class), ImmutableSet.of(new ExtractNamed()));
|
|
||||||
|
|
||||||
builder.registerTypeAdapterFactory(
|
builder.registerTypeAdapterFactory(new DeserializationConstructorAndReflectiveTypeAdapterFactory(
|
||||||
new DeserializationConstructorAndReflectiveTypeAdapterFactory(new ConstructorConstructor(),
|
new ConstructorConstructor(), serializationPolicy, Excluder.DEFAULT, deserializationPolicy));
|
||||||
serializationPolicy, Excluder.DEFAULT, deserializationPolicy));
|
|
||||||
|
|
||||||
// complicated (serializers/deserializers as they need context to operate)
|
// complicated (serializers/deserializers as they need context to operate)
|
||||||
builder.registerTypeHierarchyAdapter(Enum.class, new EnumTypeAdapterThatReturnsFromValue());
|
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.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static org.jclouds.reflect.Reflection2.typeToken;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Type;
|
import java.util.List;
|
||||||
import java.util.Map;
|
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.FieldNamingStrategy;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonSyntaxException;
|
import com.google.gson.JsonSyntaxException;
|
||||||
import com.google.gson.TypeAdapter;
|
import com.google.gson.TypeAdapter;
|
||||||
import com.google.gson.TypeAdapterFactory;
|
import com.google.gson.TypeAdapterFactory;
|
||||||
import com.google.gson.internal.$Gson$Types;
|
|
||||||
import com.google.gson.internal.ConstructorConstructor;
|
import com.google.gson.internal.ConstructorConstructor;
|
||||||
import com.google.gson.internal.Excluder;
|
import com.google.gson.internal.Excluder;
|
||||||
import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory;
|
import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory;
|
||||||
|
@ -49,21 +52,20 @@ import com.google.gson.stream.JsonWriter;
|
||||||
* <p/>
|
* <p/>
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Deserialization</li>
|
* <li>Deserialization</li>
|
||||||
* If there's an annotation designating a parameterized constructor, invoke that for fields
|
* If there's an annotation designating a parameterized constructor, invoke that for fields correlating to named
|
||||||
* correlating to named parameter annotations. Otherwise, use {@link ConstructorConstructor}, and
|
* parameter annotations. Otherwise, use {@link ConstructorConstructor}, and set fields via reflection.
|
||||||
* set fields via reflection.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* Notes: primitive constructor params are set to the Java defaults (0 or false) if not present; and
|
* Notes: primitive constructor params are set to the Java defaults (0 or false) if not present; and the empty object
|
||||||
* the empty object ({}) is treated as a null if the constructor for the object throws an NPE.
|
* ({}) is treated as a null if the constructor for the object throws an NPE.
|
||||||
* <li>Serialization</li>
|
* <li>Serialization</li> Serialize based on reflective access to fields, delegating to ReflectiveTypeAdaptor.
|
||||||
* Serialize based on reflective access to fields, delegating to ReflectiveTypeAdaptor.
|
|
||||||
* </ul>
|
* </ul>
|
||||||
* <h3>Example: Using javax inject to select a constructor and corresponding named parameters</h3>
|
* <h3>Example: Using javax inject to select a constructor and corresponding named parameters</h3>
|
||||||
* <p/>
|
* <p/>
|
||||||
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
*
|
*
|
||||||
* import NamingStrategies.*;
|
* import NamingStrategies.*;
|
||||||
*
|
*
|
||||||
* serializationStrategy = new AnnotationOrNameFieldNamingStrategy(
|
* serializationStrategy = new AnnotationOrNameFieldNamingStrategy(
|
||||||
* new ExtractSerializedName(), new ExtractNamed());
|
* new ExtractSerializedName(), new ExtractNamed());
|
||||||
*
|
*
|
||||||
|
@ -73,19 +75,20 @@ import com.google.gson.stream.JsonWriter;
|
||||||
*
|
*
|
||||||
* factory = new DeserializationConstructorAndReflectiveTypeAdapterFactory(new ConstructorConstructor(),
|
* factory = new DeserializationConstructorAndReflectiveTypeAdapterFactory(new ConstructorConstructor(),
|
||||||
* serializationStrategy, Excluder.DEFAULT, deserializationStrategy);
|
* serializationStrategy, Excluder.DEFAULT, deserializationStrategy);
|
||||||
*
|
*
|
||||||
* gson = new GsonBuilder(serializationStrategy).registerTypeAdapterFactory(factory).create();
|
* gson = new GsonBuilder(serializationStrategy).registerTypeAdapterFactory(factory).create();
|
||||||
*
|
*
|
||||||
* </pre>
|
* </pre>
|
||||||
* <p/>
|
* <p/>
|
||||||
* The above would work fine on the following class, which has no gson-specific annotations:
|
* The above would work fine on the following class, which has no gson-specific annotations:
|
||||||
* <p/>
|
* <p/>
|
||||||
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* private static class ImmutableAndVerifiedInCtor {
|
* private static class ImmutableAndVerifiedInCtor {
|
||||||
* final int foo;
|
* final int foo;
|
||||||
* @Named("_bar")
|
* @Named("_bar")
|
||||||
* final int bar;
|
* final int bar;
|
||||||
*
|
*
|
||||||
* @Inject
|
* @Inject
|
||||||
* ImmutableAndVerifiedInCtor(@Named("foo") int foo, @Named("_bar") int bar) {
|
* ImmutableAndVerifiedInCtor(@Named("foo") int foo, @Named("_bar") int bar) {
|
||||||
* if (foo < 0)
|
* if (foo < 0)
|
||||||
|
@ -97,54 +100,47 @@ import com.google.gson.stream.JsonWriter;
|
||||||
* </pre>
|
* </pre>
|
||||||
* <p/>
|
* <p/>
|
||||||
* <br/>
|
* <br/>
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
* @author Adam Lowe
|
* @author Adam Lowe
|
||||||
*/
|
*/
|
||||||
public final class DeserializationConstructorAndReflectiveTypeAdapterFactory implements TypeAdapterFactory {
|
public final class DeserializationConstructorAndReflectiveTypeAdapterFactory implements TypeAdapterFactory {
|
||||||
private final ConstructorFieldNamingStrategy constructorFieldNamingPolicy;
|
private final AnnotationConstructorNamingStrategy constructorFieldNamingPolicy;
|
||||||
private final ReflectiveTypeAdapterFactory delegateFactory;
|
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
|
* @see ReflectiveTypeAdapterFactory
|
||||||
*/
|
*/
|
||||||
public DeserializationConstructorAndReflectiveTypeAdapterFactory(
|
public DeserializationConstructorAndReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor,
|
||||||
ConstructorConstructor constructorConstructor,
|
FieldNamingStrategy serializationFieldNamingPolicy, Excluder excluder,
|
||||||
FieldNamingStrategy serializationFieldNamingPolicy,
|
AnnotationConstructorNamingStrategy deserializationFieldNamingPolicy) {
|
||||||
Excluder excluder,
|
this.constructorFieldNamingPolicy = checkNotNull(deserializationFieldNamingPolicy,
|
||||||
ConstructorFieldNamingStrategy deserializationFieldNamingPolicy) {
|
"deserializationFieldNamingPolicy");
|
||||||
this.constructorFieldNamingPolicy = checkNotNull(deserializationFieldNamingPolicy, "deserializationFieldNamingPolicy");
|
this.delegateFactory = new ReflectiveTypeAdapterFactory(constructorConstructor, checkNotNull(
|
||||||
this.delegateFactory = new ReflectiveTypeAdapterFactory(constructorConstructor, checkNotNull(serializationFieldNamingPolicy, "fieldNamingPolicy"), checkNotNull(excluder, "excluder"));
|
serializationFieldNamingPolicy, "fieldNamingPolicy"), checkNotNull(excluder, "excluder"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
|
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
||||||
Class<? super T> raw = type.getRawType();
|
com.google.common.reflect.TypeToken<T> token = typeToken(type.getType());
|
||||||
Constructor<? super T> deserializationCtor = constructorFieldNamingPolicy.getDeserializationConstructor(raw);
|
Invokable<T, T> deserializationCtor = constructorFieldNamingPolicy.getDeserializer(token);
|
||||||
|
|
||||||
if (deserializationCtor == null) {
|
if (deserializationCtor == null) {
|
||||||
return null; // allow GSON to choose the correct Adapter (can't simply return delegateFactory.create())
|
return null; // allow GSON to choose the correct Adapter (can't simply return delegateFactory.create())
|
||||||
} else {
|
} else {
|
||||||
deserializationCtor.setAccessible(true);
|
return new DeserializeIntoParameterizedConstructor<T>(delegateFactory.create(gson, type), deserializationCtor,
|
||||||
return new DeserializeWithParameterizedConstructorSerializeWithDelegate<T>(delegateFactory.create(gson, type), deserializationCtor,
|
getParameterReaders(gson, deserializationCtor));
|
||||||
getParameterReaders(gson, type, deserializationCtor));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class DeserializeWithParameterizedConstructorSerializeWithDelegate<T> extends TypeAdapter<T> {
|
private final class DeserializeIntoParameterizedConstructor<T> extends TypeAdapter<T> {
|
||||||
private final Constructor<? super T> parameterizedCtor;
|
private final TypeAdapter<T> serializer;
|
||||||
|
private final Invokable<T, T> parameterizedCtor;
|
||||||
private final Map<String, ParameterReader<?>> parameterReaders;
|
private final Map<String, ParameterReader<?>> parameterReaders;
|
||||||
private final TypeAdapter<T> delegate;
|
|
||||||
|
|
||||||
private DeserializeWithParameterizedConstructorSerializeWithDelegate(TypeAdapter<T> delegate,
|
private DeserializeIntoParameterizedConstructor(TypeAdapter<T> serializer, Invokable<T, T> deserializationCtor,
|
||||||
Constructor<? super T> parameterizedCtor, Map<String, ParameterReader<?>> parameterReaders) {
|
Map<String, ParameterReader<?>> parameterReaders) {
|
||||||
this.delegate = delegate;
|
this.serializer = serializer;
|
||||||
this.parameterizedCtor = parameterizedCtor;
|
this.parameterizedCtor = deserializationCtor;
|
||||||
this.parameterReaders = parameterReaders;
|
this.parameterReaders = parameterReaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,16 +151,16 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactory imp
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<?>[] paramTypes = parameterizedCtor.getParameterTypes();
|
List<Parameter> params = parameterizedCtor.getParameters();
|
||||||
Object[] ctorParams = new Object[paramTypes.length];
|
Object[] values = new Object[params.size()];
|
||||||
boolean empty = true;
|
boolean empty = true;
|
||||||
|
|
||||||
// Set all primitive constructor params to defaults
|
// Set all primitive constructor params to defaults
|
||||||
for (int i = 0; i < paramTypes.length; i++) {
|
for (Parameter param : params) {
|
||||||
if (paramTypes[i] == boolean.class) {
|
if (param.getType().getRawType() == boolean.class) {
|
||||||
ctorParams[i] = Boolean.FALSE;
|
values[param.hashCode()] = Boolean.FALSE;
|
||||||
} else if (paramTypes[i].isPrimitive()) {
|
} else if (param.getType().getRawType().isPrimitive()) {
|
||||||
ctorParams[i] = 0;
|
values[param.hashCode()] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,26 +174,27 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactory imp
|
||||||
in.skipValue();
|
in.skipValue();
|
||||||
} else {
|
} else {
|
||||||
Object value = parameter.read(in);
|
Object value = parameter.read(in);
|
||||||
if (value != null) ctorParams[parameter.index] = value;
|
if (value != null)
|
||||||
|
values[parameter.position] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
throw new JsonSyntaxException(e);
|
throw new JsonSyntaxException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < paramTypes.length; i++) {
|
for (Parameter param : params) {
|
||||||
if (paramTypes[i].isPrimitive()) {
|
if (param.getType().getRawType().isPrimitive()) {
|
||||||
checkArgument(ctorParams[i] != null, "Primitive param[" + i + "] in constructor " + parameterizedCtor
|
checkArgument(values[param.hashCode()] != null, "Primitive param[" + param.hashCode()
|
||||||
+ " cannot be absent!");
|
+ "] in constructor " + parameterizedCtor + " cannot be absent!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in.endObject();
|
in.endObject();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return newInstance(ctorParams);
|
return newInstance(values);
|
||||||
} catch (NullPointerException ex) {
|
} catch (NullPointerException ex) {
|
||||||
// If {} was found and constructor threw NPE, we treat the field as null
|
// 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;
|
return null;
|
||||||
}
|
}
|
||||||
throw ex;
|
throw ex;
|
||||||
|
@ -209,15 +206,12 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactory imp
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void write(JsonWriter out, T value) throws IOException {
|
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 {
|
private T newInstance(Object[] ctorParams) throws AssertionError {
|
||||||
try {
|
try {
|
||||||
return (T) parameterizedCtor.newInstance(ctorParams);
|
return (T) parameterizedCtor.invoke(null, ctorParams);
|
||||||
} catch (InstantiationException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
} catch (InvocationTargetException e) {
|
} catch (InvocationTargetException e) {
|
||||||
|
@ -226,43 +220,79 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactory imp
|
||||||
throw new AssertionError(e);
|
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
|
// logic borrowed from ReflectiveTypeAdapterFactory
|
||||||
static class ParameterReader<T> {
|
static class ParameterReader<T> {
|
||||||
final String name;
|
final String name;
|
||||||
final int index;
|
final int position;
|
||||||
final TypeAdapter<T> typeAdapter;
|
final TypeAdapter<T> typeAdapter;
|
||||||
|
|
||||||
ParameterReader(String name, int index, TypeAdapter<T> typeAdapter) {
|
ParameterReader(int position, String name, TypeAdapter<T> typeAdapter) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.index = index;
|
this.position = position;
|
||||||
this.typeAdapter = typeAdapter;
|
this.typeAdapter = typeAdapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object read(JsonReader reader) throws IOException {
|
public Object read(JsonReader reader) throws IOException {
|
||||||
return typeAdapter.read(reader);
|
return typeAdapter.read(reader);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, ParameterReader<?>> getParameterReaders(Gson context, TypeToken<?> declaring, Constructor<?> constructor) {
|
@Override
|
||||||
Map<String, ParameterReader<?>> result = Maps.newLinkedHashMap();
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof ParameterReader) {
|
||||||
for (int index = 0; index < constructor.getGenericParameterTypes().length; index++) {
|
ParameterReader<?> that = ParameterReader.class.cast(obj);
|
||||||
Type parameterType = getTypeOfConstructorParameter(declaring, constructor, index);
|
return position == that.position && name.equals(that.name);
|
||||||
TypeAdapter<?> adapter = context.getAdapter(TypeToken.get(parameterType));
|
}
|
||||||
String parameterName = constructorFieldNamingPolicy.translateName(constructor, index);
|
return false;
|
||||||
checkArgument(parameterName != null, constructor + " parameter " + 0 + " failed to be named by " + constructorFieldNamingPolicy);
|
}
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
|
||||||
ParameterReader<?> parameterReader = new ParameterReader(parameterName, index, adapter);
|
@Override
|
||||||
ParameterReader<?> previous = result.put(parameterReader.name, parameterReader);
|
public int hashCode() {
|
||||||
checkArgument(previous == null, constructor + " declares multiple JSON parameters named " + parameterReader.name);
|
return Objects.hashCode(position, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return typeAdapter + " arg" + position;
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Type getTypeOfConstructorParameter(TypeToken<?> declaring, Constructor<?> constructor, int index) {
|
private <T> Map<String, ParameterReader<?>> getParameterReaders(Gson context, Invokable<T, T> deserializationCtor) {
|
||||||
Type genericParameter = constructor.getGenericParameterTypes()[index];
|
Builder<String, ParameterReader<?>> result = ImmutableMap.builder();
|
||||||
return $Gson$Types.resolve(declaring.getType(), declaring.getRawType(), genericParameter);
|
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;
|
package org.jclouds.json.internal;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import static org.jclouds.reflect.Reflection2.method;
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
import java.lang.reflect.Type;
|
||||||
import com.google.common.cache.CacheLoader;
|
|
||||||
import com.google.common.cache.LoadingCache;
|
import com.google.common.reflect.Invokable;
|
||||||
import com.google.gson.JsonDeserializationContext;
|
import com.google.gson.JsonDeserializationContext;
|
||||||
import com.google.gson.JsonDeserializer;
|
import com.google.gson.JsonDeserializer;
|
||||||
import com.google.gson.JsonElement;
|
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());
|
return (T) Enum.valueOf((Class<T>) classOfT, json.getAsString());
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
try {
|
try {
|
||||||
Method converter = classToConvert.get((Class<?>) classOfT);
|
Invokable<?, Object> converter = method((Class<?>) classOfT, "fromValue", String.class);
|
||||||
return (T) converter.invoke(null, json.getAsString());
|
return (T) converter.invoke(null, json.getAsString());
|
||||||
} catch (Exception e1) {
|
} catch (Exception e1) {
|
||||||
throw e;
|
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.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
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.beans.ConstructorProperties;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.base.Supplier;
|
||||||
|
import com.google.common.collect.FluentIterable;
|
||||||
import com.google.common.collect.Maps;
|
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.FieldNamingStrategy;
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NamingStrategies used for JSON deserialization using GSON
|
* NamingStrategies used for JSON deserialization using GSON
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
* @author Adam Lowe
|
* @author Adam Lowe
|
||||||
*/
|
*/
|
||||||
public class NamingStrategies {
|
public class NamingStrategies {
|
||||||
/**
|
/**
|
||||||
* Specifies how to extract the name from an annotation for use in determining the serialized
|
* Specifies how to extract the name from an annotation for use in determining the serialized name.
|
||||||
* name.
|
*
|
||||||
*
|
|
||||||
* @see com.google.gson.annotations.SerializedName
|
* @see com.google.gson.annotations.SerializedName
|
||||||
* @see ExtractSerializedName
|
* @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 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.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() {
|
@Override
|
||||||
return annotationType;
|
public String apply(Annotation in) {
|
||||||
|
return extractName(annotationType.cast(in));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract String extractName(A cast);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Predicate<Annotation> get() {
|
||||||
|
return predicate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -90,7 +115,6 @@ public class NamingStrategies {
|
||||||
super(SerializedName.class);
|
super(SerializedName.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String extractName(SerializedName in) {
|
public String extractName(SerializedName in) {
|
||||||
return checkNotNull(in, "input annotation").value();
|
return checkNotNull(in, "input annotation").value();
|
||||||
}
|
}
|
||||||
|
@ -108,22 +132,22 @@ public class NamingStrategies {
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract static class AnnotationBasedNamingStrategy {
|
public abstract static class AnnotationBasedNamingStrategy {
|
||||||
protected final Map<Class<? extends Annotation>, ? extends NameExtractor> annotationToNameExtractor;
|
protected final Map<Class<? extends Annotation>, ? extends NameExtractor<?>> annotationToNameExtractor;
|
||||||
private String forToString;
|
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");
|
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
|
@Override
|
||||||
public Class<? extends Annotation> apply(NameExtractor input) {
|
public String apply(NameExtractor<?> input) {
|
||||||
return input.annotationType();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.forToString = Joiner.on(",").join(Iterables.transform(extractors, new Function<NameExtractor, String>() {
|
|
||||||
@Override
|
|
||||||
public String apply(NameExtractor input) {
|
|
||||||
return input.annotationType().getName();
|
return input.annotationType().getName();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -138,32 +162,30 @@ public class NamingStrategies {
|
||||||
/**
|
/**
|
||||||
* Definition of field naming policy for annotation-based field
|
* 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);
|
super(extractors);
|
||||||
checkArgument(extractors.iterator().hasNext(), "you must supply at least one name extractor, for example: "
|
checkArgument(extractors.iterator().hasNext(), "you must supply at least one name extractor, for example: "
|
||||||
+ ExtractSerializedName.class.getSimpleName());
|
+ ExtractSerializedName.class.getSimpleName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
@Override
|
||||||
public String translateName(Field f) {
|
public String translateName(Field f) {
|
||||||
for (Annotation annotation : f.getAnnotations()) {
|
for (Annotation annotation : f.getAnnotations()) {
|
||||||
if (annotationToNameExtractor.containsKey(annotation.annotationType())) {
|
if (annotationToNameExtractor.containsKey(annotation.annotationType())) {
|
||||||
return annotationToNameExtractor.get(annotation.annotationType()).extractName(annotation);
|
return annotationToNameExtractor.get(annotation.annotationType()).apply(annotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class AnnotationOrNameFieldNamingStrategy extends AnnotationFieldNamingStrategy implements FieldNamingStrategy {
|
public static class AnnotationOrNameFieldNamingStrategy extends AnnotationFieldNamingStrategy implements
|
||||||
public AnnotationOrNameFieldNamingStrategy(NameExtractor... extractors) {
|
FieldNamingStrategy {
|
||||||
this(ImmutableSet.copyOf(extractors));
|
|
||||||
}
|
|
||||||
|
|
||||||
public AnnotationOrNameFieldNamingStrategy(Iterable<? extends NameExtractor> extractors) {
|
public AnnotationOrNameFieldNamingStrategy(Iterable<? extends NameExtractor<?>> extractors) {
|
||||||
super(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
|
* Determines field naming from constructor annotations
|
||||||
*/
|
*/
|
||||||
public static class AnnotationConstructorNamingStrategy extends AnnotationBasedNamingStrategy implements ConstructorFieldNamingStrategy {
|
public final static class AnnotationConstructorNamingStrategy extends AnnotationBasedNamingStrategy {
|
||||||
private final Set<Class<? extends Annotation>> markers;
|
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);
|
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")
|
private static Predicate<Invokable<?, ?>> hasAnnotationIn(
|
||||||
public <T> Constructor<? super T> getDeserializationConstructor(Class<?> raw) {
|
final Collection<? extends Class<? extends Annotation>> markers) {
|
||||||
for (Constructor<?> ctor : raw.getDeclaredConstructors())
|
return new Predicate<Invokable<?, ?>>() {
|
||||||
for (Class<? extends Annotation> deserializationCtorAnnotation : markers)
|
public boolean apply(Invokable<?, ?> input) {
|
||||||
if (ctor.isAnnotationPresent(deserializationCtorAnnotation))
|
return FluentIterable.from(Arrays.asList(input.getAnnotations()))
|
||||||
return (Constructor<T>) ctor;
|
.transform(new Function<Annotation, Class<? extends Annotation>>() {
|
||||||
|
public Class<? extends Annotation> apply(Annotation input) {
|
||||||
return null;
|
return input.annotationType();
|
||||||
|
}
|
||||||
|
}).anyMatch(in(markers));
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@VisibleForTesting
|
||||||
@Override
|
<T> Invokable<T, T> getDeserializer(TypeToken<T> token) {
|
||||||
public String translateName(Constructor<?> c, int index) {
|
return tryFind(constructors(token), hasMarker).orNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
<T> String translateName(Invokable<T, T> c, int index) {
|
||||||
String name = null;
|
String name = null;
|
||||||
|
|
||||||
if (markers.contains(ConstructorProperties.class) && c.getAnnotation(ConstructorProperties.class) != 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())) {
|
if (annotationToNameExtractor.containsKey(annotation.annotationType())) {
|
||||||
name = annotationToNameExtractor.get(annotation.annotationType()).extractName(annotation);
|
name = annotationToNameExtractor.get(annotation.annotationType()).apply(annotation);
|
||||||
break;
|
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;
|
package org.jclouds.lifecycle.config;
|
||||||
|
|
||||||
import static com.google.common.base.Throwables.propagate;
|
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.common.util.concurrent.MoreExecutors.sameThreadExecutor;
|
||||||
import static com.google.inject.matcher.Matchers.any;
|
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_IO_WORKER_THREADS;
|
||||||
import static org.jclouds.Constants.PROPERTY_SCHEDULER_THREADS;
|
import static org.jclouds.Constants.PROPERTY_SCHEDULER_THREADS;
|
||||||
import static org.jclouds.Constants.PROPERTY_USER_THREADS;
|
import static org.jclouds.Constants.PROPERTY_USER_THREADS;
|
||||||
|
import static org.jclouds.reflect.Reflection2.methods;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.util.Collection;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
@ -40,6 +39,8 @@ import javax.inject.Named;
|
||||||
|
|
||||||
import org.jclouds.lifecycle.Closer;
|
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.ExecutionList;
|
||||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
|
@ -51,15 +52,15 @@ import com.google.inject.spi.TypeEncounter;
|
||||||
import com.google.inject.spi.TypeListener;
|
import com.google.inject.spi.TypeListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This associates java lifecycle annotations with guice hooks. For example, we invoke
|
* This associates java lifecycle annotations with guice hooks. For example, we invoke {@link PostConstruct} after
|
||||||
* {@link PostConstruct} after injection, and Associate {@link PreDestroy} with a global
|
* injection, and Associate {@link PreDestroy} with a global {@link Closer} object.
|
||||||
* {@link Closer} object.
|
|
||||||
*
|
*
|
||||||
* <h3>Important</h3> Make sure you create your injector with {@link Stage#PRODUCTION} and execute
|
* <h3>Important</h3> Make sure you create your injector with {@link Stage#PRODUCTION} and execute the bound
|
||||||
* the bound {@link ExecutionList} prior to using any other objects.
|
* {@link ExecutionList} prior to using any other objects.
|
||||||
*
|
*
|
||||||
* <p/>
|
* <p/>
|
||||||
* Ex.
|
* Ex.
|
||||||
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
*
|
*
|
||||||
* </pre>
|
* </pre>
|
||||||
|
@ -89,7 +90,7 @@ public class LifeCycleModule extends AbstractModule {
|
||||||
ioExecutor.shutdownNow();
|
ioExecutor.shutdownNow();
|
||||||
// ScheduledExecutor is defined in an optional module
|
// ScheduledExecutor is defined in an optional module
|
||||||
if (scheduledExecutor != null)
|
if (scheduledExecutor != null)
|
||||||
scheduledExecutor.shutdownNow();
|
scheduledExecutor.shutdownNow();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -103,67 +104,57 @@ public class LifeCycleModule extends AbstractModule {
|
||||||
bind(ExecutionList.class).toInstance(list);
|
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) {
|
protected void bindPostInjectionInvoke(final Closer closer, final ExecutionList list) {
|
||||||
bindListener(any(), new TypeListener() {
|
bindListener(any(), new TypeListener() {
|
||||||
public <I> void hear(TypeLiteral<I> injectableType, TypeEncounter<I> encounter) {
|
public <I> void hear(TypeLiteral<I> injectableType, TypeEncounter<I> encounter) {
|
||||||
Set<Method> methods = newHashSet();
|
Collection<? extends Invokable<? super I, Object>> methods = methods(injectableType.getRawType());
|
||||||
Class<? super I> type = injectableType.getRawType();
|
for (final Invokable<? super I, Object> method : filter(methods, isPostConstruct)) {
|
||||||
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) {
|
|
||||||
encounter.register(new InjectionListener<I>() {
|
encounter.register(new InjectionListener<I>() {
|
||||||
public void afterInjection(final I injectee) {
|
public void afterInjection(final I injectee) {
|
||||||
list.add(new Runnable() {
|
list.add(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
invokeOnInjectee(method, injectee);
|
||||||
method.invoke(injectee);
|
|
||||||
} catch (InvocationTargetException ie) {
|
|
||||||
Throwable e = ie.getTargetException();
|
|
||||||
throw propagate(e);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw propagate(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}, sameThreadExecutor());
|
}, 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;
|
package org.jclouds.reflect;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
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.Method;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
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.annotations.Beta;
|
||||||
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Objects;
|
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.CacheBuilder;
|
||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
import com.google.common.cache.LoadingCache;
|
import com.google.common.cache.LoadingCache;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap.Builder;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.reflect.Invokable;
|
import com.google.common.reflect.Invokable;
|
||||||
|
import com.google.common.reflect.Parameter;
|
||||||
import com.google.common.reflect.TypeToken;
|
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}.
|
* 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
|
* @since 1.6
|
||||||
*/
|
*/
|
||||||
@Beta
|
@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.
|
* gets a {@link TypeToken} for the given class.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T> TypeToken<T> typeToken(Class<T> in) {
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T, R> Invokable<T, R> method(TypeToken<T> ownerType, Method method) {
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T, R> Invokable<T, R> method(Class<T> ownerType, String name, Class<?>... parameterTypes) {
|
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));
|
parameterTypes));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,51 +137,65 @@ public final class Reflection2 {
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T> Collection<Invokable<T, Object>> methods(Class<T> ownerType) {
|
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<?, ?>>() {
|
* this gets all declared constructors, not just public ones. makes them accessible, as well.
|
||||||
public Invokable<?, ?> load(TypeTokenAndMethod key) {
|
*/
|
||||||
return key.type.method(key.method);
|
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 static List<Class<?>> toClasses(ImmutableList<Parameter> params) {
|
||||||
|
return Lists.transform(params, new Function<Parameter, Class<?>>() {
|
||||||
protected final TypeToken<?> type;
|
public Class<?> apply(Parameter input) {
|
||||||
protected final Method method;
|
return input.getType().getRawType();
|
||||||
|
}
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final LoadingCache<Class<?>, TypeToken<?>> typeTokenForClass = CacheBuilder.newBuilder().build(
|
private static LoadingCache<Type, TypeToken<?>> typeTokenForType = CacheBuilder.newBuilder().build(
|
||||||
new CacheLoader<Class<?>, TypeToken<?>>() {
|
new CacheLoader<Type, TypeToken<?>>() {
|
||||||
public TypeToken<?> load(final Class<?> key) {
|
public TypeToken<?> load(Type key) {
|
||||||
return TypeToken.of(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 {
|
private static class TypeTokenAndParameterTypes {
|
||||||
|
|
||||||
protected final TypeToken<?> type;
|
protected TypeToken<?> type;
|
||||||
protected final List<Class<?>> parameterTypes;
|
protected List<Class<?>> parameterTypes;
|
||||||
|
|
||||||
public TypeTokenAndParameterTypes(TypeToken<?> type, Class<?>... parameterTypes) {
|
public TypeTokenAndParameterTypes(TypeToken<?> type, Class<?>... parameterTypes) {
|
||||||
this.type = checkNotNull(type, "type");
|
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<?, ?>>() {
|
.newBuilder().build(new CacheLoader<TypeTokenNameAndParameterTypes, Invokable<?, ?>>() {
|
||||||
public Invokable<?, ?> load(final TypeTokenNameAndParameterTypes key) {
|
public Invokable<?, ?> load(final TypeTokenNameAndParameterTypes key) {
|
||||||
try {
|
Set<Invokable<?, ?>> methods = get(methodsForTypeToken, key.type);
|
||||||
Method method = key.type.getRawType().getMethod(key.name, toArray(key.parameterTypes, Class.class));
|
Optional<Invokable<?, ?>> method = tryFind(methods, new Predicate<Invokable<?, ?>>() {
|
||||||
return methods.apply(new TypeTokenAndMethod(key.type, method));
|
public boolean apply(Invokable<?, ?> input) {
|
||||||
} catch (SecurityException e) {
|
return Objects.equal(input.getName(), key.name)
|
||||||
throw new IllegalArgumentException(e.getMessage() + " getting method " + key.toString(), e);
|
&& Objects.equal(toClasses(input.getParameters()), key.parameterTypes);
|
||||||
} catch (NoSuchMethodException e) {
|
}
|
||||||
throw new IllegalArgumentException("no such method " + key.toString(), e);
|
});
|
||||||
}
|
if (method.isPresent())
|
||||||
|
return method.get();
|
||||||
|
throw new IllegalArgumentException("no such method " + key.toString() + "in: " + methods);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
private static class TypeTokenNameAndParameterTypes extends TypeTokenAndParameterTypes {
|
private static class TypeTokenNameAndParameterTypes extends TypeTokenAndParameterTypes {
|
||||||
|
|
||||||
private final String name;
|
private String name;
|
||||||
|
|
||||||
public TypeTokenNameAndParameterTypes(TypeToken<?> type, String name, Class<?>... parameterTypes) {
|
public TypeTokenNameAndParameterTypes(TypeToken<?> type, String name, Class<?>... parameterTypes) {
|
||||||
super(type, 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<?, ?>>>() {
|
* this gets all declared methods, not just public ones. makes them accessible. Does not include Object methods.
|
||||||
public Map<Method, Invokable<?, ?>> load(final TypeToken<?> key) {
|
*/
|
||||||
Builder<Method, Invokable<?, ?>> builder = ImmutableMap.<Method, Invokable<?, ?>> builder();
|
private static LoadingCache<TypeToken<?>, Set<Invokable<?, ?>>> methodsForTypeToken = CacheBuilder
|
||||||
for (Method method : key.getRawType().getMethods())
|
.newBuilder().build(new CacheLoader<TypeToken<?>, Set<Invokable<?, ?>>>() {
|
||||||
builder.put(method, method(key, method));
|
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();
|
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.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
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.toArray;
|
||||||
import static com.google.common.collect.Iterables.transform;
|
import static com.google.common.collect.Iterables.transform;
|
||||||
import static com.google.common.collect.Maps.transformValues;
|
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.Maps2.transformKeys;
|
||||||
import static org.jclouds.util.Predicates2.startsWith;
|
import static org.jclouds.util.Predicates2.startsWith;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
@ -47,7 +44,6 @@ import org.jclouds.http.functions.config.SaxParserModule;
|
||||||
import org.jclouds.internal.FilterStringsBoundToInjectorByName;
|
import org.jclouds.internal.FilterStringsBoundToInjectorByName;
|
||||||
import org.jclouds.json.config.GsonModule;
|
import org.jclouds.json.config.GsonModule;
|
||||||
import org.jclouds.location.config.LocationModule;
|
import org.jclouds.location.config.LocationModule;
|
||||||
import com.google.common.reflect.Invokable;
|
|
||||||
import org.jclouds.proxy.ProxyForURI;
|
import org.jclouds.proxy.ProxyForURI;
|
||||||
import org.jclouds.rest.AuthorizationException;
|
import org.jclouds.rest.AuthorizationException;
|
||||||
import org.jclouds.rest.HttpAsyncClient;
|
import org.jclouds.rest.HttpAsyncClient;
|
||||||
|
@ -55,13 +51,14 @@ import org.jclouds.rest.HttpClient;
|
||||||
import org.jclouds.rest.binders.BindToJsonPayloadWrappedWith;
|
import org.jclouds.rest.binders.BindToJsonPayloadWrappedWith;
|
||||||
import org.jclouds.rest.internal.BlockOnFuture;
|
import org.jclouds.rest.internal.BlockOnFuture;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import com.google.common.collect.ImmutableMap;
|
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.common.reflect.Parameter;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
|
@ -80,8 +77,6 @@ public class RestModule extends AbstractModule {
|
||||||
this(ImmutableMap.<Class<?>, Class<?>> of());
|
this(ImmutableMap.<Class<?>, Class<?>> of());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Set<Method> objectMethods = ImmutableSet.copyOf(Object.class.getMethods());
|
|
||||||
|
|
||||||
public RestModule(Map<Class<?>, Class<?>> sync2Async) {
|
public RestModule(Map<Class<?>, Class<?>> sync2Async) {
|
||||||
this.sync2Async = sync2Async;
|
this.sync2Async = sync2Async;
|
||||||
}
|
}
|
||||||
|
@ -92,6 +87,11 @@ public class RestModule extends AbstractModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
protected Cache<Invokable<?, ?>, Invokable<?, ?>> seedKnownSync2AsyncInvokables() {
|
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();
|
Cache<Invokable<?, ?>, Invokable<?, ?>> sync2AsyncBuilder = CacheBuilder.newBuilder().build();
|
||||||
putInvokables(HttpClient.class, HttpAsyncClient.class, sync2AsyncBuilder);
|
putInvokables(HttpClient.class, HttpAsyncClient.class, sync2AsyncBuilder);
|
||||||
for (Class<?> s : sync2Async.keySet()) {
|
for (Class<?> s : sync2Async.keySet()) {
|
||||||
|
@ -103,18 +103,10 @@ public class RestModule extends AbstractModule {
|
||||||
// accessible for ClientProvider
|
// accessible for ClientProvider
|
||||||
public static void putInvokables(Class<?> sync, Class<?> async, Cache<Invokable<?, ?>, Invokable<?, ?>> cache) {
|
public static void putInvokables(Class<?> sync, Class<?> async, Cache<Invokable<?, ?>, Invokable<?, ?>> cache) {
|
||||||
for (Invokable<?, ?> invoked : methods(sync)) {
|
for (Invokable<?, ?> invoked : methods(sync)) {
|
||||||
if (!objectMethods.contains(invoked)) {
|
Invokable<?, ?> delegatedMethod = method(async, invoked.getName(), getParameterTypes(invoked));
|
||||||
try {
|
checkArgument(delegatedMethod.getExceptionTypes().equals(invoked.getExceptionTypes()),
|
||||||
Invokable<?, ?> delegatedMethod = method(async, invoked.getName(), getParameterTypes(invoked));
|
"invoked %s has different typed exceptions than target %s", invoked, delegatedMethod);
|
||||||
checkArgument(delegatedMethod.getExceptionTypes().equals(invoked.getExceptionTypes()),
|
cache.put(invoked, delegatedMethod);
|
||||||
"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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,14 +19,13 @@
|
||||||
package org.jclouds.rest.config;
|
package org.jclouds.rest.config;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
import static com.google.inject.name.Names.named;
|
||||||
|
|
||||||
import org.jclouds.reflect.Invocation;
|
import org.jclouds.reflect.Invocation;
|
||||||
|
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Key;
|
import com.google.inject.Key;
|
||||||
import com.google.inject.Provider;
|
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.
|
* 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>() {
|
private static final Key<Invocation> CALLER_INVOCATION = Key.get(Invocation.class, named("caller"));
|
||||||
}, Names.named("caller"));
|
|
||||||
|
|
||||||
class CallerInvocationProvider implements Provider<Invocation> {
|
class CallerInvocationProvider implements Provider<Invocation> {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
package org.jclouds.json.internal;
|
package org.jclouds.json.internal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
* contributor license agreements. See the NOTICE file
|
* contributor license agreements. See the NOTICE file
|
||||||
|
@ -54,6 +55,7 @@ import com.google.gson.reflect.TypeToken;
|
||||||
* @author Adam Lowe
|
* @author Adam Lowe
|
||||||
*/
|
*/
|
||||||
@Test(testName = "DeserializationConstructorTypeAdapterFactoryTest")
|
@Test(testName = "DeserializationConstructorTypeAdapterFactoryTest")
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest {
|
public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest {
|
||||||
|
|
||||||
Gson gson = new Gson();
|
Gson gson = new Gson();
|
||||||
|
@ -61,13 +63,10 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
||||||
DeserializationConstructorAndReflectiveTypeAdapterFactory parameterizedCtorFactory = parameterizedCtorFactory();
|
DeserializationConstructorAndReflectiveTypeAdapterFactory parameterizedCtorFactory = parameterizedCtorFactory();
|
||||||
|
|
||||||
static DeserializationConstructorAndReflectiveTypeAdapterFactory parameterizedCtorFactory() {
|
static DeserializationConstructorAndReflectiveTypeAdapterFactory parameterizedCtorFactory() {
|
||||||
FieldNamingStrategy serializationPolicy = new AnnotationOrNameFieldNamingStrategy(
|
FieldNamingStrategy serializationPolicy = new AnnotationOrNameFieldNamingStrategy(ImmutableSet.of(
|
||||||
ImmutableSet.of(new ExtractSerializedName(), new ExtractNamed())
|
new ExtractSerializedName(), new ExtractNamed()));
|
||||||
);
|
NamingStrategies.AnnotationConstructorNamingStrategy deserializationPolicy = new NamingStrategies.AnnotationConstructorNamingStrategy(
|
||||||
NamingStrategies.AnnotationConstructorNamingStrategy deserializationPolicy =
|
ImmutableSet.of(ConstructorProperties.class, Inject.class), ImmutableSet.of(new ExtractNamed()));
|
||||||
new NamingStrategies.AnnotationConstructorNamingStrategy(
|
|
||||||
ImmutableSet.of(ConstructorProperties.class, Inject.class),
|
|
||||||
ImmutableSet.of(new ExtractNamed()));
|
|
||||||
|
|
||||||
return new DeserializationConstructorAndReflectiveTypeAdapterFactory(new ConstructorConstructor(),
|
return new DeserializationConstructorAndReflectiveTypeAdapterFactory(new ConstructorConstructor(),
|
||||||
serializationPolicy, Excluder.DEFAULT, deserializationPolicy);
|
serializationPolicy, Excluder.DEFAULT, deserializationPolicy);
|
||||||
|
@ -83,25 +82,11 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
||||||
|
|
||||||
private DefaultConstructor() {
|
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 {
|
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);
|
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() {
|
public void testSerializedNameRequiredOnAllParameters() {
|
||||||
try {
|
parameterizedCtorFactory
|
||||||
parameterizedCtorFactory.create(gson, TypeToken
|
.create(gson, TypeToken.get(WithDeserializationConstructorButWithoutSerializedName.class));
|
||||||
.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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class DuplicateSerializedNames {
|
private static class DuplicateSerializedNames {
|
||||||
|
@ -137,15 +116,9 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "duplicate key: foo")
|
||||||
public void testNoDuplicateSerializedNamesRequiredOnAllParameters() {
|
public void testNoDuplicateSerializedNamesRequiredOnAllParameters() {
|
||||||
try {
|
parameterizedCtorFactory.create(gson, TypeToken.get(DuplicateSerializedNames.class));
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ValidatedConstructor {
|
private static class ValidatedConstructor {
|
||||||
|
@ -160,34 +133,18 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
||||||
this.bar = bar;
|
this.bar = bar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (obj == null)
|
|
||||||
return false;
|
|
||||||
if (obj == this)
|
|
||||||
return true;
|
|
||||||
ValidatedConstructor other = ValidatedConstructor.class.cast(obj);
|
ValidatedConstructor other = ValidatedConstructor.class.cast(obj);
|
||||||
if (bar != other.bar)
|
return other != null && Objects.equal(foo, other.foo) && Objects.equal(bar, other.bar);
|
||||||
return false;
|
|
||||||
if (foo != other.foo)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() { return "ValidatedConstructor[foo=" + foo + ",bar=" + bar + "]"; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "negative!")
|
||||||
public void testValidatedConstructor() throws IOException {
|
public void testValidatedConstructor() throws IOException {
|
||||||
TypeAdapter<ValidatedConstructor> adapter = parameterizedCtorFactory.create(gson, TypeToken
|
TypeAdapter<ValidatedConstructor> adapter = parameterizedCtorFactory.create(gson,
|
||||||
.get(ValidatedConstructor.class));
|
TypeToken.get(ValidatedConstructor.class));
|
||||||
assertEquals(new ValidatedConstructor(0, 1), adapter.fromJson("{\"foo\":0,\"bar\":1}"));
|
assertEquals(new ValidatedConstructor(0, 1), adapter.fromJson("{\"foo\":0,\"bar\":1}"));
|
||||||
try {
|
adapter.fromJson("{\"foo\":-1,\"bar\":1}");
|
||||||
adapter.fromJson("{\"foo\":-1,\"bar\":1}");
|
|
||||||
fail();
|
|
||||||
} catch (IllegalArgumentException expected) {
|
|
||||||
assertEquals("negative!", expected.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class GenericParamsCopiedIn {
|
private static class GenericParamsCopiedIn {
|
||||||
|
@ -199,12 +156,11 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
||||||
this.foo = Lists.newArrayList(foo);
|
this.foo = Lists.newArrayList(foo);
|
||||||
this.bar = Maps.newHashMap(bar);
|
this.bar = Maps.newHashMap(bar);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGenericParamsCopiedIn() throws IOException {
|
public void testGenericParamsCopiedIn() throws IOException {
|
||||||
TypeAdapter<GenericParamsCopiedIn> adapter = parameterizedCtorFactory.create(gson, TypeToken
|
TypeAdapter<GenericParamsCopiedIn> adapter = parameterizedCtorFactory.create(gson,
|
||||||
.get(GenericParamsCopiedIn.class));
|
TypeToken.get(GenericParamsCopiedIn.class));
|
||||||
List<String> inputFoo = Lists.newArrayList();
|
List<String> inputFoo = Lists.newArrayList();
|
||||||
inputFoo.add("one");
|
inputFoo.add("one");
|
||||||
Map<String, String> inputBar = Maps.newHashMap();
|
Map<String, String> inputBar = Maps.newHashMap();
|
||||||
|
@ -222,29 +178,27 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
||||||
@Named("_bar")
|
@Named("_bar")
|
||||||
final int bar;
|
final int bar;
|
||||||
|
|
||||||
@ConstructorProperties({"foo", "_bar"})
|
@ConstructorProperties({ "foo", "_bar" })
|
||||||
RenamedFields(int foo, int bar) {
|
RenamedFields(int foo, int bar) {
|
||||||
if (foo < 0)
|
if (foo < 0)
|
||||||
throw new IllegalArgumentException("negative!");
|
throw new IllegalArgumentException("negative!");
|
||||||
this.foo = foo;
|
this.foo = foo;
|
||||||
this.bar = bar;
|
this.bar = bar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (obj == null)
|
|
||||||
return false;
|
|
||||||
if (obj == this)
|
|
||||||
return true;
|
|
||||||
RenamedFields other = RenamedFields.class.cast(obj);
|
RenamedFields other = RenamedFields.class.cast(obj);
|
||||||
if (bar != other.bar)
|
return other != null && Objects.equal(foo, other.foo) && Objects.equal(bar, other.bar);
|
||||||
return false;
|
|
||||||
if (foo != other.foo)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
public void testRenamedFields() throws IOException {
|
||||||
TypeAdapter<RenamedFields> adapter = parameterizedCtorFactory.create(gson, TypeToken.get(RenamedFields.class));
|
TypeAdapter<RenamedFields> adapter = parameterizedCtorFactory.create(gson, TypeToken.get(RenamedFields.class));
|
||||||
assertEquals(new RenamedFields(0, 1), adapter.fromJson("{\"foo\":0,\"_bar\":1}"));
|
assertEquals(new RenamedFields(0, 1), adapter.fromJson("{\"foo\":0,\"_bar\":1}"));
|
||||||
|
@ -255,44 +209,38 @@ public final class DeserializationConstructorAndReflectiveTypeAdapterFactoryTest
|
||||||
final ValidatedConstructor x;
|
final ValidatedConstructor x;
|
||||||
final ValidatedConstructor y;
|
final ValidatedConstructor y;
|
||||||
|
|
||||||
@ConstructorProperties({"x", "y"})
|
@ConstructorProperties({ "x", "y" })
|
||||||
ComposedObjects(ValidatedConstructor x, ValidatedConstructor y) {
|
ComposedObjects(ValidatedConstructor x, ValidatedConstructor y) {
|
||||||
this.x = checkNotNull(x);
|
this.x = checkNotNull(x);
|
||||||
this.y = checkNotNull(y);
|
this.y = checkNotNull(y);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
ComposedObjects other = ComposedObjects.class.cast(obj);
|
ComposedObjects other = ComposedObjects.class.cast(obj);
|
||||||
return other != null && Objects.equal(x, other.x) && Objects.equal(y, other.y);
|
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 {
|
public void checkSimpleComposedObject() throws IOException {
|
||||||
ValidatedConstructor x = new ValidatedConstructor(0,1);
|
ValidatedConstructor x = new ValidatedConstructor(0, 1);
|
||||||
ValidatedConstructor y = new ValidatedConstructor(1,2);
|
ValidatedConstructor y = new ValidatedConstructor(1, 2);
|
||||||
TypeAdapter<ComposedObjects> adapter = parameterizedCtorFactory.create(gson, TypeToken.get(ComposedObjects.class));
|
TypeAdapter<ComposedObjects> adapter = parameterizedCtorFactory
|
||||||
assertEquals(new ComposedObjects(x, y), adapter.fromJson("{\"x\":{\"foo\":0,\"bar\":1},\"y\":{\"foo\":1,\"bar\":2}}"));
|
.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 {
|
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("{}"));
|
assertNull(adapter.fromJson("{}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expectedExceptions = NullPointerException.class)
|
@Test(expectedExceptions = NullPointerException.class)
|
||||||
public void testPartialObjectStillThrows() throws IOException {
|
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}}"));
|
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.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import static org.jclouds.reflect.Reflection2.typeToken;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
import static org.testng.Assert.assertNotNull;
|
import static org.testng.Assert.assertNotNull;
|
||||||
import static org.testng.Assert.assertNull;
|
import static org.testng.Assert.assertNull;
|
||||||
import static org.testng.Assert.fail;
|
import static org.testng.Assert.fail;
|
||||||
|
|
||||||
import java.beans.ConstructorProperties;
|
import java.beans.ConstructorProperties;
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
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.AnnotationConstructorNamingStrategy;
|
||||||
import org.jclouds.json.internal.NamingStrategies.AnnotationFieldNamingStrategy;
|
import org.jclouds.json.internal.NamingStrategies.AnnotationFieldNamingStrategy;
|
||||||
import org.jclouds.json.internal.NamingStrategies.AnnotationOrNameFieldNamingStrategy;
|
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.ExtractNamed;
|
||||||
import org.jclouds.json.internal.NamingStrategies.ExtractSerializedName;
|
import org.jclouds.json.internal.NamingStrategies.ExtractSerializedName;
|
||||||
import org.jclouds.json.internal.NamingStrategies.NameExtractor;
|
import org.jclouds.json.internal.NamingStrategies.NameExtractor;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.common.reflect.Invokable;
|
||||||
import com.google.gson.FieldNamingStrategy;
|
import com.google.gson.FieldNamingStrategy;
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@ public final class NamingStrategiesTest {
|
||||||
private static class SimpleTest {
|
private static class SimpleTest {
|
||||||
@SerializedName("aardvark")
|
@SerializedName("aardvark")
|
||||||
private String a;
|
private String a;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
private String b;
|
private String b;
|
||||||
@Named("cat")
|
@Named("cat")
|
||||||
private String c;
|
private String c;
|
||||||
|
@ -75,7 +76,7 @@ public final class NamingStrategiesTest {
|
||||||
|
|
||||||
|
|
||||||
public void testExtractSerializedName() throws Exception {
|
public void testExtractSerializedName() throws Exception {
|
||||||
NameExtractor extractor = new ExtractSerializedName();
|
NameExtractor<SerializedName> extractor = new ExtractSerializedName();
|
||||||
assertEquals(extractor.extractName(SimpleTest.class.getDeclaredField("a").getAnnotation(SerializedName.class)),
|
assertEquals(extractor.extractName(SimpleTest.class.getDeclaredField("a").getAnnotation(SerializedName.class)),
|
||||||
"aardvark");
|
"aardvark");
|
||||||
try {
|
try {
|
||||||
|
@ -96,7 +97,7 @@ public final class NamingStrategiesTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testExtractNamed() throws Exception {
|
public void testExtractNamed() throws Exception {
|
||||||
NameExtractor extractor = new ExtractNamed();
|
NameExtractor<Named> extractor = new ExtractNamed();
|
||||||
try {
|
try {
|
||||||
extractor.extractName(SimpleTest.class.getDeclaredField("a").getAnnotation(Named.class));
|
extractor.extractName(SimpleTest.class.getDeclaredField("a").getAnnotation(Named.class));
|
||||||
} catch (NullPointerException e) {
|
} catch (NullPointerException e) {
|
||||||
|
@ -131,12 +132,12 @@ public final class NamingStrategiesTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAnnotationConstructorFieldNamingStrategyCPAndNamed() throws Exception {
|
public void testAnnotationConstructorFieldNamingStrategyCPAndNamed() throws Exception {
|
||||||
ConstructorFieldNamingStrategy strategy = new AnnotationConstructorNamingStrategy(
|
AnnotationConstructorNamingStrategy strategy = new AnnotationConstructorNamingStrategy(
|
||||||
ImmutableSet.of(ConstructorProperties.class), ImmutableSet.of(new ExtractNamed()));
|
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);
|
assertNotNull(constructor);
|
||||||
assertEquals(constructor.getParameterTypes().length, 4);
|
assertEquals(constructor.getParameters().size(), 4);
|
||||||
|
|
||||||
assertEquals(strategy.translateName(constructor, 0), "aardvark");
|
assertEquals(strategy.translateName(constructor, 0), "aardvark");
|
||||||
assertEquals(strategy.translateName(constructor, 1), "bat");
|
assertEquals(strategy.translateName(constructor, 1), "bat");
|
||||||
|
@ -144,9 +145,9 @@ public final class NamingStrategiesTest {
|
||||||
// Note: @Named overrides the ConstructorProperties setting
|
// Note: @Named overrides the ConstructorProperties setting
|
||||||
assertEquals(strategy.translateName(constructor, 3), "dingo");
|
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);
|
assertNotNull(mixedCtor);
|
||||||
assertEquals(mixedCtor.getParameterTypes().length, 4);
|
assertEquals(mixedCtor.getParameters().size(), 4);
|
||||||
|
|
||||||
assertEquals(strategy.translateName(mixedCtor, 0), "aardvark");
|
assertEquals(strategy.translateName(mixedCtor, 0), "aardvark");
|
||||||
assertEquals(strategy.translateName(mixedCtor, 1), "bat");
|
assertEquals(strategy.translateName(mixedCtor, 1), "bat");
|
||||||
|
@ -155,21 +156,21 @@ public final class NamingStrategiesTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAnnotationConstructorFieldNamingStrategyCP() throws Exception {
|
public void testAnnotationConstructorFieldNamingStrategyCP() throws Exception {
|
||||||
ConstructorFieldNamingStrategy strategy = new AnnotationConstructorNamingStrategy(
|
AnnotationConstructorNamingStrategy strategy = new AnnotationConstructorNamingStrategy(
|
||||||
ImmutableSet.of(ConstructorProperties.class), ImmutableSet.<NameExtractor>of());
|
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);
|
assertNotNull(constructor);
|
||||||
assertEquals(constructor.getParameterTypes().length, 4);
|
assertEquals(constructor.getParameters().size(), 4);
|
||||||
|
|
||||||
assertEquals(strategy.translateName(constructor, 0), "aardvark");
|
assertEquals(strategy.translateName(constructor, 0), "aardvark");
|
||||||
assertEquals(strategy.translateName(constructor, 1), "bat");
|
assertEquals(strategy.translateName(constructor, 1), "bat");
|
||||||
assertEquals(strategy.translateName(constructor, 2), "coyote");
|
assertEquals(strategy.translateName(constructor, 2), "coyote");
|
||||||
assertEquals(strategy.translateName(constructor, 3), "dog");
|
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);
|
assertNotNull(mixedCtor);
|
||||||
assertEquals(mixedCtor.getParameterTypes().length, 4);
|
assertEquals(mixedCtor.getParameters().size(), 4);
|
||||||
|
|
||||||
assertEquals(strategy.translateName(mixedCtor, 0), "thiscanbeoverriddenbyNamed");
|
assertEquals(strategy.translateName(mixedCtor, 0), "thiscanbeoverriddenbyNamed");
|
||||||
assertNull(strategy.translateName(mixedCtor, 1));
|
assertNull(strategy.translateName(mixedCtor, 1));
|
||||||
|
@ -178,21 +179,21 @@ public final class NamingStrategiesTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAnnotationConstructorFieldNamingStrategyInject() throws Exception {
|
public void testAnnotationConstructorFieldNamingStrategyInject() throws Exception {
|
||||||
ConstructorFieldNamingStrategy strategy = new AnnotationConstructorNamingStrategy(
|
AnnotationConstructorNamingStrategy strategy = new AnnotationConstructorNamingStrategy(
|
||||||
ImmutableSet.of(Inject.class), ImmutableSet.of(new ExtractNamed()));
|
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);
|
assertNotNull(constructor);
|
||||||
assertEquals(constructor.getParameterTypes().length, 5);
|
assertEquals(constructor.getParameters().size(), 5);
|
||||||
|
|
||||||
assertEquals(strategy.translateName(constructor, 0), "aa");
|
assertEquals(strategy.translateName(constructor, 0), "aa");
|
||||||
assertEquals(strategy.translateName(constructor, 1), "bb");
|
assertEquals(strategy.translateName(constructor, 1), "bb");
|
||||||
assertEquals(strategy.translateName(constructor, 2), "cc");
|
assertEquals(strategy.translateName(constructor, 2), "cc");
|
||||||
assertEquals(strategy.translateName(constructor, 3), "dd");
|
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);
|
assertNotNull(mixedCtor);
|
||||||
assertEquals(mixedCtor.getParameterTypes().length, 4);
|
assertEquals(mixedCtor.getParameters().size(), 4);
|
||||||
|
|
||||||
assertEquals(strategy.translateName(mixedCtor, 0), "aardvark");
|
assertEquals(strategy.translateName(mixedCtor, 0), "aardvark");
|
||||||
assertEquals(strategy.translateName(mixedCtor, 1), "bat");
|
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
|
* Simple type with an Optional field
|
||||||
*/
|
*/
|
||||||
static class SimpleBean {
|
static class SimpleBean {
|
||||||
private Optional<String> value;
|
private final Optional<String> value;
|
||||||
private final String someOtherValue;
|
private final String someOtherValue;
|
||||||
|
|
||||||
public SimpleBean(Optional<String> value, 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!
|
// Required to ensure GSON doesn't initialize our Optional to null!
|
||||||
|
@SuppressWarnings("unused")
|
||||||
private SimpleBean() {
|
private SimpleBean() {
|
||||||
this.value = Optional.absent();
|
this.value = Optional.absent();
|
||||||
this.someOtherValue = null;
|
this.someOtherValue = null;
|
||||||
|
@ -64,22 +65,9 @@ public class OptionalTypeAdapterFactoryTest {
|
||||||
return someOtherValue;
|
return someOtherValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean equals(Object other) {
|
||||||
public int hashCode() {
|
SimpleBean that = SimpleBean.class.cast(other);
|
||||||
return Objects.hashCode(value, someOtherValue);
|
return Objects.equal(value, that.value) && Objects.equal(someOtherValue, that.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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,12 +19,15 @@
|
||||||
package org.jclouds.reflect;
|
package org.jclouds.reflect;
|
||||||
|
|
||||||
import static com.google.common.base.Functions.toStringFunction;
|
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.method;
|
||||||
import static org.jclouds.reflect.Reflection2.methods;
|
import static org.jclouds.reflect.Reflection2.methods;
|
||||||
import static org.jclouds.reflect.Reflection2.typeToken;
|
import static org.jclouds.reflect.Reflection2.typeToken;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
|
||||||
import org.testng.annotations.Test;
|
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.FluentIterable;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.reflect.Invokable;
|
import com.google.common.reflect.Invokable;
|
||||||
|
import com.google.common.reflect.Parameter;
|
||||||
import com.google.common.reflect.TypeToken;
|
import com.google.common.reflect.TypeToken;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -41,7 +46,31 @@ import com.google.common.reflect.TypeToken;
|
||||||
@Test
|
@Test
|
||||||
public class Reflection2Test {
|
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));
|
assertEquals(typeToken(String.class), TypeToken.of(String.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,15 +94,28 @@ public class Reflection2Test {
|
||||||
assertEquals(methodInSuper.getParameters().get(0).getType().getRawType(), Object.class);
|
assertEquals(methodInSuper.getParameters().get(0).getType().getRawType(), Object.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMethods() {
|
ImmutableSet<String> setMethods = ImmutableSet.of("add", "equals", "hashCode", "clear", "isEmpty", "contains",
|
||||||
Set<String> methodNames = FluentIterable.from(methods(Set.class))
|
"addAll", "size", "toArray", "iterator", "remove", "removeAll", "containsAll", "retainAll");
|
||||||
.transform(new Function<Invokable<?, ?>, String>() {
|
|
||||||
public String apply(Invokable<?, ?> input) {
|
|
||||||
return input.getName();
|
|
||||||
}
|
|
||||||
}).transform(toStringFunction()).toSet();
|
|
||||||
|
|
||||||
assertEquals(methodNames, ImmutableSet.of("add", "equals", "hashCode", "clear", "isEmpty", "contains", "addAll",
|
public void testMethods() {
|
||||||
"size", "toArray", "iterator", "remove", "removeAll", "containsAll", "retainAll"));
|
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.base.Preconditions.checkNotNull;
|
||||||
import static com.google.common.collect.Iterables.concat;
|
import static com.google.common.collect.Iterables.concat;
|
||||||
import static com.google.common.collect.Iterables.transform;
|
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.net.URI;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
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.base.Function;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.reflect.Invokable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is used to decorate transport objects with high level
|
* This class is used to decorate transport objects with high level
|
||||||
|
@ -103,13 +105,12 @@ public abstract class DomainWrapper<T extends SingleResourceTransportDto> {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Constructor<W> cons = wrapperClass.getDeclaredConstructor(RestContext.class, target.getClass());
|
Invokable<W, W> cons = constructor(wrapperClass, RestContext.class, target.getClass());
|
||||||
if (!cons.isAccessible()) {
|
return cons.invoke(null, context, target);
|
||||||
cons.setAccessible(true);
|
} catch (InvocationTargetException e) {
|
||||||
}
|
throw new WrapperException(wrapperClass, target, e.getTargetException());
|
||||||
return cons.newInstance(context, target);
|
} catch (IllegalAccessException e) {
|
||||||
} catch (Exception ex) {
|
throw new WrapperException(wrapperClass, target, e);
|
||||||
throw new WrapperException(wrapperClass, target, ex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,8 +46,8 @@ public class ListPage<T> extends IterableWithMarker<T> {
|
||||||
private final Iterable<T> items;
|
private final Iterable<T> items;
|
||||||
|
|
||||||
protected ListPage(Kind kind, String id, URI selfLink, String nextPageToken, 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.id = checkNotNull(id, "id");
|
||||||
|
this.kind = checkNotNull(kind, "kind of %id", id);
|
||||||
this.selfLink = checkNotNull(selfLink, "selfLink of %id", id);
|
this.selfLink = checkNotNull(selfLink, "selfLink of %id", id);
|
||||||
this.nextPageToken = nextPageToken;
|
this.nextPageToken = nextPageToken;
|
||||||
this.items = items != null ? ImmutableSet.copyOf(items) : ImmutableSet.<T>of();
|
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) {
|
public boolean equals(Object obj) {
|
||||||
if (this == obj) return true;
|
if (this == obj) return true;
|
||||||
if (obj == null || getClass() != obj.getClass()) return false;
|
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)
|
return equal(this.kind, that.kind)
|
||||||
&& equal(this.id, that.id);
|
&& equal(this.id, that.id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
||||||
*/
|
*/
|
||||||
@Beta
|
@Beta
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ParseImageDetails extends ParseJson<Images<? extends ImageDetails>> {
|
public class ParseImageDetails extends ParseJson<Images> {
|
||||||
static class Images<T extends ImageDetails> extends PaginatedCollection<T> {
|
static class Images extends PaginatedCollection<ImageDetails> {
|
||||||
|
|
||||||
@ConstructorProperties({ "images", "images_links" })
|
@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);
|
super(images, images_links);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,8 +60,7 @@ public class ParseImageDetails extends ParseJson<Images<? extends ImageDetails>>
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ParseImageDetails(Json json) {
|
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> {
|
public static class ToPagedIterable extends CallerArg0ToPagedIterable<ImageDetails, ToPagedIterable> {
|
||||||
|
|
|
@ -48,11 +48,11 @@ import com.google.inject.TypeLiteral;
|
||||||
*/
|
*/
|
||||||
@Beta
|
@Beta
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ParseImages extends ParseJson<Images<? extends Image>> {
|
public class ParseImages extends ParseJson<Images> {
|
||||||
static class Images<T extends Image> extends PaginatedCollection<T> {
|
static class Images extends PaginatedCollection<Image> {
|
||||||
|
|
||||||
@ConstructorProperties({ "images", "images_links" })
|
@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);
|
super(images, images_links);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,8 +60,7 @@ public class ParseImages extends ParseJson<Images<? extends Image>> {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ParseImages(Json json) {
|
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> {
|
public static class ToPagedIterable extends CallerArg0ToPagedIterable<Image, ToPagedIterable> {
|
||||||
|
|
Loading…
Reference in New Issue