You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2019/08/26 12:50:56 UTC
[cayenne] 02/05: CAY-2527 API to map Object[] result to POJO -
simple Object[] to Pojo mapper
This is an automated email from the ASF dual-hosted git repository.
ntimofeev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit a8c2c79a01a72fcb56dafbbbd7f4844df4185480
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Mon Aug 5 17:34:21 2019 +0300
CAY-2527 API to map Object[] result to POJO
- simple Object[] to Pojo mapper
---
.../org/apache/cayenne/reflect/PojoMapper.java | 93 ++++++++++++++++++++++
.../org/apache/cayenne/reflect/PojoMapperTest.java | 88 ++++++++++++++++++++
2 files changed, 181 insertions(+)
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/reflect/PojoMapper.java b/cayenne-server/src/main/java/org/apache/cayenne/reflect/PojoMapper.java
new file mode 100644
index 0000000..c0abf6e
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/reflect/PojoMapper.java
@@ -0,0 +1,93 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.apache.cayenne.reflect;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Field;
+import java.util.function.Function;
+
+import org.apache.cayenne.CayenneRuntimeException;
+
+/**
+ * Simple mapper of Object[] to POJO class. This class relies on field order, so use with caution.
+ * @param <T> type of object to produce
+ * @since 4.2
+ */
+public class PojoMapper<T> implements Function<Object[], T> {
+
+ private static MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+ private final Class<T> type;
+ private final MethodHandle constructor;
+ private final MethodHandle[] setters;
+
+ public PojoMapper(Class<T> type) {
+ this.type = type;
+ try {
+ this.constructor = lookup.unreflectConstructor(type.getConstructor());
+ } catch (NoSuchMethodException | IllegalAccessException ex) {
+ throw new CayenneRuntimeException("No default constructor found for class '%s'.", type.getName());
+ }
+
+ Field[] declaredFields = type.getDeclaredFields();
+ this.setters = new MethodHandle[declaredFields.length];
+ int i = 0;
+ for(Field field : declaredFields) {
+ field.setAccessible(true);
+ try {
+ setters[i++] = lookup.unreflectSetter(field);
+ } catch (IllegalAccessException e) {
+ throw new CayenneRuntimeException("Field '%s'.'%s' is inaccessible.", e, type.getName(), field.getName());
+ }
+ }
+ }
+
+ private T newObject() {
+ try {
+ @SuppressWarnings("unchecked")
+ T object = (T)constructor.invoke();
+ return object;
+ } catch (Throwable ex) {
+ throw new CayenneRuntimeException("Unable to instantiate %s.", ex, type.getName());
+ }
+ }
+
+ public T apply(Object[] data) {
+ if(data.length > setters.length) {
+ throw new CayenneRuntimeException("Unable to create '%s'. Values length (%d) > fields count (%d)"
+ , type.getName(), data.length, setters.length);
+ }
+
+ T object = newObject();
+
+ for (int i = 0; i < data.length; i++) {
+ if (data[i] != null) {
+ try {
+ setters[i].invoke(object, data[i]);
+ } catch (Throwable ex) {
+ throw new CayenneRuntimeException("Unable to set field of %s.", ex, type.getName());
+ }
+ }
+ }
+
+ return object;
+ }
+}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/reflect/PojoMapperTest.java b/cayenne-server/src/test/java/org/apache/cayenne/reflect/PojoMapperTest.java
new file mode 100644
index 0000000..a593a42
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/reflect/PojoMapperTest.java
@@ -0,0 +1,88 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF 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.apache.cayenne.reflect;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @since 4.2
+ */
+public class PojoMapperTest {
+
+ @Test
+ public void testObjectCreation() {
+ PojoMapper<C1> descriptor = new PojoMapper<>(C1.class);
+
+ Object o = new Object();
+ Object[] data = {"123", o, 42};
+ C1 object = descriptor.apply(data);
+ assertEquals("123", object.a);
+ assertSame(o, object.b);
+ assertEquals(42, object.c);
+ }
+
+ @Test(expected = CayenneRuntimeException.class)
+ public void testNonPublicClass() {
+ new PojoMapper<>(C2.class);
+ }
+
+ @Test(expected = CayenneRuntimeException.class)
+ public void testNonPublicConstructor() {
+ new PojoMapper<>(C3.class);
+ }
+
+ @Test(expected = CayenneRuntimeException.class)
+ public void testNonDefaultConstructor() {
+ new PojoMapper<>(C4.class);
+ }
+
+ @Test(expected = CayenneRuntimeException.class)
+ public void testWrongArgumentCount() {
+ PojoMapper<C1> descriptor = new PojoMapper<>(C1.class);
+
+ Object[] data = {"123", new Object(), 42, 32};
+ descriptor.apply(data);
+ }
+
+ public static class C1 {
+ String a;
+ Object b;
+ int c;
+ }
+
+ private static class C2 {
+ int a;
+ }
+
+ public static class C3 {
+ int a;
+ private C3() {
+ }
+ }
+
+ public static class C4 {
+ int a;
+ public C4(int a) {
+ }
+ }
+}
\ No newline at end of file