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
