001package co.codewizards.cloudstore.core.bean;
002
003import static co.codewizards.cloudstore.core.util.ReflectionUtil.*;
004import static co.codewizards.cloudstore.core.util.Util.*;
005import static java.util.Objects.*;
006
007import java.beans.PropertyChangeListener;
008import java.beans.PropertyChangeSupport;
009
010public class BeanSupport<B, P extends PropertyBase> {
011        private final B bean;
012        private final PropertyChangeSupport propertyChangeSupport;
013
014        public BeanSupport(final B bean) {
015                this.bean = requireNonNull(bean, "bean");
016                propertyChangeSupport = new PropertyChangeSupport(bean);
017        }
018
019        public B getBean() {
020                return bean;
021        }
022
023        public void setPropertyValue(final P property, final Object value) {
024                requireNonNull(property, "property");
025
026                final Object old;
027                synchronized (getMutex()) {
028                        old = getFieldValue(bean, property.name());
029                        if (isEqual(property, old, value))
030                                return;
031
032                        setFieldValue(bean, property.name(), value);
033                }
034
035                // We *must* fire the event *outside* of the *synchronized* block to make sure the listeners
036                // do not run into a dead-lock!
037                firePropertyChange(property, old, value);
038        }
039
040        public <V> V getPropertyValue(P property) {
041                synchronized (getMutex()) {
042                        return getFieldValue(bean, property.name());
043                }
044        }
045
046        protected Object getMutex() {
047                return bean;
048        }
049
050        protected boolean isEqual(final P property, final Object oldValue, final Object newValue) {
051                return equal(oldValue, newValue);
052        }
053
054        public void addPropertyChangeListener(final PropertyChangeListener listener) {
055                requireNonNull(listener, "listener");
056                propertyChangeSupport.addPropertyChangeListener(listener);
057        }
058
059        public void removePropertyChangeListener(final PropertyChangeListener listener) {
060                requireNonNull(listener, "listener");
061                propertyChangeSupport.removePropertyChangeListener(listener);
062        }
063
064        public void addPropertyChangeListener(final P property, final PropertyChangeListener listener) {
065                requireNonNull(property, "property");
066                requireNonNull(listener, "listener");
067                propertyChangeSupport.addPropertyChangeListener(property.name(), listener);
068        }
069
070        public void removePropertyChangeListener(final P property, final PropertyChangeListener listener) {
071                requireNonNull(property, "property");
072                requireNonNull(listener, "listener");
073                propertyChangeSupport.removePropertyChangeListener(property.name(), listener);
074        }
075
076        public void firePropertyChange(final P property, Object oldValue, Object newValue) {
077                requireNonNull(property, "property");
078                propertyChangeSupport.firePropertyChange(property.name(), oldValue, newValue);
079        }
080}