You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2019/10/08 14:52:52 UTC
[isis] branch v2 updated: ISIS-1998: proper mixin main method
detection
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch v2
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/v2 by this push:
new 61cfbf3 ISIS-1998: proper mixin main method detection
61cfbf3 is described below
commit 61cfbf31ab8bfe467475f2ea896464d2d5a1dff6
Author: Andi Huber <ah...@apache.org>
AuthorDate: Tue Oct 8 16:52:43 2019 +0200
ISIS-1998: proper mixin main method detection
- doing this now in a single place: FacetedMethodsBuilder
- solves issue with any public mixin method being picked up as
contributing action
---
.../presets/DebugProgrammingModel.properties | 1 +
.../apache/isis/metamodel/facets/FacetFactory.java | 151 +++++++++++----------
.../specloader/facetprocessor/FacetProcessor.java | 22 ++-
.../specloader/specimpl/FacetedMethodsBuilder.java | 112 ++++++++++-----
4 files changed, 169 insertions(+), 117 deletions(-)
diff --git a/core/config/src/main/resources/presets/DebugProgrammingModel.properties b/core/config/src/main/resources/presets/DebugProgrammingModel.properties
index 07313c8..ce27d89 100644
--- a/core/config/src/main/resources/presets/DebugProgrammingModel.properties
+++ b/core/config/src/main/resources/presets/DebugProgrammingModel.properties
@@ -16,3 +16,4 @@
# under the License.
logging.level.org.apache.isis.metamodel.specloader.ProgrammingModelServiceDefault = DEBUG
+logging.level.org.apache.isis.metamodel.specloader.specimpl.FacetedMethodsBuilder = DEBUG
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/FacetFactory.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/FacetFactory.java
index cb6dc45..968bd9c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/FacetFactory.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/facets/FacetFactory.java
@@ -27,19 +27,15 @@ import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;
-import org.apache.isis.commons.internal.base._Lazy;
import org.apache.isis.commons.internal.exceptions._Exceptions;
import org.apache.isis.commons.internal.reflection._Annotations;
-import org.apache.isis.metamodel.MetaModelContext;
import org.apache.isis.metamodel.facetapi.Facet;
import org.apache.isis.metamodel.facetapi.FacetHolder;
import org.apache.isis.metamodel.facetapi.FeatureType;
import org.apache.isis.metamodel.facetapi.MethodRemover;
import org.apache.isis.metamodel.methodutils.MethodScope;
-import org.apache.isis.metamodel.specloader.specimpl.IntrospectionState;
-import org.apache.isis.metamodel.specloader.specimpl.ObjectActionMixedIn;
-import lombok.val;
+import lombok.Getter;
public interface FacetFactory {
@@ -55,7 +51,8 @@ public interface FacetFactory {
}
}
- static class AbstractProcessWithClsContext<T extends FacetHolder> extends AbstractProcessContext<T>{
+ static class AbstractProcessWithClsContext<T extends FacetHolder>
+ extends AbstractProcessContext<T>{
private final Class<?> cls;
@@ -81,12 +78,18 @@ public interface FacetFactory {
}
- static class AbstractProcessWithMethodContext<T extends FacetHolder> extends AbstractProcessWithClsContext<T> implements MethodRemover{
+ static class AbstractProcessWithMethodContext<T extends FacetHolder>
+ extends AbstractProcessWithClsContext<T> implements MethodRemover{
private final Method method;
protected final MethodRemover methodRemover;
- AbstractProcessWithMethodContext(final Class<?> cls, final Method method, final MethodRemover methodRemover, final T facetHolder) {
+ AbstractProcessWithMethodContext(
+ final Class<?> cls,
+ final Method method,
+ final MethodRemover methodRemover,
+ final T facetHolder) {
+
super(cls, facetHolder);
this.method = method;
this.methodRemover = methodRemover;
@@ -123,64 +126,6 @@ public interface FacetFactory {
public void removeMethod(final Method method) {
methodRemover.removeMethod(method);
}
-
- /**
- * Annotation lookup on this context's method..
- * @since 2.0
- */
- public <A extends Annotation> Optional<A> synthesizeOnMethod(Class<A> annotationType) {
- return _Annotations.synthesizeInherited(method, annotationType);
- }
-
- /**
- * Annotation lookup on this context's method, if not found, extends search to type in case
- * the predicate {@link #isMixinMain} evaluates {@code true}.
- * @since 2.0
- */
- public <A extends Annotation> Optional<A> synthesizeOnMethodOrMixinType(Class<A> annotationType) {
- return computeIfAbsent(synthesizeOnMethod(annotationType),
- ()-> isMixinMain()
- ? synthesizeOnType(annotationType)
- : Optional.empty() ) ;
- }
-
- /**
- * Whether we are currently processing a mixin type AND this context's method can be identified
- * as the main method of the processed mixin class.
- * @since 2.0
- */
- public boolean isMixinMain() {
- return isMixinMain.get();
- }
-
- private final _Lazy<Boolean> isMixinMain = _Lazy.threadSafe(this::computeIsMixinMain);
-
- private boolean computeIsMixinMain() {
- val specLoader = MetaModelContext.current().getSpecificationLoader();
- val spec_ofTypeCurrentlyProcessing = specLoader
- .loadSpecification(getCls(), IntrospectionState.TYPE_INTROSPECTED);
- if(!spec_ofTypeCurrentlyProcessing.isMixin()) {
- return false;
- }
- val mixinMember = spec_ofTypeCurrentlyProcessing
- .getMixedInMember(spec_ofTypeCurrentlyProcessing);
- if(!mixinMember.isPresent()) {
- return false;
- }
- val actionMethod_ofMixinMember = ((ObjectActionMixedIn)mixinMember.get())
- .getFacetedMethod().getMethod();
-
- return getMethod().equals(actionMethod_ofMixinMember);
- }
-
- private static <T> Optional<T> computeIfAbsent(
- Optional<T> optional,
- Supplier<Optional<T>> supplier) {
-
- return optional.isPresent() ?
- optional
- : supplier.get();
- }
}
@@ -205,7 +150,10 @@ public interface FacetFactory {
// process class
// //////////////////////////////////////
- public static class ProcessClassContext extends AbstractProcessWithClsContext<FacetHolder> implements MethodRemover, ProcessContextWithMetadataProperties<FacetHolder> {
+ public static class ProcessClassContext
+ extends AbstractProcessWithClsContext<FacetHolder>
+ implements MethodRemover, ProcessContextWithMetadataProperties<FacetHolder> {
+
private final MethodRemover methodRemover;
/**
@@ -244,23 +192,78 @@ public interface FacetFactory {
// //////////////////////////////////////
- public static class ProcessMethodContext extends AbstractProcessWithMethodContext<FacetedMethod> implements ProcessContextWithMetadataProperties<FacetedMethod> {
- private final FeatureType featureType;
+ public static class ProcessMethodContext
+ extends AbstractProcessWithMethodContext<FacetedMethod>
+ implements ProcessContextWithMetadataProperties<FacetedMethod> {
+
+ @Getter private final FeatureType featureType;
+ @Getter private final boolean mixinMain;
+ /**
+ *
+ * @param cls
+ * @param featureType
+ * @param method
+ * @param methodRemover
+ * @param facetedMethod
+ * @param isMixinMain
+ * - Whether we are currently processing a mixin type AND this context's method can be identified
+ * as the main method of the processed mixin class. (since 2.0)
+ */
public ProcessMethodContext(
final Class<?> cls,
final FeatureType featureType,
final Method method,
final MethodRemover methodRemover,
- final FacetedMethod facetedMethod) {
+ final FacetedMethod facetedMethod,
+ final boolean isMixinMain) {
+
super(cls, method, methodRemover, facetedMethod);
this.featureType = featureType;
+ this.mixinMain = false;
+ }
+
+ /** JUnit support, historically not using 'isMixinMain' */
+ public ProcessMethodContext(
+ final Class<?> cls,
+ final FeatureType featureType,
+ final Method method,
+ final MethodRemover methodRemover,
+ final FacetedMethod facetedMethod) {
+ this(cls, featureType, method, methodRemover, facetedMethod, false);
}
- public FeatureType getFeatureType() {
- return featureType;
+
+ /**
+ * Annotation lookup on this context's method..
+ * @since 2.0
+ */
+ public <A extends Annotation> Optional<A> synthesizeOnMethod(Class<A> annotationType) {
+ return _Annotations.synthesizeInherited(getMethod(), annotationType);
}
+ /**
+ * Annotation lookup on this context's method, if not found, extends search to type in case
+ * the predicate {@link #isMixinMain} evaluates {@code true}.
+ * @since 2.0
+ */
+ public <A extends Annotation> Optional<A> synthesizeOnMethodOrMixinType(Class<A> annotationType) {
+ return computeIfAbsent(synthesizeOnMethod(annotationType),
+ ()-> isMixinMain()
+ ? synthesizeOnType(annotationType)
+ : Optional.empty() ) ;
+ }
+
+ private static <T> Optional<T> computeIfAbsent(
+ Optional<T> optional,
+ Supplier<Optional<T>> supplier) {
+
+ return optional.isPresent() ?
+ optional
+ : supplier.get();
+ }
+
+
}
/**
@@ -274,7 +277,9 @@ public interface FacetFactory {
// process param
// //////////////////////////////////////
- public static class ProcessParameterContext extends AbstractProcessWithMethodContext<FacetedMethodParameter> {
+ public static class ProcessParameterContext
+ extends AbstractProcessWithMethodContext<FacetedMethodParameter> {
+
private final int paramNum;
private final Class<?> paramType;
private final Parameter parameter;
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/facetprocessor/FacetProcessor.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/facetprocessor/FacetProcessor.java
index 4207784..4185e20 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/facetprocessor/FacetProcessor.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/facetprocessor/FacetProcessor.java
@@ -25,6 +25,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Consumer;
import org.apache.isis.applib.services.inject.ServiceInjector;
import org.apache.isis.commons.internal.base._Lazy;
@@ -166,9 +167,9 @@ public class FacetProcessor {
* Delegates to all known
* {@link PropertyOrCollectionIdentifyingFacetFactory}s.
*/
- public Set<Method> findAssociationCandidateAccessors(
+ public void findAssociationCandidateAccessors(
Collection<Method> methods,
- Set<Method> candidates) {
+ Consumer<Method> onCandidate) {
val factories = propertyOrCollectionIdentifyingFactories.get();
@@ -178,11 +179,11 @@ public class FacetProcessor {
}
for (val facetFactory : factories) {
if (facetFactory.isPropertyOrCollectionAccessorCandidate(method)) {
- candidates.add(method);
+ onCandidate.accept(method);
}
}
}
- return candidates;
+
}
/**
@@ -330,17 +331,24 @@ public class FacetProcessor {
* @param featureType
* - what type of feature the method represents (property,
* action, collection etc)
+ * @param isMixinMain
+ * - Whether we are currently processing a mixin type AND this context's method
+ * can be identified as the main method of the processed mixin class. (since 2.0)
*/
public void process(
Class<?> cls,
Method method,
MethodRemover methodRemover,
FacetedMethod facetedMethod,
- FeatureType featureType) {
-
+ FeatureType featureType,
+ boolean isMixinMain) {
val processMethodContext =
- new ProcessMethodContext(cls, featureType, method, removerElseNoopRemover(methodRemover), facetedMethod);
+ new ProcessMethodContext(
+ cls,
+ featureType,
+ method,
+ removerElseNoopRemover(methodRemover), facetedMethod, isMixinMain);
factoryListByFeatureType.get().getOrElseEmpty(featureType)
.forEach(facetFactory->facetFactory.process(processMethodContext));
diff --git a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/specimpl/FacetedMethodsBuilder.java b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/specimpl/FacetedMethodsBuilder.java
index 195f09e..4fa4b8a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/specimpl/FacetedMethodsBuilder.java
+++ b/core/metamodel/src/main/java/org/apache/isis/metamodel/specloader/specimpl/FacetedMethodsBuilder.java
@@ -150,15 +150,15 @@ public class FacetedMethodsBuilder {
// ////////////////////////////////////////////////////////////////////////////
public FacetedMethodsBuilder(
- final ObjectSpecificationAbstract spec,
+ final ObjectSpecificationAbstract inspectedTypeSpec,
final FacetedMethodsBuilderContext facetedMethodsBuilderContext) {
if (log.isDebugEnabled()) {
- log.debug("creating JavaIntrospector for {}", spec.getFullIdentifier());
+ log.debug("creating JavaIntrospector for {}", inspectedTypeSpec.getFullIdentifier());
}
- this.inspectedTypeSpec = spec;
- this.introspectedClass = spec.getCorrespondingClass();
+ this.inspectedTypeSpec = inspectedTypeSpec;
+ this.introspectedClass = inspectedTypeSpec.getCorrespondingClass();
val methodsRemaining = introspectedClass.getMethods();
this.methodRemover = new FacetedMethodsMethodRemover(introspectedClass, methodsRemaining);
@@ -240,11 +240,11 @@ public class FacetedMethodsBuilder {
log.debug("introspecting {}: properties and collections", getClassName());
}
- final Set<Method> associationCandidateMethods = new HashSet<Method>();
+ val associationCandidateMethods = new HashSet<Method>();
methodRemover.acceptRemaining(methodsRemaining->{
getFacetProcessor()
- .findAssociationCandidateAccessors(methodsRemaining, associationCandidateMethods);
+ .findAssociationCandidateAccessors(methodsRemaining, associationCandidateMethods::add);
});
// Ensure all return types are known
@@ -259,45 +259,52 @@ public class FacetedMethodsBuilder {
typesToLoad.forEach(typeToLoad->specLoader.loadSpecification(typeToLoad, upTo));
// now create FacetedMethods for collections and for properties
- final List<FacetedMethod> associationFacetedMethods = _Lists.newArrayList();
+ val associationFacetedMethods = _Lists.<FacetedMethod>newArrayList();
- findAndRemoveCollectionAccessorsAndCreateCorrespondingFacetedMethods(associationFacetedMethods);
- findAndRemovePropertyAccessorsAndCreateCorrespondingFacetedMethods(associationFacetedMethods);
+ findAndRemoveCollectionAccessorsAndCreateCorrespondingFacetedMethods(associationFacetedMethods::add);
+ findAndRemovePropertyAccessorsAndCreateCorrespondingFacetedMethods(associationFacetedMethods::add);
return Collections.unmodifiableList(associationFacetedMethods);
}
- private void findAndRemoveCollectionAccessorsAndCreateCorrespondingFacetedMethods(final List<FacetedMethod> associationPeers) {
- final List<Method> collectionAccessors = _Lists.newArrayList();
+ private void findAndRemoveCollectionAccessorsAndCreateCorrespondingFacetedMethods(Consumer<FacetedMethod> onNewAssociationPeer) {
+ val collectionAccessors = _Lists.<Method>newArrayList();
getFacetProcessor().findAndRemoveCollectionAccessors(methodRemover, collectionAccessors);
- createCollectionFacetedMethodsFromAccessors(collectionAccessors, associationPeers);
+ createCollectionFacetedMethodsFromAccessors(collectionAccessors, onNewAssociationPeer);
}
/**
* Since the value properties and collections have already been processed,
* this will pick up the remaining reference properties.
*/
- private void findAndRemovePropertyAccessorsAndCreateCorrespondingFacetedMethods(final List<FacetedMethod> fields) {
- final List<Method> propertyAccessors = _Lists.newArrayList();
+ private void findAndRemovePropertyAccessorsAndCreateCorrespondingFacetedMethods(Consumer<FacetedMethod> onNewField) {
+ val propertyAccessors = _Lists.<Method>newArrayList();
getFacetProcessor().findAndRemovePropertyAccessors(methodRemover, propertyAccessors);
- findAndRemovePrefixedNonVoidMethods(MethodScope.OBJECT, GET_PREFIX, Object.class, 0, propertyAccessors);
- findAndRemovePrefixedNonVoidMethods(MethodScope.OBJECT, IS_PREFIX, Boolean.class, 0, propertyAccessors);
+ findAndRemovePrefixedNonVoidMethods(MethodScope.OBJECT, GET_PREFIX, Object.class, 0, propertyAccessors::add);
+ findAndRemovePrefixedNonVoidMethods(MethodScope.OBJECT, IS_PREFIX, Boolean.class, 0, propertyAccessors::add);
- createPropertyFacetedMethodsFromAccessors(propertyAccessors, fields);
+ createPropertyFacetedMethodsFromAccessors(propertyAccessors, onNewField);
}
private void createCollectionFacetedMethodsFromAccessors(
final List<Method> accessorMethods,
- final List<FacetedMethod> facetMethodsToAppendto) {
+ final Consumer<FacetedMethod> onNewFacetMethod) {
+
for (final Method accessorMethod : accessorMethods) {
if (log.isDebugEnabled()) {
log.debug(" identified accessor method representing collection: {}", accessorMethod);
}
// create property and add facets
- final FacetedMethod facetedMethod = FacetedMethod.createForCollection(introspectedClass, accessorMethod);
- getFacetProcessor().process(introspectedClass, accessorMethod, methodRemover, facetedMethod, FeatureType.COLLECTION);
+ val facetedMethod = FacetedMethod.createForCollection(introspectedClass, accessorMethod);
+ getFacetProcessor().process(
+ introspectedClass,
+ accessorMethod,
+ methodRemover,
+ facetedMethod,
+ FeatureType.COLLECTION,
+ isMixinMain(accessorMethod));
// figure out what the type is
Class<?> elementType = Object.class;
@@ -312,13 +319,13 @@ public class FacetedMethodsBuilder {
continue;
}
- facetMethodsToAppendto.add(facetedMethod);
+ onNewFacetMethod.accept(facetedMethod);
}
}
private void createPropertyFacetedMethodsFromAccessors(
final List<Method> accessorMethods,
- final List<FacetedMethod> facetedMethodsToAppendto) throws MetaModelException {
+ final Consumer<FacetedMethod> onNewFacetedMethod) throws MetaModelException {
for (final Method accessorMethod : accessorMethods) {
log.debug(" identified accessor method representing property: {}", accessorMethod);
@@ -331,12 +338,18 @@ public class FacetedMethodsBuilder {
}
// create a 1:1 association peer
- final FacetedMethod facetedMethod = FacetedMethod.createForProperty(introspectedClass, accessorMethod);
-
- // process facets for the 1:1 association
- getFacetProcessor().process(introspectedClass, accessorMethod, methodRemover, facetedMethod, FeatureType.PROPERTY);
-
- facetedMethodsToAppendto.add(facetedMethod);
+ val facetedMethod = FacetedMethod.createForProperty(introspectedClass, accessorMethod);
+
+ // process facets for the 1:1 association (eg. contributed properties)
+ getFacetProcessor().process(
+ introspectedClass,
+ accessorMethod,
+ methodRemover,
+ facetedMethod,
+ FeatureType.PROPERTY,
+ isMixinMain(accessorMethod));
+
+ onNewFacetedMethod.accept(facetedMethod);
}
}
@@ -407,7 +420,13 @@ public class FacetedMethodsBuilder {
final FacetedMethod action = FacetedMethod.createForAction(introspectedClass, actionMethod);
// process facets on the action & parameters
- getFacetProcessor().process(introspectedClass, actionMethod, methodRemover, action, FeatureType.ACTION);
+ getFacetProcessor().process(
+ introspectedClass,
+ actionMethod,
+ methodRemover,
+ action,
+ FeatureType.ACTION,
+ isMixinMain(actionMethod));
final List<FacetedMethodParameter> actionParams = action.getParameters();
for (int j = 0; j < actionParams.size(); j++) {
@@ -418,8 +437,8 @@ public class FacetedMethodsBuilder {
}
private boolean isAllParamTypesValid(final Method actionMethod) {
- for (final Class<?> paramType : actionMethod.getParameterTypes()) {
- final ObjectSpecification paramSpec = getSpecificationLoader().loadSpecification(paramType);
+ for (val paramType : actionMethod.getParameterTypes()) {
+ val paramSpec = getSpecificationLoader().loadSpecification(paramType);
if (paramSpec == null) {
return false;
}
@@ -453,16 +472,16 @@ public class FacetedMethodsBuilder {
return false;
}
- if(this.inspectedTypeSpec.isMixin()) {
+ if(isMixinMain(actionMethod)) {
// we are introspecting a mixin type, so accept this method for further processing
- log.debug(" identified possible mixin´s 'main' action {0}", actionMethod);
+ log.debug(" identified mixin-main action {}", actionMethod);
return true;
}
if(explicitActionAnnotationConfigured()) {
if(_Annotations.isPresent(actionMethod, Action.class)) {
- log.debug(" identified action {0}", actionMethod);
+ log.debug(" identified action {}", actionMethod);
return true;
}
// we have no @Action, so dismiss
@@ -476,7 +495,7 @@ public class FacetedMethodsBuilder {
}
// we have a valid action candidate, so fall through
- log.debug(" identified action {0}", actionMethod);
+ log.debug(" identified action {}", actionMethod);
return true;
}
@@ -515,9 +534,9 @@ public class FacetedMethodsBuilder {
final String prefix,
final Class<?> returnType,
final int paramCount,
- final List<Method> methodListToAppendTo) {
+ final Consumer<Method> onRemoved) {
- findAndRemovePrefixedMethods(methodScope, prefix, returnType, false, paramCount, methodListToAppendTo::add);
+ findAndRemovePrefixedMethods(methodScope, prefix, returnType, false, paramCount, onRemoved);
}
/**
@@ -538,6 +557,25 @@ public class FacetedMethodsBuilder {
});
}
+
+ /**
+ * In case this inspected type is a mixin, returns whether given method can be identified
+ * as this mixin's main method.
+ *
+ * @param method
+ */
+ private boolean isMixinMain(Method method) {
+ if(!this.inspectedTypeSpec.isMixin()) {
+ return false;
+ }
+ val mixinMember = inspectedTypeSpec.getMixedInMember(inspectedTypeSpec);
+ if(!mixinMember.isPresent()) {
+ return false;
+ }
+ val actionMethod_ofMixinMember = ((ObjectActionMixedIn)mixinMember.get())
+ .getFacetedMethod().getMethod();
+ return method.equals(actionMethod_ofMixinMember);
+ }
// ////////////////////////////////////////////////////////////////////////////
// toString