Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend ResourceVisitor to hold parameters type information #11

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ public abstract class CollectorResourceVisitor implements ResourceVisitor {
public static class Param {
public final String k;
public final Object v;
public Param(String k, Object v) { this.k = k; this.v = v; }
public final Type t;
public Param(String k, Object v) { this(k, v, Type.undefined()); }
public Param(String k, Object v, Type t) { this.k = k; this.v = v; this.t = t;}

public static List<Param> expand(List<Param> in) {
List<Param> out = new ArrayList<>();
for (Param p : in) {
Expand All @@ -28,7 +31,7 @@ public static List<Param> expand(List<Param> in) {
}
return out;
}
@Override public String toString() { return "Param{k='" + k + "', v=" + v + '}'; }
@Override public String toString() { return "Param{k='" + k + "', v=" + v + ", t='" + t + '}'; }
}

protected List<String> paths = new ArrayList<>();
Expand All @@ -39,6 +42,7 @@ public static List<Param> expand(List<Param> in) {
protected String produces[] = { "application/json" };
protected String consumes[] = { "application/json" };
protected Object data = null;
protected Type dataType;
private List<Integer> expectedStatuses = DEFAULT_EXPECTED_STATUS;

@Override public ResourceVisitor method(String method) {
Expand Down Expand Up @@ -69,26 +73,27 @@ public ResourceVisitor path(String path) {
return this;
}

@Override public ResourceVisitor param(String key, @Nullable Object value) {
@Override public ResourceVisitor param(String key, @Nullable Object value, Type type) {
Objects.requireNonNull(key, "query param key required");
if (value != null) queryParams.add(new Param(key, value));
if (value != null) queryParams.add(new Param(key, value, type));
return this;
}

@Override public ResourceVisitor header(String key, @Nullable Object value) {
@Override public ResourceVisitor header(String key, @Nullable Object value, Type type) {
Objects.requireNonNull(key, "header param key required");
if (value != null) headerParams.add(new Param(key, value));
if (value != null) headerParams.add(new Param(key, value, type));
return this;
}

@Override public ResourceVisitor form(String key, @Nullable Object value) {
@Override public ResourceVisitor form(String key, @Nullable Object value, Type type) {
Objects.requireNonNull(key, "form param key required");
if (value != null) formParams.add(new Param(key, value));
if (value != null) formParams.add(new Param(key, value, type));
return this;
}

@Override public ResourceVisitor data(Object data) {
@Override public ResourceVisitor data(Object data, Type type) {
this.data = data;
this.dataType = type;
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@ public interface ResourceVisitor {
/** Sets the consumed media-type. */
ResourceVisitor consumes(String... consumes);

/** Sets a query param. */
ResourceVisitor param(String key, @Nullable Object value);
/** Sets a query param with its type */
ResourceVisitor param(String key, @Nullable Object value, Type type);

/** Sets a header param. */
ResourceVisitor header(String key, @Nullable Object value);
/** Sets a header param with its type. */
ResourceVisitor header(String key, @Nullable Object value, Type type);

/** Sets a from param. */
ResourceVisitor form(String key, @Nullable Object value);
/** Sets a from param with its type. */
ResourceVisitor form(String key, @Nullable Object value, Type type);

/** Sets the content data with its type. */
ResourceVisitor data(Object data, Type typeInfo);

/** Sets the content data. */
ResourceVisitor data(Object data);

/** Wrap the current resource state into a {@code container}. */
<T> T as(Class<? super T> container, Class<?> type);
/** Wrap the current resource state into a {@code type}. */
<T> T as(Type type);

interface Supplier {
ResourceVisitor get();
Expand Down
118 changes: 118 additions & 0 deletions core/src/main/java/com/intendia/gwt/autorest/client/Type.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.intendia.gwt.autorest.client;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* Recursive structure to model Java type meta-data
* associated with corresponding parameter or method
* return result.
*/
public class Type {
private final boolean array;
private final List<Type> typeParameters;
private final Class<?> clazz;

private Type(Class<?> clazz) {
this(clazz, Collections.<Type>emptyList(), false);
}

private Type(Class<?> clazz, List<Type> typeParameters, boolean isArray) {
this.clazz = clazz;
this.typeParameters = typeParameters;
this.array = isArray;
}

public boolean isArray() {
return array;
}

public boolean isGeneric() {
return !typeParameters.isEmpty();
}

public boolean isDefined() {
return clazz == null;
}

public static Type of(Class<?> clazz) {
return new Type(clazz);
}

public static Type array(Type type) {
return new Type(type.clazz, type.typeParameters, true);
}

public static Type undefined() {
return new Type(null);
}

public Type typeParam(Type type) {
List<Type> typeParams = new ArrayList<>(this.typeParameters);
typeParams.add(type);
return new Type(this.clazz, typeParams, type.array);
}

public Class<?> getClazz() {
return clazz;
}

public List<Type> getTypeParams() {
return Collections.unmodifiableList(typeParameters);
}

@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append(clazz.getSimpleName());

if (!typeParameters.isEmpty()) {
result.append("<");
for(Type t: typeParameters) {
result.append(t.toString());
result.append(", ");
}
result.delete(result.length()-2, result.length());
result.append(">");
}

return result.toString();
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (array ? 1231 : 1237);
result = prime * result + ((clazz == null) ? 0 : clazz.hashCode());
result = prime * result + ((typeParameters == null) ? 0 : typeParameters.hashCode());
return result;
}

@Override
public boolean equals(Object other) {
if (other == this)
return true;

if(!(other instanceof Type))
return false;

Type otype = (Type)other;
if (array != otype.array)
return false;

if (clazz == null) {
if (otype.clazz != null)
return false;
} else {
if (!clazz.equals(otype.clazz))
return false;
}

if (!typeParameters.equals(otype.typeParameters))
return false;

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;

import java.awt.List;
ibaca marked this conversation as resolved.
Show resolved Hide resolved

import org.junit.Test;

public class ResourceVisitorTest {
Expand All @@ -16,20 +18,20 @@ public class ResourceVisitorTest {
assertThat(rb0.uri(), equalTo("http://base"));

final ResourceVisitor.Supplier ch1 = new ResourceVisitor.Supplier() {
public ResourceVisitor get() { return ch0.get().path("path1").param("p1", "v1"); }
public ResourceVisitor get() { return ch0.get().path("path1").param("p1", "v1", Type.of(String.class)); }
};
MyCollectorResourceVisitor rb1 = (MyCollectorResourceVisitor) ch1.get();
assertThat(rb1.uri(), equalTo("http://base/path1?p1=v1"));

ResourceVisitor.Supplier ch2 = new ResourceVisitor.Supplier() {
public ResourceVisitor get() { return ch1.get().path("path2").param("p2", asList("v2a", "v2b")); }
public ResourceVisitor get() { return ch1.get().path("path2").param("p2", asList("v2a", "v2b"), Type.of(List.class).typeParam(Type.of(String.class))); }
};
MyCollectorResourceVisitor rb2 = (MyCollectorResourceVisitor) ch2.get();
assertThat(rb2.uri(), equalTo("http://base/path1/path2?p1=v1&p2=v2a&p2=v2b"));
}

private static class MyCollectorResourceVisitor extends CollectorResourceVisitor {
@Override protected String encodeComponent(String str) { return str; }
@Override public <T> T as(Class<? super T> container, Class<?> type) { return null; }
@Override public <T> T as(Type type) { return null; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.google.web.bindery.event.shared.HandlerRegistration;
import com.intendia.gwt.autorest.client.RequestResourceBuilder;
import com.intendia.gwt.autorest.client.ResourceVisitor;
import com.intendia.gwt.autorest.client.Type;
import com.intendia.gwt.autorest.example.client.ExampleService.Greeting;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
Expand All @@ -27,7 +28,7 @@ public void onModuleLoad() {
HTML out = append(new HTML());

ResourceVisitor.Supplier getApi = () -> new RequestResourceBuilder().path(GWT.getModuleBaseURL(), "api");
ExampleService srv = new ExampleService_RestServiceModel(() -> getApi.get().header("auth", "ok"));
ExampleService srv = new ExampleService_RestServiceModel(() -> getApi.get().header("auth", "ok", Type.undefined()));

Observable.merge(valueChange(name), keyUp(name)).map(e -> name.getValue())
.switchMap(q -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import static jsinterop.annotations.JsPackage.GLOBAL;

import java.util.List;
import java.util.Map;

import com.intendia.gwt.autorest.client.AutoRestGwt;
import io.reactivex.Completable;
import io.reactivex.Maybe;
Expand Down Expand Up @@ -33,7 +36,23 @@ public interface ExampleService {
@PathParam("foo") String foo,
@QueryParam("bar") String bar,
@QueryParam("unk") String oth);


@GET @Path("observable/foo/{foo}") Greeting getFoo(
@PathParam("foo") String foo,
@QueryParam("bar") String bar,
@QueryParam("unk") Integer anInt,
@QueryParam("iii") int i,
@QueryParam("greeting") Greeting g);

@GET @Path("observable/foo/{foo}") Greeting getFoo(
@PathParam("foo") String foo,
@QueryParam("unk") List<String> oth,
@QueryParam("greeting") List<Map<String, Greeting>> g);

@POST @Path("observable/foo/{foo}") Greeting getFoo4(
@PathParam("foo") String foo,
List<Greeting> oth[]);

@JsType(namespace = GLOBAL, name = "Object", isNative = true) class Greeting {
public String greeting;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,21 @@ public RequestResourceBuilder requestTransformer(
}

@SuppressWarnings("unchecked")
@Override public <T> T as(Class<? super T> container, Class<?> type) {
if (Completable.class.equals(container)) return (T) request().toCompletable();
if (Maybe.class.equals(container)) return (T) request().flatMapMaybe(ctx -> {
@Override public <T> T as(Type type) {
if (Completable.class.equals(type.getClazz())) return (T) request().toCompletable();
if (Maybe.class.equals(type.getClazz())) return (T) request().flatMapMaybe(ctx -> {
@Nullable Object decode = decode(ctx);
return decode == null ? Maybe.empty() : Maybe.just(decode);
});
if (Single.class.equals(container)) return (T) request().map(ctx -> {
if (Single.class.equals(type.getClazz())) return (T) request().map(ctx -> {
@Nullable Object decode = decode(ctx);
return requireNonNull(decode, "null response forbidden, use Maybe instead");
});
if (Observable.class.equals(container)) return (T) request().toObservable().flatMapIterable(ctx -> {
if (Observable.class.equals(type.getClazz())) return (T) request().toObservable().flatMapIterable(ctx -> {
@Nullable Object[] decode = decode(ctx);
return decode == null ? Collections.emptyList() : Arrays.asList(decode);
});
throw new UnsupportedOperationException("unsupported type " + container);
throw new UnsupportedOperationException("unsupported type " + type.getClazz());
}

private @Nullable <T> T decode(XMLHttpRequest ctx) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,11 @@ public JreResourceBuilder(String root, ConnectionFactory factory, JsonCodec code
} catch (Exception e) { throw new RuntimeException(e); }
}

@Override public <T> T as(Class<? super T> container, Class<?> type) {
return json.fromJson(request(), container, type);
@Override public <T> T as(Type type) {
return json.fromJson(
request(),
type.getClazz(),
!type.getTypeParams().isEmpty()? type.getTypeParams().get(0).getClazz(): Void.class);
}

private Single<Reader> request() {
Expand Down Expand Up @@ -100,7 +103,7 @@ public interface ConnectionFactory {

public interface JsonCodec {
void toJson(Object src, Appendable writer);
<C> C fromJson(Single<Reader> json, Class<? super C> container, Class<?> type);
<C> C fromJson(Single<Reader> json, Class<?> container, Class<?> type);
}

public static class GsonCodec implements JsonCodec {
Expand All @@ -111,7 +114,7 @@ public static class GsonCodec implements JsonCodec {
}

@SuppressWarnings("unchecked")
@Override public <T> T fromJson(Single<Reader> req, Class<? super T> container, Class<?> type) {
@Override public <T> T fromJson(Single<Reader> req, Class<?> container, Class<?> type) {
if (Completable.class.equals(container)) return (T) req.doOnSuccess(this::consume).toCompletable();
if (Single.class.equals(container)) return (T) req.map(reader -> {
if (Reader.class.equals(type)) return reader;
Expand Down
Loading