001package co.codewizards.cloudstore.core.bean;
002
003import static co.codewizards.cloudstore.core.util.ReflectionUtil.*;
004import static java.util.Objects.*;
005
006import java.beans.PropertyChangeEvent;
007import java.beans.PropertyChangeListener;
008import java.lang.ref.Reference;
009import java.lang.ref.ReferenceQueue;
010import java.lang.ref.WeakReference;
011import java.util.Collections;
012import java.util.IdentityHashMap;
013import java.util.Map;
014
015public class WeakPropertyChangeListener implements PropertyChangeListener {
016        private static ReferenceQueue<PropertyChangeListener> listenerRefQueue = new ReferenceQueue<PropertyChangeListener>();
017        private static Map<Reference<PropertyChangeListener>, WeakPropertyChangeListener> listenerRef2WeakPropertyChangeListener =
018                        Collections.synchronizedMap(new IdentityHashMap<Reference<PropertyChangeListener>, WeakPropertyChangeListener>());
019
020        private final Object bean;
021        private final Object property;
022        private final WeakReference<PropertyChangeListener> listenerRef;
023        private boolean registered;
024
025        public WeakPropertyChangeListener(final Object bean, final PropertyChangeListener listener) {
026                this(bean, null, listener);
027        }
028
029        public WeakPropertyChangeListener(final Object bean, final Object property, final PropertyChangeListener listener) {
030                expunge();
031
032                this.bean = requireNonNull(bean, "bean");
033                this.property = property;
034
035                listenerRef = new WeakReference<PropertyChangeListener>(listener, listenerRefQueue);
036                listenerRef2WeakPropertyChangeListener.put(listenerRef, this);
037        }
038
039        @Override
040        public void propertyChange(final PropertyChangeEvent event) {
041                expunge();
042
043                final PropertyChangeListener listener = listenerRef.get();
044                if (listener != null)
045                        listener.propertyChange(event);
046        }
047
048        private static void expunge() {
049                Reference<? extends PropertyChangeListener> ref;
050                while ((ref = listenerRefQueue.poll()) != null) {
051                        final WeakPropertyChangeListener weakPropertyChangeListener = listenerRef2WeakPropertyChangeListener.remove(ref);
052
053                        if (weakPropertyChangeListener != null)
054                                weakPropertyChangeListener.removePropertyChangeListener();
055                }
056        }
057
058        public synchronized void addPropertyChangeListener() {
059                if (registered)
060                        return;
061
062                if (property != null)
063                        invoke(bean, "addPropertyChangeListener", property, this);
064                else
065                        invoke(bean, "addPropertyChangeListener", this);
066
067                registered = true;
068        }
069
070        public synchronized void removePropertyChangeListener() {
071                if (! registered)
072                        return;
073
074                if (property != null)
075                        invoke(bean, "removePropertyChangeListener", property, this);
076                else
077                        invoke(bean, "removePropertyChangeListener", this);
078
079                registered = false;
080        }
081}