Repository: incubator-juneau
Updated Branches:
  refs/heads/master a7104d8ae -> e5f46938c


Add PojoMerge class.

Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/fffaec49
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/fffaec49
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/fffaec49

Branch: refs/heads/master
Commit: fffaec491a02040dd0e7609edcb6bf01304ec7b0
Parents: a7104d8
Author: JamesBognar <[email protected]>
Authored: Sun Sep 3 10:36:16 2017 -0400
Committer: JamesBognar <[email protected]>
Committed: Sun Sep 3 10:36:16 2017 -0400

----------------------------------------------------------------------
 .../org/apache/juneau/utils/PojoMergeTest.java  |  86 +++++++++++
 .../java/org/apache/juneau/utils/PojoMerge.java | 142 +++++++++++++++++++
 .../doc-files/ReleaseNotes_632_DarkStyle.png    | Bin 0 -> 221326 bytes
 3 files changed, 228 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/fffaec49/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/PojoMergeTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/PojoMergeTest.java
 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/PojoMergeTest.java
new file mode 100644
index 0000000..7698e5a
--- /dev/null
+++ 
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/utils/PojoMergeTest.java
@@ -0,0 +1,86 @@
+// 
***************************************************************************************************************************
+// * 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.juneau.utils;
+
+import static org.junit.Assert.*;
+
+import org.junit.*;
+
+/**
+ * Test the PojoMerge class.
+ */
+public class PojoMergeTest {
+
+       
//====================================================================================================
+       // Basic tests
+       
//====================================================================================================
+       @Test
+       public void basicTests() throws Exception {
+               IA a1, a2, am;
+               
+               a1 = new A("1"); a2 = new A("2");
+               am = PojoMerge.merge(IA.class, a1, a2);
+               assertEquals("1", am.getA());
+               am.setA("x");
+               assertEquals("x", am.getA());
+               assertEquals("x", a1.getA());
+               assertEquals("2", a2.getA());
+
+               a1 = new A("1"); a2 = new A("2");
+               am = PojoMerge.merge(IA.class, true, a1, a2);
+               assertEquals("1", am.getA());
+               am.setA("x");
+               assertEquals("x", am.getA());
+               assertEquals("x", a1.getA());
+               assertEquals("x", a2.getA());
+               
+               a1 = new A(null); a2 = new A("2");
+               am = PojoMerge.merge(IA.class, a1, a2);
+               assertEquals("2", am.getA());
+               am.setA("x");
+               assertEquals("x", am.getA());
+               assertEquals("x", a1.getA());
+               assertEquals("2", a2.getA());
+               
+               a1 = new A(null); a2 = new A(null);
+               am = PojoMerge.merge(IA.class, a1, a2);
+               assertEquals(null, am.getA());
+               
+               a1 = new A(null); a2 = new A("2");
+               am = PojoMerge.merge(IA.class, null, a1, null, null, a2, null);
+               assertEquals("2", am.getA());
+       }
+
+       public static interface IA {
+               String getA();
+               void setA(String a);
+       }
+
+       public static class A implements IA {
+               private String a;
+
+               public A(String a) {
+                       this.a = a;
+               }
+
+               @Override
+               public String getA() {
+                       return a;
+               }
+
+               @Override
+               public void setA(String a) {
+                       this.a = a;
+               }
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/fffaec49/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/PojoMerge.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/PojoMerge.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/PojoMerge.java
new file mode 100644
index 0000000..7f90048
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/PojoMerge.java
@@ -0,0 +1,142 @@
+// 
***************************************************************************************************************************
+// * 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.juneau.utils;
+
+import java.lang.reflect.*;
+
+/**
+ * Utility class for merging POJOs behind a single interface.
+ *
+ * <p>
+ * Useful in cases where you want to define beans with 'default' values.
+ *
+ * <p>
+ * For example, given the following bean classes...
+ *
+ * <p class='bcode'>
+ *     <jk>public interface</jk> IA {
+ *             String getX();
+ *             <jk>void</jk> setX(String x);
+ *     }
+ *
+ *     <jk>public class</jk> A <jk>implements</jk> IA {
+ *             <jk>private</jk> String <jf>x</jf>;
+ *
+ *             <jk>public</jk> A(String x) {
+ *                     <jk>this</jk>.<jf>x</jf> = x;
+ *             }
+ *
+ *             <jk>public</jk> String getX() {
+ *                     <jk>return</jk> <jf>x</jf>;
+ *             }
+ *
+ *             <jk>public void</jk> setX(String x) {
+ *                     <jk>this</jk>.<jf>x</jf> = x;
+ *             }
+ *     }
+ * </p>
+ *
+ * <p>
+ * The getters will be called in order until the first non-null value is 
returned...
+ *
+ * <p class='bcode'>
+ *     PojoMerge m;
+ *
+ *     m = PojoMerge.<jsm>merge</jsm>(IA.<jk>class</jk>, <jk>new</jk> 
A(<js>"1"</js>), <jk>new</jk> A(<js>"2"</js>));
+ *     <jsm>assertEquals</jsm>(<js>"1"</js>, m.getX());
+ *
+ *     m = PojoMerge.<jsm>merge</jsm>(IA.<jk>class</jk>, <jk>new</jk> 
A(<jk>null</jk>), <jk>new</jk> A(<js>"2"</js>));
+ *     <jsm>assertEquals</jsm>(<js>"2"</js>, m.getX());
+ *
+ *     m = PojoMerge.<jsm>merge</jsm>(IA.<jk>class</jk>, <jk>new</jk> 
A(<jk>null</jk>), <jk>new</jk> A(<jk>null</jk>));
+ *     <jsm>assertEquals</jsm>(<jk>null</jk>, m.getX());
+ * </p>
+ *
+ * <h5 class='section'>Notes:</h5>
+ * <ul>
+ *     <li>Null POJOs are ignored.
+ *     <li>Non-getter methods are either invoked on the first POJO or all 
POJOs depending on the <code>callAllNonGetters</code> flag
+ *             passed into the constructor.
+ *     <li>For purposes of this interface, a getter is any method with zero 
arguments and a non-<code>void</code> return type.
+ * </ul>
+ */
+public class PojoMerge {
+
+       /**
+        * Create a proxy interface on top of zero or more POJOs.
+        *
+        * <p>
+        * This is a shortcut to calling <code>merge(interfaceClass, 
<jk>false</jk>, pojos);</code>
+        *
+        * @param interfaceClass The common interface class.
+        * @param pojos
+        *      Zero or more POJOs to merge.
+        *      <br>Can contain nulls.
+        * @return A proxy interface over the merged POJOs.
+        */
+       public static <T> T merge(Class<T> interfaceClass, T...pojos) {
+               return merge(interfaceClass, false, pojos);
+       }
+
+       /**
+        * Create a proxy interface on top of zero or more POJOs.
+        *
+        * @param interfaceClass The common interface class.
+        * @param callAllNonGetters
+        *      If <jk>true</jk>, when calling a method that's not a getter, 
the method will be invoked on all POJOs.
+        *      <br>Otherwise, the method will only be called on the first POJO.
+        * @param pojos
+        *      Zero or more POJOs to merge.
+        *      <br>Can contain nulls.
+        * @return A proxy interface over the merged POJOs.
+        */
+       @SuppressWarnings("unchecked")
+       public static <T> T merge(Class<T> interfaceClass, boolean 
callAllNonGetters, T...pojos) {
+               return 
(T)Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[] { 
interfaceClass }, new PojoMergeInvocationHandler(callAllNonGetters, pojos));
+       }
+
+       private static class PojoMergeInvocationHandler implements 
InvocationHandler {
+               private final Object[] pojos;
+               private final boolean callAllNonGetters;
+
+               public PojoMergeInvocationHandler(boolean callAllNonGetters, 
Object...pojos) {
+                       this.callAllNonGetters = callAllNonGetters;
+                       this.pojos = pojos;
+               }
+
+               /**
+                * Implemented to handle the method called.
+                * @throws InvocationTargetException
+                * @throws IllegalAccessException
+                * @throws IllegalArgumentException
+                */
+               @Override /* InvocationHandler */
+               public Object invoke(Object proxy, Method method, Object[] 
args) throws IllegalArgumentException, IllegalAccessException, 
InvocationTargetException {
+                       Object r = null;
+                       boolean isGetter = args == null && 
method.getReturnType() != Void.class;
+                       for (Object pojo : pojos) {
+                               if (pojo != null) {
+                                       r = method.invoke(pojo, args);
+                                       if (isGetter) {
+                                               if (r != null)
+                                                       return r;
+                                       } else {
+                                               if (! callAllNonGetters)
+                                                       return r;
+                                       }
+                               }
+                       }
+                       return r;
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/fffaec49/juneau-core/juneau-marshall/src/main/javadoc/doc-files/ReleaseNotes_632_DarkStyle.png
----------------------------------------------------------------------
diff --git 
a/juneau-core/juneau-marshall/src/main/javadoc/doc-files/ReleaseNotes_632_DarkStyle.png
 
b/juneau-core/juneau-marshall/src/main/javadoc/doc-files/ReleaseNotes_632_DarkStyle.png
new file mode 100644
index 0000000..73732dc
Binary files /dev/null and 
b/juneau-core/juneau-marshall/src/main/javadoc/doc-files/ReleaseNotes_632_DarkStyle.png
 differ

Reply via email to