diff --git a/src/main/java/casser/config/CasserSettings.java b/src/main/java/casser/config/CasserSettings.java index 57e594f..2436da2 100644 --- a/src/main/java/casser/config/CasserSettings.java +++ b/src/main/java/casser/config/CasserSettings.java @@ -19,6 +19,7 @@ import java.lang.reflect.Method; import java.util.function.Function; import casser.core.Instantiator; +import casser.core.WrapperInstantiator; public interface CasserSettings { @@ -33,5 +34,7 @@ public interface CasserSettings { Instantiator getDslInstantiator(); Instantiator getPojoInstantiator(); + + WrapperInstantiator getWrapperInstantiator(); } diff --git a/src/main/java/casser/config/DefaultCasserSettings.java b/src/main/java/casser/config/DefaultCasserSettings.java index 36630b6..1a8688e 100644 --- a/src/main/java/casser/config/DefaultCasserSettings.java +++ b/src/main/java/casser/config/DefaultCasserSettings.java @@ -19,8 +19,10 @@ import java.lang.reflect.Method; import java.util.function.Function; import casser.core.Instantiator; +import casser.core.WrapperInstantiator; import casser.core.reflect.ReflectionDslInstantiator; import casser.core.reflect.ReflectionPojoInstantiator; +import casser.core.reflect.ReflectionWrapperInstantiator; import casser.mapping.convert.CamelCaseToUnderscoreConverter; import casser.mapping.convert.MethodNameToPropertyConverter; @@ -56,6 +58,9 @@ public class DefaultCasserSettings implements CasserSettings { return ReflectionPojoInstantiator.INSTANCE; } - - + @Override + public WrapperInstantiator getWrapperInstantiator() { + return ReflectionWrapperInstantiator.INSTANCE; + } + } diff --git a/src/main/java/casser/core/Casser.java b/src/main/java/casser/core/Casser.java index b3a6524..b6171f7 100644 --- a/src/main/java/casser/core/Casser.java +++ b/src/main/java/casser/core/Casser.java @@ -15,15 +15,12 @@ */ package casser.core; -import java.lang.reflect.Proxy; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import casser.config.CasserSettings; import casser.config.DefaultCasserSettings; -import casser.core.reflect.DslInvocationHandler; -import casser.core.reflect.PojoInvocationHandler; -import casser.mapping.convert.UDTValueWritable; import com.datastax.driver.core.Cluster; import com.datastax.driver.core.Session; @@ -97,5 +94,13 @@ public final class Casser { public static E pojo(Class iface, ClassLoader classLoader) { return settings.getPojoInstantiator().instantiate(iface, classLoader); } + + public static E wrap(Map map, Class iface) { + return wrap(map, iface, iface.getClassLoader()); + } + + public static E wrap(Map map, Class iface, ClassLoader classLoader) { + return settings.getWrapperInstantiator().instantiate(map, iface, classLoader); + } } diff --git a/src/main/java/casser/core/WrapperInstantiator.java b/src/main/java/casser/core/WrapperInstantiator.java new file mode 100644 index 0000000..e2a2378 --- /dev/null +++ b/src/main/java/casser/core/WrapperInstantiator.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 Noorq, Inc. + * + * Licensed 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 casser.core; + +import java.util.Map; + +public interface WrapperInstantiator { + + E instantiate(Map map, Class iface, ClassLoader classLoader); + +} diff --git a/src/main/java/casser/core/reflect/ReflectionWrapperInstantiator.java b/src/main/java/casser/core/reflect/ReflectionWrapperInstantiator.java new file mode 100644 index 0000000..c0f83b6 --- /dev/null +++ b/src/main/java/casser/core/reflect/ReflectionWrapperInstantiator.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015 Noorq, Inc. + * + * Licensed 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 casser.core.reflect; + +import java.lang.reflect.Proxy; +import java.util.Map; + +import casser.core.WrapperInstantiator; +import casser.mapping.MapExportable; + +public enum ReflectionWrapperInstantiator implements WrapperInstantiator { + + INSTANCE; + + @Override + @SuppressWarnings("unchecked") + public E instantiate(Map map, Class iface, + ClassLoader classLoader) { + + WrapperInvocationHandler handler = new WrapperInvocationHandler(map, iface); + E proxy = (E) Proxy.newProxyInstance( + classLoader, + new Class[] { iface, MapExportable.class }, + handler); + return proxy; + + } + + + +} diff --git a/src/main/java/casser/core/reflect/WrapperInvocationHandler.java b/src/main/java/casser/core/reflect/WrapperInvocationHandler.java new file mode 100644 index 0000000..b18b0d9 --- /dev/null +++ b/src/main/java/casser/core/reflect/WrapperInvocationHandler.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2015 Noorq, Inc. + * + * Licensed 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 casser.core.reflect; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Map; + +import casser.mapping.MapExportable; +import casser.support.CasserException; + +public class WrapperInvocationHandler implements InvocationHandler { + + private final Map map; + private final Class iface; + + public WrapperInvocationHandler(Map map, Class iface) { + this.map = map; + this.iface = iface; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + + if (method.getParameterCount() != 0 || method.getReturnType() == void.class) { + throw new CasserException("invalid getter method " + method); + } + + String methodName = method.getName(); + + if ("toString".equals(methodName)) { + return "Wrapper:" + iface + ":" + map.toString(); + } + + if (MapExportable.TO_MAP_METHOD.equals(methodName)) { + return Collections.unmodifiableMap(map); + } + + Object value = map.get(methodName); + + if (value == null) { + + Class returnType = method.getReturnType(); + + if (returnType.isPrimitive()) { + + DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType); + if (type == null) { + throw new CasserException("unknown primitive type " + returnType); + } + + return type.getDefaultValue(); + + } + + } + + return value; + } + +} diff --git a/src/main/java/casser/mapping/MapExportable.java b/src/main/java/casser/mapping/MapExportable.java new file mode 100644 index 0000000..6ab0287 --- /dev/null +++ b/src/main/java/casser/mapping/MapExportable.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2015 Noorq, Inc. + * + * Licensed 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 casser.mapping; + +import java.util.Map; + +public interface MapExportable { + + public static final String TO_MAP_METHOD = "toMap"; + + Map toMap(); + +} diff --git a/src/test/java/casser/test/unit/core/dsl/Account.java b/src/test/java/casser/test/unit/core/dsl/Account.java index 44e7e06..2577e2f 100644 --- a/src/test/java/casser/test/unit/core/dsl/Account.java +++ b/src/test/java/casser/test/unit/core/dsl/Account.java @@ -15,17 +15,14 @@ */ package casser.test.unit.core.dsl; +import casser.mapping.MapExportable; import casser.mapping.Table; @Table -public interface Account { +public interface Account extends MapExportable { - String getId(); + Long id(); - void setId(String acc); - - boolean isActive(); - - void setActive(boolean a); + boolean active(); } diff --git a/src/test/java/casser/test/unit/core/dsl/DslTest.java b/src/test/java/casser/test/unit/core/dsl/DslTest.java index 71af7b8..d492408 100644 --- a/src/test/java/casser/test/unit/core/dsl/DslTest.java +++ b/src/test/java/casser/test/unit/core/dsl/DslTest.java @@ -29,12 +29,14 @@ public class DslTest { System.out.println("account = " + account); + /* try { - account.getId(); + account.id(); } catch(DslPropertyException e) { System.out.println(e.getProperty()); } + */ } diff --git a/src/test/java/casser/test/unit/core/dsl/PojoTest.java b/src/test/java/casser/test/unit/core/dsl/WrapperTest.java similarity index 57% rename from src/test/java/casser/test/unit/core/dsl/PojoTest.java rename to src/test/java/casser/test/unit/core/dsl/WrapperTest.java index 9842e21..743812d 100644 --- a/src/test/java/casser/test/unit/core/dsl/PojoTest.java +++ b/src/test/java/casser/test/unit/core/dsl/WrapperTest.java @@ -15,44 +15,52 @@ */ package casser.test.unit.core.dsl; +import java.util.HashMap; +import java.util.Map; + import org.junit.Assert; import org.junit.Test; import casser.core.Casser; import casser.support.CasserException; -public class PojoTest { - - Account account = Casser.pojo(Account.class); +public class WrapperTest { @Test - public void testObject() throws Exception { + public void testWrap() throws Exception { - Assert.assertNull(account.getId()); + Map map = new HashMap(); + + map.put("id", 123L); + map.put("active", Boolean.TRUE); + map.put("unknownField", "he-he"); - account.setId("testAcc"); - - Assert.assertEquals("testAcc", account.getId()); - + Account account = Casser.wrap(map, Account.class); + + Assert.assertEquals(Long.valueOf(123L), account.id()); + Assert.assertTrue(account.active()); } @Test public void testPrimitive() throws Exception { - Assert.assertFalse(account.isActive()); + Map map = new HashMap(); - account.setActive(true); - - Assert.assertEquals(true, account.isActive()); - + map.put("id", 123L); + Account account = Casser.wrap(map, Account.class); + + Assert.assertFalse(account.active()); + } @Test(expected=CasserException.class) public void testWrongMethods() throws Exception { - Casser.pojo(WrongAccount.class); + WrongAccount wrongAccount = Casser.wrap(new HashMap(), WrongAccount.class); + + wrongAccount.id(); } diff --git a/src/test/java/casser/test/unit/core/dsl/WrongAccount.java b/src/test/java/casser/test/unit/core/dsl/WrongAccount.java index 11908c5..c2eed1d 100644 --- a/src/test/java/casser/test/unit/core/dsl/WrongAccount.java +++ b/src/test/java/casser/test/unit/core/dsl/WrongAccount.java @@ -17,6 +17,6 @@ package casser.test.unit.core.dsl; public interface WrongAccount { - void getId(); + void id(); }