= WrapperFactory
:Notice: 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.

Provides the ability to 'wrap' a domain object such that it can be interacted with while enforcing the hide/disable/validate rules implied by the Apache Causeway programming model.

This capability goes beyond enforcing the (imperative) constraints within the `hideXxx()`, `disableXxx()` and `validateXxx()` supporting methods; it also enforces (declarative) constraints such as those represented by annotations, eg `@Parameter(maxLength=...)` or `@Property(mustSatisfy=...)`.

The wrapper can alternatively also be used to execute the action asynchronously, through an _java.util.concurrent.ExecutorService_ . Any business rules will be invoked synchronously beforehand, however.

The 'wrap' is a runtime-code-generated proxy that wraps the underlying domain object. The wrapper can then be interacted with as follows:

* a `get` method for properties or collections
* a `set` method for properties
* any action

Calling any of the above methods may result in a (subclass of) _InteractionException_ if the object disallows it. For example, if a property is annotated as hidden then a xref:refguide:applib:index/services/wrapper/HiddenException.adoc[HiddenException] will be thrown. Similarly if an action has a `validate` method and the supplied arguments are invalid then a xref:refguide:applib:index/services/wrapper/InvalidException.adoc[InvalidException] will be thrown.

In addition, the following methods may also be called:

* the `title` method
* any `defaultXxx` or `choicesXxx` method

