/* Reversound is used to get the music sheet of a piece from a music file. Copyright (C) 2014 Gabriel AUGENDRE Copyright (C) 2014 Gabriel DIENY Copyright (C) 2014 Arthur GAUCHER Copyright (C) 2014 Gabriel LEPETIT-AIMON This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package processing.buffer; import conteneurs.ObservableObject; import conteneurs.ObservableObjectEvent; import processing.processes.Process; import javax.swing.*; import java.util.ArrayList; /** * Classe * @param */ public class Buffer { private final ArrayList bufferListeners; private final ArrayList> 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 samples; private final StorageArrayList 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 getSamplesIndex(){ return samples.getSamplesIndex(); } public StorageIterator 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 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> 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; } }