Skip to content

Commit

Permalink
Insert a cache in OutlineBuilder and make it immutable in which case …
Browse files Browse the repository at this point in the history
…it returns by default

Add a method to allow child elements to be serialized more easily
  • Loading branch information
rchodava committed Nov 17, 2016
1 parent c7e8e5b commit fad2d60
Show file tree
Hide file tree
Showing 16 changed files with 168 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class DatabaseRowBuilder<T> {
private final Outline<T> outline;

public DatabaseRowBuilder(Class<T> clazz) {
this.outline = new OutlineBuilder().build(clazz);
this.outline = OutlineBuilder.DEFAULT.build(clazz);
}

public Row build(Func2<RowBuilder, Outline<T>, Map<String, ?>> rowBuilder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public interface Json {
static <T> Func1<T, JsonObject> serializer(Outline<T> outline, SerializationStrategy<T> strategy) {
return o -> {
JsonObject json = new JsonObject();
strategy.serialize(json, outline, o);
strategy.serialize(json, outline.wrap(o));
return json;
};
}
Expand Down
32 changes: 28 additions & 4 deletions core/src/main/java/foundation/stack/datamill/json/JsonObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
import foundation.stack.datamill.values.MutableStructuredValue;
import foundation.stack.datamill.values.ReflectableValue;
import foundation.stack.datamill.reflection.impl.TripleArgumentTypeSwitch;
import foundation.stack.datamill.values.SerializationStrategy;
import foundation.stack.datamill.values.Value;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.function.Function;

/**
Expand All @@ -39,7 +38,7 @@ protected Object caseCharacter(JSONObject value1, String value2, JsonProperty va
@Override
protected Object caseShort(JSONObject value1, String value2, JsonProperty value3) {
try {
return value1.has(value2) ? value3.asShort() : null;
return value1.has(value2) ? value3.asShort() : null;
} catch (JSONException __) {
return null;
}
Expand Down Expand Up @@ -265,6 +264,31 @@ public MutableStructuredValue put(String name, Map<String, ?> value) {
return this;
}

@Override
public <T> MutableStructuredValue put(
String name,
Collection<T> values,
SerializationStrategy<T> valueSerializationStrategy) {
if (values != null) {
// Json.serializer()
JSONArray array = new JSONArray();
// for (T value : values) {
// JsonObject transformed = transformer.call(new JsonObject(), value);
// if (transformed != null) {
// array.put(transformed.object);
// } else {
// array.put((Object) null);
// }
// }

object.put(name, array);
} else {
object.put(name, (Object) null);
}

return this;
}

public Set<String> propertyNames() {
return object.keySet();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@ public interface Bean<T> {
<R, A1> R invoke(Method method, A1 argument);
<R, A1, A2> R invoke(Method method, A1 argument1, A2 argument2);
<R> R invoke(Method method, Object... arguments);

Bean<T> set(Consumer<T> propertyInvoker, Value value);
<P> Bean<T> set(Consumer<T> propertyInvoker, P value);

<P> P get(Consumer<T> propertyInvoker);

Member member(Consumer<T> memberInvoker);
Outline<T> outline();
T unwrap();

T get();
}
Original file line number Diff line number Diff line change
@@ -1,38 +1,81 @@
package foundation.stack.datamill.reflection;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import javassist.util.proxy.ProxyFactory;
import foundation.stack.datamill.reflection.impl.OutlineImpl;
import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;

import java.util.concurrent.ExecutionException;

/**
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
public class OutlineBuilder {
private static final Objenesis objenesis = new ObjenesisStd();
public static final OutlineBuilder DEFAULT = new OutlineBuilder();

private static <T> Outline<T> buildOutline(Class<T> classToOutline, boolean camelCased) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setSuperclass(classToOutline);
Class<? extends T> outlineClass = proxyFactory.createClass();
return new OutlineImpl<>(objenesis.newInstance(outlineClass), camelCased);
}

private final LoadingCache<Class<?>, Outline<?>> camelCasedOutlineCache;
private final LoadingCache<Class<?>, Outline<?>> snakeCasedOutlineCache;

private boolean camelCased;

public OutlineBuilder() {
this(false,
CacheBuilder.newBuilder().build(new OutlineCacheLoader(true)),
CacheBuilder.newBuilder().build(new OutlineCacheLoader(false)));
}

private OutlineBuilder(
boolean camelCased,
LoadingCache<Class<?>, Outline<?>> camelCasedOutlineCache,
LoadingCache<Class<?>, Outline<?>> snakeCasedOutlineCache) {
this.camelCased = camelCased;
this.camelCasedOutlineCache = camelCasedOutlineCache;
this.snakeCasedOutlineCache = snakeCasedOutlineCache;
}

public OutlineBuilder defaultCamelCased() {
camelCased = true;
return this;
return new OutlineBuilder(true, camelCasedOutlineCache, snakeCasedOutlineCache);
}

public OutlineBuilder defaultSnakeCased() {
camelCased = false;
return this;
return new OutlineBuilder(false, camelCasedOutlineCache, snakeCasedOutlineCache);
}

public <T> Outline<T> build(Class<T> classToOutline) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setSuperclass(classToOutline);
Class<? extends T> outlineClass = proxyFactory.createClass();
return new OutlineImpl<>(objenesis.newInstance(outlineClass), camelCased);
try {
return camelCased ?
(Outline<T>) camelCasedOutlineCache.get(classToOutline) :
(Outline<T>) snakeCasedOutlineCache.get(classToOutline);
} catch (ExecutionException e) {
throw new ReflectionException(e.getCause());
}
}

public <T> Bean<T> wrap(T instance) {
return build((Class<T>) instance.getClass()).wrap(instance);
}

private static class OutlineCacheLoader extends CacheLoader<Class<?>, Outline<?>> {
private final boolean camelCased;

public OutlineCacheLoader(boolean camelCased) {
this.camelCased = camelCased;
}

@Override
public Outline<?> load(Class<?> key) throws Exception {
return buildOutline(key, camelCased);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,11 @@ public <R> R invoke(foundation.stack.datamill.reflection.Method method, Object..
return method.invoke(instance, arguments);
}

@Override
public Member member(Consumer<T> memberInvoker) {
return outline().member(memberInvoker);
}

@Override
public Outline<T> outline() {
return (Outline<T>) OutlineImpl.this;
Expand All @@ -421,7 +426,7 @@ public <P> Bean<T> set(Consumer<T> propertyInvoker, P value) {
}

@Override
public T unwrap() {
public T get() {
return instance;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package foundation.stack.datamill.values;

import foundation.stack.datamill.reflection.Bean;

/**
* A {@link DeserializationStrategy} defines a function which is used in deserializing an object. It should take values
* from the given {@link StructuredValue} and insert them into the object so that the {@link StructuredValue} can
* be deserialized.
* <p>
* For example, deserializing User objects may look like this:
* <pre>
* public static final DeserializationStrategy&lt;User&gt; PUBLIC =
* (user, source) -> user
* .set(m -> m.getId(), source.get(user.member(m -> m.getId())))
* .set(m -> m.getName(), source.get(user.member(m -> m.getName())));
* </pre>
* <p>
* User objects can then be serialized to JSON by calling:
* <pre>
* STANDARD.serialize(new JsonObject(), userOutline, user);
* </pre>
*
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
@FunctionalInterface
public interface DeserializationStrategy<T> {
T deserialize(Bean<T> target, StructuredValue source);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import foundation.stack.datamill.reflection.Member;

import java.util.Collection;
import java.util.Map;

/**
Expand All @@ -12,11 +13,21 @@ public interface MutableStructuredValue extends StructuredValue {
MutableStructuredValue put(String name, Object[] value);
MutableStructuredValue put(String name, Map<String, ?> value);

<T> MutableStructuredValue put(
String name,
Collection<T> values,
SerializationStrategy<T> valueSerializationStrategy);

default <T> MutableStructuredValue put(Member member, T value) {
return put(member.name(), value);
}

default MutableStructuredValue put(Member member, Object[] value) {
return put(member.name(), value);
}
default <T> MutableStructuredValue put(
Member member,
Collection<T> values,
SerializationStrategy<T> valueSerializationStrategy) {
return put(member.name(), values, valueSerializationStrategy);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package foundation.stack.datamill.values;

import foundation.stack.datamill.reflection.Outline;
import foundation.stack.datamill.reflection.Bean;

/**
* A {@link SerializationStrategy} defines a function which is used in serializing an object. It should insert values
Expand All @@ -10,19 +10,19 @@
* For example, serializing User objects may look like this:
* <pre>
* public static final SerializationStrategy&lt;User&gt; PUBLIC =
* (target, outline, user) -> target
* .put(outline.member(m -> m.getId()), user.getId())
* .put(outline.member(m -> m.getName()), user.getName());
* (target, user) -> target
* .put(user.member(m -> m.getId()), user.get().getId())
* .put(user.member(m -> m.getName()), user.get().getName());
* </pre>
*
* <p>
* User objects can then be serialized to JSON by calling:
* <pre>
* STANDARD.serialize(new JsonObject(), userOutline, user);
* STANDARD.serialize(new JsonObject(), userOutline.wrap(user));
* </pre>
*
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
@FunctionalInterface
public interface SerializationStrategy<T> {
MutableStructuredValue serialize(MutableStructuredValue target, Outline<T> sourceOutline, T source);
MutableStructuredValue serialize(MutableStructuredValue target, Bean<T> source);
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public Quark setSpin(int spin) {
@Test
public void queries() {
DatabaseClient client = new DatabaseClient("jdbc:hsqldb:mem:test");
Outline<Quark> outline = new OutlineBuilder().build(Quark.class);
Outline<Quark> outline = OutlineBuilder.DEFAULT.build(Quark.class);

client.update("create table quarks(name varchar(64), spin integer)", 0)
.count()
Expand All @@ -57,7 +57,7 @@ public void queries() {
List<Quark> quarks = client.selectAll().from(outline).all().getAs(r -> outline.wrap(new Quark())
.set(p -> p.getName(), r.get(outline.member(m -> m.getName())))
.set(p -> p.getSpin(), r.get(outline.member(m -> m.getSpin())))
.unwrap())
.get())
.toBlocking().last();

assertEquals(1, quarks.size());
Expand All @@ -67,7 +67,7 @@ public void queries() {
Quark quark = client.selectAll().from(outline).all().firstAs(r -> outline.wrap(new Quark())
.set(p -> p.getName(), r.get(outline.member(m -> m.getName())))
.set(p -> p.getSpin(), r.get(outline.member(m -> m.getSpin())))
.unwrap())
.get())
.toBlocking().last();

assertEquals(1, quarks.size());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public UpdateQueryExecution update(String query, Object... parameters) {

@Test
public void selectQueries() {
Outline<QueryTestBean> outline = new OutlineBuilder().build(QueryTestBean.class);
Outline<QueryTestBean> outline = OutlineBuilder.DEFAULT.build(QueryTestBean.class);
TestQueryBuilderImpl queryBuilder = new TestQueryBuilderImpl();

queryBuilder.selectAll().from("table_name").all();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public int getPropertyName() {

@Test
public void memberNameBasedValueRetrieval() throws Exception {
Outline<TestBean> outline = new OutlineBuilder().build(TestBean.class);
Outline<TestBean> outline = OutlineBuilder.DEFAULT.build(TestBean.class);

ResultSet resultSet = mock(ResultSet.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public void databaseMethodsInvoked() {

@Test
public void rowBuilding() {
Outline<TestModel> testModelOutline = new OutlineBuilder().build(TestModel.class);
Outline<TestModel> testModelOutline = OutlineBuilder.DEFAULT.build(TestModel.class);

TestDatabaseClient client = new TestDatabaseClient(database);

Expand All @@ -93,7 +93,7 @@ public void rowBuilding() {
.firstAs(row -> testModelOutline.wrap(new TestModel())
.set(m -> m.getProperty(), row.get(testModelOutline.member(m -> m.getProperty())))
.set(m -> m.getStringProperty(), row.get(testModelOutline.member(m -> m.getStringProperty())))
.unwrap())
.get())
.subscribe(testSubscriber);

assertEquals(5, testSubscriber.getOnNextEvents().get(0).getProperty());
Expand Down

0 comments on commit fad2d60

Please sign in to comment.