Skip to content

Commit

Permalink
[sre] Refactor the event dispatching to a specific and provided liste…
Browse files Browse the repository at this point in the history
…ner.

This refactoring enables to fire an event into the provided listener
even if it was not registered to on the event bus. It enables to
propagate event to objects without receiving events that are fired to
all the registered listeners.

close #1052

Signed-off-by: Stéphane Galland <galland@arakhne.org>
  • Loading branch information
gallandarakhneorg committed Mar 5, 2021
1 parent c5457d1 commit 2c68ed1
Show file tree
Hide file tree
Showing 17 changed files with 293 additions and 180 deletions.
Expand Up @@ -24,13 +24,14 @@ package io.sarl.sre.internal.eventguard
import io.sarl.lang.core.Event
import io.sarl.sre.internal.MutableBoolean
import io.sarl.sre.internal.ObjectComparator
import io.sarl.util.ConcurrentCollection
import io.sarl.util.ConcurrentCollectionLinkedDeque
import io.sarl.util.ConcurrentSet
import io.sarl.util.ConcurrentSetSkipListSet
import java.lang.reflect.Method
import java.util.Set
import java.util.TreeSet
import java.util.concurrent.ConcurrentLinkedDeque
import java.util.concurrent.ConcurrentMap
import java.util.concurrent.CopyOnWriteArraySet

import static extension io.sarl.sre.internal.eventguard.reflect.StaticReflectBehaviorGuardEvaluatorDictionary.*

