This is an automated email from the ASF dual-hosted git repository. danhaywood pushed a commit to branch v2 in repository https://gitbox.apache.org/repos/asf/isis.git
commit 972218af497c0ab80f013ef7c403e024dfdc1652 Merge: c4b2d96 901b63e Author: danhaywood <d...@haywood-associates.co.uk> AuthorDate: Fri Nov 23 10:59:16 2018 +0000 Merge branch 'master' into v2 # Conflicts: # adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Action_domainEvent.adoc # adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Collection_domainEvent.adoc # adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-DomainObject_publishing.adoc # adocs/documentation/src/main/asciidoc/guides/rgant/_rgant-Property_domainEvent.adoc # core/applib/src/main/java/org/apache/isis/applib/annotation/DomainObject.java # core/applib/src/main/java/org/apache/isis/applib/events/domain/AbstractDomainEvent.java # core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/DomainEventHelper.java # core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java # core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionDomainEventFacetAbstract.java # core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java # core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionAddToFacetForDomainEventFromAbstract.java # core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionDomainEventFacetAbstract.java # core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionRemoveFromFacetForDomainEventFromAbstract.java # core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java # core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java # core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetAbstract.java # core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetDefault.java # core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPostsPropertyChangedEventAnnotation.java # core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPropertyAnnotation.java # core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPropertyInteractionAnnotation.java # core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertySetterOrClearFacetForDomainEventAbstract.java # core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/param/DeriveFacetsPostProcessor.java .../guides/rgant/_rgant-Action_domainEvent.adoc | 72 +++++++---- .../rgant/_rgant-Collection_domainEvent.adoc | 37 +++--- .../_rgant-DomainObjectLayout_bookmarking.adoc | 6 +- .../_rgant-DomainObjectLayout_cssClassFa.adoc | 8 +- .../_rgant-DomainObjectLayout_describedAs.adoc | 4 +- .../_rgant-DomainObjectLayout_iconUiEvent.adoc | 6 +- .../rgant/_rgant-DomainObjectLayout_named.adoc | 3 +- .../rgant/_rgant-DomainObjectLayout_paged.adoc | 6 +- .../rgant/_rgant-DomainObjectLayout_plural.adoc | 3 +- .../_rgant-DomainObjectLayout_titleUiEvent.adoc | 34 +++--- .../_rgant-DomainObject_actionDomainEvent.adoc | 45 +++++++ .../_rgant-DomainObject_collectionDomainEvent.adoc | 62 ++++++++++ ...rgant-DomainObject_persistedLifecycleEvent.adoc | 18 ++- ...gant-DomainObject_persistingLifecycleEvent.adoc | 15 +-- .../_rgant-DomainObject_propertyDomainEvent.adoc | 62 ++++++++++ ..._rgant-DomainObject_removingLifecycleEvent.adoc | 24 ++-- .../_rgant-DomainObject_updatedLifecycleEvent.adoc | 25 ++-- ..._rgant-DomainObject_updatingLifecycleEvent.adoc | 30 ++--- .../guides/rgant/_rgant-Property_domainEvent.adoc | 37 +++--- ..._ugvw_hints-and-tips_highlight-current-row.adoc | 4 +- .../org/apache/isis/applib/annotation/Action.java | 6 - .../apache/isis/applib/annotation/Collection.java | 6 - .../isis/applib/annotation/DomainObject.java | 81 +++++++++++++ .../apache/isis/applib/annotation/Property.java | 6 - .../applib/events/domain/AbstractDomainEvent.java | 32 +++++ .../services/eventbus/ActionDomainEvent.java | 16 --- .../core/metamodel/facets/DomainEventHelper.java | 17 ++- .../facets/SingleClassValueFacetAbstract.java | 2 +- .../action/ActionAnnotationFacetFactory.java | 18 ++- .../invocation/ActionDomainEventFacetAbstract.java | 23 +++- .../CollectionAnnotationFacetFactory.java | 29 ++++- ...ectionAddToFacetForDomainEventFromAbstract.java | 8 +- .../modify/CollectionDomainEventFacetAbstract.java | 29 ++++- ...nRemoveFromFacetForDomainEventFromAbstract.java | 7 +- .../autocomplete/AutoCompleteFacetAbstract.java | 4 + .../DomainObjectAnnotationFacetFactory.java | 86 +++++++++++--- ...AutoCompleteFacetForDomainObjectAnnotation.java | 8 -- ...EventDefaultFacetForDomainObjectAnnotation.java | 54 +++++++++ ...EventDefaultFacetForDomainObjectAnnotation.java | 53 +++++++++ ...EventDefaultFacetForDomainObjectAnnotation.java | 53 +++++++++ .../property/PropertyAnnotationFacetFactory.java | 27 ++++- .../modify/PropertyDomainEventFacetAbstract.java | 71 +++++++---- .../modify/PropertyDomainEventFacetDefault.java | 8 +- ...pertyDomainEventFacetForPropertyAnnotation.java | 8 +- ...tySetterOrClearFacetForDomainEventAbstract.java | 16 +-- .../param/DeriveFacetsPostProcessor.java | 132 +++++++++++++++++++++ .../action/ActionAnnotationFacetFactoryTest.java | 4 + ...ollectionAnnotationFacetFactoryTest_typeOf.java | 22 ++++ .../PropertyAnnotationFacetFactoryTest.java | 16 +++ .../integtests/smoke/Smoke_IntegTest.java | 1 + .../simple/subscribers/SimpleObjectListener.java | 4 + 51 files changed, 1064 insertions(+), 284 deletions(-) diff --cc core/applib/src/main/java/org/apache/isis/applib/annotation/DomainObject.java index b3d3ff8,abce74b..5388086 --- a/core/applib/src/main/java/org/apache/isis/applib/annotation/DomainObject.java +++ b/core/applib/src/main/java/org/apache/isis/applib/annotation/DomainObject.java @@@ -24,13 -24,17 +24,16 @@@ import java.lang.annotation.Retention import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.apache.isis.applib.services.eventbus.ActionDomainEvent; -import org.apache.isis.applib.services.eventbus.CollectionDomainEvent; -import org.apache.isis.applib.services.eventbus.ObjectCreatedEvent; -import org.apache.isis.applib.services.eventbus.ObjectLoadedEvent; -import org.apache.isis.applib.services.eventbus.ObjectPersistedEvent; -import org.apache.isis.applib.services.eventbus.ObjectPersistingEvent; -import org.apache.isis.applib.services.eventbus.ObjectRemovingEvent; -import org.apache.isis.applib.services.eventbus.ObjectUpdatedEvent; -import org.apache.isis.applib.services.eventbus.ObjectUpdatingEvent; -import org.apache.isis.applib.services.eventbus.PropertyDomainEvent; -import org.apache.isis.applib.services.publish.PublisherService; ++import org.apache.isis.applib.events.domain.ActionDomainEvent; ++import org.apache.isis.applib.events.domain.CollectionDomainEvent; ++import org.apache.isis.applib.events.domain.PropertyDomainEvent; +import org.apache.isis.applib.events.lifecycle.ObjectCreatedEvent; +import org.apache.isis.applib.events.lifecycle.ObjectLoadedEvent; +import org.apache.isis.applib.events.lifecycle.ObjectPersistedEvent; +import org.apache.isis.applib.events.lifecycle.ObjectPersistingEvent; +import org.apache.isis.applib.events.lifecycle.ObjectRemovingEvent; +import org.apache.isis.applib.events.lifecycle.ObjectUpdatedEvent; +import org.apache.isis.applib.events.lifecycle.ObjectUpdatingEvent; /** * Domain semantics for domain objects (entities and view models; for services see {@link org.apache.isis.applib.annotation.DomainService}). @@@ -238,4 -249,82 +241,82 @@@ public @interface DomainObject * </p> */ Class<? extends ObjectRemovingEvent<?>> removingLifecycleEvent() default ObjectRemovingEvent.Default.class; + + + /** + * Indicates that an invocation of <i>any</i> action of the domain object (that do not themselves specify their own + * <tt>@Action(domainEvent=...)</tt> should be posted to the + * {@link org.apache.isis.applib.services.eventbus.EventBusService event bus} using the specified custom - * (subclass of) {@link org.apache.isis.applib.services.eventbus.ActionDomainEvent}. ++ * (subclass of) {@link ActionDomainEvent}. + * + * <p>For example: + * </p> + * + * <pre> + * @DomainObject(actionDomainEvent=SomeObject.GenericActionDomainEvent.class) + * public class SomeObject{ + * public static class GenericActionDomainEvent extends ActionDomainEvent<Object> { ... } + * + * public void changeStartDate(final Date startDate) { ...} + * ... + * } + * </pre> + * + * <p> + * This will result in all actions as a more specific type to use) to emit this event. + * </p> + * <p> + * This subclass must provide a no-arg constructor; the fields are set reflectively. + * It must also use <tt>Object</tt> as its generic type. This is to allow mixins to also emit the same event. + * </p> + */ + Class<? extends ActionDomainEvent<?>> actionDomainEvent() default ActionDomainEvent.Default.class; + + /** + * Indicates that changes to <i>any</i> property of the domain object (that do not themselves specify their own + * <tt>@Property(domainEvent=...)</tt> should be posted to the + * {@link org.apache.isis.applib.services.eventbus.EventBusService event bus} using the specified custom - * (subclass of) {@link org.apache.isis.applib.services.eventbus.PropertyDomainEvent}. ++ * (subclass of) {@link PropertyDomainEvent}. + * + * <p>For example: + * </p> + * + * <pre> + * @DomainObject(propertyDomainEvent=SomeObject.GenericPropertyDomainEvent.class) + * public class SomeObject{ + * + * public LocalDate getStartDate() { ...} + * } + * </pre> + * + * <p> + * This subclass must provide a no-arg constructor; the fields are set reflectively. + * It must also use <tt>Object</tt> as its generic type. This is to allow mixins to also emit the same event. + * </p> + */ + Class<? extends PropertyDomainEvent<?,?>> propertyDomainEvent() default PropertyDomainEvent.Default.class; + + /** + * Indicates that changes to <i>any</i> collection of the domain object (that do not themselves specify their own + * <tt>@Collection(domainEvent=...)</tt> should be posted to the + * {@link org.apache.isis.applib.services.eventbus.EventBusService event bus} using a custom (subclass of) - * {@link org.apache.isis.applib.services.eventbus.CollectionDomainEvent}. ++ * {@link CollectionDomainEvent}. + * + * <p>For example: + * </p> + * <pre> + * @DomainObject(collectionDomainEvent=Order.GenericCollectionDomainEvent.class) + * public class Order { + * + * public SortedSet<OrderLine> getLineItems() { ...} + * } + * </pre> + * + * <p> + * This subclass must provide a no-arg constructor; the fields are set reflectively. + * It must also use <tt>Object</tt> as its generic type. This is to allow mixins to also emit the same event. + * </p> + */ + Class<? extends CollectionDomainEvent<?,?>> collectionDomainEvent() default CollectionDomainEvent.Default.class; + } diff --cc core/applib/src/main/java/org/apache/isis/applib/events/domain/AbstractDomainEvent.java index 0e5f9b7,0000000..7f71ca0 mode 100644,000000..100644 --- a/core/applib/src/main/java/org/apache/isis/applib/events/domain/AbstractDomainEvent.java +++ b/core/applib/src/main/java/org/apache/isis/applib/events/domain/AbstractDomainEvent.java @@@ -1,336 -1,0 +1,368 @@@ +/* + * 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.isis.applib.events.domain; + +import java.util.EventObject; +import java.util.Map; + +import org.apache.isis.applib.Identifier; +import org.apache.isis.applib.annotation.Programmatic; +import org.apache.isis.applib.services.i18n.TranslatableString; +import org.apache.isis.applib.util.ObjectContracts; +import org.apache.isis.applib.util.ToString; +import org.apache.isis.commons.internal.collections._Maps; +import org.apache.isis.commons.internal.exceptions._Exceptions; + +public abstract class AbstractDomainEvent<S> extends java.util.EventObject { + + private static final long serialVersionUID = 1L; + + /** + * If used then the framework will set state via (non-API) setters. + * + * <p> + * Because the {@link EventObject} superclass prohibits a null source, a dummy value is temporarily used. + * </p> + */ + public AbstractDomainEvent() { + this(null, null); + } + + public AbstractDomainEvent( + final S source, + final Identifier identifier) { + super(sourceElseDummy(source)); + this.identifier = identifier; + } + + private static Object sourceElseDummy(final Object source) { + return source != null ? source : new Object(); + } + ++ // - mixedIn ++ ++ private Object mixedIn; ++ ++ /** ++ * Populated only for mixins; holds the underlying domain object that the mixin contributes to. ++ */ ++ public Object getMixedIn() { ++ return mixedIn; ++ } ++ /** ++ * Not API - set by the framework. ++ */ ++ public void setMixedIn(final Object mixedIn) { ++ this.mixedIn = mixedIn; ++ } ++ ++ ++ ++ // -- Subject ++ ++ /** ++ * The subject of the event, which will be either the {@link #getSource() source} for a regular action, or the ++ * {@link #getMixedIn() mixed-in} domain object for a mixin. ++ */ ++ public Object getSubject() { ++ final Object mixedIn = getMixedIn(); ++ return mixedIn != null ? mixedIn : getSource(); ++ } ++ ++ ++ + // -- Phase + + public enum Phase { + HIDE, + DISABLE, + VALIDATE, + EXECUTING, + EXECUTED; + + /** + * The significance being that at this point the proposed values/arguments are known, and so the event can be + * fully populated. + */ + public boolean isValidatingOrLater() { + return this == VALIDATE || isExecutingOrLater(); + } + + /** + * When the {@link org.apache.isis.applib.services.command.Command} is made available on the {@link org.apache.isis.applib.events.domain.ActionDomainEvent} + * via {@link org.apache.isis.applib.events.domain.ActionDomainEvent#getCommand()}. + */ + public boolean isExecutingOrLater() { + return isExecuting() || isExecuted(); + } + + public boolean isExecuting() { + return this == EXECUTING; + } + + public boolean isExecuted() { + return this == EXECUTED; + } + } + + private Phase phase; + + /** + * Whether the framework is checking visibility, enablement, validity or actually executing (invoking action, + * updating property or collection). + */ + public Phase getEventPhase() { + return phase; + } + + /** + * Not API, set by the framework. + */ + public void setEventPhase(Phase phase) { + this.phase = phase; + } + + + // -- source (downcast to S) + @Override + @SuppressWarnings("unchecked") + public S getSource() { + return (S)source; + } + + /** + * Not API, set by the framework if the no-arg constructor is used. + */ + public void setSource(S source) { + this.source = source; + } + + + // -- identifier + /** + * If the no-arg constructor is used, then the framework will populate this field reflectively. + */ + private Identifier identifier; + public Identifier getIdentifier() { + return identifier; + } + + /** + * Not API, set by the framework if the no-arg constructor is used. + */ + public void setIdentifier(final Identifier identifier) { + this.identifier = identifier; + } + + + // -- hide, isHidden + private boolean hidden; + public boolean isHidden() { + return hidden; + } + + /** + * @see #veto(String, Object...) + */ + public void hide() { + this.hidden = true; + } + + + // -- disable, isDisabled, getDisabledReason, getDisabledReasonTranslatable + private String disabledReason; + + public boolean isDisabled() { + return disabledReason != null || disabledReasonTranslatable != null; + } + + /** + * If {@link #isDisabled() disabled}, then either this method returns non-null or {@link #getDisabledReasonTranslatable()} will. + */ + public String getDisabledReason() { + return disabledReason; + } + + /** + * @see #disable(org.apache.isis.applib.services.i18n.TranslatableString) + * @see #veto(String, Object...) + */ + public void disable(final String reason) { + this.disabledReason = reason; + } + + private TranslatableString disabledReasonTranslatable; + /** + * If {@link #isDisabled() disabled}, then either this method returns non-null or {@link #getDisabledReason()} will. + */ + public TranslatableString getDisabledReasonTranslatable() { + return disabledReasonTranslatable; + } + /** + * @see #disable(java.lang.String) + * @see #veto(org.apache.isis.applib.services.i18n.TranslatableString) + */ + public void disable(final TranslatableString reason) { + this.disabledReasonTranslatable = reason; + } + + + // -- invalidate, isInvalid, getInvalidityReason, getInvalidityReasonTranslatable + private String invalidatedReason; + public boolean isInvalid() { + return invalidatedReason != null || invalidatedReasonTranslatable != null; + } + + /** + * If {@link #isInvalid() invalid}, then either this method returns non-null or {@link #getInvalidityReasonTranslatable()} will. + */ + public String getInvalidityReason() { + return invalidatedReason; + } + /** + * @see #invalidate(org.apache.isis.applib.services.i18n.TranslatableString) + * @see #veto(String, Object...) + */ + public void invalidate(final String reason) { + this.invalidatedReason = reason; + } + + private TranslatableString invalidatedReasonTranslatable; + /** + * If {@link #isInvalid() invalid}, then either this method returns non-null or {@link #getInvalidityReason()} will. + */ + public TranslatableString getInvalidityReasonTranslatable() { + return invalidatedReasonTranslatable; + } + + /** + * @see #invalidate(String) + * @see #veto(org.apache.isis.applib.services.i18n.TranslatableString) + */ + public void invalidate(final TranslatableString reason) { + this.invalidatedReasonTranslatable = reason; + } + + + + // -- veto + /** + * Use instead of {@link #hide()}, {@link #disable(String)} and {@link #invalidate(String)}; just delegates to + * appropriate vetoing method based upon the {@link #getEventPhase() phase}. + * + * <p> + * If hiding, just pass <tt>null</tt> for the parameter. + * </p> + * + * @param reason - reason why the interaction is being invalidated (ignored if in {@link org.apache.isis.applib.events.domain.AbstractDomainEvent.Phase#HIDE hide} phase). + * @param args + * + * @see #veto(org.apache.isis.applib.services.i18n.TranslatableString) + */ + @Programmatic + public void veto(final String reason, final Object... args) { + switch (getEventPhase()) { + case HIDE: + hide(); + break; + case DISABLE: + if(reason == null) { + throw new IllegalArgumentException("Reason must be non-null"); + } + disable(String.format(reason, args)); + break; + case VALIDATE: + if(reason == null) { + throw new IllegalArgumentException("Reason must be non-null"); + } + invalidate(String.format(reason, args)); + break; + case EXECUTED: + case EXECUTING: + break; + default: + throw _Exceptions.unmatchedCase(getEventPhase()); + } + } + /** + * Use instead of {@link #hide()}, {@link #disable(org.apache.isis.applib.services.i18n.TranslatableString)} and {@link #invalidate(org.apache.isis.applib.services.i18n.TranslatableString)}; just delegates to + * appropriate vetoing method based upon the {@link #getEventPhase() phase}. + * + * <p> + * If hiding, just pass <tt>null</tt> for the parameter. + * </p> + * + * @param translatableReason - reason why the interaction is being invalidated (ignored if in {@link org.apache.isis.applib.events.domain.AbstractDomainEvent.Phase#HIDE hide} phase). + * + * @see #veto(String, Object...) + */ + @Programmatic + public void veto(final TranslatableString translatableReason) { + switch (getEventPhase()) { + case HIDE: + hide(); + break; + case DISABLE: + disable(translatableReason); + break; + case VALIDATE: + invalidate(translatableReason); + break; + case EXECUTED: + case EXECUTING: + break; + default: + throw _Exceptions.unmatchedCase(getEventPhase()); + } + } + + + // -- userData + /** + * Provides a mechanism to pass data to the next {@link #getEventPhase() phase}. + */ + private final Map<Object, Object> userData = _Maps.newHashMap(); + + /** + * Obtain user-data, as set by a previous {@link #getEventPhase() phase}. + */ + public Object get(Object key) { + return userData.get(key); + } + /** + * Set user-data, for the use of a subsequent {@link #getEventPhase() phase}. + */ + public void put(Object key, Object value) { + userData.put(key, value); + } + + private final static ToString<AbstractDomainEvent<?>> toString = + ObjectContracts.<AbstractDomainEvent<?>> + toString("source", AbstractDomainEvent::getSource) + .thenToString("identifier", AbstractDomainEvent::getIdentifier) + .thenToString("eventPhase", AbstractDomainEvent::getEventPhase) + ; + + @Override + public String toString() { + return toString.toString(this); + } + + +} diff --cc core/legacy/transition-1-2/src/main/java/org/apache/isis/applib/services/eventbus/ActionDomainEvent.java index e857451,0000000..7342cab mode 100644,000000..100644 --- a/core/legacy/transition-1-2/src/main/java/org/apache/isis/applib/services/eventbus/ActionDomainEvent.java +++ b/core/legacy/transition-1-2/src/main/java/org/apache/isis/applib/services/eventbus/ActionDomainEvent.java @@@ -1,223 -1,0 +1,207 @@@ +/* + * 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.isis.applib.services.eventbus; + +import java.util.List; + +import org.apache.isis.applib.annotation.SemanticsOf; +import org.apache.isis.applib.events.domain.AbstractDomainEvent; +import org.apache.isis.applib.services.command.Command; +import org.apache.isis.applib.services.command.CommandContext; +import org.apache.isis.applib.util.ObjectContracts; +import org.apache.isis.applib.util.ToString; + +@Deprecated +public abstract class ActionDomainEvent<S> extends AbstractDomainEvent<S> { + + private static final long serialVersionUID = 1L; + + // -- Default class + /** + * This class is the default for the + * {@link org.apache.isis.applib.annotation.Action#domainEvent()} annotation attribute. Whether this + * raises an event or not depends upon the "isis.reflector.facet.actionAnnotation.domainEvent.postForDefault" + * configuration property. + */ + public static class Default extends ActionDomainEvent<Object> { + private static final long serialVersionUID = 1L; + } + + + // -- Noop class + + /** + * Convenience class to use indicating that an event should <i>not</i> be posted (irrespective of the configuration + * property setting for the {@link Default} event. + */ + public static class Noop extends ActionDomainEvent<Object> { + private static final long serialVersionUID = 1L; + } + + + // -- Doop class + + /** + * Convenience class meaning that an event <i>should</i> be posted (irrespective of the configuration + * property setting for the {@link Default} event.. + */ + public static class Doop extends ActionDomainEvent<Object> { + private static final long serialVersionUID = 1L; + } + + + + /** + * If used then the framework will set state via (non-API) setters. + * + * <p> + * Recommended because it reduces the amount of boilerplate in the domain object classes. + * </p> + */ + public ActionDomainEvent() { + } + + // -- command + private Command command; + + /** + * @deprecated - use {@link CommandContext#getCommand()} to obtain the current {@link Command}. + */ + @Deprecated + public Command getCommand() { + return command; + } + + /** + * Not API - set by the framework. + * + * @deprecated - the corresponding {@link #getCommand()} should not be called, instead use {@link CommandContext#getCommand()} to obtain the current {@link Command}. + */ + @Deprecated + public void setCommand(Command command) { + this.command = command; + } + + + // -- actionSemantics + public SemanticsOf getSemantics() { + return actionSemantics; + } + + private SemanticsOf actionSemantics; + + /** + * @deprecated - use {@link #getSemantics()} instead. + */ + @Deprecated + public SemanticsOf getActionSemantics() { + return actionSemantics; + } + + /** + * Not API - set by the framework. + */ + public void setActionSemantics(SemanticsOf actionSemantics) { + this.actionSemantics = actionSemantics; + } + + + + // -- parameterNames + private List<String> parameterNames; + public List<String> getParameterNames() { + return parameterNames; + } + public void setParameterNames(final List<String> parameterNames) { + this.parameterNames = parameterNames; + } + + + // -- parameterTypes + private List<Class<?>> parameterTypes; + public List<Class<?>> getParameterTypes() { + return parameterTypes; + } + + public void setParameterTypes(final List<Class<?>> parameterTypes) { + this.parameterTypes = parameterTypes; + } + + + - // region > mixedIn - private Object mixedIn; - - /** - * Populated only for mixins; holds the underlying domain object that the mixin contributes to. - */ - public Object getMixedIn() { - return mixedIn; - } - /** - * Not API - set by the framework. - */ - public void setMixedIn(final Object mixedIn) { - this.mixedIn = mixedIn; - } - // endregion + + // -- arguments + private List<Object> arguments; + /** + * The arguments being used to invoke the action; populated at {@link Phase#VALIDATE} and subsequent phases + * (but null for {@link Phase#HIDE hidden} and {@link Phase#DISABLE disable} phases). + */ + public List<Object> getArguments() { + return arguments; + } + + /** + * Not API - set by the framework. + */ + public void setArguments(List<Object> arguments) { + this.arguments = arguments; + } + + + // -- returnValue + /** + * + */ + private Object returnValue; + + /** + * The value returned by the action. + * + * <p> + * Only available for the {@link org.apache.isis.applib.events.domain.AbstractDomainEvent.Phase#EXECUTED} + * {@link #getEventPhase() phase}. + * </p> + */ + public Object getReturnValue() { + return returnValue; + } + + /** + * Not API - set by the framework + */ + public void setReturnValue(final Object returnValue) { + this.returnValue = returnValue; + } + + + + private final static ToString<ActionDomainEvent<?>> toString = ObjectContracts.<ActionDomainEvent<?>> + toString("source", ActionDomainEvent::getSource) + .thenToString("identifier", ActionDomainEvent::getIdentifier) + .thenToString("eventPhase", ActionDomainEvent::getEventPhase) + ; + + @Override + public String toString() { + return toString.toString(this); + } + + +} diff --cc core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/DomainEventHelper.java index b8755e9,f881113..db5a09b --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/DomainEventHelper.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/DomainEventHelper.java @@@ -43,6 -46,6 +41,8 @@@ import org.apache.isis.core.metamodel.s import org.apache.isis.core.metamodel.spec.feature.ObjectAction; import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter; ++import static org.apache.isis.commons.internal.base._Casts.uncheckedCast; ++ public class DomainEventHelper { private final ServicesInjector servicesInjector; @@@ -164,23 -181,24 +164,24 @@@ private static List<Object> asList(final Object[] arguments) { return arguments != null ? Arrays.asList(arguments) - : Collections.emptyList(); + : Collections.emptyList(); } - //endregion - //region > postEventForProperty, newPropertyInteraction - public PropertyDomainEvent<?, ?> postEventForProperty( + + // -- postEventForProperty, newPropertyInteraction + public <S, T> PropertyDomainEvent<S, T> postEventForProperty( final AbstractDomainEvent.Phase phase, - final Class eventType, - final PropertyDomainEvent<?, ?> existingEvent, + final Class<? extends PropertyDomainEvent<S, T>> eventType, + final PropertyDomainEvent<S, T> existingEvent, final IdentifiedHolder identified, - final ObjectAdapter targetAdapter, - final ObjectAdapter mixedInAdapter, - final Object oldValue, - final Object newValue) { + final ManagedObject targetAdapter, ++ final ManagedObject mixedInAdapter, + final T oldValue, + final T newValue) { try { - final PropertyDomainEvent<?, ?> event; - final Object source = ObjectAdapter.Util.unwrap(targetAdapter); + final PropertyDomainEvent<S, T> event; + final S source = uncheckedCast(ObjectAdapter.Util.unwrapPojo(targetAdapter)); final Identifier identifier = identified.getIdentifier(); if(existingEvent != null && phase.isExecuted()) { @@@ -189,6 -207,12 +190,12 @@@ } else { // all other phases, create a new event event = newPropertyDomainEvent(eventType, identifier, source, oldValue, newValue); + + // copy over if have + if(mixedInAdapter != null ) { - event.setMixedIn(mixedInAdapter.getObject()); ++ event.setMixedIn(mixedInAdapter.getPojo()); + } + } event.setEventPhase(phase); @@@ -254,28 -280,34 +261,34 @@@ throw new NoSuchMethodException(type.getName()+".<init>(? super " + source.getClass().getName() + ", " + Identifier.class.getName() + ", java.lang.Object, java.lang.Object)"); } - //endregion - //region > postEventForCollection, newCollectionDomainEvent - public CollectionDomainEvent<?, ?> postEventForCollection( + // -- postEventForCollection, newCollectionDomainEvent + + public <S, T> CollectionDomainEvent<S, T> postEventForCollection( AbstractDomainEvent.Phase phase, - final Class eventType, - final CollectionDomainEvent<?, ?> existingEvent, + final Class<? extends CollectionDomainEvent<S, T>> eventType, + final CollectionDomainEvent<S, T> existingEvent, final IdentifiedHolder identified, - final ObjectAdapter targetAdapter, - final ObjectAdapter mixedInAdapter, + final ManagedObject targetAdapter, ++ final ManagedObject mixedInAdapter, final CollectionDomainEvent.Of of, - final Object reference) { + final T reference) { try { - final CollectionDomainEvent<?, ?> event; + final CollectionDomainEvent<S, T> event; if (existingEvent != null && phase.isExecuted()) { // reuse existing event from the executing phase event = existingEvent; } else { // all other phases, create a new event - final Object source = ObjectAdapter.Util.unwrap(targetAdapter); + final S source = uncheckedCast(ObjectAdapter.Util.unwrapPojo(targetAdapter)); final Identifier identifier = identified.getIdentifier(); event = newCollectionDomainEvent(eventType, phase, identifier, source, of, reference); + + // copy over if have + if(mixedInAdapter != null ) { - event.setMixedIn(mixedInAdapter.getObject()); ++ event.setMixedIn(mixedInAdapter.getPojo()); + } } event.setEventPhase(phase); diff --cc core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java index 12cd9b8,255f7c0..b0dda30 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactory.java @@@ -53,11 -83,31 +53,12 @@@ import org.apache.isis.core.metamodel.f import org.apache.isis.core.metamodel.facets.actions.publish.PublishedActionFacet; import org.apache.isis.core.metamodel.facets.actions.semantics.ActionSemanticsFacet; import org.apache.isis.core.metamodel.facets.all.hide.HiddenFacet; -import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacet; import org.apache.isis.core.metamodel.facets.members.order.annotprop.MemberOrderFacetForActionAnnotation; + import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.ActionDomainEventDefaultFacetForDomainObjectAnnotation; -import org.apache.isis.core.metamodel.services.ServicesInjector; import org.apache.isis.core.metamodel.spec.ObjectSpecification; -import org.apache.isis.core.metamodel.specloader.CollectionUtils; -import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorComposite; -import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorForDeprecatedAnnotation; import org.apache.isis.core.metamodel.util.EventUtil; -public class ActionAnnotationFacetFactory extends FacetFactoryAbstract - implements MetaModelValidatorRefiner { - - private final MetaModelValidatorForDeprecatedAnnotation actionSemanticsValidator = new MetaModelValidatorForDeprecatedAnnotation(ActionSemantics.class); - private final MetaModelValidatorForDeprecatedAnnotation actionInteractionValidator = new MetaModelValidatorForDeprecatedAnnotation(ActionInteraction.class); - private final MetaModelValidatorForDeprecatedAnnotation postsActionInvokedEventValidator = new MetaModelValidatorForDeprecatedAnnotation(PostsActionInvokedEvent.class); - private final MetaModelValidatorForDeprecatedAnnotation bulkValidator = new MetaModelValidatorForDeprecatedAnnotation(Bulk.class); - private final MetaModelValidatorForDeprecatedAnnotation commandValidator = new MetaModelValidatorForDeprecatedAnnotation(Command.class); - private final MetaModelValidatorForDeprecatedAnnotation queryOnlyValidator = new MetaModelValidatorForDeprecatedAnnotation(QueryOnly.class); - private final MetaModelValidatorForDeprecatedAnnotation idempotentValidator = new MetaModelValidatorForDeprecatedAnnotation(Idempotent.class); - private final MetaModelValidatorForDeprecatedAnnotation publishedActionValidator = new MetaModelValidatorForDeprecatedAnnotation(PublishedAction.class); - private final MetaModelValidatorForDeprecatedAnnotation typeOfValidator = new MetaModelValidatorForDeprecatedAnnotation(TypeOf.class); - private final MetaModelValidatorForDeprecatedAnnotation hiddenValidator = new MetaModelValidatorForDeprecatedAnnotation(Hidden.class); - private final MetaModelValidatorForDeprecatedAnnotation disabledValidator = new MetaModelValidatorForDeprecatedAnnotation(Disabled.class); - private final MetaModelValidatorForDeprecatedAnnotation prototypeValidator = new MetaModelValidatorForDeprecatedAnnotation(Prototype.class); +public class ActionAnnotationFacetFactory extends FacetFactoryAbstract { @@@ -101,22 -153,42 +102,22 @@@ // // Set up ActionDomainEventFacet, which will act as the hiding/disabling/validating advisor // - final PostsActionInvokedEvent postsActionInvokedEvent = Annotations.getAnnotation(actionMethod, PostsActionInvokedEvent.class); - final ActionInteraction actionInteraction =Annotations.getAnnotation(actionMethod, ActionInteraction.class); - final Action action = Annotations.getAnnotation(actionMethod, Action.class); - final Class<? extends ActionDomainEvent<?>> actionDomainEventType; - - final ActionDomainEventFacetAbstract actionDomainEventFacet; - - - // can't really do this, because would result in the event being fired for the - // hidden/disable/validate phases, most likely breaking existing code. + final List<Action> actions = Annotations.getAnnotations(actionMethod, Action.class); + + // search for @Action(domainEvent=...), else use the default event type + final ActionDomainEventFacetAbstract actionDomainEventFacet = + actions.stream() + .map(Action::domainEvent) + .filter(domainEvent -> domainEvent != ActionDomainEvent.Default.class) + .findFirst() + .map(domainEvent -> + (ActionDomainEventFacetAbstract) new ActionDomainEventFacetForActionAnnotation( - domainEvent, servicesInjector, getSpecificationLoader(), holder)) ++ defaultFromDomainObjectIfRequired(typeSpec, domainEvent), servicesInjector, getSpecificationLoader(), holder)) + .orElse( + new ActionDomainEventFacetDefault( - ActionDomainEvent.Default.class, servicesInjector, getSpecificationLoader(), holder) ++ defaultFromDomainObjectIfRequired(typeSpec, ActionDomainEvent.Default.class), servicesInjector, getSpecificationLoader(), holder) + ); -// // search for @PostsActionInvoked(value=...) -// if(postsActionInvokedEvent != null) { -// actionDomainEventType = postsActionInvokedEvent.value(); -// actionDomainEventFacet = new ActionDomainEventFacetForPostsActionInvokedEventAnnotation( -// actionDomainEventType, servicesInjector, getSpecificationLoader(), holder); -// } else - - // search for @ActionInteraction(value=...) - if(actionInteraction != null) { - actionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, actionInteraction.value()); - actionDomainEventFacet = new ActionDomainEventFacetForActionInteractionAnnotation( - actionDomainEventType, servicesInjector, getSpecificationLoader(), holder); - } else - // search for @Action(domainEvent=...) - if(action != null) { - actionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, action.domainEvent()); - actionDomainEventFacet = new ActionDomainEventFacetForActionAnnotation( - actionDomainEventType, servicesInjector, getSpecificationLoader(), holder); - } else - // else use default event type - { - actionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, ActionDomainEvent.Default.class); - actionDomainEventFacet = new ActionDomainEventFacetDefault( - actionDomainEventType, servicesInjector, getSpecificationLoader(), holder); - } if(EventUtil.eventTypeIsPostable( actionDomainEventFacet.getEventType(), ActionDomainEvent.Noop.class, diff --cc core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionDomainEventFacetAbstract.java index 60f520e,0763fb9..ed146a3 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionDomainEventFacetAbstract.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/ActionDomainEventFacetAbstract.java @@@ -41,13 -42,13 +41,14 @@@ import org.apache.isis.core.metamodel.i import org.apache.isis.core.metamodel.interactions.ValidityContext; import org.apache.isis.core.metamodel.interactions.VisibilityContext; import org.apache.isis.core.metamodel.services.ServicesInjector; -import org.apache.isis.core.metamodel.specloader.SpecificationLoader; +import org.apache.isis.core.metamodel.spec.ManagedObject; import org.apache.isis.core.metamodel.spec.feature.ObjectAction; +import org.apache.isis.core.metamodel.specloader.SpecificationLoader; public abstract class ActionDomainEventFacetAbstract - extends SingleClassValueFacetAbstract implements ActionDomainEventFacet { +extends SingleClassValueFacetAbstract implements ActionDomainEventFacet { + private Class<? extends ActionDomainEvent<?>> eventType; private final TranslationService translationService; private final String translationContext; @@@ -59,18 -60,36 +60,36 @@@ public ActionDomainEventFacetAbstract( final Class<? extends ActionDomainEvent<?>> eventType, - final FacetHolder holder, - final ServicesInjector servicesInjector, - final SpecificationLoader specificationLoader) { + final FacetHolder holder, + final ServicesInjector servicesInjector, + final SpecificationLoader specificationLoader) { super(type(), holder, eventType, specificationLoader); + this.eventType = eventType; - this.translationService = servicesInjector.lookupService(TranslationService.class); + this.translationService = servicesInjector.lookupService(TranslationService.class).orElse(null); // sadness: same as in TranslationFactory this.translationContext = ((IdentifiedHolder)holder).getIdentifier().toClassAndNameIdentityString(); domainEventHelper = new DomainEventHelper(servicesInjector); } + @Override + public Class<?> value() { + return eventType; + } + + protected Class eventType() { + return eventType; + } + - public Class<? extends ActionDomainEvent<?>> getEventType() { - return eventType; ++ public <S> Class<? extends ActionDomainEvent<S>> getEventType() { ++ return _Casts.uncheckedCast(eventType); + } + public void setEventType(final Class<? extends ActionDomainEvent<?>> eventType) { + this.eventType = eventType; + } + + @Override public String hides(final VisibilityContext<? extends VisibilityEvent> ic) { diff --cc core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java index d974aa0,1372406..0ce6680 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactory.java @@@ -54,11 -69,27 +54,13 @@@ import org.apache.isis.core.metamodel.f import org.apache.isis.core.metamodel.facets.collections.modify.CollectionAddToFacet; import org.apache.isis.core.metamodel.facets.collections.modify.CollectionRemoveFromFacet; import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacet; + import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.CollectionDomainEventDefaultFacetForDomainObjectAnnotation; import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet; import org.apache.isis.core.metamodel.facets.propcoll.notpersisted.NotPersistedFacet; -import org.apache.isis.core.metamodel.services.ServicesInjector; + import org.apache.isis.core.metamodel.spec.ObjectSpecification; -import org.apache.isis.core.metamodel.specloader.CollectionUtils; -import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorComposite; -import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorForDeprecatedAnnotation; import org.apache.isis.core.metamodel.util.EventUtil; -public class CollectionAnnotationFacetFactory extends FacetFactoryAbstract implements MetaModelValidatorRefiner { - - private final MetaModelValidatorForDeprecatedAnnotation postsCollectionAddedToEventValidator = new MetaModelValidatorForDeprecatedAnnotation(PostsCollectionAddedToEvent.class); - private final MetaModelValidatorForDeprecatedAnnotation postsCollectionRemovedFromEventValidator = new MetaModelValidatorForDeprecatedAnnotation(PostsCollectionRemovedFromEvent.class); - private final MetaModelValidatorForDeprecatedAnnotation collectionInteractionValidator = new MetaModelValidatorForDeprecatedAnnotation(CollectionInteraction.class); - private final MetaModelValidatorForDeprecatedAnnotation hiddenValidator = new MetaModelValidatorForDeprecatedAnnotation(Hidden.class); - private final MetaModelValidatorForDeprecatedAnnotation disabledValidator = new MetaModelValidatorForDeprecatedAnnotation(Disabled.class); - private final MetaModelValidatorForDeprecatedAnnotation notPersistedValidator = new MetaModelValidatorForDeprecatedAnnotation(NotPersisted.class); - private final MetaModelValidatorForDeprecatedAnnotation typeOfValidator = new MetaModelValidatorForDeprecatedAnnotation(TypeOf.class); - +public class CollectionAnnotationFacetFactory extends FacetFactoryAbstract { public CollectionAnnotationFacetFactory() { super(FeatureType.COLLECTIONS_AND_ACTIONS); @@@ -86,21 -125,54 +96,21 @@@ // // Set up CollectionDomainEventFacet, which will act as the hiding/disabling/validating advisor // - final PostsCollectionAddedToEvent postsCollectionAddedToEvent = Annotations.getAnnotation(method, PostsCollectionAddedToEvent.class); - final PostsCollectionRemovedFromEvent postsCollectionRemovedFromEvent = Annotations.getAnnotation(method, PostsCollectionRemovedFromEvent.class); - final CollectionInteraction collectionInteraction = Annotations.getAnnotation(method, CollectionInteraction.class); - final Collection collection = Annotations.getAnnotation(method, Collection.class); - final Class<? extends CollectionDomainEvent<?, ?>> collectionDomainEventType; - - final CollectionDomainEventFacetAbstract collectionDomainEventFacet; + final List<Collection> collections = Annotations.getAnnotations(method, Collection.class); - // can't really do this, because would result in the event being fired for the - // hidden/disable/validate phases, most likely breaking existing code. - // - // in any case, which to use... addTo or removeFrom ? - -// // search for @PostsCollectionAddedToEvent(value=...) -// if(postsCollectionAddedToEvent != null) { -// collectionDomainEventType = postsCollectionAddedToEvent.value(); -// collectionDomainEventFacet = postsCollectionAddedToEventValidator.flagIfPresent( -// new CollectionDomainEventFacetForPostsCollectionAddedToEventAnnotation( -// collectionDomainEventType, servicesInjector, getSpecificationLoader(), holder)); -// } else -// // search for @PostsCollectionRemovedFromEvent(value=...) -// if(postsCollectionRemovedFromEvent != null) { -// collectionDomainEventType = postsCollectionRemovedFromEvent.value(); -// collectionDomainEventFacet = postsCollectionRemovedFromEventValidator.flagIfPresent( -// new CollectionDomainEventFacetForPostsCollectionRemovedFromEventAnnotation( -// collectionDomainEventType, servicesInjector, getSpecificationLoader(), holder)); -// } else - - // search for @CollectionInteraction(value=...) - if(collectionInteraction != null) { - collectionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, collectionInteraction.value()); - collectionDomainEventFacet = collectionInteractionValidator.flagIfPresent( - new CollectionDomainEventFacetForCollectionInteractionAnnotation( - collectionDomainEventType, servicesInjector, getSpecificationLoader(), holder), processMethodContext); - } else // search for @Collection(domainEvent=...) - if(collection != null) { - collectionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, collection.domainEvent()); - collectionDomainEventFacet = new CollectionDomainEventFacetForCollectionAnnotation( - collectionDomainEventType, servicesInjector, getSpecificationLoader(), holder); - - } else - // else use default event type - { - collectionDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, CollectionDomainEvent.Default.class); - collectionDomainEventFacet = new CollectionDomainEventFacetDefault( - collectionDomainEventType, servicesInjector, getSpecificationLoader(), holder); - } + final CollectionDomainEventFacetAbstract collectionDomainEventFacet = collections.stream() + .map(Collection::domainEvent) + .filter(domainEvent -> domainEvent != CollectionDomainEvent.Default.class) + .findFirst() + .map(domainEvent -> + (CollectionDomainEventFacetAbstract) + new CollectionDomainEventFacetForCollectionAnnotation( - domainEvent, servicesInjector, getSpecificationLoader(), holder)) ++ defaultFromDomainObjectIfRequired(typeSpec, domainEvent), servicesInjector, getSpecificationLoader(), holder)) + .orElse( + new CollectionDomainEventFacetDefault( - CollectionDomainEvent.Default.class, servicesInjector, getSpecificationLoader(), holder) ++ defaultFromDomainObjectIfRequired(typeSpec, CollectionDomainEvent.Default.class), servicesInjector, getSpecificationLoader(), holder) + ); if(!CollectionDomainEvent.Noop.class.isAssignableFrom(collectionDomainEventFacet.getEventType())) { FacetUtil.addFacet(collectionDomainEventFacet); } @@@ -254,5 -387,37 +278,4 @@@ } - - - // ////////////////////////////////////// - - @Override - public void refineMetaModelValidator(final MetaModelValidatorComposite metaModelValidator, final IsisConfiguration configuration) { - metaModelValidator.add(postsCollectionAddedToEventValidator); - metaModelValidator.add(postsCollectionRemovedFromEventValidator); - metaModelValidator.add(collectionInteractionValidator); - metaModelValidator.add(notPersistedValidator); - metaModelValidator.add(typeOfValidator); - metaModelValidator.add(hiddenValidator); - metaModelValidator.add(disabledValidator); - } - - // ////////////////////////////////////// - - - @Override - public void setServicesInjector(final ServicesInjector servicesInjector) { - super.setServicesInjector(servicesInjector); - final IsisConfiguration configuration = getConfiguration(); - - postsCollectionAddedToEventValidator.setConfiguration(configuration); - postsCollectionRemovedFromEventValidator.setConfiguration(configuration); - collectionInteractionValidator.setConfiguration(configuration); - typeOfValidator.setConfiguration(configuration); - notPersistedValidator.setConfiguration(configuration); - hiddenValidator.setConfiguration(configuration); - disabledValidator.setConfiguration(configuration); - } - -- } diff --cc core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionAddToFacetForDomainEventFromAbstract.java index 1d33b5e,fe3927b..892446d --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionAddToFacetForDomainEventFromAbstract.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionAddToFacetForDomainEventFromAbstract.java @@@ -36,10 -34,9 +36,9 @@@ import org.apache.isis.core.metamodel.f import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet; import org.apache.isis.core.metamodel.services.ServicesInjector; - public abstract class CollectionAddToFacetForDomainEventFromAbstract - extends SingleValueFacetAbstract<Class<? extends CollectionDomainEvent<?,?>>> - implements CollectionAddToFacet { +extends SingleValueFacetAbstract<Class<? extends CollectionDomainEvent<?,?>>> +implements CollectionAddToFacet { private final DomainEventHelper domainEventHelper; @@@ -90,14 -86,14 +89,15 @@@ // either doesn't contain object, or doesn't have set semantics, so // execute the add wrapped between the executing and executed events ... + final ObjectAdapter mixedInAdapter = null; // ... post the executing event + final CollectionDomainEvent<?, ?> event = domainEventHelper.postEventForCollection( AbstractDomainEvent.Phase.EXECUTING, - eventType(), null, + getEventType(), null, - getIdentified(), targetAdapter, + getIdentified(), targetAdapter, mixedInAdapter, CollectionDomainEvent.Of.ADD_TO, referencedObject); @@@ -107,8 -103,8 +107,8 @@@ // ... post the executed event domainEventHelper.postEventForCollection( AbstractDomainEvent.Phase.EXECUTED, - getEventType(), uncheckedCast((CollectionDomainEvent<?, ?>)event), - getIdentified(), targetAdapter, - value(), verify(event), ++ getEventType(), uncheckedCast(event), + getIdentified(), targetAdapter, mixedInAdapter, CollectionDomainEvent.Of.ADD_TO, referencedObject); } diff --cc core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionDomainEventFacetAbstract.java index 8d60be4,6569e78..baf9f95 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionDomainEventFacetAbstract.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionDomainEventFacetAbstract.java @@@ -47,26 -46,46 +47,42 @@@ public abstract class CollectionDomainE public CollectionDomainEventFacetAbstract( final Class<? extends CollectionDomainEvent<?, ?>> eventType, - final FacetHolder holder, - final ServicesInjector servicesInjector, - final SpecificationLoader specificationLoader) { + final FacetHolder holder, + final ServicesInjector servicesInjector, + final SpecificationLoader specificationLoader) { super(CollectionDomainEventFacet.class, holder, eventType, specificationLoader); + this.eventType = eventType; - this.translationService = servicesInjector.lookupService(TranslationService.class); + this.translationService = servicesInjector.lookupService(TranslationService.class).orElse(null);; // sadness: same as in TranslationFactory this.translationContext = ((IdentifiedHolder)holder).getIdentifier().toClassAndNameIdentityString(); domainEventHelper = new DomainEventHelper(servicesInjector); } + private Class<? extends CollectionDomainEvent<?, ?>> eventType; + - private Class<?> eventType() { - return value(); - } - + @Override + public Class<?> value() { + return eventType; + } + - public Class<? extends CollectionDomainEvent<?, ?>> getEventType() { - return eventType; ++ public <S, T> Class<? extends CollectionDomainEvent<S, T>> getEventType() { ++ return _Casts.uncheckedCast(eventType); + } + + public void setEventType(final Class<? extends CollectionDomainEvent<?, ?>> eventType) { + this.eventType = eventType; + } + @Override public String hides(final VisibilityContext<? extends VisibilityEvent> ic) { final CollectionDomainEvent<?, ?> event = domainEventHelper.postEventForCollection( AbstractDomainEvent.Phase.HIDE, - eventType(), null, + getEventType(), null, - getIdentified(), ic.getTarget(), + getIdentified(), ic.getTarget(), ic.getMixedIn(), CollectionDomainEvent.Of.ACCESS, null); if (event != null && event.isHidden()) { @@@ -81,8 -100,8 +97,8 @@@ final CollectionDomainEvent<?, ?> event = domainEventHelper.postEventForCollection( AbstractDomainEvent.Phase.DISABLE, - eventType(), null, + getEventType(), null, - getIdentified(), ic.getTarget(), + getIdentified(), ic.getTarget(), ic.getMixedIn(), CollectionDomainEvent.Of.ACCESS, null); if (event != null && event.isDisabled()) { @@@ -98,8 -117,12 +114,12 @@@ @Override public String invalidates(final ValidityContext<? extends ValidityEvent> ic) { + // if this is a mixin, then this ain't true. + if(!(ic instanceof ProposedHolder)) { + return null; + } final ProposedHolder catc = (ProposedHolder) ic; - final Object proposed = catc.getProposed().getObject(); + final Object proposed = catc.getProposed().getPojo(); final CollectionDomainEvent.Of of = ic instanceof CollectionAddToContext @@@ -109,8 -132,8 +129,8 @@@ final CollectionDomainEvent<?, ?> event = domainEventHelper.postEventForCollection( AbstractDomainEvent.Phase.VALIDATE, - eventType(), null, + getEventType(), null, - getIdentified(), ic.getTarget(), + getIdentified(), ic.getTarget(), ic.getMixedIn(), of, proposed); if (event != null && event.isInvalid()) { @@@ -124,8 -147,4 +144,5 @@@ return null; } - public <S, T> Class<? extends CollectionDomainEvent<S, T>> getEventType() { - return _Casts.uncheckedCast(value()); - } + } diff --cc core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionRemoveFromFacetForDomainEventFromAbstract.java index 60f4882,95ef179..5bdd17b --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionRemoveFromFacetForDomainEventFromAbstract.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/collections/collection/modify/CollectionRemoveFromFacetForDomainEventFromAbstract.java @@@ -96,8 -92,8 +97,8 @@@ implements CollectionRemoveFromFacet final CollectionDomainEvent<?, ?> event = domainEventHelper.postEventForCollection( AbstractDomainEvent.Phase.EXECUTING, - eventType(), null, - getIdentified(), targetAdapter, null, + getEventType(), null, - getIdentified(), targetAdapter, ++ getIdentified(), targetAdapter, mixedInAdapter, CollectionDomainEvent.Of.REMOVE_FROM, referencedObject); @@@ -107,8 -103,8 +108,8 @@@ // ... and post the executed event domainEventHelper.postEventForCollection( AbstractDomainEvent.Phase.EXECUTED, - getEventType(), uncheckedCast((CollectionDomainEvent<?, ?>)event), - getIdentified(), targetAdapter, - value(), verify(event), ++ getEventType(), uncheckedCast(event), + getIdentified(), targetAdapter, mixedInAdapter, CollectionDomainEvent.Of.REMOVE_FROM, referencedObject); } diff --cc core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java index cc2c67f,35808b6..01f5d50 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactory.java @@@ -26,18 -23,29 +26,21 @@@ import java.util.stream.Collectors import javax.annotation.PostConstruct; -import com.google.common.collect.Maps; - -import org.apache.isis.applib.annotation.Audited; -import org.apache.isis.applib.annotation.AutoComplete; -import org.apache.isis.applib.annotation.Bounded; import org.apache.isis.applib.annotation.DomainObject; -import org.apache.isis.applib.annotation.Immutable; import org.apache.isis.applib.annotation.Nature; -import org.apache.isis.applib.annotation.ObjectType; -import org.apache.isis.applib.annotation.PublishedObject; -import org.apache.isis.applib.services.HasTransactionId; -import org.apache.isis.applib.services.eventbus.ActionDomainEvent; -import org.apache.isis.applib.services.eventbus.CollectionDomainEvent; -import org.apache.isis.applib.services.eventbus.ObjectCreatedEvent; -import org.apache.isis.applib.services.eventbus.ObjectLoadedEvent; -import org.apache.isis.applib.services.eventbus.ObjectPersistedEvent; -import org.apache.isis.applib.services.eventbus.ObjectPersistingEvent; -import org.apache.isis.applib.services.eventbus.ObjectRemovingEvent; -import org.apache.isis.applib.services.eventbus.ObjectUpdatedEvent; -import org.apache.isis.applib.services.eventbus.ObjectUpdatingEvent; -import org.apache.isis.applib.services.eventbus.PropertyDomainEvent; ++import org.apache.isis.applib.events.domain.ActionDomainEvent; ++import org.apache.isis.applib.events.domain.CollectionDomainEvent; ++import org.apache.isis.applib.events.domain.PropertyDomainEvent; +import org.apache.isis.applib.events.lifecycle.ObjectCreatedEvent; +import org.apache.isis.applib.events.lifecycle.ObjectLoadedEvent; +import org.apache.isis.applib.events.lifecycle.ObjectPersistedEvent; +import org.apache.isis.applib.events.lifecycle.ObjectPersistingEvent; +import org.apache.isis.applib.events.lifecycle.ObjectRemovingEvent; +import org.apache.isis.applib.events.lifecycle.ObjectUpdatedEvent; +import org.apache.isis.applib.events.lifecycle.ObjectUpdatingEvent; +import org.apache.isis.applib.services.HasUniqueId; +import org.apache.isis.commons.internal.collections._Maps; import org.apache.isis.core.commons.config.IsisConfiguration; -import org.apache.isis.core.commons.lang.Nullable; import org.apache.isis.core.metamodel.facetapi.Facet; import org.apache.isis.core.metamodel.facetapi.FacetHolder; import org.apache.isis.core.metamodel.facetapi.FacetUtil; @@@ -56,9 -65,15 +60,12 @@@ import org.apache.isis.core.metamodel.f import org.apache.isis.core.metamodel.facets.object.callbacks.RemovingLifecycleEventFacetForDomainObjectAnnotation; import org.apache.isis.core.metamodel.facets.object.callbacks.UpdatedLifecycleEventFacetForDomainObjectAnnotation; import org.apache.isis.core.metamodel.facets.object.callbacks.UpdatingLifecycleEventFacetForDomainObjectAnnotation; -import org.apache.isis.core.metamodel.facets.object.domainobject.auditing.AuditableFacetForAuditedAnnotation; import org.apache.isis.core.metamodel.facets.object.domainobject.auditing.AuditableFacetForDomainObjectAnnotation; -import org.apache.isis.core.metamodel.facets.object.domainobject.autocomplete.AutoCompleteFacetForAutoCompleteAnnotation; import org.apache.isis.core.metamodel.facets.object.domainobject.autocomplete.AutoCompleteFacetForDomainObjectAnnotation; import org.apache.isis.core.metamodel.facets.object.domainobject.choices.ChoicesFacetForDomainObjectAnnotation; -import org.apache.isis.core.metamodel.facets.object.domainobject.choices.ChoicesFacetFromBoundedAnnotation; + import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.ActionDomainEventDefaultFacetForDomainObjectAnnotation; + import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.CollectionDomainEventDefaultFacetForDomainObjectAnnotation; + import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.PropertyDomainEventDefaultFacetForDomainObjectAnnotation; import org.apache.isis.core.metamodel.facets.object.domainobject.editing.ImmutableFacetForDomainObjectAnnotation; import org.apache.isis.core.metamodel.facets.object.domainobject.objectspecid.ObjectSpecIdFacetForDomainObjectAnnotation; import org.apache.isis.core.metamodel.facets.object.domainobject.objectspecid.ObjectSpecIdFacetForJdoPersistenceCapableAnnotation; @@@ -83,9 -102,15 +90,9 @@@ import org.apache.isis.objectstore.jdo. public class DomainObjectAnnotationFacetFactory extends FacetFactoryAbstract - implements MetaModelValidatorRefiner, PostConstructMethodCache { - - private final MetaModelValidatorForDeprecatedAnnotation auditedValidator = new MetaModelValidatorForDeprecatedAnnotation(Audited.class); - private final MetaModelValidatorForDeprecatedAnnotation publishedObjectValidator = new MetaModelValidatorForDeprecatedAnnotation(PublishedObject.class); - private final MetaModelValidatorForDeprecatedAnnotation autoCompleteValidator = new MetaModelValidatorForDeprecatedAnnotation(AutoComplete.class); - private final MetaModelValidatorForDeprecatedAnnotation boundedValidator = new MetaModelValidatorForDeprecatedAnnotation(Bounded.class); - private final MetaModelValidatorForDeprecatedAnnotation immutableValidator = new MetaModelValidatorForDeprecatedAnnotation(Immutable.class); - private final MetaModelValidatorForDeprecatedAnnotation objectTypeValidator = new MetaModelValidatorForDeprecatedAnnotation(ObjectType.class); +implements MetaModelValidatorRefiner, PostConstructMethodCache { + - private final MetaModelValidatorForValidationFailures autoCompleteInvalid = new MetaModelValidatorForValidationFailures(); + private final MetaModelValidatorForValidationFailures autoCompleteMethodInvalid = new MetaModelValidatorForValidationFailures(); private final MetaModelValidatorForMixinTypes mixinTypeValidator = new MetaModelValidatorForMixinTypes("@DomainObject#nature=MIXIN"); @@@ -169,48 -219,47 +177,34 @@@ } private AutoCompleteFacet createFor( + final List<DomainObject> domainObjects, final FacetHolder facetHolder, - final AutoComplete annotation, final Class<?> cls) { - if(annotation == null) { - return null; - } - - final Class<?> repositoryClass = annotation.repository(); - final String actionName = annotation.action(); - - final Method repositoryMethod = findRepositoryMethod(cls, "@AutoComplete", repositoryClass, actionName); - if(repositoryMethod == null) { + if(domainObjects.isEmpty()) { return null; } - return new AutoCompleteFacetForAutoCompleteAnnotation( - facetHolder, repositoryClass, repositoryMethod, servicesInjector); - } - private AutoCompleteFacet createFor( - final DomainObject domainObject, - final FacetHolder facetHolder, - final Class<?> cls) { - if(domainObject == null) { - return null; - } - - final Class<?> repositoryClass = domainObject.autoCompleteRepository(); - if(repositoryClass == Object.class) { - return null; - } - final String actionName = domainObject.autoCompleteAction(); - - final Method repositoryMethod = findRepositoryMethod(cls, "@DomainObject", repositoryClass, actionName); - if(repositoryMethod == null) { - return null; + class Annot { + Annot(final DomainObject domainObject) { + this.autoCompleteRepository = domainObject.autoCompleteRepository(); + this.autoCompleteAction = domainObject.autoCompleteAction(); + } + Class<?> autoCompleteRepository; + String autoCompleteAction; + Method repositoryMethod; } - return new AutoCompleteFacetForDomainObjectAnnotation( - facetHolder, repositoryClass, repositoryMethod, servicesInjector); + return domainObjects.stream() + .map(domainObject -> new Annot(domainObject)) + .filter(a -> a.autoCompleteRepository != Object.class) - .filter(a -> isServiceType(cls, "@DomainObject", a.autoCompleteRepository)) + .peek(a -> a.repositoryMethod = findRepositoryMethod(cls, "@DomainObject", a.autoCompleteRepository, a.autoCompleteAction)) + .filter(a -> a.repositoryMethod != null) + .findFirst() + .map(a -> new AutoCompleteFacetForDomainObjectAnnotation( + facetHolder, a.autoCompleteRepository, a.repositoryMethod, servicesInjector)) + .orElse(null); } - private boolean isServiceType( - final Class<?> cls, - final String annotationName, - final Class<?> repositoryClass) { - final boolean isRegistered = servicesInjector.isRegisteredService(repositoryClass); - if(!isRegistered) { - autoCompleteInvalid.addFailure( - "%s annotation on %s specifies unknown repository '%s'", - annotationName, cls.getName(), repositoryClass.getName()); - } - return isRegistered; - } - private Method findRepositoryMethod( final Class<?> cls, final String annotationName, @@@ -321,23 -391,39 +315,37 @@@ } final FacetHolder holder = processClassContext.getFacetHolder(); - processLifecycleEventCreated(domainObject, holder); - processLifecycleEventLoaded(domainObject, holder); - processLifecycleEventPersisted(domainObject, holder); - processLifecycleEventPersisting(domainObject, holder); - processLifecycleEventRemoving(domainObject, holder); - processLifecycleEventUpdated(domainObject, holder); - processLifecycleEventUpdating(domainObject, holder); + processLifecycleEventCreated(domainObjects, holder); + processLifecycleEventLoaded(domainObjects, holder); + processLifecycleEventPersisted(domainObjects, holder); + processLifecycleEventPersisting(domainObjects, holder); + processLifecycleEventRemoving(domainObjects, holder); + processLifecycleEventUpdated(domainObjects, holder); + processLifecycleEventUpdating(domainObjects, holder); + } + + + private void processDomainEvents(final ProcessClassContext processClassContext) { + + final Class<?> cls = processClassContext.getCls(); - final DomainObject domainObject = Annotations.getAnnotation(cls, DomainObject.class); - if(domainObject == null) { ++ final List<DomainObject> domainObjects = Annotations.getAnnotations(cls, DomainObject.class); ++ if(domainObjects == null) { + return; + } + final FacetHolder holder = processClassContext.getFacetHolder(); - processDomainEventAction(domainObject, holder); - processDomainEventProperty(domainObject, holder); - processDomainEventCollection(domainObject, holder); ++ processDomainEventAction(domainObjects, holder); ++ processDomainEventProperty(domainObjects, holder); ++ processDomainEventCollection(domainObjects, holder); } - private void processLifecycleEventCreated(final DomainObject domainObject, final FacetHolder holder) { - final Class<? extends ObjectCreatedEvent<?>> lifecycleEvent = domainObject.createdLifecycleEvent(); + private void processLifecycleEventCreated(final List<DomainObject> domainObjects, final FacetHolder holder) { - final CreatedLifecycleEventFacetForDomainObjectAnnotation facet = - new CreatedLifecycleEventFacetForDomainObjectAnnotation( - holder, lifecycleEvent, getSpecificationLoader()); - - if(EventUtil.eventTypeIsPostable( - facet.getEventType(), + domainObjects.stream() + .map(DomainObject::createdLifecycleEvent) + .filter(lifecycleEvent -> + EventUtil.eventTypeIsPostable( + lifecycleEvent, ObjectCreatedEvent.Noop.class, ObjectCreatedEvent.Default.class, "isis.reflector.facet.domainObjectAnnotation.createdLifecycleEvent.postForDefault", @@@ -449,14 -529,44 +457,45 @@@ ObjectUpdatingEvent.Noop.class, ObjectUpdatingEvent.Default.class, "isis.reflector.facet.domainObjectAnnotation.updatingLifecycleEvent.postForDefault", - getConfiguration())) { - FacetUtil.addFacet(facet); - } + getConfiguration()) + ) + .findFirst() + .map(lifecycleEvent -> new UpdatingLifecycleEventFacetForDomainObjectAnnotation( + holder, lifecycleEvent, getSpecificationLoader())) + .ifPresent(FacetUtil::addFacet); } - private void processDomainEventAction(final DomainObject domainObject, final FacetHolder holder) { - final Class<? extends ActionDomainEvent<?>> domainEvent = domainObject.actionDomainEvent(); ++ private void processDomainEventAction(final List<DomainObject> domainObjects, final FacetHolder holder) { ++ domainObjects.stream() ++ .map(DomainObject::actionDomainEvent) ++ .filter(domainEvent -> domainEvent != ActionDomainEvent.Default.class) ++ .findFirst() ++ .map(domainEvent -> new ActionDomainEventDefaultFacetForDomainObjectAnnotation( ++ holder, domainEvent, getSpecificationLoader())) ++ .ifPresent(FacetUtil::addFacet); + - if(domainEvent != ActionDomainEvent.Default.class) { - final ActionDomainEventDefaultFacetForDomainObjectAnnotation facet = - new ActionDomainEventDefaultFacetForDomainObjectAnnotation( - holder, domainEvent, getSpecificationLoader()); - FacetUtil.addFacet(facet); - } + } + - private void processDomainEventProperty(final DomainObject domainObject, final FacetHolder holder) { - final Class<? extends PropertyDomainEvent<?,?>> domainEvent = domainObject.propertyDomainEvent(); - - if(domainEvent != PropertyDomainEvent.Default.class) { - final PropertyDomainEventDefaultFacetForDomainObjectAnnotation facet = - new PropertyDomainEventDefaultFacetForDomainObjectAnnotation( - holder, domainEvent, getSpecificationLoader()); - FacetUtil.addFacet(facet); - } ++ private void processDomainEventProperty(final List<DomainObject> domainObjects, final FacetHolder holder) { ++ domainObjects.stream() ++ .map(DomainObject::propertyDomainEvent) ++ .filter(domainEvent -> domainEvent != PropertyDomainEvent.Default.class) ++ .findFirst() ++ .map(domainEvent -> new PropertyDomainEventDefaultFacetForDomainObjectAnnotation( ++ holder, domainEvent, getSpecificationLoader())) ++ .ifPresent(FacetUtil::addFacet); + } + - private void processDomainEventCollection(final DomainObject domainObject, final FacetHolder holder) { - final Class<? extends CollectionDomainEvent<?,?>> domainEvent = domainObject.collectionDomainEvent(); - - if(domainEvent != CollectionDomainEvent.Default.class) { - final CollectionDomainEventDefaultFacetForDomainObjectAnnotation facet = - new CollectionDomainEventDefaultFacetForDomainObjectAnnotation( - holder, domainEvent, getSpecificationLoader()); - FacetUtil.addFacet(facet); - } ++ private void processDomainEventCollection(final List<DomainObject> domainObjects, final FacetHolder holder) { ++ domainObjects.stream() ++ .map(DomainObject::collectionDomainEvent) ++ .filter(domainEvent -> domainEvent != CollectionDomainEvent.Default.class) ++ .findFirst() ++ .map(domainEvent -> new CollectionDomainEventDefaultFacetForDomainObjectAnnotation( ++ holder, domainEvent, getSpecificationLoader())) ++ .ifPresent(FacetUtil::addFacet); + } + // ////////////////////////////////////// @Override @@@ -496,16 -606,36 +535,29 @@@ } validationFailures.add( "%s: cannot have two entities with same object type (@Discriminator, @DomainObject(objectType=...), @ObjectType or @PersistenceCapable(schema=...)); %s " + - "has same value (%s).", - existingSpec.getFullIdentifier(), - otherSpec.getFullIdentifier(), - objectSpecId); + "has same value (%s).", + existingSpec.getFullIdentifier(), + otherSpec.getFullIdentifier(), + objectSpecId); } + + final AutoCompleteFacet autoCompleteFacet = thisSpec.getFacet(AutoCompleteFacet.class); + if(autoCompleteFacet != null && !autoCompleteFacet.isNoop() && autoCompleteFacet instanceof AutoCompleteFacetAbstract) { + final AutoCompleteFacetAbstract facet = (AutoCompleteFacetForDomainObjectAnnotation) autoCompleteFacet; + final Class<?> repositoryClass = facet.getRepositoryClass(); + final boolean isRegistered = servicesInjector.isRegisteredService(repositoryClass); + if(!isRegistered) { + validationFailures.add( + "@DomainObject annotation on %s specifies unknown repository '%s'", + thisSpec.getFullIdentifier(), repositoryClass.getName()); + } + + } } })); - metaModelValidator.add(autoCompleteInvalid); - metaModelValidator.add(publishedObjectValidator); - metaModelValidator.add(auditedValidator); - metaModelValidator.add(autoCompleteValidator); - metaModelValidator.add(boundedValidator); - metaModelValidator.add(immutableValidator); - metaModelValidator.add(objectTypeValidator); - + metaModelValidator.add(autoCompleteMethodInvalid); metaModelValidator.add(mixinTypeValidator); } diff --cc core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/ActionDomainEventDefaultFacetForDomainObjectAnnotation.java index 0000000,2bb8aef..bc1ccf9 mode 000000,100644..100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/ActionDomainEventDefaultFacetForDomainObjectAnnotation.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/ActionDomainEventDefaultFacetForDomainObjectAnnotation.java @@@ -1,0 -1,54 +1,54 @@@ + /* + * 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.isis.core.metamodel.facets.object.domainobject.domainevents; + -import org.apache.isis.applib.services.eventbus.ActionDomainEvent; ++import org.apache.isis.applib.events.domain.ActionDomainEvent; + import org.apache.isis.core.metamodel.facetapi.Facet; + import org.apache.isis.core.metamodel.facetapi.FacetHolder; + import org.apache.isis.core.metamodel.facets.SingleClassValueFacetAbstract; + import org.apache.isis.core.metamodel.facets.actions.action.invocation.ActionDomainEventFacet; + import org.apache.isis.core.metamodel.specloader.SpecificationLoader; + + /** + * This does <i>NOT</i> implement {@link ActionDomainEventFacet}, rather it is to record the default type to use + * for any actions as a fallback/default. + */ + public class ActionDomainEventDefaultFacetForDomainObjectAnnotation + extends SingleClassValueFacetAbstract { + + + private final Class<? extends ActionDomainEvent<?>> eventType; + public Class<? extends ActionDomainEvent<?>> getEventType() { + return eventType; + } + + static Class<? extends Facet> type() { + return ActionDomainEventDefaultFacetForDomainObjectAnnotation.class; + } + + public ActionDomainEventDefaultFacetForDomainObjectAnnotation( + final FacetHolder holder, + final Class<? extends ActionDomainEvent<?>> value, + final SpecificationLoader specificationLoader) { + super(type(), holder, value, specificationLoader); + this.eventType = value; + } + + } diff --cc core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/CollectionDomainEventDefaultFacetForDomainObjectAnnotation.java index 0000000,627b09d..c92c5e2 mode 000000,100644..100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/CollectionDomainEventDefaultFacetForDomainObjectAnnotation.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/CollectionDomainEventDefaultFacetForDomainObjectAnnotation.java @@@ -1,0 -1,53 +1,53 @@@ + /* + * 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.isis.core.metamodel.facets.object.domainobject.domainevents; + -import org.apache.isis.applib.services.eventbus.CollectionDomainEvent; ++import org.apache.isis.applib.events.domain.CollectionDomainEvent; + import org.apache.isis.core.metamodel.facetapi.Facet; + import org.apache.isis.core.metamodel.facetapi.FacetHolder; + import org.apache.isis.core.metamodel.facets.SingleClassValueFacetAbstract; + import org.apache.isis.core.metamodel.facets.actions.action.invocation.ActionDomainEventFacet; + import org.apache.isis.core.metamodel.specloader.SpecificationLoader; + + /** + * This does <i>NOT</i> implement {@link ActionDomainEventFacet}, rather it is to record the default type to use + * for any actions as a fallback/default. + */ + public class CollectionDomainEventDefaultFacetForDomainObjectAnnotation + extends SingleClassValueFacetAbstract { + + private final Class<? extends CollectionDomainEvent<?, ?>> eventType; + public Class<? extends CollectionDomainEvent<?, ?>> getEventType() { + return eventType; + } + + static Class<? extends Facet> type() { + return CollectionDomainEventDefaultFacetForDomainObjectAnnotation.class; + } + + public CollectionDomainEventDefaultFacetForDomainObjectAnnotation( + final FacetHolder holder, + final Class<? extends CollectionDomainEvent<?,?>> value, + final SpecificationLoader specificationLoader) { + super(type(), holder, value, specificationLoader); + this.eventType = value; + } + + } diff --cc core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/PropertyDomainEventDefaultFacetForDomainObjectAnnotation.java index 0000000,4688bbd..86ea13c mode 000000,100644..100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/PropertyDomainEventDefaultFacetForDomainObjectAnnotation.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/object/domainobject/domainevents/PropertyDomainEventDefaultFacetForDomainObjectAnnotation.java @@@ -1,0 -1,53 +1,53 @@@ + /* + * 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.isis.core.metamodel.facets.object.domainobject.domainevents; + -import org.apache.isis.applib.services.eventbus.PropertyDomainEvent; ++import org.apache.isis.applib.events.domain.PropertyDomainEvent; + import org.apache.isis.core.metamodel.facetapi.Facet; + import org.apache.isis.core.metamodel.facetapi.FacetHolder; + import org.apache.isis.core.metamodel.facets.SingleClassValueFacetAbstract; + import org.apache.isis.core.metamodel.facets.actions.action.invocation.ActionDomainEventFacet; + import org.apache.isis.core.metamodel.specloader.SpecificationLoader; + + /** + * This does <i>NOT</i> implement {@link ActionDomainEventFacet}, rather it is to record the default type to use + * for any actions as a fallback/default. + */ + public class PropertyDomainEventDefaultFacetForDomainObjectAnnotation + extends SingleClassValueFacetAbstract { + + private final Class<? extends PropertyDomainEvent<?, ?>> eventType; + public Class<? extends PropertyDomainEvent<?, ?>> getEventType() { + return eventType; + } + + static Class<? extends Facet> type() { + return PropertyDomainEventDefaultFacetForDomainObjectAnnotation.class; + } + + public PropertyDomainEventDefaultFacetForDomainObjectAnnotation( + final FacetHolder holder, + final Class<? extends PropertyDomainEvent<?,?>> value, + final SpecificationLoader specificationLoader) { + super(type(), holder, value, specificationLoader); + this.eventType = value; + } + + } diff --cc core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java index df84463,d7869dc..7c5183a --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactory.java @@@ -68,8 -90,12 +69,9 @@@ import org.apache.isis.core.metamodel.f import org.apache.isis.core.metamodel.facets.properties.publish.PublishedPropertyFacet; import org.apache.isis.core.metamodel.facets.properties.update.clear.PropertyClearFacet; import org.apache.isis.core.metamodel.facets.properties.update.modify.PropertySetterFacet; -import org.apache.isis.core.metamodel.services.ServicesInjector; + import org.apache.isis.core.metamodel.spec.ObjectSpecification; import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorComposite; import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorForConflictingOptionality; -import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidatorForDeprecatedAnnotation; import org.apache.isis.core.metamodel.util.EventUtil; public class PropertyAnnotationFacetFactory extends FacetFactoryAbstract implements MetaModelValidatorRefiner { @@@ -110,18 -153,42 +119,18 @@@ // // Set up PropertyDomainEventFacet, which will act as the hiding/disabling/validating advisor // - final PostsPropertyChangedEvent postsPropertyChangedEvent = Annotations.getAnnotation(method, PostsPropertyChangedEvent.class); - final PropertyInteraction propertyInteraction = Annotations.getAnnotation(method, PropertyInteraction.class); - final Property property = Annotations.getAnnotation(method, Property.class); - final Class<? extends PropertyDomainEvent<?, ?>> propertyDomainEventType; - - final PropertyDomainEventFacetAbstract propertyDomainEventFacet; - - // can't really do this, because would result in the event being fired for the - // hidden/disable/validate phases, most likely breaking existing code. -// if(postsPropertyChangedEvent != null) { -// propertyDomainEventType = postsPropertyChangedEvent.value(); -// propertyDomainEventFacet = postsPropertyChangedEventValidator.flagIfPresent( -// new PropertyDomainEventFacetForPostsPropertyChangedEventAnnotation( -// propertyDomainEventType, getterFacet, servicesInjector, getSpecificationLoader(), holder)); -// } else - - // search for @PropertyInteraction(value=...) - if(propertyInteraction != null) { - propertyDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, propertyInteraction.value()); - propertyDomainEventFacet = propertyInteractionValidator.flagIfPresent( - new PropertyDomainEventFacetForPropertyInteractionAnnotation( - propertyDomainEventType, getterFacet, servicesInjector, getSpecificationLoader(), holder), processMethodContext); - } else - // search for @Property(domainEvent=...) - if(property != null) { - propertyDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, property.domainEvent()); - propertyDomainEventFacet = new PropertyDomainEventFacetForPropertyAnnotation( - propertyDomainEventType, getterFacet, servicesInjector, getSpecificationLoader(), holder); - - } else - // else use default event type - { - propertyDomainEventType = defaultFromDomainObjectIfRequired(typeSpec, PropertyDomainEvent.Default.class); - propertyDomainEventFacet = new PropertyDomainEventFacetDefault( - propertyDomainEventType, getterFacet, servicesInjector, getSpecificationLoader(), holder); - } + final List<Property> properties = Annotations.getAnnotations(method, Property.class); + + // search for @Property(domainEvent=...), else use default event type + final PropertyDomainEventFacetAbstract propertyDomainEventFacet = properties.stream() + .map(Property::domainEvent) + .filter(domainEvent -> domainEvent != PropertyDomainEvent.Default.class) + .findFirst() + .map(domainEvent -> (PropertyDomainEventFacetAbstract) new PropertyDomainEventFacetForPropertyAnnotation( - domainEvent, getterFacet, servicesInjector, getSpecificationLoader(), holder)) ++ defaultFromDomainObjectIfRequired(typeSpec, domainEvent), getterFacet, servicesInjector, getSpecificationLoader(), holder)) + .orElse(new PropertyDomainEventFacetDefault( - PropertyDomainEvent.Default.class, getterFacet, servicesInjector, getSpecificationLoader(), ++ defaultFromDomainObjectIfRequired(typeSpec, PropertyDomainEvent.Default.class), getterFacet, servicesInjector, getSpecificationLoader(), + holder)); if(EventUtil.eventTypeIsPostable( propertyDomainEventFacet.getEventType(), diff --cc core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetAbstract.java index 544da97,4b00586..9fbdd39 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetAbstract.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetAbstract.java @@@ -19,16 -19,17 +19,17 @@@ package org.apache.isis.core.metamodel.facets.properties.property.modify; - import org.apache.isis.applib.events.domain.AbstractDomainEvent; - import org.apache.isis.applib.events.domain.PropertyDomainEvent; import java.util.Map; + import org.apache.isis.applib.annotation.DomainObject; -import org.apache.isis.applib.events.UsabilityEvent; -import org.apache.isis.applib.events.ValidityEvent; -import org.apache.isis.applib.events.VisibilityEvent; -import org.apache.isis.applib.services.eventbus.AbstractDomainEvent; -import org.apache.isis.applib.services.eventbus.PropertyDomainEvent; ++import org.apache.isis.applib.events.domain.AbstractDomainEvent; ++import org.apache.isis.applib.events.domain.PropertyDomainEvent; import org.apache.isis.applib.services.i18n.TranslatableString; import org.apache.isis.applib.services.i18n.TranslationService; -import org.apache.isis.core.metamodel.adapter.ObjectAdapter; +import org.apache.isis.applib.services.wrapper.events.UsabilityEvent; +import org.apache.isis.applib.services.wrapper.events.ValidityEvent; +import org.apache.isis.applib.services.wrapper.events.VisibilityEvent; +import org.apache.isis.commons.internal.base._Casts; import org.apache.isis.core.metamodel.facetapi.FacetHolder; import org.apache.isis.core.metamodel.facetapi.IdentifiedHolder; import org.apache.isis.core.metamodel.facets.DomainEventHelper; @@@ -39,11 -40,11 +40,12 @@@ import org.apache.isis.core.metamodel.i import org.apache.isis.core.metamodel.interactions.ValidityContext; import org.apache.isis.core.metamodel.interactions.VisibilityContext; import org.apache.isis.core.metamodel.services.ServicesInjector; +import org.apache.isis.core.metamodel.spec.ManagedObject; import org.apache.isis.core.metamodel.specloader.SpecificationLoader; + import org.apache.isis.core.metamodel.specloader.specimpl.OneToOneAssociationMixedIn; public abstract class PropertyDomainEventFacetAbstract - extends SingleClassValueFacetAbstract implements PropertyDomainEventFacet { +extends SingleClassValueFacetAbstract implements PropertyDomainEventFacet { private final DomainEventHelper domainEventHelper; @@@ -51,30 -52,57 +53,53 @@@ private final TranslationService translationService; private final String translationContext; + /** + * @param getterFacetIfAny - will be null if this is for a mixin {@link OneToOneAssociationMixedIn}. + */ public PropertyDomainEventFacetAbstract( final Class<? extends PropertyDomainEvent<?, ?>> eventType, - final PropertyOrCollectionAccessorFacet getterFacet, - final PropertyOrCollectionAccessorFacet getterFacetIfAny, - final FacetHolder holder, - final ServicesInjector servicesInjector, - final SpecificationLoader specificationLoader) { ++ final PropertyOrCollectionAccessorFacet getterFacetIfAny, + final FacetHolder holder, + final ServicesInjector servicesInjector, + final SpecificationLoader specificationLoader) { super(PropertyDomainEventFacet.class, holder, eventType, specificationLoader); - this.getterFacet = getterFacet; + this.eventType = eventType; + this.getterFacetIfAny = getterFacetIfAny; - this.translationService = servicesInjector.lookupService(TranslationService.class); + this.translationService = servicesInjector.lookupService(TranslationService.class).orElse(null);; // sadness: same as in TranslationFactory this.translationContext = ((IdentifiedHolder)holder).getIdentifier().toClassAndNameIdentityString(); domainEventHelper = new DomainEventHelper(servicesInjector); } + private Class<? extends PropertyDomainEvent<?, ?>> eventType; + + @Override + public Class<?> value() { + return eventType; + } + - private Class<?> eventType() { - return eventType; - } - - public Class<? extends PropertyDomainEvent<?, ?>> getEventType() { - return eventType; ++ public <S, T> Class<? extends PropertyDomainEvent<S, T>> getEventType() { ++ return _Casts.uncheckedCast(eventType); + } + + /** + * Can be overwritten if this facet is on a mixin where the subject (mixedInType) is annotated with + * {@link DomainObject#propertyDomainEvent()}. + */ + public void setEventType(final Class<? extends PropertyDomainEvent<?, ?>> eventType) { + this.eventType = eventType; + } + @Override public String hides(VisibilityContext<? extends VisibilityEvent> ic) { final PropertyDomainEvent<?, ?> event = domainEventHelper.postEventForProperty( AbstractDomainEvent.Phase.HIDE, - eventType(), null, + getEventType(), null, - getIdentified(), ic.getTarget(), + getIdentified(), ic.getTarget(), ic.getMixedIn(), null, null); if (event != null && event.isHidden()) { return "Hidden by subscriber"; @@@ -88,8 -116,8 +113,8 @@@ final PropertyDomainEvent<?, ?> event = domainEventHelper.postEventForProperty( AbstractDomainEvent.Phase.DISABLE, - eventType(), null, + getEventType(), null, - getIdentified(), ic.getTarget(), + getIdentified(), ic.getTarget(), ic.getMixedIn(), null, null); if (event != null && event.isDisabled()) { final TranslatableString reasonTranslatable = event.getDisabledReasonTranslatable(); @@@ -104,15 -132,25 +129,25 @@@ @Override public String invalidates(ValidityContext<? extends ValidityEvent> ic) { - final Object oldValue = getterFacet.getProperty(ic.getTarget(), - ic.getInitiatedBy()); - final Object proposedValue = proposedFrom(ic); + if(getterFacetIfAny == null) { + return null; + } + + // if this is a mixin, then this ain't true. + if(!(ic instanceof ProposedHolder)) { + return null; + } + final ProposedHolder ph = (ProposedHolder) ic; + + final Object oldValue = getterFacetIfAny.getProperty(ic.getTarget(), ic.getInitiatedBy()); - final ObjectAdapter proposedAdapter = ph.getProposed(); - final Object proposedValue = proposedAdapter != null ? proposedAdapter.getObject() : null; ++ final ManagedObject proposedAdapter = ph.getProposed(); ++ final Object proposedValue = proposedAdapter != null ? proposedAdapter.getPojo() : null; final PropertyDomainEvent<?, ?> event = domainEventHelper.postEventForProperty( AbstractDomainEvent.Phase.VALIDATE, - eventType(), null, + getEventType(), null, - getIdentified(), ic.getTarget(), + getIdentified(), ic.getTarget(), ic.getMixedIn(), oldValue, proposedValue); if (event != null && event.isInvalid()) { final TranslatableString reasonTranslatable = event.getInvalidityReasonTranslatable(); @@@ -125,19 -163,10 +160,11 @@@ return null; } - private static Object proposedFrom(ValidityContext<? extends ValidityEvent> ic) { - final ProposedHolder ph = (ProposedHolder) ic; - final ManagedObject proposedAdapter = ph.getProposed(); - return proposedAdapter != null? proposedAdapter.getPojo(): null; - } - - public <S, T> Class<? extends PropertyDomainEvent<S, T>> getEventType() { - return _Casts.uncheckedCast(value()); - } + - @Override public void appendAttributesTo(final Map<String, Object> attributeMap) { + @Override + public void appendAttributesTo(final Map<String, Object> attributeMap) { super.appendAttributesTo(attributeMap); - attributeMap.put("getterFacet", getterFacet); + attributeMap.put("getterFacet", getterFacetIfAny); } } diff --cc core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetDefault.java index acab6fc,f228dbd..3482ef6 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetDefault.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetDefault.java @@@ -27,11 -28,14 +28,14 @@@ import org.apache.isis.core.metamodel.s public class PropertyDomainEventFacetDefault extends PropertyDomainEventFacetAbstract { + /** + * @param getterFacetIfAny - will be null if this is for a mixin {@link OneToOneAssociationMixedIn}. + */ public PropertyDomainEventFacetDefault( final Class<? extends PropertyDomainEvent<?, ?>> eventType, - final PropertyOrCollectionAccessorFacet getterFacet, - final PropertyOrCollectionAccessorFacet getterFacetIfAny, - final ServicesInjector servicesInjector, final SpecificationLoader specificationLoader, final FacetHolder holder) { ++ final PropertyOrCollectionAccessorFacet getterFacetIfAny, + final ServicesInjector servicesInjector, final SpecificationLoader specificationLoader, final FacetHolder holder) { - super(eventType, getterFacet, holder, servicesInjector, specificationLoader); + super(eventType, getterFacetIfAny, holder, servicesInjector, specificationLoader); } } diff --cc core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPropertyAnnotation.java index f9361a4,b731d35..c1b81ed --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPropertyAnnotation.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertyDomainEventFacetForPropertyAnnotation.java @@@ -27,11 -28,14 +28,14 @@@ import org.apache.isis.core.metamodel.s public class PropertyDomainEventFacetForPropertyAnnotation extends PropertyDomainEventFacetAbstract { + /** + * @param getterFacetIfAny - will be null if this is for a mixin {@link OneToOneAssociationMixedIn}. + */ public PropertyDomainEventFacetForPropertyAnnotation( final Class<? extends PropertyDomainEvent<?, ?>> eventType, - final PropertyOrCollectionAccessorFacet getterFacet, - final PropertyOrCollectionAccessorFacet getterFacetIfAny, - final ServicesInjector servicesInjector, final SpecificationLoader specificationLoader, final FacetHolder holder) { ++ final PropertyOrCollectionAccessorFacet getterFacetIfAny, + final ServicesInjector servicesInjector, final SpecificationLoader specificationLoader, final FacetHolder holder) { - super(eventType, getterFacet, holder, servicesInjector, specificationLoader); + super(eventType, getterFacetIfAny, holder, servicesInjector, specificationLoader); } diff --cc core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertySetterOrClearFacetForDomainEventAbstract.java index 7a723bc,1fd5017..f11728f --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertySetterOrClearFacetForDomainEventAbstract.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertySetterOrClearFacetForDomainEventAbstract.java @@@ -19,14 -19,11 +19,12 @@@ package org.apache.isis.core.metamodel.facets.properties.property.modify; - import static org.apache.isis.commons.internal.base._Casts.uncheckedCast; import java.sql.Timestamp; import java.util.Map; - +import java.util.Objects; -import com.google.common.base.Objects; - +import org.apache.isis.applib.events.domain.AbstractDomainEvent; +import org.apache.isis.applib.events.domain.PropertyDomainEvent; import org.apache.isis.applib.services.clock.ClockService; import org.apache.isis.applib.services.command.Command; import org.apache.isis.applib.services.command.CommandContext; @@@ -50,10 -49,11 +48,12 @@@ import org.apache.isis.core.metamodel.s import org.apache.isis.core.metamodel.services.persistsession.PersistenceSessionServiceInternal; import org.apache.isis.core.metamodel.services.publishing.PublishingServiceInternal; import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation; -import org.apache.isis.core.runtime.system.transaction.TransactionalClosure; import org.apache.isis.schema.ixn.v1.PropertyEditDto; ++import static org.apache.isis.commons.internal.base._Casts.uncheckedCast; ++ public abstract class PropertySetterOrClearFacetForDomainEventAbstract - extends SingleValueFacetAbstract<Class<? extends PropertyDomainEvent<?,?>>> { +extends SingleValueFacetAbstract<Class<? extends PropertyDomainEvent<?,?>>> { private final DomainEventHelper domainEventHelper; @@@ -161,9 -164,17 +162,10 @@@ final ObjectAdapter newValueAdapter, final InteractionInitiatedBy interactionInitiatedBy) { + final ObjectAdapter mixedInAdapter = null; - getPersistenceSessionServiceInternal().executeWithinTransaction( - new TransactionalClosure(){ - @Override - public void execute() { - doSetOrClearProperty(style, owningProperty, targetAdapter, mixedInAdapter, newValueAdapter, interactionInitiatedBy); - } - } - ); - - + getPersistenceSessionServiceInternal().executeWithinTransaction(()->{ - doSetOrClearProperty(style, owningProperty, targetAdapter, newValueAdapter, interactionInitiatedBy); ++ doSetOrClearProperty(style, owningProperty, targetAdapter, mixedInAdapter, newValueAdapter, interactionInitiatedBy); + }); } @@@ -213,72 -225,72 +216,71 @@@ new Interaction.PropertyEdit(interaction, propertyId, target, argValue, targetMember, targetClass); final Interaction.MemberExecutor<Interaction.PropertyEdit> executor = new Interaction.MemberExecutor<Interaction.PropertyEdit>() { - @Override - public Object execute(final Interaction.PropertyEdit currentExecution) { - - try { + @Override + public Object execute(final Interaction.PropertyEdit currentExecution) { - // update the current execution with the DTO (memento) - final PropertyEditDto editDto = - getInteractionDtoServiceInternal().asPropertyEditDto( - owningProperty, targetAdapter, newValueAdapter); - currentExecution.setDto(editDto); + try { + // update the current execution with the DTO (memento) + final PropertyEditDto editDto = + getInteractionDtoServiceInternal().asPropertyEditDto( + owningProperty, targetAdapter, newValueAdapter); + currentExecution.setDto(editDto); - // set the startedAt (and update command if this is the top-most member execution) - // (this isn't done within Interaction#execute(...) because it requires the DTO - // to have been set on the current execution). - final Timestamp startedAt = getClockService().nowAsJavaSqlTimestamp(); - execution.setStartedAt(startedAt); - if(command.getStartedAt() == null) { - command.setStartedAt(startedAt); - } - // ... post the executing event - final Object oldValue = getterFacet.getProperty(targetAdapter, interactionInitiatedBy); - final Object newValue = ObjectAdapter.Util.unwrap(newValueAdapter); + // set the startedAt (and update command if this is the top-most member execution) + // (this isn't done within Interaction#execute(...) because it requires the DTO + // to have been set on the current execution). + final Timestamp startedAt = getClockService().nowAsJavaSqlTimestamp(); + execution.setStartedAt(startedAt); + if(command.getStartedAt() == null) { + command.internal().setStartedAt(startedAt); + } - final PropertyDomainEvent<?, ?> event = - domainEventHelper.postEventForProperty( - AbstractDomainEvent.Phase.EXECUTING, - eventType(), null, - getIdentified(), targetAdapter, mixedInAdapter, - oldValue, newValue); + // ... post the executing event + final Object oldValue = getterFacet.getProperty(targetAdapter, interactionInitiatedBy); + final Object newValue = ObjectAdapter.Util.unwrapPojo(newValueAdapter); + final PropertyDomainEvent<?, ?> event = + domainEventHelper.postEventForProperty( + AbstractDomainEvent.Phase.EXECUTING, + getEventType(), null, - getIdentified(), targetAdapter, ++ getIdentified(), targetAdapter, mixedInAdapter, + oldValue, newValue); - - // set event onto the execution - currentExecution.setEvent(event); + // set event onto the execution + currentExecution.setEvent(event); - // invoke method - style.invoke(PropertySetterOrClearFacetForDomainEventAbstract.this, owningProperty, - targetAdapter, newValueAdapter, interactionInitiatedBy); + // invoke method + style.invoke(PropertySetterOrClearFacetForDomainEventAbstract.this, owningProperty, + targetAdapter, newValueAdapter, interactionInitiatedBy); - // reading the actual value from the target object, playing it safe... - final Object actualNewValue = getterFacet.getProperty(targetAdapter, interactionInitiatedBy); - if (!Objects.equal(oldValue, actualNewValue)) { + // reading the actual value from the target object, playing it safe... + final Object actualNewValue = getterFacet.getProperty(targetAdapter, interactionInitiatedBy); + if (!Objects.equals(oldValue, actualNewValue)) { - // ... post the executed event - domainEventHelper.postEventForProperty( - AbstractDomainEvent.Phase.EXECUTED, - eventType(), verify(event), - getIdentified(), targetAdapter, mixedInAdapter, - oldValue, actualNewValue); - } + // ... post the executed event + domainEventHelper.postEventForProperty( + AbstractDomainEvent.Phase.EXECUTED, - getEventType(), uncheckedCast((PropertyDomainEvent<?, ?>)event), - getIdentified(), targetAdapter, ++ getEventType(), uncheckedCast(event), ++ getIdentified(), targetAdapter, mixedInAdapter, + oldValue, actualNewValue); + } - return null; + return null; - // - // REVIEW: the corresponding action has a whole bunch of error handling here. - // we probably should do something similar... - // + // + // REVIEW: the corresponding action has a whole bunch of error handling here. + // we probably should do something similar... + // - } finally { + } finally { - } - } - }; + } + } + }; // sets up startedAt and completedAt on the execution, also manages the execution call graph interaction.execute(executor, execution); diff --cc core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/param/DeriveFacetsPostProcessor.java index f2addb9,8c390b2..35b2d09 --- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/param/DeriveFacetsPostProcessor.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/postprocessors/param/DeriveFacetsPostProcessor.java @@@ -19,20 -19,35 +19,33 @@@ package org.apache.isis.core.metamodel.postprocessors.param; + import java.lang.reflect.Method; +import java.util.Collections; import java.util.List; - -import com.google.common.collect.FluentIterable; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; +import java.util.stream.Stream; + import org.apache.isis.applib.annotation.Collection; + import org.apache.isis.applib.annotation.Property; -import org.apache.isis.applib.filter.Filters; -import org.apache.isis.applib.services.eventbus.ActionDomainEvent; -import org.apache.isis.applib.services.eventbus.CollectionDomainEvent; -import org.apache.isis.applib.services.eventbus.PropertyDomainEvent; ++import org.apache.isis.applib.events.domain.ActionDomainEvent; ++import org.apache.isis.applib.events.domain.CollectionDomainEvent; ++import org.apache.isis.applib.events.domain.PropertyDomainEvent; +import org.apache.isis.commons.internal.collections._Lists; +import org.apache.isis.commons.internal.context._Context; import org.apache.isis.core.commons.authentication.AuthenticationSessionProvider; -import org.apache.isis.core.metamodel.deployment.DeploymentCategory; -import org.apache.isis.core.metamodel.deployment.DeploymentCategoryProvider; +import org.apache.isis.core.metamodel.adapter.ObjectAdapterProvider; import org.apache.isis.core.metamodel.facetapi.Facet; import org.apache.isis.core.metamodel.facetapi.FacetUtil; + import org.apache.isis.core.metamodel.facets.Annotations; import org.apache.isis.core.metamodel.facets.FacetedMethod; import org.apache.isis.core.metamodel.facets.TypedHolder; + import org.apache.isis.core.metamodel.facets.actions.action.invocation.ActionDomainEventFacet; + import org.apache.isis.core.metamodel.facets.actions.action.invocation.ActionDomainEventFacetAbstract; import org.apache.isis.core.metamodel.facets.actions.defaults.ActionDefaultsFacet; import org.apache.isis.core.metamodel.facets.all.describedas.DescribedAsFacet; + import org.apache.isis.core.metamodel.facets.collections.collection.CollectionAnnotationFacetFactory; + import org.apache.isis.core.metamodel.facets.collections.collection.modify.CollectionDomainEventFacet; + import org.apache.isis.core.metamodel.facets.collections.collection.modify.CollectionDomainEventFacetAbstract; + import org.apache.isis.core.metamodel.facets.collections.collection.modify.CollectionDomainEventFacetForCollectionAnnotation; import org.apache.isis.core.metamodel.facets.collections.disabled.fromimmutable.DisabledFacetOnCollectionDerivedFromImmutable; import org.apache.isis.core.metamodel.facets.collections.disabled.fromimmutable.DisabledFacetOnCollectionDerivedFromImmutableFactory; import org.apache.isis.core.metamodel.facets.members.describedas.annotprop.DescribedAsFacetOnMemberDerivedFromType; @@@ -90,9 -117,11 +114,10 @@@ import org.apache.isis.core.metamodel.s public class DeriveFacetsPostProcessor implements ObjectSpecificationPostProcessor, ServicesInjectorAware { - private DeploymentCategoryProvider deploymentCategoryProvider; private SpecificationLoader specificationLoader; private AuthenticationSessionProvider authenticationSessionProvider; - private PersistenceSessionServiceInternal adapterManager; + private ObjectAdapterProvider adapterProvider; + private ServicesInjector servicesInjector; @Override public void postProcess(final ObjectSpecification objectSpecification) { @@@ -127,10 -157,11 +152,12 @@@ // corresponds to CopyImmutableFacetOntoMembersFactory. However, ImmutableFacet only ever disables for // properties and collections, so no point in copying over. + tweakActionDomainEventForMixin(objectSpecification, objectAction); - } ++ + }); // for each property, ... - for (final OneToOneAssociation property : properties) { + properties.forEach(property -> { derivePropertyChoicesFromExistingChoices(property); derivePropertyDefaultsFromType(property); derivePropertyTypicalLengthFromType(property); @@@ -138,7 -169,8 +165,8 @@@ derivePropertyDisabledFromViewModel(property); derivePropertyOrCollectionImmutableFromSpec(property); derivePropertyDisabledFromImmutable(property); + tweakPropertyMixinDomainEvent(objectSpecification, property); - } + }); // for each collection, ... @@@ -193,11 -225,113 +221,113 @@@ for (final ObjectActionParameter scalarParam : compatibleScalarParams) { addCollectionParamChoicesFacetIfNoneAlready(collection, scalarParam); } - } - + }); + deriveCollectionDomainEventForMixins(objectSpecification, collection); - } + }); + } + private void tweakActionDomainEventForMixin( + final ObjectSpecification objectSpecification, + final ObjectAction objectAction) { + if(objectAction instanceof ObjectActionMixedIn) { + // unlike collection and property mixins, there is no need to create the DomainEventFacet, it will + // have been created in the ActionAnnotationFacetFactory + final ActionDomainEventDefaultFacetForDomainObjectAnnotation actionDomainEventDefaultFacet = + objectSpecification.getFacet(ActionDomainEventDefaultFacetForDomainObjectAnnotation.class); + + if(actionDomainEventDefaultFacet != null) { + final ObjectActionMixedIn actionMixedIn = (ObjectActionMixedIn) objectAction; + final ActionDomainEventFacet actionFacet = actionMixedIn.getFacet(ActionDomainEventFacet.class); + if (actionFacet instanceof ActionDomainEventFacetAbstract) { + final ActionDomainEventFacetAbstract facetAbstract = (ActionDomainEventFacetAbstract) actionFacet; + if (facetAbstract.getEventType() == ActionDomainEvent.Default.class) { + final ActionDomainEventFacetAbstract existing = (ActionDomainEventFacetAbstract) actionFacet; + existing.setEventType(actionDomainEventDefaultFacet.getEventType()); + } + } + } + } + } + + private void deriveCollectionDomainEventForMixins( + final ObjectSpecification objectSpecification, + final OneToManyAssociation collection) { + + if(collection instanceof OneToManyAssociationMixedIn) { + final OneToManyAssociationMixedIn collectionMixin = (OneToManyAssociationMixedIn) collection; + final FacetedMethod facetedMethod = collectionMixin.getFacetedMethod(); + final Method method = facetedMethod != null ? facetedMethod.getMethod() : null; + + if(method != null) { + // this is basically a subset of the code that is in CollectionAnnotationFacetFactory, + // ignoring stuff which is deprecated for Isis v2 + final Collection collectionAnnot = Annotations.getAnnotation(method, Collection.class); + if(collectionAnnot != null) { + final Class<? extends CollectionDomainEvent<?, ?>> collectionDomainEventType = + CollectionAnnotationFacetFactory.defaultFromDomainObjectIfRequired( + objectSpecification, collectionAnnot.domainEvent()); + final CollectionDomainEventFacetForCollectionAnnotation collectionDomainEventFacet = new CollectionDomainEventFacetForCollectionAnnotation( + collectionDomainEventType, servicesInjector, specificationLoader, collection); + FacetUtil.addFacet(collectionDomainEventFacet); + } + + final CollectionDomainEventDefaultFacetForDomainObjectAnnotation collectionDomainEventDefaultFacet = + objectSpecification.getFacet(CollectionDomainEventDefaultFacetForDomainObjectAnnotation.class); + if(collectionDomainEventDefaultFacet != null) { + final CollectionDomainEventFacet collectionFacet = collection.getFacet(CollectionDomainEventFacet.class); + if (collectionFacet instanceof CollectionDomainEventFacetAbstract) { + final CollectionDomainEventFacetAbstract facetAbstract = (CollectionDomainEventFacetAbstract) collectionFacet; + if (facetAbstract.getEventType() == CollectionDomainEvent.Default.class) { + final CollectionDomainEventFacetAbstract existing = (CollectionDomainEventFacetAbstract) collectionFacet; + existing.setEventType(collectionDomainEventDefaultFacet.getEventType()); + } + } + } + } + } + } + + private void tweakPropertyMixinDomainEvent( + final ObjectSpecification objectSpecification, + final OneToOneAssociation property) { + + if(property instanceof OneToOneAssociationMixedIn) { + final OneToOneAssociationMixedIn propertyMixin = (OneToOneAssociationMixedIn) property; + final FacetedMethod facetedMethod = propertyMixin.getFacetedMethod(); + final Method method = facetedMethod != null ? facetedMethod.getMethod() : null; + + if(method != null) { + // this is basically a subset of the code that is in CollectionAnnotationFacetFactory, + // ignoring stuff which is deprecated for Isis v2 + final Property propertyAnnot = Annotations.getAnnotation(method, Property.class); + if(propertyAnnot != null) { + final Class<? extends PropertyDomainEvent<?, ?>> propertyDomainEventType = + PropertyAnnotationFacetFactory.defaultFromDomainObjectIfRequired( + objectSpecification, propertyAnnot.domainEvent()); + final PropertyOrCollectionAccessorFacet getterFacetIfAny = null; + final PropertyDomainEventFacetForPropertyAnnotation propertyDomainEventFacet = + new PropertyDomainEventFacetForPropertyAnnotation( + propertyDomainEventType, getterFacetIfAny, + servicesInjector, specificationLoader, property); + FacetUtil.addFacet(propertyDomainEventFacet); + } + } + final PropertyDomainEventDefaultFacetForDomainObjectAnnotation propertyDomainEventDefaultFacet = + objectSpecification.getFacet(PropertyDomainEventDefaultFacetForDomainObjectAnnotation.class); + if(propertyDomainEventDefaultFacet != null) { + final PropertyDomainEventFacet propertyFacet = property.getFacet(PropertyDomainEventFacet.class); + if (propertyFacet instanceof PropertyDomainEventFacetAbstract) { + final PropertyDomainEventFacetAbstract facetAbstract = (PropertyDomainEventFacetAbstract) propertyFacet; + if (facetAbstract.getEventType() == PropertyDomainEvent.Default.class) { + final PropertyDomainEventFacetAbstract existing = (PropertyDomainEventFacetAbstract) propertyFacet; + existing.setEventType(propertyDomainEventDefaultFacet.getEventType()); + } + } + } + } + } + static DisabledFacetAbstract.Semantics inferSemanticsFrom(final ViewModelFacet facet) { return facet.isImplicitlyImmutable() ? DisabledFacetAbstract.Semantics.DISABLED : @@@ -491,9 -626,19 +621,11 @@@ @Override public void setServicesInjector(final ServicesInjector servicesInjector) { + this.servicesInjector = servicesInjector; - deploymentCategoryProvider = servicesInjector.getDeploymentCategoryProvider(); specificationLoader = servicesInjector.getSpecificationLoader(); authenticationSessionProvider = servicesInjector.getAuthenticationSessionProvider(); - adapterManager = servicesInjector.getPersistenceSessionServiceInternal(); - } - - private ServicesInjector getServicesInjector() { - return servicesInjector; + adapterProvider = servicesInjector.getPersistenceSessionServiceInternal(); } + } diff --cc core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest.java index 5ff7b55,cac11d7..fdd281e --- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest.java +++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest.java @@@ -65,6 -87,7 +65,7 @@@ import org.apache.isis.core.metamodel.f import org.apache.isis.core.metamodel.facets.actions.semantics.ActionSemanticsFacet; import org.apache.isis.core.metamodel.facets.actions.semantics.ActionSemanticsFacetAbstract; import org.apache.isis.core.metamodel.facets.all.hide.HiddenFacet; ++import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.ActionDomainEventDefaultFacetForDomainObjectAnnotation; import org.apache.isis.core.metamodel.spec.ObjectSpecification; import static org.apache.isis.core.commons.matchers.IsisMatchers.classEqualTo; @@@ -115,6 -135,12 +116,9 @@@ public class ActionAnnotationFacetFacto allowing(mockServicesInjector).getConfigurationServiceInternal(); will(returnValue(mockConfiguration)); - allowing(mockServicesInjector).lookupService(DeploymentCategoryProvider.class); - will(returnValue(mockDeploymentCategoryProvider)); - - allowing(mockDeploymentCategoryProvider).getDeploymentCategory(); - will(returnValue(DeploymentCategory.PRODUCTION)); ++ allowing(mockTypeSpec).getFacet(ActionDomainEventDefaultFacetForDomainObjectAnnotation.class); ++ will(returnValue(null)); + }}); actionMethod = findMethod(Customer.class, "someAction"); diff --cc core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactoryTest_typeOf.java index a8ffda1,5687eba..6b99c27 --- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactoryTest_typeOf.java +++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/collections/collection/CollectionAnnotationFacetFactoryTest_typeOf.java @@@ -21,13 -21,16 +21,17 @@@ package org.apache.isis.core.metamodel. import java.lang.reflect.Method; import java.util.Collection; -import java.util.List; -import java.util.Set; -import org.apache.isis.applib.annotation.TypeOf; + ++import org.jmock.Expectations; ++import org.jmock.auto.Mock; ++ import org.apache.isis.core.metamodel.facetapi.Facet; import org.apache.isis.core.metamodel.facets.AbstractFacetFactoryTest; import org.apache.isis.core.metamodel.facets.FacetFactory.ProcessMethodContext; import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacet; import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacetInferredFromArray; import org.apache.isis.core.metamodel.facets.actcoll.typeof.TypeOfFacetInferredFromGenerics; -import org.apache.isis.core.metamodel.facets.collections.collection.typeof.TypeOfFacetOnCollectionFromTypeOfAnnotation; ++import org.apache.isis.core.metamodel.spec.ObjectSpecification; public class CollectionAnnotationFacetFactoryTest_typeOf extends AbstractFacetFactoryTest { @@@ -58,7 -108,7 +62,12 @@@ } final Method actionMethod = findMethod(Customer.class, "someAction"); - facetFactory.process(new ProcessMethodContext(Customer.class, null, null, actionMethod, methodRemover, facetedMethod)); ++ context.checking(new Expectations() {{ ++ allowing(mockSpecificationLoader).loadSpecification(Customer.class); ++ will(returnValue(mockCustomerSpec)); ++ }}); ++ + facetFactory.process(new ProcessMethodContext(Customer.class, null, actionMethod, methodRemover, facetedMethod)); final Facet facet = facetedMethod.getFacet(TypeOfFacet.class); assertNotNull(facet); @@@ -77,9 -127,9 +86,15 @@@ return null; } } ++ ++ context.checking(new Expectations() {{ ++ allowing(mockSpecificationLoader).loadSpecification(Customer.class); ++ will(returnValue(mockCustomerSpec)); ++ }}); ++ final Method collectionAccessorMethod = findMethod(Customer.class, "getOrders"); - facetFactory.process(new ProcessMethodContext(Customer.class, null, null, collectionAccessorMethod, methodRemover, facetedMethod)); + facetFactory.process(new ProcessMethodContext(Customer.class, null, collectionAccessorMethod, methodRemover, facetedMethod)); final Facet facet = facetedMethod.getFacet(TypeOfFacet.class); assertNotNull(facet); @@@ -89,6 -139,100 +104,8 @@@ } - public void testTypeOfFacetInferredForActionWithJavaUtilListReturnType() { - class Order { - } - class Customer { - // rawTypes is intentional here - @SuppressWarnings({ "unused", "rawtypes" }) - @TypeOf(Order.class) - public List someAction() { - return null; - } - } - final Method actionMethod = findMethod(Customer.class, "someAction"); - - facetFactory.process(new ProcessMethodContext(Customer.class, null, null, actionMethod, methodRemover, facetedMethod)); - - final Facet facet = facetedMethod.getFacet(TypeOfFacet.class); - assertNotNull(facet); - assertTrue(facet instanceof TypeOfFacetOnCollectionFromTypeOfAnnotation); - final TypeOfFacetOnCollectionFromTypeOfAnnotation typeOfFacetOnCollectionAnnotation = (TypeOfFacetOnCollectionFromTypeOfAnnotation) facet; - assertEquals(Order.class, typeOfFacetOnCollectionAnnotation.value()); - - assertNoMethodsRemoved(); - } - - public void testTypeOfFacetInferredForCollectionWithJavaUtilListReturnType() { - class Order { - } - class Customer { - // rawTypes is intentional here - @SuppressWarnings({ "unused", "rawtypes" }) - @TypeOf(Order.class) - public List getOrders() { - return null; - } - } - final Method accessorMethod = findMethod(Customer.class, "getOrders"); - - facetFactory.process(new ProcessMethodContext(Customer.class, null, null, accessorMethod, methodRemover, facetedMethod)); - - final Facet facet = facetedMethod.getFacet(TypeOfFacet.class); - assertNotNull(facet); - assertTrue(facet instanceof TypeOfFacetOnCollectionFromTypeOfAnnotation); - final TypeOfFacetOnCollectionFromTypeOfAnnotation typeOfFacetOnCollectionAnnotation = (TypeOfFacetOnCollectionFromTypeOfAnnotation) facet; - assertEquals(Order.class, typeOfFacetOnCollectionAnnotation.value()); - - assertNoMethodsRemoved(); - } - - public void testTypeOfFacetInferredForActionWithJavaUtilSetReturnType() { - class Order { - } - class Customer { - @SuppressWarnings("unused") - @TypeOf(Order.class) - public Set someAction() { - return null; - } - } - final Method actionMethod = findMethod(Customer.class, "someAction"); - - facetFactory.process(new ProcessMethodContext(Customer.class, null, null, actionMethod, methodRemover, facetedMethod)); - - final Facet facet = facetedMethod.getFacet(TypeOfFacet.class); - assertNotNull(facet); - assertTrue(facet instanceof TypeOfFacetOnCollectionFromTypeOfAnnotation); - final TypeOfFacetOnCollectionFromTypeOfAnnotation typeOfFacetOnCollectionAnnotation = (TypeOfFacetOnCollectionFromTypeOfAnnotation) facet; - assertEquals(Order.class, typeOfFacetOnCollectionAnnotation.value()); - - assertNoMethodsRemoved(); - } - - public void testTypeOfFacetInferredForCollectionWithJavaUtilSetReturnType() { - class Order { - } - class Customer { - @SuppressWarnings("unused") - @TypeOf(Order.class) - public Set getOrders() { - return null; - } - } - final Method accessorMethod = findMethod(Customer.class, "getOrders"); - - facetFactory.process(new ProcessMethodContext(Customer.class, null, null, accessorMethod, methodRemover, facetedMethod)); - - final Facet facet = facetedMethod.getFacet(TypeOfFacet.class); - assertNotNull(facet); - assertTrue(facet instanceof TypeOfFacetOnCollectionFromTypeOfAnnotation); - final TypeOfFacetOnCollectionFromTypeOfAnnotation typeOfFacetOnCollectionAnnotation = (TypeOfFacetOnCollectionFromTypeOfAnnotation) facet; - assertEquals(Order.class, typeOfFacetOnCollectionAnnotation.value()); - - assertNoMethodsRemoved(); - } - ++ @Mock ++ ObjectSpecification mockCustomerSpec; public void testTypeOfFacetIsInferredForCollectionFromOrderArray() { class Order { @@@ -101,7 -245,7 +118,12 @@@ } final Method collectionAccessorMethod = findMethod(Customer.class, "getOrders"); - facetFactory.process(new ProcessMethodContext(Customer.class, null, null, collectionAccessorMethod, methodRemover, facetedMethod)); ++ context.checking(new Expectations() {{ ++ allowing(mockSpecificationLoader).loadSpecification(Customer.class); ++ will(returnValue(mockCustomerSpec)); ++ }}); ++ + facetFactory.process(new ProcessMethodContext(Customer.class, null, collectionAccessorMethod, methodRemover, facetedMethod)); final Facet facet = facetedMethod.getFacet(TypeOfFacet.class); assertNotNull(facet); diff --cc core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactoryTest.java index 1c1fb43,2d487c9..26ab6c7 --- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactoryTest.java +++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactoryTest.java @@@ -44,6 -49,6 +44,7 @@@ import org.apache.isis.core.metamodel.f import org.apache.isis.core.metamodel.facets.FacetFactory; import org.apache.isis.core.metamodel.facets.all.hide.HiddenFacet; import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacet; ++import org.apache.isis.core.metamodel.facets.object.domainobject.domainevents.PropertyDomainEventDefaultFacetForDomainObjectAnnotation; import org.apache.isis.core.metamodel.facets.objectvalue.mandatory.MandatoryFacet; import org.apache.isis.core.metamodel.facets.objectvalue.maxlen.MaxLengthFacet; import org.apache.isis.core.metamodel.facets.objectvalue.mustsatisfyspec.MustSatisfySpecificationFacet; @@@ -179,14 -190,13 +180,17 @@@ public class PropertyAnnotationFacetFac // expect allowingLoadSpecificationRequestsFor(cls, propertyMethod.getReturnType()); context.checking(new Expectations() {{ - oneOf(mockConfiguration).getBoolean("isis.reflector.facet.propertyAnnotation.domainEvent.postForDefault", true); - will(returnValue(true)); - }}); + //[ahuber] never called during this test ... + //oneOf(mockConfiguration).getBoolean("isis.reflector.facet.propertyAnnotation.domainEvent.postForDefault", true); + //will(returnValue(true)); + ++ allowing(mockTypeSpec).getFacet(PropertyDomainEventDefaultFacetForDomainObjectAnnotation.class); ++ will(returnValue(null)); + }}); // when - final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null, null, propertyMethod, mockMethodRemover, facetedMethod); + final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null, + propertyMethod, mockMethodRemover, facetedMethod); facetFactory.processModify(processMethodContext); // then @@@ -237,9 -254,8 +241,14 @@@ // expect allowingLoadSpecificationRequestsFor(cls, propertyMethod.getReturnType()); ++ context.checking(new Expectations() {{ ++ allowing(mockTypeSpec).getFacet(PropertyDomainEventDefaultFacetForDomainObjectAnnotation.class); ++ will(returnValue(null)); ++ }}); ++ // when - final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null, null, propertyMethod, mockMethodRemover, facetedMethod); + final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null, + propertyMethod, mockMethodRemover, facetedMethod); facetFactory.processModify(processMethodContext); // then @@@ -289,9 -312,8 +298,13 @@@ // expect allowingLoadSpecificationRequestsFor(cls, propertyMethod.getReturnType()); ++ context.checking(new Expectations() {{ ++ allowing(mockTypeSpec).getFacet(PropertyDomainEventDefaultFacetForDomainObjectAnnotation.class); ++ will(returnValue(null)); ++ }}); // when - final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null, null, propertyMethod, mockMethodRemover, facetedMethod); + final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null, + propertyMethod, mockMethodRemover, facetedMethod); facetFactory.processModify(processMethodContext); // then @@@ -340,6 -362,6 +353,9 @@@ context.checking(new Expectations() {{ oneOf(mockConfiguration).getBoolean("isis.reflector.facet.propertyAnnotation.domainEvent.postForDefault", true); will(returnValue(true)); ++ ++ allowing(mockTypeSpec).getFacet(PropertyDomainEventDefaultFacetForDomainObjectAnnotation.class); ++ will(returnValue(null)); }}); // when diff --cc example/application/simpleapp/application/src/test/java/domainapp/application/integtests/smoke/Smoke_IntegTest.java index 326ea11,326ea11..69e0dae --- a/example/application/simpleapp/application/src/test/java/domainapp/application/integtests/smoke/Smoke_IntegTest.java +++ b/example/application/simpleapp/application/src/test/java/domainapp/application/integtests/smoke/Smoke_IntegTest.java @@@ -77,6 -77,6 +77,7 @@@ public class Smoke_IntegTest extends Do // when wrap(fred).setNotes("These are some notes"); ++ transactionService.flushTransaction(); // then assertThat(wrap(fred).getNotes()).isEqualTo("These are some notes");