diff options
| author | Benjamin Culkin <scorpress@gmail.com> | 2024-05-27 11:38:33 -0400 |
|---|---|---|
| committer | Benjamin Culkin <scorpress@gmail.com> | 2024-05-27 11:38:33 -0400 |
| commit | 7c279747beb43c7e88633a6228a155a30e6834f7 (patch) | |
| tree | 511176048944fa7332dc1a163a6148c46e7c61b3 /israfil-foundation-valuemodel/src | |
Initial import
Diffstat (limited to 'israfil-foundation-valuemodel/src')
6 files changed, 377 insertions, 0 deletions
diff --git a/israfil-foundation-valuemodel/src/main/java/net/israfil/foundation/valuemodel/MutableValue.java b/israfil-foundation-valuemodel/src/main/java/net/israfil/foundation/valuemodel/MutableValue.java new file mode 100644 index 0000000..74d4dd1 --- /dev/null +++ b/israfil-foundation-valuemodel/src/main/java/net/israfil/foundation/valuemodel/MutableValue.java @@ -0,0 +1,23 @@ +/*
+ * Copyright (c) 2003, Israfil Consulting Services Corporation
+ *
+ * $Id$
+ * $Revision$
+ *
+ */
+package net.israfil.foundation.valuemodel;
+
+
+/**
+ * A smalltalk-style generic value accessor/mutator system, where individual
+ * items are accessed through a single value
+ *
+ * @author Original: <a href="mailto:cgruber@israfil.net">Christian Edward Gruber</a>
+ * @author Recent: $Author$
+ *
+ */
+public interface MutableValue<E> extends Value<E>, Vetoable {
+
+ public void set(E value);
+
+}
diff --git a/israfil-foundation-valuemodel/src/main/java/net/israfil/foundation/valuemodel/Observable.java b/israfil-foundation-valuemodel/src/main/java/net/israfil/foundation/valuemodel/Observable.java new file mode 100644 index 0000000..574c8cf --- /dev/null +++ b/israfil-foundation-valuemodel/src/main/java/net/israfil/foundation/valuemodel/Observable.java @@ -0,0 +1,37 @@ +/*
+ * Copyright (c) 2003-2007 Israfil Consulting Services Corporation
+ * Copyright (c) 2003-2007 Christian Edward Gruber
+ * All Rights Reserved
+ *
+ * $Id: Types.java 129 2006-12-31 23:20:02Z cgruber $
+ */
+package net.israfil.foundation.valuemodel;
+
+/**
+ * An interface for objects that can notify observers of changes to itself. The
+ * changing object should call the provided callback signature, with the
+ * parameter being the object that was changed in its new state.
+ *
+ * Using a dynamic selector rather than a strong interface allows code
+ * to be notified of the change even if it was not constructed with the
+ * notification interface, though it does need to match the signature.
+ *
+ * @author <a href="mailto:cgruber@israfil.net">Christian Edward Gruber</a>
+ * @author Latest: $Author: cgruber $
+ * @version $Revision: 129 $
+ */
+public interface Observable {
+
+ /** Add an observer to this object, with a callback method spec that will
+ * be invoked upon a data change. The callback method should contain three
+ * object parameters, one for the sender, one for the old value, and one for
+ * the new value. The meaning of the value (same object, copy, values, etc.)
+ * is left to the implementation object and the observers.
+ */
+ public void addObservers(String callback, Object ... observers);
+ /**
+ * Remove observer from observable object.
+ */
+ public void removeObservers(Object ... observers);
+
+}
diff --git a/israfil-foundation-valuemodel/src/main/java/net/israfil/foundation/valuemodel/Value.java b/israfil-foundation-valuemodel/src/main/java/net/israfil/foundation/valuemodel/Value.java new file mode 100644 index 0000000..8c545bd --- /dev/null +++ b/israfil-foundation-valuemodel/src/main/java/net/israfil/foundation/valuemodel/Value.java @@ -0,0 +1,22 @@ +/*
+ * Copyright (c) 2003, Israfil Consulting Services Corporation
+ *
+ * $Id$
+ * $Revision$
+ *
+ */
+package net.israfil.foundation.valuemodel;
+
+/**
+ * A smalltalk-style generic value accessor/mutator system, where individual
+ * items are accessed through a single value
+ *
+ * @author Original: <a href="mailto:cgruber@israfil.net">Christian Edward Gruber</a>
+ * @author Recent: $Author$
+ *
+ */
+public interface Value<E> extends Observable {
+
+ public E get();
+
+}
diff --git a/israfil-foundation-valuemodel/src/main/java/net/israfil/foundation/valuemodel/ValueHolder.java b/israfil-foundation-valuemodel/src/main/java/net/israfil/foundation/valuemodel/ValueHolder.java new file mode 100644 index 0000000..55f23c3 --- /dev/null +++ b/israfil-foundation-valuemodel/src/main/java/net/israfil/foundation/valuemodel/ValueHolder.java @@ -0,0 +1,108 @@ +/*
+ * Created on Dec 8, 2003
+ *
+ * To change the template for this generated file go to
+ * Window>Preferences>Java>Code Generation>Code and Comments
+ */
+package net.israfil.foundation.valuemodel;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import net.israfil.foundation.dynamic.DynamicUtil;
+
+
+/**
+ * @author cgruber
+ *
+ * To change the template for this generated type comment go to
+ * Window>Preferences>Java>Code Generation>Code and Comments
+ */
+public class ValueHolder<E> implements MutableValue<E> {
+
+ private E _value = null;
+ private Map<Integer, ObserverHolder> _observers = new HashMap<Integer, ObserverHolder>();
+ private Map<Integer, VetoerHolder> _vetoers = new HashMap<Integer, VetoerHolder>();
+
+ public ValueHolder(E o) {
+ set(o);
+ }
+
+ protected ValueHolder() { }
+
+ public E get() {
+ return _value;
+ }
+
+ public void set(E newValue) {
+ boolean approved = true;
+ E oldValue = _value;
+ for (VetoerHolder vetoer : _vetoers.values()) {
+ approved &= vetoer.evaluate(this, oldValue, newValue);
+ }
+
+ if (!approved) return;
+ else _value = newValue;
+ for (ObserverHolder observer : _observers.values()) {
+ observer.notify(this, oldValue, newValue);
+ }
+
+ }
+
+ /**
+ * @see org.israfil.maveric.Observable#addObserver(java.lang.Object, java.lang.String)
+ */
+ public void addObservers(String callback, Object ... observers) {
+ for (Object observer : observers) {
+ _observers.put(observer.hashCode(),new ObserverHolder(observer,callback));
+ }
+ }
+
+ /**
+ * @see org.israfil.maveric.Observable#removeObserver(java.lang.Object)
+ */
+ public void removeObservers(Object ... observers) {
+ for (Object observer : observers) {
+ _observers.remove(observer.hashCode());
+ }
+ }
+ /**
+ * @see org.israfil.maveric.Vetoable#addVetoer(java.lang.Object, java.lang.String)
+ */
+ public void addVetoers(String callback, Object ... vetoers) {
+ for (Object vetoer : vetoers) {
+ _vetoers.put(vetoer.hashCode(),new VetoerHolder(vetoer,callback));
+ }
+ }
+
+ /**
+ * @see org.israfil.maveric.Vetoable#removeVetoer(java.lang.Object)
+ */
+ public void removeVetoers(Object ... vetoers) {
+ for (Object vetoer : vetoers) {
+ _vetoers.remove(vetoer.hashCode());
+ }
+ }
+
+ protected class CallbackObjectHolder {
+ public final Object receiver;
+ protected final String callback;
+ public CallbackObjectHolder(Object receiver, String callback) {
+ this.receiver = receiver;
+ this.callback = callback;
+ }
+ }
+ protected class ObserverHolder extends CallbackObjectHolder {
+ public ObserverHolder(Object observer, String callback) { super(observer, callback); }
+ public void notify(Object notifier, Object oldValue, Object newValue) {
+ DynamicUtil.performOn(receiver,callback,new Object[]{notifier, oldValue , newValue});
+ }
+ }
+ protected class VetoerHolder extends CallbackObjectHolder {
+ public VetoerHolder(Object vetoer, String callback) { super(vetoer, callback); }
+ public Boolean evaluate(Object notifier, Object oldValue, Object newValue) {
+ Boolean result = (Boolean)DynamicUtil.performOn(receiver,callback,new Object[]{notifier,oldValue,newValue});
+ return (result == null) ? true : result.booleanValue();
+ }
+ }
+}
diff --git a/israfil-foundation-valuemodel/src/main/java/net/israfil/foundation/valuemodel/Vetoable.java b/israfil-foundation-valuemodel/src/main/java/net/israfil/foundation/valuemodel/Vetoable.java new file mode 100644 index 0000000..5369287 --- /dev/null +++ b/israfil-foundation-valuemodel/src/main/java/net/israfil/foundation/valuemodel/Vetoable.java @@ -0,0 +1,44 @@ +/*
+ * Copyright (c) 2003-2007 Israfil Consulting Services Corporation
+ * Copyright (c) 2003-2007 Christian Edward Gruber
+ * All Rights Reserved
+ *
+ * $Id: Types.java 129 2006-12-31 23:20:02Z cgruber $
+ */
+package net.israfil.foundation.valuemodel;
+
+/**
+ * An interface for objects that can notify observers of changes to itself
+ * before the change. The objects thus notified can veto the change. The
+ * specific behaviour for vetoing is undefined and is dependent upon the
+ * particular implementation of the vetoable object and its vetoers.
+ * The changing object should call the provided callback signature, with the
+ * parameter being the object that was changed in its new state. The vetoer
+ * is responsible for knowing what it needs to know in order to validate or
+ * veto the change.
+ *
+ * Using a dynamic selector rather than a strong interface allows code
+ * to be notified of the change even if it was not constructed with the
+ * notification interface, though it does need to match the signature.
+ *
+ * @author <a href="mailto:cgruber@israfil.net">Christian Edward Gruber</a>
+ * @author Latest: $Author: cgruber $
+ * @version $Revision: 129 $
+ */
+public interface Vetoable {
+
+ /** Add one or more vetoers to this object, with a callback method spec
+ * that will be invoked upon a data change. The callback method should
+ * contain three parameters. The first is the notification source (the
+ * holder or whatever), the second the old value, and the third the new
+ * value. It also should return a boolean approving the change (or
+ * returning false to veto it)
+ */
+ public void addVetoers(String callback, Object ... approvers);
+
+ /**
+ * Remove one or more vetoers from vetoable object.
+ */
+ public void removeVetoers(Object ... approvers);
+
+}
diff --git a/israfil-foundation-valuemodel/src/test/java/net/israfil/foundation/valuemodel/ValueHolderTest.java b/israfil-foundation-valuemodel/src/test/java/net/israfil/foundation/valuemodel/ValueHolderTest.java new file mode 100644 index 0000000..271c4a0 --- /dev/null +++ b/israfil-foundation-valuemodel/src/test/java/net/israfil/foundation/valuemodel/ValueHolderTest.java @@ -0,0 +1,143 @@ +/*
+ * Copyright (c) 2003-2008Israfil Consulting Services Corporation
+ * Copyright (c) 2003-2008 Christian Edward Gruber
+ * All Rights Reserved
+ *
+ * $Id: Types.java 129 2006-12-31 23:20:02Z cgruber $
+ */
+package net.israfil.foundation.valuemodel;
+
+import net.israfil.foundation.core.Copyright;
+
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ * Tests for ValueHolder
+ *
+ * @author <a href="mailto:cgruber@israfil.net">Christian Edward Gruber</a>
+ * @author Latest: $Author: cgruber $
+ * @version $Revision: 129 $
+ */
+@Copyright(years={"2003","2004","2005","2006","2007","2008"},owner="Israfil Consulting Services Corporation",license="BSD")
+@Test
+public class ValueHolderTest {
+
+ private String stringValue = null;
+ private Integer integerValue = null;
+ private ValueHolder<String> stringValueHolder = null;
+ private ValueHolder<Integer> integerValueHolder = null;
+
+ @BeforeMethod
+ protected void setUp() throws Exception {
+ stringValue = "String Value";
+ integerValue = new Integer(5);
+ stringValueHolder = new ValueHolder<String>(stringValue);
+ integerValueHolder = new ValueHolder<Integer>(integerValue);
+ }
+
+ @AfterMethod
+ protected void tearDown() throws Exception {
+ stringValue = null;
+ integerValue = null;
+ stringValueHolder = null;
+ integerValueHolder = null;
+ }
+
+ /*
+ * Test for void ValueHolder(Object)
+ */
+ public void testValueHolderObject() {
+ String valueString = "Value";
+ ValueHolder<String>valueHolder = new ValueHolder<String>(valueString);
+ Assert.assertEquals(valueString,valueHolder.get());
+ }
+
+ public void testGetValue() {
+ Assert.assertEquals(stringValue,stringValueHolder.get());
+ Assert.assertEquals(integerValue,integerValueHolder.get());
+ }
+
+ public void testSetValueObject() {
+ String stringValue2 = "String Value 2";
+ stringValueHolder.set(stringValue2);
+ Assert.assertEquals(stringValue2,stringValueHolder.get());
+ }
+
+ public void testAddObserverObjectString() {
+ String stringValue2 = "String Value 2";
+ CallbackObject<String> callback = new CallbackObject<String>(stringValueHolder);
+ stringValueHolder.addObservers("notifyMe:java.lang.Object:java.lang.Object:java.lang.Object",callback);
+ stringValueHolder.set(stringValue2);
+ Assert.assertTrue(callback.notified);
+ }
+ public void testRemoveObserverObjectString() {
+ String stringValue2 = "String Value 2";
+ CallbackObject<String> callback = new CallbackObject<String>(stringValueHolder);
+ stringValueHolder.addObservers("notifyMe:java.lang.Object:java.lang.Object:java.lang.Object",callback);
+ stringValueHolder.removeObservers(callback);
+ stringValueHolder.set(stringValue2);
+ Assert.assertFalse(callback.notified);
+ }
+ public void testAddApproverObjectString() {
+ String stringValue2 = "String Value 2";
+ CallbackObject<String> callback = new CallbackObject<String>(stringValueHolder);
+ stringValueHolder.addVetoers("approve:java.lang.Object:java.lang.Object:java.lang.Object",callback);
+ stringValueHolder.set(stringValue2);
+ Assert.assertEquals(stringValue2,stringValueHolder.get());
+
+ callback = new CallbackObject<String>(stringValueHolder);
+ stringValueHolder.addVetoers("approve:java.lang.Object:java.lang.Object:java.lang.Object",callback);
+ stringValueHolder.set(stringValue2);
+ stringValueHolder.set(stringValue);
+ Assert.assertEquals(stringValue2,stringValueHolder.get());
+ Assert.assertFalse(stringValue.equals(stringValueHolder.get()));
+ }
+ public void testRemoveApproverObjectString() {
+ String stringValue2 = "String Value 2";
+ CallbackObject<String> callback = new CallbackObject<String>(stringValueHolder);
+ stringValueHolder.addVetoers("approve:java.lang.Object:java.lang.Object:java.lang.Object",callback);
+ stringValueHolder.removeVetoers(callback);
+ stringValueHolder.set(stringValue2);
+ stringValueHolder.set(stringValue);
+ Assert.assertEquals(stringValue,stringValueHolder.get());
+ Assert.assertFalse(stringValue2.equals(stringValueHolder.get()));
+ }
+ public void finalize() throws Throwable {
+ stringValue = null;
+ integerValue = null;
+ stringValueHolder = null;
+ integerValueHolder = null;
+ super.finalize();
+ }
+
+ public class CallbackObject<E> {
+ private final MutableValue<E> valueModel;
+ public boolean notified = false;
+ public Object oldValue = null;
+ public Object newValue = null;
+ public CallbackObject(MutableValue<E> valueModel) {
+ this.valueModel = valueModel;
+ }
+ public void notifyMe(Object sender, Object oldValue, Object newValue) {
+ this.oldValue = oldValue;
+ this.newValue = newValue;
+ Assert.assertEquals(this.valueModel,sender);
+ notified = true;
+ }
+ public boolean approve(Object sender, Object oldValue, Object newValue) {
+ this.oldValue = oldValue;
+ this.newValue = newValue;
+ Assert.assertEquals(this.valueModel,sender);
+ if (!newValue.equals(stringValue)) return true;
+ return false;
+ }
+ public void finalize() throws Throwable {
+ this.oldValue = null;
+ this.newValue = null;
+ super.finalize();
+ }
+ }
+}
|
