Compare commits

...

3 Commits

Author SHA1 Message Date
722e6fe5cf [DEV] simplify the code 2021-06-04 15:17:39 +02:00
0708bf47dc [DEV] reduce code with generic class 2021-06-04 15:12:18 +02:00
e43388d091 [DOC] add generic documentation 2021-06-04 15:11:57 +02:00
6 changed files with 315 additions and 390 deletions

View File

@ -0,0 +1,77 @@
package org.atriasoft.esignal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.atriasoft.esignal.internal.ConnectedElement;
import org.atriasoft.esignal.internal.ConnectedElementDynamic;
/**
* Generic interface for a signaling model...
*
* @param <T> generic Runnable, Consumer, or BiConsumer template...
*/
public class GenericSignal<T> implements ConnectionRemoveInterface {
List<ConnectedElement<T>> data = new ArrayList<>();
public void clear() {
List<ConnectedElement<T>> data2 = this.data;
synchronized(this.data) {
this.data = new ArrayList<>();
}
final Iterator<ConnectedElement<T>> iterator = data2.iterator();
while (iterator.hasNext()) {
final ConnectedElement<T> elem = iterator.next();
elem.disconnect();
}
}
public void connect(final T function) {
synchronized(this.data) {
this.data.add(new ConnectedElement<T>(function));
}
}
public void disconnect(final T obj) {
synchronized(this.data) {
final Iterator<ConnectedElement<T>> iterator = this.data.iterator();
while (iterator.hasNext()) {
final ConnectedElement<T> elem = iterator.next();
if (elem.isCompatibleWith(obj)) {
iterator.remove();
}
}
}
}
public Connection connectDynamic(final T function) {
Connection out = new Connection(this);
synchronized(this.data) {
this.data.add(new ConnectedElementDynamic<T>(out, function));
}
return out;
}
public void connectAutoRemoveObject(final Object reference, final T function) {
synchronized(this.data) {
this.data.add(new ConnectedElementDynamic<T>(reference, function));
}
}
@Override
public void disconnect(final Connection connection) {
synchronized(this.data) {
final Iterator<ConnectedElement<T>> iterator = this.data.iterator();
while (iterator.hasNext()) {
final ConnectedElement<T> elem = iterator.next();
if (elem.isCompatibleWith(connection)) {
elem.disconnect();
iterator.remove();
}
}
}
}
public int size() {
return this.data.size();
}
}

View File