Expand All @@ -40,41 +41,63 @@ import static extension io.sarl.sre.internal.eventguard.reflect.StaticReflectBeh
*
* <p>This class is thread-safe.
*
* @param <T> the type of stored data.
* <p>Two types of listeners are supported:<ul>
* <li><i>Registered listener</i>: it is registered with the function of the same name.
* It is notified when an event should be provided to anyone;</li>
* <li><i>Direct-access listener</i>: it is not registered (if it is, it does not matter).
* It is notified when it is passed as argument of a notification function. In this
* case, only this listener will receive the event.</li>
* </ul>
*
* @param <REGT> the type of stored data for registered listeners.
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
abstract class AbstractBehaviorGuardEvaluatorRegistry<T> implements IBehaviorGuardEvaluatorRegistry {
abstract class AbstractBehaviorGuardEvaluatorRegistry<REGT> implements IBehaviorGuardEvaluatorRegistry {

/** Replies the internal data structure.
/** Replies the internal data structure for the registered listeners.
*
* @return the internal data structure
*/
@Pure
abstract def getInternalDataStructure : ConcurrentMap<Class<? extends Event>, CopyOnWriteArraySet<T>>
abstract def getInternalDataStructureForRegisteredListeners : ConcurrentMap<Class<? extends Event>, ConcurrentSet<REGT>>

/** Replies the mapper from the internal guard evaluator description to the public description.
/** Replies the mapper from the internal guard evaluator description of register listeners to the public description.
*
* @return the internal data structure
*/
@Pure
protected abstract def getInternalEvaluatorMapper : (T)=>IBehaviorGuardEvaluator
protected abstract def getInternalEvaluatorMapperForRegisteredListeners : (REGT)=>IBehaviorGuardEvaluator

/** Replies the mapper from the internal guard evaluator description of direct-access listeners to the public description.
*
* @param listener the listener for which the guard evaluator should be replied.
* @return the internal data structure
* @since 0.12
*/
@Pure
protected def getInternalEvaluatorMapperForDirectAccessListeners(listener : Object) : (Method)=>IBehaviorGuardEvaluator {
[
new ReflectBehaviorGuardEvaluator(listener, it)
]
}

/** Replies the filtering of evaluator.
*
* @return the filter
*/
@Pure
protected abstract def getInternalEvaluatorFilter : (T, Event)=>boolean
protected abstract def getInternalEvaluatorFilterForRegisteredListeners : (REGT, Event)=>boolean

override unregisterAll(callback : (Object)=>boolean) : void {
val removedListeners = new TreeSet(ObjectComparator::SINGLETON)
val dt = internalDataStructureForRegisteredListeners
if (callback !== null) {
val hasCallback = new MutableBoolean(true)
val iterator0 = internalDataStructure.values.iterator
val mapper = getInternalEvaluatorMapper
val iterator0 = dt.values.iterator
val mapper = getInternalEvaluatorMapperForRegisteredListeners
while (hasCallback.get && iterator0.hasNext) {
val iterator1 = iterator0.next.iterator
while (hasCallback.get && iterator1.hasNext) {
Expand All @@ -90,15 +113,15 @@ abstract class AbstractBehaviorGuardEvaluatorRegistry<T> implements IBehaviorGua
}
}
// TODO: Is it the most efficient way to clear the map?
internalDataStructure.clear
dt.clear
}

override unregister(listenerType : Class<?>, callback : (Object)=>boolean) {
assert listenerType !== null
val notifiedListeners = new TreeSet(ObjectComparator::SINGLETON)
val hasCallback = new MutableBoolean(callback !== null)
val mapper = getInternalEvaluatorMapper
for (guardedEvaluators : internalDataStructure.values) {
val mapper = getInternalEvaluatorMapperForRegisteredListeners
for (guardedEvaluators : internalDataStructureForRegisteredListeners.values) {
guardedEvaluators.removeIf [
val target = mapper.apply(it).target
assert target !== null
Expand All @@ -123,8 +146,8 @@ abstract class AbstractBehaviorGuardEvaluatorRegistry<T> implements IBehaviorGua
@Pure
override hasRegisteredEventListener(type : Class<?>) : boolean {
if (type !== null) {
val mapper = getInternalEvaluatorMapper
for (guardedEvaluators : internalDataStructure.values) {
val mapper = getInternalEvaluatorMapperForRegisteredListeners
for (guardedEvaluators : internalDataStructureForRegisteredListeners.values) {
for (evaluator : guardedEvaluators) {
if (type.isInstance(mapper.apply(evaluator).target)) {
return true
Expand All @@ -136,14 +159,15 @@ abstract class AbstractBehaviorGuardEvaluatorRegistry<T> implements IBehaviorGua
}

@Pure
override getBehaviorGuardEvaluators(^event : Event) : ConcurrentLinkedDeque<? extends IBehaviorGuardEvaluator> {
override getBehaviorGuardEvaluatorsForRegisteredListeners(^event : Event) : ConcurrentCollection<? extends IBehaviorGuardEvaluator> {
assert ^event !== null
val eventTypes = ^event.class.flattenHierarchy
val allEvaluators : ConcurrentLinkedDeque<IBehaviorGuardEvaluator> = new ConcurrentLinkedDeque
val mapper = getInternalEvaluatorMapper
val filter = getInternalEvaluatorFilter
val allEvaluators = new ConcurrentCollectionLinkedDeque
val mapper = getInternalEvaluatorMapperForRegisteredListeners
val filter = getInternalEvaluatorFilterForRegisteredListeners
val dt = internalDataStructureForRegisteredListeners
for (eventType : eventTypes) {
val eventSubscribers = internalDataStructure.get(eventType)
val eventSubscribers = dt.get(eventType)
if (eventSubscribers !== null) {
for (guardedEvaluator : eventSubscribers) {
if (filter.apply(guardedEvaluator, ^event)) {
Expand All @@ -155,34 +179,12 @@ abstract class AbstractBehaviorGuardEvaluatorRegistry<T> implements IBehaviorGua
return allEvaluators
}

@Pure
override getBehaviorGuardEvaluatorsFor(^event : Event,
listener : Object) : ConcurrentLinkedDeque<? extends IBehaviorGuardEvaluator> {
assert ^event !== null
assert listener !== null
val allEvaluators : ConcurrentLinkedDeque<IBehaviorGuardEvaluator> = new ConcurrentLinkedDeque
val eventTypes = ^event.class.flattenHierarchy
val mapper = getInternalEvaluatorMapper
for (eventType : eventTypes) {
val eventSubscribers = internalDataStructure.get(eventType)
if (eventSubscribers !== null) {
for (guardedEvaluator : eventSubscribers) {
val target = mapper.apply(guardedEvaluator).target
if (target === listener) {
allEvaluators += mapper.apply(guardedEvaluator)
}
}
}
}
return allEvaluators
}

override getRegisteredEventListeners(type : Class<TT>, collection : Set<? super TT>) : int with TT {
assert type !== null
assert collection !== null
var nb = 0
val mapper = getInternalEvaluatorMapper
for (guardedEvaluators : internalDataStructure.values) {
val mapper = getInternalEvaluatorMapperForRegisteredListeners
for (guardedEvaluators : internalDataStructureForRegisteredListeners.values) {
for (evaluator : guardedEvaluators) {
val target = mapper.apply(evaluator).target
if (type.isInstance(target)) {
Expand Down
Expand Up @@ -24,9 +24,9 @@ package io.sarl.sre.internal.eventguard
import io.sarl.lang.core.Event
import io.sarl.sre.internal.ObjectComparator
import io.sarl.util.ConcurrentSet
import io.sarl.util.ConcurrentSetSkipListSet
import java.util.Set
import java.util.concurrent.ConcurrentLinkedDeque
import io.sarl.util.ConcurrentCollection
import io.sarl.util.ConcurrentSetSkipListSet

/**
* Registry of all {@code IBehaviorGuardEvaluator}.
Expand Down Expand Up @@ -111,7 +111,8 @@ interface IBehaviorGuardEvaluatorRegistry {
* @return the set of guard evaluators associated to the specified event
*/
@Pure
def getBehaviorGuardEvaluators(^event : Event) : ConcurrentLinkedDeque<? extends IBehaviorGuardEvaluator>
def getBehaviorGuardEvaluatorsForRegisteredListeners(^event : Event) : ConcurrentCollection<? extends IBehaviorGuardEvaluator>

/**
* Gets an iterator representing an immutable snapshot of all BehaviorGuardEvaluators of the given listener
* to the given event at the time this method is called.
Expand All @@ -125,7 +126,7 @@ interface IBehaviorGuardEvaluatorRegistry {
* @since 0.5
*/
@Pure
def getBehaviorGuardEvaluatorsFor(^event : Event, listener : Object) : ConcurrentLinkedDeque<? extends IBehaviorGuardEvaluator>
def getBehaviorGuardEvaluatorsForDirectAccessListener(^event : Event, listener : Object) : ConcurrentCollection<? extends IBehaviorGuardEvaluator>

/** Extract the registered listeners with the given type.
*
Expand Down
Expand Up @@ -19,7 +19,7 @@
* limitations under the License.
*/

package io.sarl.sre.internal.eventguard.reflect
package io.sarl.sre.internal.eventguard

import org.eclipse.osgi.util.NLS

Expand All @@ -39,8 +39,8 @@ final class Messages extends NLS {
NLS.initializeMessages(BUNDLE_NAME, typeof(Messages))
}

public static var BehaviorGuardEvaluator_0 : String
public static var BehaviorGuardEvaluator_1 : String
public static var ReflectBehaviorGuardEvaluator_0 : String
public static var ReflectBehaviorGuardEvaluator_1 : String

private new {
}
Expand Down
Expand Up @@ -19,7 +19,7 @@
* limitations under the License.
*/

package io.sarl.sre.internal.eventguard.reflect
package io.sarl.sre.internal.eventguard

import com.google.common.base.Strings
import io.sarl.sre.IssueCodes
Expand Down Expand Up @@ -69,10 +69,10 @@ class ReflectBehaviorGuardEvaluator implements IBehaviorGuardEvaluator {
this.method.invoke(this.target, ^event, behaviorsMethodsToExecute)
} catch (e : IllegalArgumentException) {
throw new Error(
MessageFormat::format(Messages::BehaviorGuardEvaluator_0, IssueCodes::EVENT_GUARD_ERROR, ^event), e)
MessageFormat::format(Messages::ReflectBehaviorGuardEvaluator_0, IssueCodes::EVENT_GUARD_ERROR, ^event), e)
} catch (e : IllegalAccessException) {
throw new Error(
MessageFormat::format(Messages::BehaviorGuardEvaluator_1, IssueCodes::EVENT_GUARD_ERROR, ^event), e)
MessageFormat::format(Messages::ReflectBehaviorGuardEvaluator_1, IssueCodes::EVENT_GUARD_ERROR, ^event), e)
} catch (e : InvocationTargetException) {
if (e.cause instanceof Error) {
throw e.cause as Error
Expand Down
@@ -0,0 +1,2 @@
ReflectBehaviorGuardEvaluator_0=Invalid argument for the event guard evaluator method: {1} [{0}]
ReflectBehaviorGuardEvaluator_1=Event guard evaluator method becomes inaccessible: {1} [{0}]
Expand Up @@ -58,9 +58,9 @@ class PolymorphicBehaviorGuardEvaluator implements IBehaviorGuardEvaluator {
/** Creates a {@code Subscriber}.
*
* @param target the listener
* @param filter is the filter to apply when emitting; or {@code null} if no filter.
* @param filter the filter
*/
new (target : IBehaviorGuardEvaluatorReceiver, filter : (Event)=>boolean) {
new (target : IBehaviorGuardEvaluatorReceiver, filter : (Event) => boolean) {
assert target !== null
this.target = target
this.filter = filter
Expand Down
Expand Up @@ -28,12 +28,15 @@ import io.sarl.lang.core.IBehaviorGuardEvaluatorReceiver
import io.sarl.sre.internal.MutableBoolean
import io.sarl.sre.internal.eventguard.AbstractBehaviorGuardEvaluatorRegistry
import io.sarl.sre.internal.eventguard.IBehaviorGuardEvaluator
import io.sarl.util.ConcurrentCollection
import io.sarl.util.ConcurrentCollectionLinkedDeque
import io.sarl.util.ConcurrentSet
import io.sarl.util.ConcurrentSetCopyOnWriteSet
import java.util.Comparator
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArraySet
import java.util.concurrent.ConcurrentMap

import static extension io.sarl.lang.core.SREutils.*
import java.util.Comparator
import java.util.concurrent.ConcurrentMap

/**
* Registry of all {@code IBehaviorGuardEvaluator}.
Expand Down Expand Up @@ -63,11 +66,8 @@ class PolymorphicBehaviorGuardEvaluatorRegistry extends AbstractBehaviorGuardEva

/**
* All registered guard evaluator receivers, indexed by event type.
*
* <p>The {@link CopyOnWriteArraySet} values make it easy and relatively lightweight to get an immutable snapshot of all current
* receivers to an event without any locking.
*/
val evaluators : ConcurrentMap<Class<? extends Event>, CopyOnWriteArraySet<PolymorphicBehaviorGuardEvaluator>>
val registeredEvaluators : ConcurrentMap<Class<? extends Event>, ConcurrentSet<PolymorphicBehaviorGuardEvaluator>>

/**
* Instanciates a new registry linked with the {@link PerceptGuardEvaluator} annotation.
Expand All @@ -77,31 +77,44 @@ class PolymorphicBehaviorGuardEvaluatorRegistry extends AbstractBehaviorGuardEva
* @param internalMap the internal map.
*/
new {
this.evaluators = new ConcurrentHashMap
this.registeredEvaluators = new ConcurrentHashMap
}

/** Replies the internal data structure.
/** Replies the internal data structure for registered listeners.
*
* @return the internal data structure
*/
@Pure
override getInternalDataStructure : ConcurrentMap<Class<? extends Event>, CopyOnWriteArraySet<PolymorphicBehaviorGuardEvaluator>> {
this.evaluators
override getInternalDataStructureForRegisteredListeners : ConcurrentMap<Class<? extends Event>, ConcurrentSet<PolymorphicBehaviorGuardEvaluator>> {
this.registeredEvaluators
}

protected override getInternalEvaluatorMapper : (PolymorphicBehaviorGuardEvaluator)=>IBehaviorGuardEvaluator {
protected override getInternalEvaluatorMapperForRegisteredListeners : (PolymorphicBehaviorGuardEvaluator)=>IBehaviorGuardEvaluator {
[
it
]
}

protected override getInternalEvaluatorFilter : (PolymorphicBehaviorGuardEvaluator, Event)=>boolean {
protected override getInternalEvaluatorFilterForRegisteredListeners : (PolymorphicBehaviorGuardEvaluator, Event)=>boolean {
[
val filter = $0.filter
return filter === null || filter.apply($1)
]
}

@Pure
override getBehaviorGuardEvaluatorsForDirectAccessListener(^event : Event, listener : Object) : ConcurrentCollection<? extends IBehaviorGuardEvaluator> {
assert ^event !== null
assert listener !== null
val allEvaluators = new ConcurrentCollectionLinkedDeque
if (listener instanceof IBehaviorGuardEvaluatorReceiver) {
if (listener.doIsSupportedEvent(^event.class)) {
allEvaluators += new PolymorphicBehaviorGuardEvaluator(listener, null)
}
}
return allEvaluators
}

override register(listener : Object, filter : (Event)=>boolean, callback : (Object)=>void = null) {
if (listener instanceof IBehaviorGuardEvaluatorReceiver) {
val hasCallback = new MutableBoolean(callback !== null)
Expand All @@ -111,9 +124,10 @@ class PolymorphicBehaviorGuardEvaluatorRegistry extends AbstractBehaviorGuardEva
listener.doGetSupportedEvents(events)

if (!events.isEmpty) {
val dt = internalDataStructureForRegisteredListeners
for (eventType : events) {
val evaluators = internalDataStructure.computeIfAbsent(eventType) [
new CopyOnWriteArraySet
val evaluators = dt.computeIfAbsent(eventType) [
new ConcurrentSetCopyOnWriteSet
]
val success = evaluators += evaluator
if (!success) {
Expand All @@ -139,8 +153,9 @@ class PolymorphicBehaviorGuardEvaluatorRegistry extends AbstractBehaviorGuardEva
val hasCallback = new MutableBoolean(callback !== null)

if (!events.isEmpty) {
val dt = internalDataStructureForRegisteredListeners
for (eventType : events) {
val evaluators = internalDataStructure.get(eventType)
val evaluators = dt.get(eventType)
if (evaluators !== null) {
val success = evaluators.removeIf [
listener === it.target
Expand Down
Expand Up @@ -23,6 +23,7 @@ package io.sarl.sre.internal.eventguard.reflect

import io.sarl.lang.core.Event
import org.eclipse.xtend.lib.annotations.Data
import io.sarl.sre.internal.eventguard.ReflectBehaviorGuardEvaluator

/**
* Description of a set of guard evaluators with a shared activation guard.
Expand Down

0 comments on commit 2c68ed1

Please sign in to comment.