package org.andork.tracker.model; import java.util.function.UnaryOperator; import org.andork.tracker.model.QSpec.Property; /** * {@code QObject} (along with {@link QSpec}) provides the closest functionality * to reflection on a POJO that is possible without actually using reflection. * It also provides property {@linkplain #changeSupport() change support} that * will notify listeners of any property changes, so that you don't have to * write any boilerplate property change notification code.<br> * <br> * The Q doesn't stand for anything. * * @author andy.edwards * @param <S> * the type of {@link QSpec} for this object. */ public abstract class QObject<S extends QSpec> { final S spec; final KeyedDependency<Property<?>> deps = new KeyedDependency<>(); public QObject(S spec) { this.spec = spec; } protected abstract <T> T doGet(Property<T> property); protected abstract <T> T doSet(Property<T> property, T newValue); @Override public boolean equals(Object other) { deps.depend(); return spec.equals(this, other); } public <T> T get(Property<T> property) { deps.depend(property); return property.get(this); } @Override public int hashCode() { deps.depend(); return spec.hashCode(this); } public <T> QObject<S> set(Property<T> property, T newValue) { T oldValue = property.set(this, newValue); if (oldValue != newValue) { deps.changed(property); } return this; } public S spec() { return spec; } public <T> QObject<S> update(Property<T> property, UnaryOperator<T> op) { T oldValue = property.get(this); T newValue = op.apply(oldValue); property.set(this, newValue); if (oldValue != newValue) { deps.changed(property); } return this; } }