@ -1,140 +1,64 @@
package org.atriasoft.esignal; package org.atriasoft.esignal;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
class ConnectedElement<T> { import org.atriasoft.esignal.internal.ConnectedElement;
protected final WeakReference<Consumer<T>> consumer;
public ConnectedElement(final Consumer<T> consumer) {
this.consumer = new WeakReference<Consumer<T>>(consumer);
}
public Consumer<T> getConsumer() {
return this.consumer.get();
}
public boolean isCompatibleWith(final Object elem) {
Object out = this.consumer.get();
if (out == elem) {
return true;
}
return false;
}
public void disconnect() {
}
}
class ConnectedElementDynamic<T> extends ConnectedElement<T> {
protected final WeakReference<Object> linkedObject;
public ConnectedElementDynamic(final Object linkedObject, final Consumer<T> consumer) {
super(consumer);
this.linkedObject = new WeakReference<Object>(linkedObject);
}
@Override
public Consumer<T> getConsumer() {
if (this.linkedObject.get() == null) {
return null;
}
return this.consumer.get();
}
@Override
public boolean isCompatibleWith(final Object elem) {
if (super.isCompatibleWith(elem)) {
return true;
}
Object obj = this.linkedObject.get();
if (obj == elem) {
return true;
}
return false;
}
@Override
public void disconnect() {
Object obj = this.linkedObject.get();
if (obj == null) {
return;
}
if (obj instanceof Connection tmp) {
tmp.connectionIsRemovedBySignal();
}
}
}
public class Signal<T> implements ConnectionRemoveInterface {
List<ConnectedElement<T>> data = new ArrayList<>();
public void clear() {
List<ConnectedElement<T>> data2 = this.data;
synchronized(this.data) {
this.data = new ArrayList<>();
}
final Iterator<ConnectedElement<T>> iterator = data2.iterator();
while (iterator.hasNext()) {
final ConnectedElement<T> elem = iterator.next();
elem.disconnect();
}
}
public void connect(final Consumer<T> function) {
synchronized(this.data) {
this.data.add(new ConnectedElement<T>(function));
}
}
public void disconnect(final Consumer<T> obj) {
synchronized(this.data) {
final Iterator<ConnectedElement<T>> iterator = this.data.iterator();
while (iterator.hasNext()) {
final ConnectedElement<T> elem = iterator.next();
if (elem.isCompatibleWith(obj)) {
iterator.remove();
}
}
}
}
public Connection connectDynamic(final Consumer<T> function) {
Connection out = new Connection(this);
synchronized(this.data) {
this.data.add(new ConnectedElementDynamic<T>(out, function));
}
return out;
}
public void connectAutoRemoveObject(final Object reference, final Consumer<T> function) {
synchronized(this.data) {
this.data.add(new ConnectedElementDynamic<T>(reference, function));
}
}
@Override
public void disconnect(final Connection connection) {
synchronized(this.data) {
final Iterator<ConnectedElement<T>> iterator = this.data.iterator();
while (iterator.hasNext()) {
final ConnectedElement<T> elem = iterator.next();
if (elem.isCompatibleWith(connection)) {
elem.disconnect();
iterator.remove();
}
}
}
}
/**
* Simple interface to manage signal connection and disconnection
* <pre>{@code
* class EmiterSimple {
* public Signal<String> signalEvent = new Signal<>();
* }
*
* class ReceiverSimple {
* public void onEvent(String data) {
* Log.error("function receive: " + data);
* }
* public connectLambda(EmiterSimple other) {
* // Note : this lambda is reference a a global, then it will never removed in the connection list ==> refer the local class or @see connectAutoRemoveObject
* other.signalEvent.connect((data) -> {
* Log.error("lambda receive: " + data);
* });
* }
* }
* // use :
* EmiterSimple aaa = new EmiterSimple();
* ReceiverSimple bbb = new ReceiverSimple();
* // Emit a signal:
* aaa.signalEvent.emit("My message ...");
* // simple direct connection:
* aaa.signalEvent.connect(bbb::onEvent);
* //removable connection (2 possibilities:)
* // First solution (best way ==> does not need to lock a reference on the current object and the remote)
* {
* Connection connect = aaa.signalEvent.connectDynamic(bbb::onEvent());
* // disconnect
* connect.disconnect();
* }
* // Second solution
* {
* Consumer<?> connect = bbb::onEvent;
* aaa.signalEvent.connect(connect);
* // disconnect
* aaa.signalEvent.disconnect(connect);
* }
* }</pre>
*
* @param <T> Type of the signal
*
*/
public class Signal<T> extends GenericSignal<Consumer<T>> {
public void emit(final T value) { public void emit(final T value) {
List<ConnectedElement<T>> tmp; List<ConnectedElement<Consumer<T>>> tmp;
// clean the list: // clean the list:
synchronized(this.data) { synchronized(this.data) {
final Iterator<ConnectedElement<T>> iterator = this.data.iterator(); final Iterator<ConnectedElement<Consumer<T>>> iterator = this.data.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final ConnectedElement<T> elem = iterator.next(); final ConnectedElement<Consumer<T>> elem = iterator.next();
Object tmpObject = elem.getConsumer(); Object tmpObject = elem.getConsumer();
if (tmpObject == null) { if (tmpObject == null) {
elem.disconnect(); elem.disconnect();
@ -150,9 +74,9 @@ public class Signal<T> implements ConnectionRemoveInterface {
} }
// real call elements // real call elements
{ {
final Iterator<ConnectedElement<T>> iterator = tmp.iterator(); final Iterator<ConnectedElement<Consumer<T>>> iterator = tmp.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final ConnectedElement<T> elem = iterator.next(); final ConnectedElement<Consumer<T>> elem = iterator.next();
Consumer<T> tmpObject = elem.getConsumer(); Consumer<T> tmpObject = elem.getConsumer();
if (tmpObject == null) { if (tmpObject == null) {
continue; continue;
@ -162,6 +86,7 @@ public class Signal<T> implements ConnectionRemoveInterface {
} }
} }
@Override
public int size() { public int size() {
return this.data.size(); return this.data.size();
} }

View File

@ -1,140 +1,66 @@
package org.atriasoft.esignal; package org.atriasoft.esignal;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
class BiConnectedElement<T, U> { import org.atriasoft.esignal.internal.ConnectedElement;
protected final WeakReference<BiConsumer<T, U>> consumer;
public BiConnectedElement(final BiConsumer<T, U> consumer) {
this.consumer = new WeakReference<BiConsumer<T, U>>(consumer);
}
public BiConsumer<T, U> getConsumer() {
return this.consumer.get();
}
public boolean isCompatibleWith(final Object elem) {
Object out = this.consumer.get();
if (out == elem) {
return true;
}
return false;
}
public void disconnect() {
}
}
class BiConnectedElementDynamic<T, U> extends BiConnectedElement<T, U> {
protected final WeakReference<Object> linkedObject;
public BiConnectedElementDynamic(final Object linkedObject, final BiConsumer<T, U> consumer) {
super(consumer);
this.linkedObject = new WeakReference<Object>(linkedObject);
}
@Override
public BiConsumer<T, U> getConsumer() {
if (this.linkedObject.get() == null) {
return null;
}
return this.consumer.get();
}
@Override
public boolean isCompatibleWith(final Object elem) {
if (super.isCompatibleWith(elem)) {
return true;
}
Object obj = this.linkedObject.get();
if (obj == elem) {
return true;
}
return false;
}
@Override
public void disconnect() {
Object obj = this.linkedObject.get();
if (obj == null) {
return;
}
if (obj instanceof Connection tmp) {
tmp.connectionIsRemovedBySignal();
}
}
}
public class Signal2<T, U> implements ConnectionRemoveInterface {
List<BiConnectedElement<T, U>> data = new ArrayList<>();
public void clear() {
List<BiConnectedElement<T, U>> data2 = this.data;
synchronized(this.data) {
this.data = new ArrayList<>();
}
final Iterator<BiConnectedElement<T, U>> iterator = data2.iterator();
while (iterator.hasNext()) {
final BiConnectedElement<T, U> elem = iterator.next();
elem.disconnect();
}
}
public void connect(final BiConsumer<T, U> function) {
synchronized(this.data) {
this.data.add(new BiConnectedElement<T, U>(function));
}
}
public void disconnect(final BiConsumer<T, U> obj) {
synchronized(this.data) {
final Iterator<BiConnectedElement<T, U>> iterator = this.data.iterator();
while (iterator.hasNext()) {
final BiConnectedElement<T, U> elem = iterator.next();
if (elem.isCompatibleWith(obj)) {
iterator.remove();
}
}
}
}
public Connection connectDynamic(final BiConsumer<T, U> function) {
Connection out = new Connection(this);
synchronized(this.data) {
this.data.add(new BiConnectedElementDynamic<T, U>(out, function));
}
return out;
}
public void connectAutoRemoveObject(final Object reference, final BiConsumer<T, U> function) {
synchronized(this.data) {
this.data.add(new BiConnectedElementDynamic<T, U>(reference, function));
}
}
@Override
public void disconnect(final Connection connection) {
synchronized(this.data) {
final Iterator<BiConnectedElement<T, U>> iterator = this.data.iterator();
while (iterator.hasNext()) {
final BiConnectedElement<T, U> elem = iterator.next();
if (elem.isCompatibleWith(connection)) {
elem.disconnect();
iterator.remove();
}
}
}
}
/**
* Simple interface to manage signal connection and disconnection
* <pre>{@code
* class EmiterSimple {
* public Signal2<String, Double> signalEvent = new Signal2<>();
* }
*
* class ReceiverSimple {
* public void onEvent(String data, Double data2) {
* Log.error("function receive: " + data + " " + data2);
* }
* public connectLambda(EmiterSimple other) {
* // Note : this lambda is reference a a global, then it will never removed in the connection list ==> refer the local class or @see connectAutoRemoveObject
* other.signalEvent.connect((data, data2) -> {
* Log.error("lambda receive: " + data + " " + data2);
* });
* }
* }
* // use :
* EmiterSimple aaa = new EmiterSimple();
* ReceiverSimple bbb = new ReceiverSimple();
* // Emit a signal:
* aaa.signalEvent.emit("My message ...", 16516.541654);
* // simple direct connection:
* aaa.signalEvent.connect(bbb::onEvent);
* //removable connection (2 possibilities:)
* // First solution (best way ==> does not need to lock a reference on the current object and the remote)
* {
* Connection connect = aaa.signalEvent.connectDynamic(bbb::onEvent());
* // disconnect
* connect.disconnect();
* }
* // Second solution
* {
* Consumer<?> connect = bbb::onEvent;
* aaa.signalEvent.connect(connect);
* // disconnect
* aaa.signalEvent.disconnect(connect);
* }
* }</pre>
*
* @param <T> First type of the signal
* @param <U> Second type of the signal
*
*/
public class Signal2<T, U> extends GenericSignal<BiConsumer<T, U>> {
public void emit(final T valueT, final U valueU) { public void emit(final T valueT, final U valueU) {
List<BiConnectedElement<T, U>> tmp; List<ConnectedElement<BiConsumer<T, U>>> tmp;
// clean the list: // clean the list:
synchronized(this.data) { synchronized(this.data) {
final Iterator<BiConnectedElement<T, U>> iterator = this.data.iterator(); final Iterator<ConnectedElement<BiConsumer<T, U>>> iterator = this.data.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final BiConnectedElement<T, U> elem = iterator.next(); final ConnectedElement<BiConsumer<T, U>> elem = iterator.next();
Object tmpObject = elem.getConsumer(); Object tmpObject = elem.getConsumer();
if (tmpObject == null) { if (tmpObject == null) {
elem.disconnect(); elem.disconnect();
@ -150,9 +76,9 @@ public class Signal2<T, U> implements ConnectionRemoveInterface {
} }
// real call elements // real call elements
{ {
final Iterator<BiConnectedElement<T, U>> iterator = tmp.iterator(); final Iterator<ConnectedElement<BiConsumer<T, U>>> iterator = tmp.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final BiConnectedElement<T, U> elem = iterator.next(); final ConnectedElement<BiConsumer<T, U>> elem = iterator.next();
BiConsumer<T, U> tmpObject = elem.getConsumer(); BiConsumer<T, U> tmpObject = elem.getConsumer();
if (tmpObject == null) { if (tmpObject == null) {
continue; continue;
@ -161,9 +87,5 @@ public class Signal2<T, U> implements ConnectionRemoveInterface {
} }
} }
} }
public int size() {
return this.data.size();
} }
}

View File

@ -1,140 +1,63 @@
package org.atriasoft.esignal; package org.atriasoft.esignal;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
class ConnectedElementEmpty { import org.atriasoft.esignal.internal.ConnectedElement;
protected final WeakReference<Runnable> runner;
public ConnectedElementEmpty(final Runnable runner) { /**
this.runner = new WeakReference<Runnable>(runner); * Simple interface to manage signal connection and disconnection
} * <pre>{@code
* class EmiterSimple {
public Runnable getRunner() { * public SignalEmpty signalEvent = new SignalEmpty();
return this.runner.get(); * }
} *
* class ReceiverSimple {
public boolean isCompatibleWith(final Object elem) { * public void onEvent() {
Object out = this.runner.get(); * Log.error("function receive event ...");
if (out == elem) { * }
return true; * public connectLambda(EmiterSimple other) {
} * // Note : this lambda is reference a a global, then it will never removed in the connection list ==> refer the local class or @see connectAutoRemoveObject
return false; * other.signalEvent.connect(() -> {
} * Log.error("lambda receive event");
* });
public void disconnect() { * }
* }
} * // use :
} * EmiterSimple aaa = new EmiterSimple();
* ReceiverSimple bbb = new ReceiverSimple();
class ConnectedElementDynamicEmpty extends ConnectedElementEmpty { * // Emit a signal:
protected final WeakReference<Object> linkedObject; * aaa.signalEvent.emit();
* // simple direct connection:
public ConnectedElementDynamicEmpty(final Object linkedObject, final Runnable runner) { * aaa.signalEvent.connect(bbb::onEvent);
super(runner); * //removable connection (2 possibilities:)
this.linkedObject = new WeakReference<Object>(linkedObject); * // First solution (best way ==> does not need to lock a reference on the current object and the remote)
} * {
* Connection connect = aaa.signalEvent.connectDynamic(bbb::onEvent());
@Override * // disconnect
public Runnable getRunner() { * connect.disconnect();
if (this.linkedObject.get() == null) { * }
return null; * // Second solution
} * {
return this.runner.get(); * Consumer<?> connect = bbb::onEvent;
} * aaa.signalEvent.connect(connect);
* // disconnect
@Override * aaa.signalEvent.disconnect(connect);
public boolean isCompatibleWith(final Object elem) { * }
if (super.isCompatibleWith(elem)) { * }</pre>
return true; *
} */
Object obj = this.linkedObject.get(); public class SignalEmpty extends GenericSignal<Runnable> {
if (obj == elem) {
return true;
}
return false;
}
@Override
public void disconnect() {
Object obj = this.linkedObject.get();
if (obj == null) {
return;
}
if (obj instanceof Connection tmp) {
tmp.connectionIsRemovedBySignal();
}
}
}
public class SignalEmpty implements ConnectionRemoveInterface {
List<ConnectedElementEmpty> data = new ArrayList<>();
public void clear() {
List<ConnectedElementEmpty> data2 = this.data;
synchronized(this.data) {
this.data = new ArrayList<>();
}
final Iterator<ConnectedElementEmpty> iterator = data2.iterator();
while (iterator.hasNext()) {
final ConnectedElementEmpty elem = iterator.next();
elem.disconnect();
}
}
public void connect(final Runnable function) {
synchronized(this.data) {
this.data.add(new ConnectedElementEmpty(function));
}
}
public void disconnect(final Runnable obj) {
synchronized(this.data) {
final Iterator<ConnectedElementEmpty> iterator = this.data.iterator();
while (iterator.hasNext()) {
final ConnectedElementEmpty elem = iterator.next();
if (elem.isCompatibleWith(obj)) {
iterator.remove();
}
}
}
}
public Connection connectDynamic(final Runnable function) {
Connection out = new Connection(this);
synchronized(this.data) {
this.data.add(new ConnectedElementDynamicEmpty(out, function));
}
return out;
}
public void connectAutoRemoveObject(final Object reference, final Runnable function) {
synchronized(this.data) {
this.data.add(new ConnectedElementDynamicEmpty(reference, function));
}
}
@Override
public void disconnect(final Connection connection) {
synchronized(this.data) {
final Iterator<ConnectedElementEmpty> iterator = this.data.iterator();
while (iterator.hasNext()) {
final ConnectedElementEmpty elem = iterator.next();
if (elem.isCompatibleWith(connection)) {
elem.disconnect();
iterator.remove();
}
}
}
}
public void emit() { public void emit() {
List<ConnectedElementEmpty> tmp; List<ConnectedElement<Runnable>> tmp;
// clean the list: // clean the list:
synchronized(this.data) { synchronized(this.data) {
final Iterator<ConnectedElementEmpty> iterator = this.data.iterator(); final Iterator<ConnectedElement<Runnable>> iterator = this.data.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final ConnectedElementEmpty elem = iterator.next(); final ConnectedElement<Runnable> elem = iterator.next();
Object tmpObject = elem.getRunner(); Object tmpObject = elem.getConsumer();
if (tmpObject == null) { if (tmpObject == null) {
elem.disconnect(); elem.disconnect();
iterator.remove(); iterator.remove();
@ -149,10 +72,10 @@ public class SignalEmpty implements ConnectionRemoveInterface {
} }
// real call elements // real call elements
{ {
final Iterator<ConnectedElementEmpty> iterator = tmp.iterator(); final Iterator<ConnectedElement<Runnable>> iterator = tmp.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
final ConnectedElementEmpty elem = iterator.next(); final ConnectedElement<Runnable> elem = iterator.next();
Runnable tmpObject = elem.getRunner(); Runnable tmpObject = elem.getConsumer();
if (tmpObject == null) { if (tmpObject == null) {
continue; continue;
} }
@ -161,8 +84,4 @@ public class SignalEmpty implements ConnectionRemoveInterface {
} }
} }
public int size() {
return this.data.size();
}
} }

View File

@ -0,0 +1,32 @@
package org.atriasoft.esignal.internal;
import java.lang.ref.WeakReference;
/**
* Connected element on a consumer to Manage event
*
* @param <T> Type Of the Signal
*/
public class ConnectedElement<T> {
protected final WeakReference<T> consumer;
public ConnectedElement(final T consumer) {
this.consumer = new WeakReference<T>(consumer);
}
public T getConsumer() {
return this.consumer.get();
}
public boolean isCompatibleWith(final Object elem) {
Object out = this.consumer.get();
if (out == elem) {
return true;
}
return false;
}
public void disconnect() {
}
}

View File

@ -0,0 +1,50 @@
package org.atriasoft.esignal.internal;
import java.lang.ref.WeakReference;
import org.atriasoft.esignal.Connection;
/**
* Connected element with a dependency of an other object to auto remove the signals
*
* @param <T> Type Of the Signal
*/
public class ConnectedElementDynamic<T> extends ConnectedElement<T> {
protected final WeakReference<Object> linkedObject;
public ConnectedElementDynamic(final Object linkedObject, final T consumer) {
super(consumer);
this.linkedObject = new WeakReference<Object>(linkedObject);
}
@Override
public T getConsumer() {
if (this.linkedObject.get() == null) {
return null;
}
return this.consumer.get();
}
@Override
public boolean isCompatibleWith(final Object elem) {
if (super.isCompatibleWith(elem)) {
return true;
}
Object obj = this.linkedObject.get();
if (obj == elem) {
return true;
}
return false;
}
@Override
public void disconnect() {
Object obj = this.linkedObject.get();
if (obj == null) {
return;
}
if (obj instanceof Connection tmp) {
tmp.connectionIsRemovedBySignal();
}
}
}