From 0a557fa6df31c6105f910077ad8a5e54dbe620ff Mon Sep 17 00:00:00 2001 From: Yuriy Artamonov Date: Tue, 20 Mar 2018 15:16:15 +0400 Subject: [PATCH] New event publishing mechanism with Consumer #327 --- .../haulmont/bali/events/EventPublisher.java | 157 ++++++++++++++++++ .../haulmont/bali/events/Subscription.java | 27 +++ 2 files changed, 184 insertions(+) create mode 100644 modules/global/src/com/haulmont/bali/events/EventPublisher.java create mode 100644 modules/global/src/com/haulmont/bali/events/Subscription.java diff --git a/modules/global/src/com/haulmont/bali/events/EventPublisher.java b/modules/global/src/com/haulmont/bali/events/EventPublisher.java new file mode 100644 index 0000000000..15de5d6c2b --- /dev/null +++ b/modules/global/src/com/haulmont/bali/events/EventPublisher.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2008-2018 Haulmont. + * + * Licensed 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 com.haulmont.bali.events; + +import javax.annotation.concurrent.NotThreadSafe; +import java.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +/** + * EventPublisher class implementing the event listening model with concrete event classes. + */ +@NotThreadSafe +public class EventPublisher { + protected static final int EVENTS_MAP_EXPECTED_MAX_SIZE = 4; + protected static final int EVENTS_LIST_INITIAL_CAPACITY = 2; + + // Map with listener classes and listener lists + // Lists are created on demand + protected Map> events = null; + + /** + * Add an event listener for events with type T. + * + * @param eventType event class + * @param listener listener + * @param type of event + * @return registration object + */ + public Subscription subscribe(Class eventType, Consumer listener) { + if (eventType == null) { + throw new IllegalArgumentException("eventType cannot be null"); + } + if (listener == null) { + throw new IllegalArgumentException("listener cannot be null"); + } + + if (events == null) { + events = new IdentityHashMap<>(EVENTS_MAP_EXPECTED_MAX_SIZE); + } + + List listeners = events.computeIfAbsent(eventType, + eventClass -> new ArrayList<>(EVENTS_LIST_INITIAL_CAPACITY)); + if (!listeners.contains(listener)) { + listeners.add(listener); + } + + return new SubscriptionImpl<>(this, eventType, listener); + } + + /** + * Remove an event listener for events with type T. + * + * @param eventType event class + * @param listener listener + * @param type of event + * @return true if listener has been removed + */ + public boolean unsubscribe(Class eventType, Consumer listener) { + if (eventType == null) { + throw new IllegalArgumentException("eventType cannot be null"); + } + if (listener == null) { + throw new IllegalArgumentException("listener cannot be null"); + } + + if (events != null) { + List listenersList = events.get(eventType); + if (listenersList != null) { + boolean wasRemoved = listenersList.remove(listener); + if (listenersList.isEmpty()) { + events.remove(eventType); + } + return wasRemoved; + } + return false; + } + return false; + } + + /** + * Check if there are listeners for event type T. + * + * @param eventType event class + * @param type of event + * @return true if there are one or more listeners for type T + */ + public boolean hasListeners(Class eventType) { + if (eventType == null) { + throw new IllegalArgumentException("eventType cannot be null"); + } + + return events != null + && events.get(eventType) != null; + } + + /** + * Fire listeners for event type T. + * + * @param eventType event class + * @param event event object + * @param type of event + */ + public void publish(Class eventType, T event) { + if (eventType == null) { + throw new IllegalArgumentException("eventType cannot be null"); + } + if (event == null) { + throw new IllegalArgumentException("event cannot be null"); + } + + if (events != null) { + @SuppressWarnings("unchecked") + List listeners = events.get(eventType); + if (listeners != null) { + for (Object listenerEntry : listeners.toArray()) { + @SuppressWarnings("unchecked") + Consumer listener = (Consumer) listenerEntry; + listener.accept(event); + } + } + } + } + + protected static class SubscriptionImpl implements Subscription { + protected EventPublisher router; + protected Class eventClass; + protected Consumer listener; + + public SubscriptionImpl(EventPublisher router, Class eventClass, Consumer listener) { + this.router = router; + this.eventClass = eventClass; + this.listener = listener; + } + + @Override + public void remove() { + router.unsubscribe(eventClass, listener); + } + } +} \ No newline at end of file diff --git a/modules/global/src/com/haulmont/bali/events/Subscription.java b/modules/global/src/com/haulmont/bali/events/Subscription.java new file mode 100644 index 0000000000..3a5e437a66 --- /dev/null +++ b/modules/global/src/com/haulmont/bali/events/Subscription.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2008-2018 Haulmont. + * + * Licensed 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 com.haulmont.bali.events; + +/** + * A registration object for removing an event listener added to a source. + */ +public interface Subscription { + /** + * Removes the associated listener from the event source. + */ + void remove(); +} \ No newline at end of file