This is an automated email from the ASF dual-hosted git repository. ahuber pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push: new 297899cae9 ISIS-3084: Wrapper: refactors proxied methods for Map/Collection/List into ProgrammingModelConstants 297899cae9 is described below commit 297899cae9a25c3dc32e61836d5dd05a264d9460 Author: Andi Huber <ahu...@apache.org> AuthorDate: Thu Jun 30 10:29:02 2022 +0200 ISIS-3084: Wrapper: refactors proxied methods for Map/Collection/List into ProgrammingModelConstants --- .../isis/commons/internal/collections/_Lists.java | 27 +++++++++- .../progmodel/ProgrammingModelConstants.java | 58 ++++++++++++++++++++++ .../handlers/CollectionInvocationHandler.java | 40 +++++++-------- .../wrapper/handlers/MapInvocationHandler.java | 39 +++++++-------- ...ava => NonScalarInvocationHandlerAbstract.java} | 42 +++++++++++++--- 5 files changed, 155 insertions(+), 51 deletions(-) diff --git a/commons/src/main/java/org/apache/isis/commons/internal/collections/_Lists.java b/commons/src/main/java/org/apache/isis/commons/internal/collections/_Lists.java index 1dec99f051..2d5ed27a6d 100644 --- a/commons/src/main/java/org/apache/isis/commons/internal/collections/_Lists.java +++ b/commons/src/main/java/org/apache/isis/commons/internal/collections/_Lists.java @@ -37,6 +37,7 @@ import org.springframework.lang.Nullable; import org.apache.isis.commons.internal.base._NullSafe; import lombok.NonNull; +import lombok.val; /** * <h1>- internal use only -</h1> @@ -70,6 +71,30 @@ public final class _Lists { return Optional.ofNullable(list.get(list.size()-1)); } + // -- LIST CONCATENATION + + /** + * Returns an unmodifiable list containing all elements from given list + * and the specified element. + */ + public static <T> List<T> append(final List<T> list, final T element) { + val resultList = new ArrayList<T>(list.size() + 1); + resultList.addAll(list); + resultList.add(element); + return Collections.unmodifiableList(resultList); + } + + /** + * Returns an unmodifiable list containing all elements from given lists + * list1 and list2. + */ + public static <T> List<T> concat(final List<T> list1, final List<T> list2) { + val resultList = new ArrayList<T>(list1.size() + list2.size()); + resultList.addAll(list1); + resultList.addAll(list2); + return Collections.unmodifiableList(resultList); + } + // -- UNMODIFIABLE LIST /** @@ -206,6 +231,4 @@ public final class _Lists { return toUnmodifiable(ArrayList::new); } - - } diff --git a/core/config/src/main/java/org/apache/isis/core/config/progmodel/ProgrammingModelConstants.java b/core/config/src/main/java/org/apache/isis/core/config/progmodel/ProgrammingModelConstants.java index d93d294b24..ea17f20629 100644 --- a/core/config/src/main/java/org/apache/isis/core/config/progmodel/ProgrammingModelConstants.java +++ b/core/config/src/main/java/org/apache/isis/core/config/progmodel/ProgrammingModelConstants.java @@ -28,6 +28,7 @@ import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.util.Collection; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; @@ -52,6 +53,7 @@ import org.apache.isis.commons.functional.Try; import org.apache.isis.commons.internal.base._Casts; import org.apache.isis.commons.internal.base._Refs; import org.apache.isis.commons.internal.base._Strings; +import org.apache.isis.commons.internal.collections._Lists; import org.apache.isis.commons.internal.exceptions._Exceptions; import org.apache.isis.commons.internal.reflection._Annotations; import org.apache.isis.commons.internal.reflection._Reflect; @@ -62,6 +64,7 @@ import static org.apache.isis.commons.internal.reflection._Reflect.Filter.paramC import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import lombok.val; public final class ProgrammingModelConstants { @@ -505,6 +508,61 @@ public final class ProgrammingModelConstants { } + //TODO needs an update to reflect Java 7->11 Language changes + @RequiredArgsConstructor + public static enum WrapperFactoryProxy { + COLLECTION( + List.of( + getMethod(Collection.class, "contains", Object.class), + getMethod(Collection.class, "size"), + getMethod(Collection.class, "isEmpty") + ), + List.of( + getMethod(Collection.class, "add", Object.class), + getMethod(Collection.class, "remove", Object.class), + getMethod(Collection.class, "addAll", Collection.class), + getMethod(Collection.class, "removeAll", Collection.class), + getMethod(Collection.class, "retainAll", Collection.class), + getMethod(Collection.class, "clear") + )), + LIST( + _Lists.concat( + WrapperFactoryProxy.COLLECTION.intercepted, + List.of( + getMethod(List.class, "get", int.class) + ) + ), + _Lists.concat( + WrapperFactoryProxy.COLLECTION.vetoed, + List.of( + ) + )), + MAP( + List.of( + getMethod(Map.class, "containsKey", Object.class), + getMethod(Map.class, "containsValue", Object.class), + getMethod(Map.class, "size"), + getMethod(Map.class, "isEmpty") + ), + List.of( + getMethod(Map.class, "put", Object.class, Object.class), + getMethod(Map.class, "remove", Object.class), + getMethod(Map.class, "putAll", Map.class), + getMethod(Map.class, "clear") + )) + ; + @Getter private final List<Method> intercepted; + @Getter private final List<Method> vetoed; + // -- HELPER + @SneakyThrows + private static Method getMethod( + final Class<?> cls, + final String methodName, + final Class<?>... parameterClass) { + return cls.getMethod(methodName, parameterClass); + } + } + // -- HELPER private static String getCapitalizedMemberName(final Member member) { diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/CollectionInvocationHandler.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/CollectionInvocationHandler.java index 14fd232dda..6ce0ac9692 100644 --- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/CollectionInvocationHandler.java +++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/CollectionInvocationHandler.java @@ -21,36 +21,34 @@ package org.apache.isis.core.runtimeservices.wrapper.handlers; import java.util.Collection; import java.util.List; -import org.apache.isis.core.metamodel.commons.ObjectExtensions; +import org.apache.isis.commons.internal.assertions._Assert; +import org.apache.isis.core.config.progmodel.ProgrammingModelConstants; import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; -class CollectionInvocationHandler<T, R> extends AbstractCollectionInvocationHandler<T, R> { +import lombok.val; + +class CollectionInvocationHandler<T, C extends Collection<?>> +extends NonScalarInvocationHandlerAbstract<T, C> { public CollectionInvocationHandler( - final R collectionToProxy, + final C collectionToProxy, final DomainObjectInvocationHandler<T> handler, final OneToManyAssociation otma) { super(collectionToProxy, handler, otma); - try { - intercept(ObjectExtensions.getMethod(collectionToProxy, "contains", Object.class)); - intercept(ObjectExtensions.getMethod(collectionToProxy, "size")); - intercept(ObjectExtensions.getMethod(collectionToProxy, "isEmpty")); - if (collectionToProxy instanceof List) { - intercept(ObjectExtensions.getMethod(collectionToProxy, "get", int.class)); - } - veto(ObjectExtensions.getMethod(collectionToProxy, "add", Object.class)); - veto(ObjectExtensions.getMethod(collectionToProxy, "remove", Object.class)); - veto(ObjectExtensions.getMethod(collectionToProxy, "addAll", Collection.class)); - veto(ObjectExtensions.getMethod(collectionToProxy, "removeAll", Collection.class)); - veto(ObjectExtensions.getMethod(collectionToProxy, "retainAll", Collection.class)); - veto(ObjectExtensions.getMethod(collectionToProxy, "clear")); - } catch (final NoSuchMethodException e) { - // ///CLOVER:OFF - throw new RuntimeException("A Collection method could not be found: " + e.getMessage()); - // ///CLOVER:ON - } + _Assert.assertTrue(collectionToProxy.getClass().isAssignableFrom(Collection.class), + ()->String.format("Cannot use %s for type %s, these are not compatible.", + this.getClass().getName(), + collectionToProxy.getClass())); + + val methodSets = (collectionToProxy instanceof List) + ? ProgrammingModelConstants.WrapperFactoryProxy.LIST + : ProgrammingModelConstants.WrapperFactoryProxy.COLLECTION; + + methodSets.getIntercepted().forEach(this::intercept); + methodSets.getVetoed().forEach(this::veto); + } } diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/MapInvocationHandler.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/MapInvocationHandler.java index 2ee8c8b2c8..ff22c492ef 100644 --- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/MapInvocationHandler.java +++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/MapInvocationHandler.java @@ -20,33 +20,32 @@ package org.apache.isis.core.runtimeservices.wrapper.handlers; import java.util.Map; -import org.apache.isis.core.metamodel.commons.ObjectExtensions; +import org.apache.isis.commons.internal.assertions._Assert; +import org.apache.isis.core.config.progmodel.ProgrammingModelConstants; import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; -class MapInvocationHandler<T, C> -extends AbstractCollectionInvocationHandler<T, C> { +class MapInvocationHandler<T, M extends Map<?,?>> +extends NonScalarInvocationHandlerAbstract<T, M> { public MapInvocationHandler( - final C collectionToProxy, + final M mapToProxy, final DomainObjectInvocationHandler<T> handler, final OneToManyAssociation otma) { - super(collectionToProxy, handler, otma); - - try { - intercept(ObjectExtensions.getMethod(collectionToProxy, "containsKey", Object.class)); - intercept(ObjectExtensions.getMethod(collectionToProxy, "containsValue", Object.class)); - intercept(ObjectExtensions.getMethod(collectionToProxy, "size")); - intercept(ObjectExtensions.getMethod(collectionToProxy, "isEmpty")); - veto(ObjectExtensions.getMethod(collectionToProxy, "put", Object.class, Object.class)); - veto(ObjectExtensions.getMethod(collectionToProxy, "remove", Object.class)); - veto(ObjectExtensions.getMethod(collectionToProxy, "putAll", Map.class)); - veto(ObjectExtensions.getMethod(collectionToProxy, "clear")); - } catch (final NoSuchMethodException e) { - // ///CLOVER:OFF - throw new RuntimeException("A Collection method could not be found: " + e.getMessage()); - // ///CLOVER:ON - } + super(mapToProxy, handler, otma); + + _Assert.assertTrue(mapToProxy.getClass().isAssignableFrom(Map.class), + ()->String.format("Cannot use %s for type %s, these are not compatible.", + this.getClass().getName(), + mapToProxy.getClass())); + + ProgrammingModelConstants.WrapperFactoryProxy.MAP + .getIntercepted() + .forEach(this::intercept); + + ProgrammingModelConstants.WrapperFactoryProxy.MAP + .getVetoed() + .forEach(this::veto); } } diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/AbstractCollectionInvocationHandler.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/NonScalarInvocationHandlerAbstract.java similarity index 66% rename from core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/AbstractCollectionInvocationHandler.java rename to core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/NonScalarInvocationHandlerAbstract.java index 7048438e52..55ec020e56 100644 --- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/AbstractCollectionInvocationHandler.java +++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/wrapper/handlers/NonScalarInvocationHandlerAbstract.java @@ -20,14 +20,22 @@ package org.apache.isis.core.runtimeservices.wrapper.handlers; import java.lang.reflect.Method; import java.util.List; +import java.util.Map; import org.apache.isis.applib.services.wrapper.events.CollectionMethodEvent; -import org.apache.isis.applib.services.wrapper.events.InteractionEvent; import org.apache.isis.commons.internal.collections._Lists; import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation; -abstract class AbstractCollectionInvocationHandler<T, C> -extends DelegatingInvocationHandlerDefault<C> { +import lombok.val; + +/** + * Base class in support of non-scalar types to be proxied up. + * + * @param <T> Domain Object type + * @param <P> non-scalar type (eg. {@link Collection} or {@link Map}) to be proxied + */ +abstract class NonScalarInvocationHandlerAbstract<T, P> +extends DelegatingInvocationHandlerDefault<P> { private final List<Method> interceptedMethods = _Lists.newArrayList(); private final List<Method> vetoedMethods = _Lists.newArrayList(); @@ -35,8 +43,8 @@ extends DelegatingInvocationHandlerDefault<C> { private final OneToManyAssociation oneToManyAssociation; private final T domainObject; - public AbstractCollectionInvocationHandler( - final C collectionOrMapToProxy, + protected NonScalarInvocationHandlerAbstract( + final P collectionOrMapToProxy, final DomainObjectInvocationHandler<T> handler, final OneToManyAssociation otma) { @@ -48,11 +56,21 @@ extends DelegatingInvocationHandlerDefault<C> { this.domainObject = handler.getDelegate(); } + /** + * Adds given method to the list of intercepted methods, + * those which will trigger {@link CollectionMethodEvent}(s) + * on invocation. + */ protected Method intercept(final Method method) { this.interceptedMethods.add(method); return method; } + /** + * Adds given method to the list of vetoed methods, + * those which will cause an {@link UnsupportedOperationException} + * on invocation. + */ protected Method veto(final Method method) { this.vetoedMethods.add(method); return method; @@ -76,13 +94,21 @@ extends DelegatingInvocationHandlerDefault<C> { resolveIfRequired(domainObject); - final InteractionEvent ev = new CollectionMethodEvent(getDelegate(), getCollection().getFeatureIdentifier(), getDomainObject(), method.getName(), args, returnValueObj); - notifyListeners(ev); + val event = + new CollectionMethodEvent( + getDelegate(), + getCollection().getFeatureIdentifier(), + getDomainObject(), + method.getName(), + args, + returnValueObj); + notifyListeners(event); return returnValueObj; } if (vetoedMethods.contains(method)) { - throw new UnsupportedOperationException(String.format("Method '%s' may not be called directly.", method.getName())); + throw new UnsupportedOperationException( + String.format("Method '%s' may not be called directly.", method.getName())); } return returnValueObj;