If the object has (see _#isWrapper(Object)_ already been wrapped), then should just return the object back unchanged.

== API

[source,java]
.WrapperFactory.java
----
interface WrapperFactory {
  T wrap(T domainObject, SyncControl syncControl)     // <.>
  T wrap(T domainObject)     // <.>
  T wrapMixin(Class<T> mixinClass, Object mixee, SyncControl syncControl)     // <.>
  T wrapMixin(Class<T> mixinClass, Object mixee)     // <.>
  T wrapMixinT(Class<T> mixinClass, MIXEE mixee, SyncControl syncControl)     // <.>
  T wrapMixinT(Class<T> mixinClass, MIXEE mixee)     // <.>
  T unwrap(T possibleWrappedDomainObject)     // <.>
  boolean isWrapper(T possibleWrappedDomainObject)     // <.>
  AsyncProxy<T> asyncWrap(T domainObject, AsyncControl asyncControl)     // <.>
  AsyncProxy<T> asyncWrap(T domainObject)     // <.>
  AsyncProxy<T> asyncWrapMixin(Class<T> mixinClass, Object mixee, AsyncControl asyncControl)     // <.>
  AsyncProxy<T> asyncWrapMixin(Class<T> mixinClass, Object mixee)     // <.>
  List<InteractionListener> getListeners()     // <.>
  boolean addInteractionListener(InteractionListener listener)     // <.>
  boolean removeInteractionListener(InteractionListener listener)     // <.>
  void notifyListeners(InteractionEvent ev)
}
----

<.> xref:#wrap_T_SyncControl[wrap(T, SyncControl)]
+
--
Provides the'wrapper' of a domain object against which to invoke the action.
--
<.> xref:#wrap_T[wrap(T)]
+
--
A convenience overload for _#wrap(Object, SyncControl)_ with _SyncControl.defaults()_ .
--
<.> xref:#wrapMixin_Class_Object_SyncControl[wrapMixin(Class, Object, SyncControl)]
+
--
Provides the wrapper for a _FactoryService#mixin(Class, Object) mixin_ , against which to invoke the action.
--
<.> xref:#wrapMixin_Class_Object[wrapMixin(Class, Object)]
+
--
A convenience overload for _#wrapMixin(Class, Object, SyncControl)_ with _SyncControl.defaults()_ .
--
<.> xref:#wrapMixinT_Class_MIXEE_SyncControl[wrapMixinT(Class, MIXEE, SyncControl)]
+
--
Provides the wrapper for a _Mixin typesafe_ _FactoryService#mixin(Class, Object) mixin_ , against which to invoke the action.
--
<.> xref:#wrapMixinT_Class_MIXEE[wrapMixinT(Class, MIXEE)]
+
--
A convenience overload for _#wrapMixinT(Class, Object, SyncControl)_ , returning a wrapper to invoke the action synchronously, enforcing business rules. Any exceptions will be propagated, not swallowed.
--
<.> xref:#unwrap_T[unwrap(T)]
+
--
Obtains the underlying domain object, if wrapped.
--
<.> xref:#isWrapper_T[isWrapper(T)]
+
--
Whether the supplied object is a wrapper around a domain object.
--
<.> xref:#asyncWrap_T_AsyncControl[asyncWrap(T, AsyncControl)]
+
--
Returns a _CompletableFuture_ holding a proxy object for the provided _domainObject_ , through which one can execute the action asynchronously (in another thread).
--
<.> xref:#asyncWrap_T[asyncWrap(T)]
+
--
A convenience overload for _#asyncWrap(Object, AsyncControl)_ with _AsyncControl.defaults()_ .
--
<.> xref:#asyncWrapMixin_Class_Object_AsyncControl[asyncWrapMixin(Class, Object, AsyncControl)]
+
--
Returns a _AsyncProxy_ holding a proxy object for the provided _mixinClass_ , through which one can execute the action asynchronously (in another thread).
--
<.> xref:#asyncWrapMixin_Class_Object[asyncWrapMixin(Class, Object)]
+
--
A convenience overload for _#asyncWrapMixin(Class, Object, AsyncControl)_ with _AsyncControl.defaults()_ .
--
<.> xref:#getListeners_[getListeners()]
+
--
All xref:refguide:applib:index/services/wrapper/listeners/InteractionListener.adoc[InteractionListener] s that have been registered using _#addInteractionListener(InteractionListener)_ .
--
<.> xref:#addInteractionListener_InteractionListener[addInteractionListener(InteractionListener)]
+
--
Registers an xref:refguide:applib:index/services/wrapper/listeners/InteractionListener.adoc[InteractionListener] , to be notified of interactions on all wrappers.
--
<.> xref:#removeInteractionListener_InteractionListener[removeInteractionListener(InteractionListener)]
+
--
Remove an xref:refguide:applib:index/services/wrapper/listeners/InteractionListener.adoc[InteractionListener] , to no longer be notified of interactions on wrappers.
--

== Members

[#wrap_T_SyncControl]
=== wrap(T, SyncControl)

Provides the'wrapper' of a domain object against which to invoke the action.

The provided xref:refguide:applib:index/services/wrapper/control/SyncControl.adoc[SyncControl] determines whether business rules are checked first, and conversely whether the action is executed. There are therefore three typical cases:

* check rules, execute action
* skip rules, execute action
* check rules, skip action

The last logical option (skip rules, skip action) is valid but doesn't make sense, as it's basically a no-op.

Otherwise, will do all the validations (raise exceptions as required etc.), but doesn't modify the model.

Any exceptions will be propagated, not swallowed.

[#wrap_T]
=== wrap(T)

A convenience overload for _#wrap(Object, SyncControl)_ with _SyncControl.defaults()_ .

[#wrapMixin_Class_Object_SyncControl]
=== wrapMixin(Class, Object, SyncControl)

Provides the wrapper for a _FactoryService#mixin(Class, Object) mixin_ , against which to invoke the action.

The provided xref:refguide:applib:index/services/wrapper/control/SyncControl.adoc[SyncControl] determines whether business rules are checked first, and conversely whether the action is executed.

Any exceptions will be propagated, not swallowed.

[#wrapMixin_Class_Object]
=== wrapMixin(Class, Object)

A convenience overload for _#wrapMixin(Class, Object, SyncControl)_ with _SyncControl.defaults()_ .

[#wrapMixinT_Class_MIXEE_SyncControl]
=== wrapMixinT(Class, MIXEE, SyncControl)

Provides the wrapper for a _Mixin typesafe_ _FactoryService#mixin(Class, Object) mixin_ , against which to invoke the action.

The provided xref:refguide:applib:index/services/wrapper/control/SyncControl.adoc[SyncControl] determines whether business rules are checked first, and conversely whether the action is executed. See _#wrap(Object, SyncControl)_ for more details on this.

[#wrapMixinT_Class_MIXEE]
=== wrapMixinT(Class, MIXEE)

A convenience overload for _#wrapMixinT(Class, Object, SyncControl)_ , returning a wrapper to invoke the action synchronously, enforcing business rules. Any exceptions will be propagated, not swallowed.

[#unwrap_T]
=== unwrap(T)

Obtains the underlying domain object, if wrapped.

If the object _#isWrapper(Object) is not wrapped_ , then should just return the object back unchanged.

[#isWrapper_T]
=== isWrapper(T)

Whether the supplied object is a wrapper around a domain object.

[#asyncWrap_T_AsyncControl]
=== asyncWrap(T, AsyncControl)

Returns a _CompletableFuture_ holding a proxy object for the provided _domainObject_ , through which one can execute the action asynchronously (in another thread).

[#asyncWrap_T]
=== asyncWrap(T)

A convenience overload for _#asyncWrap(Object, AsyncControl)_ with _AsyncControl.defaults()_ .

[#asyncWrapMixin_Class_Object_AsyncControl]
=== asyncWrapMixin(Class, Object, AsyncControl)

Returns a _AsyncProxy_ holding a proxy object for the provided _mixinClass_ , through which one can execute the action asynchronously (in another thread).

[#asyncWrapMixin_Class_Object]
=== asyncWrapMixin(Class, Object)

A convenience overload for _#asyncWrapMixin(Class, Object, AsyncControl)_ with _AsyncControl.defaults()_ .

[#getListeners_]
=== getListeners()

All xref:refguide:applib:index/services/wrapper/listeners/InteractionListener.adoc[InteractionListener] s that have been registered using _#addInteractionListener(InteractionListener)_ .

[#addInteractionListener_InteractionListener]
=== addInteractionListener(InteractionListener)

Registers an xref:refguide:applib:index/services/wrapper/listeners/InteractionListener.adoc[InteractionListener] , to be notified of interactions on all wrappers.

This is retrospective: the listener will be notified of interactions even on wrappers created before the listener was installed. (From an implementation perspective this is because the wrappers delegate back to the container to fire the events).

[#removeInteractionListener_InteractionListener]
=== removeInteractionListener(InteractionListener)

Remove an xref:refguide:applib:index/services/wrapper/listeners/InteractionListener.adoc[InteractionListener] , to no longer be notified of interactions on wrappers.

This is retrospective: the listener will no longer be notified of any interactions created on any wrappers, not just on those wrappers created subsequently. (From an implementation perspective this is because the wrappers delegate back to the container to fire the events).

include::hooks/WrapperFactory_010-implementation.adoc[]

include::hooks/WrapperFactory_020-examples-and-usage.adoc[]
