2014-06-11 15:35:06 +02:00
|
|
|
package processing.buffer;
|
|
|
|
|
|
|
|
import conteneurs.ObservableObject;
|
|
|
|
import conteneurs.ObservableObjectEvent;
|
|
|
|
import processing.processes.Process;
|
|
|
|
|
|
|
|
import javax.swing.*;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
2014-06-14 20:45:14 +02:00
|
|
|
/**
|
|
|
|
* Classe
|
|
|
|
* @param <T>
|
|
|
|
*/
|
2014-06-11 15:35:06 +02:00
|
|
|
public class Buffer <T> {
|
|
|
|
|
|
|
|
private final ArrayList<BufferEventListener> bufferListeners;
|
|
|
|
private final ArrayList<Process<?>> usedBy;
|
|
|
|
final private Storage.Type arrayType;
|
|
|
|
private String name = "";
|
|
|
|
private Class type = null;
|
|
|
|
private boolean itemObservable = false;
|
|
|
|
private Process.ProcessingOrder processingOrder = Process.ProcessingOrder.NO_ORDER;
|
|
|
|
|
|
|
|
private Storage<T> samples;
|
|
|
|
private final StorageArrayList<Integer> updateIDs;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construit une instance de buffer selon un modéle de stockage
|
|
|
|
* @param arrayType Modèle de stockage
|
|
|
|
*/
|
|
|
|
public Buffer(Storage.Type arrayType) {
|
|
|
|
switch(arrayType){
|
|
|
|
case ArrayList:
|
|
|
|
samples = new StorageArrayList<>();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
updateIDs = new StorageArrayList<>();
|
|
|
|
|
|
|
|
bufferListeners = new ArrayList<>();
|
|
|
|
usedBy = new ArrayList<>();
|
|
|
|
this.arrayType = arrayType;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void emitBufferEvent(BufferEvent event) {
|
|
|
|
|
|
|
|
for(Process p: usedBy)
|
|
|
|
p.addBufferEvent(event);
|
|
|
|
|
|
|
|
SwingUtilities.invokeLater(() -> {
|
|
|
|
for(BufferEventListener p: bufferListeners)
|
|
|
|
p.bufferEvent(event);
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
private void emitBufferEvent(BufferEvent.Type type, int sampleID) {
|
|
|
|
emitBufferEvent(new BufferEvent(type, sampleID, this));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Connecte ce buffer à un listener qu'il fournit, de manière à propager les BufferEvent jusqu'à cet objet
|
|
|
|
* @param p objet à connecter
|
|
|
|
* @return Vrai si l'opération à fontionner (si le process n'était pas déjà ajouté)
|
|
|
|
*/
|
|
|
|
public boolean addBufferListener(BufferEventListener p){
|
|
|
|
if(bufferListeners.contains(p))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bufferListeners.add(p);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Déconnecte ce buffer d'un listener
|
|
|
|
* @param p listener à déconnecter
|
|
|
|
* @return Vrai si l'opération à fontionner (si le listener était dans la liste)
|
|
|
|
*/
|
|
|
|
public boolean removeBufferListener(BufferEventListener p){
|
|
|
|
int id = bufferListeners.indexOf(p);
|
|
|
|
if(id == -1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bufferListeners.remove(id);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Connecte ce buffer à une entrée d'un algo
|
|
|
|
* @param p Algo à connecte
|
|
|
|
* @return Vrai si l'opération c'est bien passée (si il n'avait pas déjà été appelée)
|
|
|
|
*/
|
|
|
|
public boolean connectProcess(Process p){
|
|
|
|
if(usedBy.contains(p))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
usedBy.add(p);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean disconnectProcess(Process p){
|
|
|
|
if(!usedBy.contains(p))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
usedBy.remove(p);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retourne le type de stockage de ce buffer
|
|
|
|
* @return type de stockage
|
|
|
|
*/
|
|
|
|
public Storage.Type getArrayType(){
|
|
|
|
return arrayType;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retourne l'indice de version associé à un échantillon.
|
|
|
|
* @param id index de l'échantillon
|
|
|
|
* @return indice de version
|
|
|
|
*/
|
|
|
|
public Integer getUpdateID(int id) {
|
|
|
|
return updateIDs.get(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Surcharge de set(int,T,int) mais avec un indice de version = 1
|
|
|
|
* @param index indice de l'échantillon à modifier ou ajouter
|
|
|
|
* @param value valeur de l'échantillon
|
|
|
|
* @return la valeur de l'échantillon précédant /!\ Peut retourner null si aucun n'échantillon ne correspondait à l'index /!\
|
|
|
|
*/
|
|
|
|
public T set(int index, T value){ return set(index, value, null);}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Modifie un échantillon ou l'ajoute à au stockage si index ne correspond à aucun élément, complète alors avec des échantillons = null.
|
|
|
|
* De plus, updateId est associé à l'échantillon en tant qu'indice de version
|
|
|
|
* @param index indice de l'échantillon à modifier ou ajouter
|
|
|
|
* @param value valeur de l'échantillon
|
|
|
|
* @param updateId indice de version à associer
|
|
|
|
* @return la valeur de l'échantillon précédant /!\ Peut retourner null si aucun n'échantillon ne correspondait à l'index /!\
|
|
|
|
*/
|
|
|
|
public T set(int index, T value, Integer updateId){
|
|
|
|
|
|
|
|
T previous = samples.set( index, value);
|
|
|
|
updateIDs.set(index, updateId);
|
|
|
|
|
|
|
|
if(itemObservable)
|
|
|
|
((ObservableObject)value).addListener(
|
|
|
|
new ObservableObject.SignedListener() {
|
|
|
|
@Override
|
|
|
|
public void observableOjectEvent(ObservableObjectEvent e) {
|
|
|
|
emitBufferEvent(BufferEvent.Type.SAMPLE_CHANGED, index);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String signature() {
|
|
|
|
return this.hashCode() + ":" + index;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if(previous==null) {
|
|
|
|
emitBufferEvent(BufferEvent.Type.SAMPLE_ADDED, index);
|
|
|
|
return null;
|
|
|
|
}else{
|
|
|
|
if(value==null)
|
|
|
|
emitBufferEvent(BufferEvent.Type.SAMPLE_DELETED, index);
|
|
|
|
else
|
|
|
|
emitBufferEvent(BufferEvent.Type.SAMPLE_CHANGED, index);
|
|
|
|
|
|
|
|
if(itemObservable)
|
|
|
|
((ObservableObject)previous).removeSignedListener(this.hashCode()+":"+index);
|
|
|
|
}
|
|
|
|
|
|
|
|
return previous;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Surcharge de addNote(T) mais avec un indice de version = 1
|
|
|
|
* @param value valeur de l'échantillon à ajouter
|
|
|
|
* @return vrai si l'opération c'est bien passé (mais peut-elle mal se passer?)...
|
|
|
|
*/
|
|
|
|
public boolean add(T value){
|
|
|
|
return add(value,0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Ajoute un élément après le dernier élément du stockage et lui associe un indice de version
|
|
|
|
* @param value valeur de l'échantillon à ajouter
|
|
|
|
* @param updateId indice de version à associer
|
|
|
|
* @return vrai si l'opération c'est bien passé (mais peut-elle rater?)...
|
|
|
|
*/
|
|
|
|
boolean add(T value, int updateId){
|
|
|
|
if(!samples.add(value))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
updateIDs.add(updateId);
|
|
|
|
emitBufferEvent(BufferEvent.Type.SAMPLE_ADDED, samples.size() - 1);
|
|
|
|
|
|
|
|
if(itemObservable)
|
|
|
|
((ObservableObject)value).addListener(new ObservableObject.Listener() {
|
|
|
|
@Override
|
|
|
|
public void observableOjectEvent(ObservableObjectEvent e) {
|
|
|
|
emitBufferEvent(BufferEvent.Type.SAMPLE_CHANGED, samples.size()-1);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Renvoie la valeur d'un échantillon
|
|
|
|
* @param id index de l'échantillon
|
|
|
|
* @return valeur de l'échantillon /!\ Peut revoyer null si aucun échantillon ne correspond à l'index /!\
|
|
|
|
*/
|
|
|
|
public T get(int id){
|
|
|
|
return samples.get(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Supprime un échantillon du buffer
|
|
|
|
* @param id index de l'échantillon à supprimer
|
|
|
|
* @return la valeur de l'échantillon supprimer /!\ Peut renvoyer null si aucun échantillon ne correspondait à cet index /!\
|
|
|
|
*/
|
|
|
|
public T remove(int id){
|
|
|
|
|
|
|
|
T sampleDeleted = samples.remove(id);
|
|
|
|
updateIDs.remove(id);
|
|
|
|
|
|
|
|
if(sampleDeleted!=null) {
|
|
|
|
emitBufferEvent(BufferEvent.Type.SAMPLE_DELETED, id);
|
|
|
|
if(itemObservable)
|
|
|
|
((ObservableObject)sampleDeleted).removeSignedListener(this.hashCode() + ":" + id);
|
|
|
|
}
|
|
|
|
|
|
|
|
return sampleDeleted;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retourne le nombre maximum d'élément de tableau (dernier index +1)
|
|
|
|
* @return la taille de l'espace de stockage
|
|
|
|
*/
|
|
|
|
public int size(){
|
|
|
|
return samples.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Vérifie si le buffer est vide où non
|
|
|
|
* @return Vrai si il est vide
|
|
|
|
*/
|
|
|
|
public boolean isEmpty(){
|
|
|
|
return size()==0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isCorrectIndex(int id){
|
|
|
|
return get(id)!=null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retourne la liste des indexs associés à des éléments dans le Storage: aucun de ces indexs ne retournera null par get(index)
|
|
|
|
* * @return la liste des indexs de tout les éléments du Storage
|
|
|
|
*/
|
|
|
|
public ArrayList<Integer> getSamplesIndex(){
|
|
|
|
return samples.getSamplesIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public StorageIterator<T> getIterator(){
|
|
|
|
return samples.getIterator();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Génère un iterateur pointant initialement sur l'élément de la liste correspondant à l'index en argument
|
|
|
|
* @param id index de l'élément sur lequel pointe initialement l'iterateur
|
|
|
|
* @return Un iterateur sur ce stockage
|
|
|
|
*/
|
|
|
|
|
|
|
|
public StorageIterator<T> getIterator(int id){
|
|
|
|
return samples.getIterator(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void lastSampleAdded(){
|
|
|
|
emitBufferEvent(BufferEvent.Type.INTERRUPT_PROCESS,-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void clear(){
|
|
|
|
switch(arrayType){
|
|
|
|
case ArrayList:
|
|
|
|
samples = new StorageArrayList<>();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
emitBufferEvent(BufferEvent.Type.INVALIDATE,-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void invalidateProcessedSamples(){
|
|
|
|
emitBufferEvent(BufferEvent.Type.INVALIDATE,-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
public ArrayList<processing.processes.Process<?>> getUsedBy() {
|
|
|
|
return usedBy;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Réserve directement un espace sans modifier la taille du tableau (si la daforme si prête)
|
|
|
|
* @param capacity capacité maximale;
|
|
|
|
*/
|
|
|
|
public void tryEnsureCapacity(int capacity){
|
|
|
|
samples.tryEnsureCapacity(capacity);
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getName() {
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setName(String name) {
|
|
|
|
this.name = name;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Class getType() {
|
|
|
|
if(type==null&&!samples.isEmpty())
|
|
|
|
return samples.get(getSamplesIndex().get(0)).getClass();
|
|
|
|
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setType(Class type) {
|
|
|
|
this.type = type;
|
|
|
|
Class[] classes = type.getClasses();
|
|
|
|
for (int i = 0; i < classes.length; i++)
|
|
|
|
if (classes[i].equals(ObservableObject.class)) {
|
|
|
|
itemObservable = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
itemObservable = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public Process.ProcessingOrder getProcessingOrder() {
|
|
|
|
return processingOrder;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setProcessingOrder(Process.ProcessingOrder processingOrder) {
|
|
|
|
this.processingOrder = processingOrder;
|
|
|
|
}
|
|
|
|
}
|