All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jalse.attributes.DefaultAttributeContainer Maven / Gradle / Ivy

There is a newer version: 1.1.0
Show newest version
package jalse.attributes;

import static jalse.attributes.Attributes.requireNotEmpty;
import jalse.listeners.AttributeEvent;
import jalse.listeners.AttributeListener;
import jalse.listeners.ListenerSet;
import jalse.listeners.Listeners;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Stream;

/**
 * An DefaultAttributeContainer is a thread-safe implementation of {@link AttributeContainer}.
*
* * DefaultAttributeContainer can take a delegate AttributeContainer to supply to * {@link AttributeEvent}. Attribute updates will trigger these events using * {@link AttributeListener}. * * @author Elliot Ford * */ public class DefaultAttributeContainer implements AttributeContainer { private static void checkNameAndType(final String name, final AttributeType type) { requireNotEmpty(name); Objects.requireNonNull(type); } private final Map, ListenerSet>> listeners; private final Map, Object>> attributes; private final AttributeContainer delegateContainer; private final Lock read; private final Lock write; /** * Creates a new instance of DefaultAttributeContainer with no delegate container (self). */ public DefaultAttributeContainer() { this(null); } /** * Creates a new instance of DefaultAttributeContainer with a delegate container. * * @param delegateContainer * Delegate AttributeContainer for events. */ public DefaultAttributeContainer(final AttributeContainer delegateContainer) { this.delegateContainer = delegateContainer != null ? delegateContainer : this; attributes = new ConcurrentHashMap<>(); listeners = new ConcurrentHashMap<>(); final ReadWriteLock rwLock = new ReentrantReadWriteLock(); read = rwLock.readLock(); write = rwLock.writeLock(); } @Override public boolean addAttributeListener(final String name, final AttributeType type, final AttributeListener listener) { checkNameAndType(name, type); Objects.requireNonNull(listener); write.lock(); try { final Map, ListenerSet> lsn = listeners.computeIfAbsent(name, k -> new ConcurrentHashMap<>()); @SuppressWarnings({ "unchecked" }) final ListenerSet> lst = (ListenerSet>) lsn.computeIfAbsent(type, k -> { return Listeners. newAttributeListenerSet(); }); return lst.add(listener); } finally { write.unlock(); } } @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (!(obj instanceof DefaultAttributeContainer)) { return false; } final DefaultAttributeContainer other = (DefaultAttributeContainer) obj; return attributes.equals(other.attributes) && listeners.equals(other.listeners); } @Override public void fireAttributeChanged(final String name, final AttributeType type) { checkNameAndType(name, type); read.lock(); try { final Map, Object> atrn = attributes.get(name); if (atrn == null) { return; } @SuppressWarnings("unchecked") final T current = (T) atrn.get(type); if (current == null) { return; } @SuppressWarnings("unchecked") final ListenerSet> ls = (ListenerSet>) getAttributeListeners0( name, type); if (ls != null) { ls.getProxy().attributeChanged(new AttributeEvent<>(delegateContainer, name, type, current)); } } finally { read.unlock(); } } @Override @SuppressWarnings("unchecked") public T getAttribute(final String name, final AttributeType type) { checkNameAndType(name, type); read.lock(); try { final Map, Object> atrn = attributes.get(name); return atrn != null ? (T) atrn.get(type) : null; } finally { read.unlock(); } } @Override public int getAttributeCount() { return attributes.values().stream().mapToInt(Map::size).sum(); } @Override public Set getAttributeListenerNames() { return Collections.unmodifiableSet(listeners.keySet()); } @Override public Set> getAttributeListeners(final String name, final AttributeType type) { checkNameAndType(name, type); read.lock(); try { @SuppressWarnings("unchecked") final Set> ls = (Set>) getAttributeListeners0( name, type); return ls != null ? Collections.unmodifiableSet(ls) : Collections.emptySet(); } finally { read.unlock(); } } private ListenerSet getAttributeListeners0(final String name, final AttributeType type) { final Map, ListenerSet> ls = listeners.get(name); return ls != null ? ls.get(type) : null; } @Override public Set> getAttributeListenerTypes(final String name) { requireNotEmpty(name); read.lock(); try { final Map, ListenerSet> ls = listeners.get(name); return ls != null ? Collections.unmodifiableSet(ls.keySet()) : Collections.emptySet(); } finally { read.unlock(); } } @Override public Set getAttributeNames() { return Collections.unmodifiableSet(attributes.keySet()); } @Override public Set> getAttributeTypes(final String name) { requireNotEmpty(name); read.lock(); try { final Map, Object> atrn = attributes.get(name); return atrn != null ? Collections.unmodifiableSet(atrn.keySet()) : Collections.emptySet(); } finally { read.unlock(); } } /** * Gets the delegate container. * * @return Delegate event container. */ public AttributeContainer getDelegateContainer() { return delegateContainer; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + attributes.hashCode(); result = prime * result + listeners.hashCode(); return result; } @Override public T removeAttribute(final String name, final AttributeType type) { checkNameAndType(name, type); write.lock(); try { final Map, Object> atrn = attributes.get(name); if (atrn == null) { return null; } @SuppressWarnings("unchecked") final T prev = (T) atrn.remove(type); if (prev != null) { attributes.computeIfPresent(name, (k, v) -> v.isEmpty() ? null : v); @SuppressWarnings("unchecked") final ListenerSet> ls = (ListenerSet>) getAttributeListeners0( name, type); if (ls != null) { ls.getProxy().attributeRemoved(new AttributeEvent<>(delegateContainer, name, type, prev)); } } return prev; } finally { write.unlock(); } } @Override public boolean removeAttributeListener(final String name, final AttributeType type, final AttributeListener listener) { checkNameAndType(name, type); Objects.requireNonNull(listener); write.lock(); try { final Map, ListenerSet> lsn = listeners.get(name); if (lsn == null) { return false; } @SuppressWarnings("unchecked") final ListenerSet> lst = (ListenerSet>) lsn.get(type); if (lst == null) { return false; } if (!lst.remove(listener)) { return false; } lsn.computeIfPresent(type, (k, v) -> v.isEmpty() ? null : v); listeners.computeIfPresent(name, (k, v) -> v.isEmpty() ? null : v); return true; } finally { write.unlock(); } } @Override public void removeAttributeListeners() { write.lock(); try { listeners.clear(); } finally { write.unlock(); } } @Override public void removeAttributeListeners(final String name, final AttributeType type) { checkNameAndType(name, type); write.lock(); try { final Map, ListenerSet> lsn = listeners.get(name); if (lsn == null) { return; } if (lsn.remove(type) != null) { listeners.computeIfPresent(name, (k, v) -> v.isEmpty() ? null : v); } } finally { write.unlock(); } } @Override public void removeAttributes() { write.lock(); try { final Map>> namesToTypes = new HashMap<>(); attributes.entrySet().forEach(e -> { namesToTypes.put(e.getKey(), new HashSet<>(e.getValue().keySet())); }); namesToTypes.forEach((n, ts) -> { ts.forEach(t -> { removeAttribute(n, t); }); }); } finally { write.unlock(); } } @Override public T setAttribute(final String name, final AttributeType type, final T attr) { checkNameAndType(name, type); Objects.requireNonNull(attr); write.lock(); try { final Map, Object> atrn = attributes.computeIfAbsent(name, k -> new ConcurrentHashMap<>()); @SuppressWarnings("unchecked") final T prev = (T) atrn.put(type, attr); @SuppressWarnings("unchecked") final ListenerSet> ls = (ListenerSet>) getAttributeListeners0( name, type); if (ls != null) { ls.getProxy().attributeAdded(new AttributeEvent<>(delegateContainer, name, type, attr, prev)); } return prev; } finally { write.unlock(); } } @Override public Stream streamAttributes() { return attributes.values().stream().flatMap(m -> m.values().stream()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy