Java tutorial
/** * Copyright Vclav Brodec 2014. * * This file is part of Botn?ek. * * Botn?ek is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Botn?ek is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Botn?ek. If not, see <http://www.gnu.org/licenses/>. */ package cz.cuni.mff.ms.brodecva.botnicek.ide.utils.events; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import cz.cuni.mff.ms.brodecva.botnicek.ide.utils.data.Presence; /** * <p> * Vchoz implementace sprvce udlost. * </p> * <p> * Krom toho, e realizuje odesln zprv o udlosti tak, aby klient nemusel * rozliovat, jak typ odesl, tak dovoluje v ppad poteby neodebrat * poslucha?, ani by dochzelo k unikn pamti. * </p> * <p> * Pestoe jsou instance tto tdy serializovateln, nelze toto garantovat pro * vloen objekty. * </p> * * @author Vclav Brodec * @version 1.0 */ public final class DefaultEventManager implements EventManager, Serializable { /** * <p> * Nvtvnk pro rozlien typu udlosti pi vypravovn poslucha?m. * </p> * <p> * Dky nmu je udlost podle svho typu rozeslna sprvnm poslucha?m. * </p> * <p> * Pi prchodu poslucha?i bere v potaz vlastnosti {@link WeakHashMap} pi * iteraci pes ?leny. * </p> */ private final class MappedEventVisitor implements Visitor { @Override public <L> void visit(final Event<L> event) { visitCommon(event); } @Override public <K, L> void visit(final MappedEvent<K, L> event) { visitMapped(event); visitCommon(event); } private <L> void visitCommon(final Event<L> event) { @SuppressWarnings("unchecked") final Set<L> listeners = (Set<L>) DefaultEventManager.this.eventsToListeners.get(event.getClass()); if (Presence.isAbsent(listeners)) { return; } final Set<L> listenersSnapshot = ImmutableSet.copyOf(listeners); for (final L listener : listenersSnapshot) { event.dispatchTo(listener); } } private <K, L> void visitMapped(final MappedEvent<K, L> event) { @SuppressWarnings("unchecked") final Map<K, Set<?>> keysTolisteners = (Map<K, Set<?>>) DefaultEventManager.this.eventsAndKeysToListeners .get(event.getClass()); if (Presence.isAbsent(keysTolisteners)) { return; } @SuppressWarnings("unchecked") final Set<L> mappedListeners = (Set<L>) keysTolisteners.get(event.getKey()); if (Presence.isAbsent(mappedListeners)) { return; } final Set<L> listenersSnapshot = ImmutableSet.copyOf(mappedListeners); for (final L listener : listenersSnapshot) { event.dispatchTo(listener); } } } private static final long serialVersionUID = 1L; /** * Vytvo manaer udlost. * * @return manaer */ public static DefaultEventManager create() { return new DefaultEventManager(); } private final Map<Class<? extends MappedEvent<?, ?>>, Map<?, Set<?>>> eventsAndKeysToListeners = new HashMap<>(); private final Map<Class<? extends Event<?>>, Set<?>> eventsToListeners = new HashMap<>(); private DefaultEventManager() { } /* * (non-Javadoc) * * @see cz.cuni.mff.ms.brodecva.botnicek.ide.utils.events.EventRegister# * addEventListener(java.lang.Class, java.lang.Object) */ @Override public <L> void addListener(final Class<? extends Event<L>> type, final L listener) { Preconditions.checkNotNull(type); Preconditions.checkNotNull(listener); @SuppressWarnings("unchecked") final Set<L> listeners = (Set<L>) this.eventsToListeners.get(type); final Set<L> usedListeners; if (Presence.isAbsent(listeners)) { usedListeners = Sets.newSetFromMap(new WeakHashMap<L, Boolean>()); this.eventsToListeners.put(type, usedListeners); } else { usedListeners = listeners; } final boolean fresh = usedListeners.add(listener); Preconditions.checkArgument(fresh); } /* * (non-Javadoc) * * @see * cz.cuni.mff.ms.brodecva.botnicek.ide.utils.EventRegister#addEventListener * (java.lang.Class, java.lang.Object) */ @Override public <K, L> void addListener(final Class<? extends MappedEvent<K, L>> type, final K key, final L listener) { Preconditions.checkNotNull(type); Preconditions.checkNotNull(key); Preconditions.checkNotNull(listener); @SuppressWarnings("unchecked") final Map<K, Set<?>> keysTolisteners = (Map<K, Set<?>>) this.eventsAndKeysToListeners.get(type); final Map<K, Set<?>> usedKeysToListeners; if (Presence.isAbsent(keysTolisteners)) { usedKeysToListeners = new WeakHashMap<>(); this.eventsAndKeysToListeners.put(type, usedKeysToListeners); } else { usedKeysToListeners = keysTolisteners; } @SuppressWarnings("unchecked") final Set<L> listeners = (Set<L>) usedKeysToListeners.get(key); final Set<L> usedListeners; if (Presence.isAbsent(listeners)) { usedListeners = Collections.newSetFromMap(new WeakHashMap<L, Boolean>()); usedKeysToListeners.put(key, usedListeners); } else { usedListeners = listeners; } final boolean fresh = usedListeners.add(listener); Preconditions.checkArgument(fresh); } /* * (non-Javadoc) * * @see * cz.cuni.mff.ms.brodecva.botnicek.ide.utils.events.Dispatcher#fire(cz. * cuni.mff.ms.brodecva.botnicek.ide.utils.events.SimpleEvent) */ @Override public <L> void fire(final Event<L> event) { Preconditions.checkNotNull(event); event.accept(new MappedEventVisitor()); } /* * (non-Javadoc) * * @see cz.cuni.mff.ms.brodecva.botnicek.ide.utils.events.EventRegister# * removeAllListeners(java.lang.Class) */ @Override public <K, L> void removeAllListeners(final Class<? extends Event<L>> type) { Preconditions.checkNotNull(type); Preconditions.checkNotNull(type); this.eventsToListeners.remove(type); } /* * (non-Javadoc) * * @see cz.cuni.mff.ms.brodecva.botnicek.ide.utils.events.EventRegister# * removeAllListeners(java.lang.Class, java.lang.Object) */ @Override public <K, L> void removeAllListeners(final Class<? extends MappedEvent<K, L>> type, final K key) { Preconditions.checkNotNull(type); Preconditions.checkNotNull(key); Preconditions.checkNotNull(type); Preconditions.checkNotNull(key); @SuppressWarnings("unchecked") final Map<K, Set<?>> keysTolisteners = (Map<K, Set<?>>) this.eventsAndKeysToListeners.get(type); Preconditions.checkArgument(Presence.isPresent(keysTolisteners)); keysTolisteners.remove(key); if (keysTolisteners.isEmpty()) { this.eventsAndKeysToListeners.remove(type); } } /* * (non-Javadoc) * * @see cz.cuni.mff.ms.brodecva.botnicek.ide.utils.events.EventRegister# * removeEventListener(java.lang.Class, java.lang.Object) */ @Override public <L> void removeListener(final Class<? extends Event<L>> type, final L listener) { Preconditions.checkNotNull(type); Preconditions.checkNotNull(listener); @SuppressWarnings("unchecked") final Set<L> listeners = (Set<L>) this.eventsToListeners.get(type); Preconditions.checkArgument(Presence.isPresent(listeners)); final boolean contained = listeners.remove(listener); Preconditions.checkArgument(contained); if (listeners.isEmpty()) { this.eventsToListeners.remove(listeners); } } /* * (non-Javadoc) * * @see * cz.cuni.mff.ms.brodecva.botnicek.ide.utils.EventRegister#removeEventListener * (java.lang.Class, java.lang.Object) */ @Override public <K, L> void removeListener(final Class<? extends MappedEvent<K, L>> type, final K key, final L listener) { Preconditions.checkNotNull(type); Preconditions.checkNotNull(key); Preconditions.checkNotNull(listener); @SuppressWarnings("unchecked") final Map<K, Set<?>> keysTolisteners = (Map<K, Set<?>>) this.eventsAndKeysToListeners.get(type); Preconditions.checkArgument(Presence.isPresent(keysTolisteners)); @SuppressWarnings("unchecked") final Set<L> listeners = (Set<L>) keysTolisteners.get(key); Preconditions.checkArgument(Presence.isPresent(listeners)); final boolean contained = listeners.remove(listener); Preconditions.checkArgument(contained); if (listeners.isEmpty()) { keysTolisteners.remove(key); } if (keysTolisteners.isEmpty()) { this.eventsAndKeysToListeners.remove(type); } } }