Compare commits

...

2 Commits

Author SHA1 Message Date
e6905e78a7 [DOC] add all the comments 2021-06-04 15:45:31 +02:00
70755dbbf4 [DEV] add some generic code 2021-06-04 15:45:13 +02:00
6 changed files with 127 additions and 99 deletions

View File

@ -1,5 +1,12 @@
package org.atriasoft.esignal;
/**
* Interface to permit to the connection to unlink itself.
*/
public interface ConnectionRemoveInterface {
/**
* Request the removing on the specify connection
* @param connection Connection to removed.
*/
void disconnect(final Connection connection);
}

View File

@ -13,8 +13,11 @@ import org.atriasoft.esignal.internal.ConnectedElementDynamic;
* @param <T> generic Runnable, Consumer, or BiConsumer template...
*/
public class GenericSignal<T> implements ConnectionRemoveInterface {
List<ConnectedElement<T>> data = new ArrayList<>();
// List of all connected Links
protected List<ConnectedElement<T>> data = new ArrayList<>();
/**
* Clear all connection on this signal.
*/
public void clear() {
List<ConnectedElement<T>> data2 = this.data;
synchronized(this.data) {
@ -26,12 +29,21 @@ public class GenericSignal<T> implements ConnectionRemoveInterface {
elem.disconnect();
}
}
/**
* Connect a Function to this signal
* @param function Function to connect (Keep a WeakReference on it only)
* @apiNote Make some attention when you connect a global lamba, nothing can pertmit to remove it @see connectAutoRemoveObject()
*/
public void connect(final T function) {
synchronized(this.data) {
this.data.add(new ConnectedElement<T>(function));
}
}
/**
* Disconnect all connection that have this Object/function in reference
* @param obj Object to check the compatibility.
* @apiNote if you add a direct connection like {code connect(this::onEvent(...)) } you can not disconnect it
*/
public void disconnect(final T obj) {
synchronized(this.data) {
final Iterator<ConnectedElement<T>> iterator = this.data.iterator();
@ -43,6 +55,11 @@ public class GenericSignal<T> implements ConnectionRemoveInterface {
}
}
}
/**
* Connect to the signal with a @see Connection object that permit to remove the connection to the signal.
* @param function Function to connect (Keep a WeakReference on it only)
* @return The connection interface.
*/
public Connection connectDynamic(final T function) {
Connection out = new Connection(this);
synchronized(this.data) {
@ -50,9 +67,14 @@ public class GenericSignal<T> implements ConnectionRemoveInterface {
}
return out;
}
public void connectAutoRemoveObject(final Object reference, final T function) {
/**
* Connect to the signal and automatically disconnect when the object is removed
* @param object Object to check if remove to continue keeping the signal active (Keep a WeakReference on it only)
* @param function Function to connect (Keep a WeakReference on it only)
*/
public void connectAutoRemoveObject(final Object object, final T function) {
synchronized(this.data) {
this.data.add(new ConnectedElementDynamic<T>(reference, function));
this.data.add(new ConnectedElementDynamic<T>(object, function));
}
}
@ -70,8 +92,57 @@ public class GenericSignal<T> implements ConnectionRemoveInterface {
}
}
/**
* Get a copy on the List of current connection and remove all the deprecated connection.
* @return The copy of the available connection (Note: the connection can be removed when return)
*/
protected List<ConnectedElement<T>> getACleanedList() {
// first clean the list
cleanedList();
// get a copy of elements
List<ConnectedElement<T>> out = null;
// clean the list:
synchronized(this.data) {
// simple optimization:
if (this.data.isEmpty()) {
return null;
}
// clone the list to permit to have asynchronous remove call
out = new ArrayList<>(this.data);
}
return out;
}
/**
* Clean all the deprecated list of removed elements
*/
protected void cleanedList() {
// clean the list:
synchronized(this.data) {
final Iterator<ConnectedElement<T>> iterator = this.data.iterator();
while (iterator.hasNext()) {
final ConnectedElement<T> elem = iterator.next();
Object tmpObject = elem.getConsumer();
if (tmpObject == null) {
elem.disconnect();
iterator.remove();
}
}
}
}
/**
* Get the Number of current connection (clean is not done).
* @return Number of connection
*/
public int size() {
return this.data.size();
}
/**
* Get the Number of current connection (clean is done).
* @return Number of connection
*/
public int sizeCleaned() {
cleanedList();
return this.data.size();
}
}

View File

@ -1,6 +1,5 @@
package org.atriasoft.esignal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
@ -52,43 +51,25 @@ import org.atriasoft.esignal.internal.ConnectedElement;
*
*/
public class Signal<T> extends GenericSignal<Consumer<T>> {
/**
* Emit a signal on all element connect (and clean the list of unlinked elements).
* @param value Value to set in parameter.
*/
public void emit(final T value) {
List<ConnectedElement<Consumer<T>>> tmp;
// clean the list:
synchronized(this.data) {
final Iterator<ConnectedElement<Consumer<T>>> iterator = this.data.iterator();
while (iterator.hasNext()) {
final ConnectedElement<Consumer<T>> elem = iterator.next();
Object tmpObject = elem.getConsumer();
if (tmpObject == null) {
elem.disconnect();
iterator.remove();
}
}
// simple optimization:
if (this.data.isEmpty()) {
return;
}
// clone the list to permit to have asynchronous remove call
tmp = new ArrayList<>(this.data);
List<ConnectedElement<Consumer<T>>> tmp = getACleanedList();
if (tmp == null) {
return;
}
// real call elements
{
final Iterator<ConnectedElement<Consumer<T>>> iterator = tmp.iterator();
while (iterator.hasNext()) {
final ConnectedElement<Consumer<T>> elem = iterator.next();
Consumer<T> tmpObject = elem.getConsumer();
if (tmpObject == null) {
continue;
}
tmpObject.accept(value);
final Iterator<ConnectedElement<Consumer<T>>> iterator = tmp.iterator();
while (iterator.hasNext()) {
final ConnectedElement<Consumer<T>> elem = iterator.next();
Consumer<T> tmpObject = elem.getConsumer();
if (tmpObject == null) {
// Not a dead code, but very hard to simply test it.
continue;
}
tmpObject.accept(value);
}
}
@Override
public int size() {
return this.data.size();
}
}

View File

@ -1,6 +1,5 @@
package org.atriasoft.esignal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiConsumer;
@ -54,37 +53,25 @@ import org.atriasoft.esignal.internal.ConnectedElement;
*
*/
public class Signal2<T, U> extends GenericSignal<BiConsumer<T, U>> {
/**
* Emit a signal on all element connect (and clean the list of unlinked elements).
* @param valueT First parameter value to emit.
* @param valueU Second parameter value to emit.
*/
public void emit(final T valueT, final U valueU) {
List<ConnectedElement<BiConsumer<T, U>>> tmp;
// clean the list:
synchronized(this.data) {
final Iterator<ConnectedElement<BiConsumer<T, U>>> iterator = this.data.iterator();
while (iterator.hasNext()) {
final ConnectedElement<BiConsumer<T, U>> elem = iterator.next();
Object tmpObject = elem.getConsumer();
if (tmpObject == null) {
elem.disconnect();
iterator.remove();
}
}
// simple optimization:
if (this.data.isEmpty()) {
return;
}
// clone the list to permit to have asynchronous remove call
tmp = new ArrayList<>(this.data);
List<ConnectedElement<BiConsumer<T, U>>> tmp = getACleanedList();
if (tmp == null) {
return;
}
// real call elements
{
final Iterator<ConnectedElement<BiConsumer<T, U>>> iterator = tmp.iterator();
while (iterator.hasNext()) {
final ConnectedElement<BiConsumer<T, U>> elem = iterator.next();
BiConsumer<T, U> tmpObject = elem.getConsumer();
if (tmpObject == null) {
continue;
}
tmpObject.accept(valueT, valueU);
final Iterator<ConnectedElement<BiConsumer<T, U>>> iterator = tmp.iterator();
while (iterator.hasNext()) {
final ConnectedElement<BiConsumer<T, U>> elem = iterator.next();
BiConsumer<T, U> tmpObject = elem.getConsumer();
if (tmpObject == null) {
continue;
}
tmpObject.accept(valueT, valueU);
}
}
}

View File

@ -1,6 +1,5 @@
package org.atriasoft.esignal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@ -49,38 +48,23 @@ import org.atriasoft.esignal.internal.ConnectedElement;
*
*/
public class SignalEmpty extends GenericSignal<Runnable> {
/**
* Emit a signal on all element connect (and clean the list of unlinked elements).
*/
public void emit() {
List<ConnectedElement<Runnable>> tmp;
// clean the list:
synchronized(this.data) {
final Iterator<ConnectedElement<Runnable>> iterator = this.data.iterator();
while (iterator.hasNext()) {
final ConnectedElement<Runnable> elem = iterator.next();
Object tmpObject = elem.getConsumer();
if (tmpObject == null) {
elem.disconnect();
iterator.remove();
}
}
// simple optimization:
if (this.data.isEmpty()) {
return;
}
// clone the list to permit to have asynchronous remove call
tmp = new ArrayList<>(this.data);
List<ConnectedElement<Runnable>> tmp = getACleanedList();
if (tmp == null) {
return;
}
// real call elements
{
final Iterator<ConnectedElement<Runnable>> iterator = tmp.iterator();
while (iterator.hasNext()) {
final ConnectedElement<Runnable> elem = iterator.next();
Runnable tmpObject = elem.getConsumer();
if (tmpObject == null) {
continue;
}
tmpObject.run();
final Iterator<ConnectedElement<Runnable>> iterator = tmp.iterator();
while (iterator.hasNext()) {
final ConnectedElement<Runnable> elem = iterator.next();
Runnable tmpObject = elem.getConsumer();
if (tmpObject == null) {
continue;
}
tmpObject.run();
}
}

View File

@ -117,9 +117,7 @@ public class TestSignalEmpty {
receiver = null;
Assertions.assertEquals(1, sender.signalEvent.size());
System.gc();
sender.sendEvent();
Assertions.assertEquals(0, sender.signalEvent.size());
Assertions.assertEquals(0, sender.signalEvent.sizeCleaned());
Log.warning("Test 1 [ END ]");
}