/* DefaultDocumentManager.java created 2007-09-10 * */ package org.signalml.app.document; import java.io.File; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Vector; import javax.swing.event.EventListenerList; import org.apache.log4j.Logger; import org.signalml.exception.SanityCheckException; import org.signalml.plugin.export.signal.Document; /** * Implementation of {@link DocumentManager}. * Each {@link Document document} is stored in three collections: * <ul> * <li>the vector with all documents,</li> * <li>the vector containing documents of a specified {@link ManagedDocumentType * type},</li> * <li>the map associating files with the documents backed with these files. * </li> * </ul> * Contains {@link DocumentManagerListener listeners} and informs them about * changes. * * @author Michal Dobaczewski © 2007-2008 CC Otwarte Systemy Komputerowe Sp. z o.o. */ public class DefaultDocumentManager implements DocumentManager { protected static final Logger logger = Logger.getLogger(DefaultDocumentManager.class); /** * the vector containing all {@link Document documents} in this manager */ private Vector<Document> documents = new Vector<Document>(100,100); /** * the map associating files with the {@link Document documents} backed * with them */ private Map<File,Document> documentsByFile = new HashMap<File,Document>(100); /** * the map associating {@link ManagedDocumentType types} of {@link Document * documents} with vectors of {@link Document documents} of these types */ private Map<ManagedDocumentType,Vector<Document>> documentVectorsByType = new HashMap<ManagedDocumentType,Vector<Document>>(10); /** * the list of {@link DocumentManagerListener listeners} */ private EventListenerList listenerList = new EventListenerList(); @Override public boolean isAllSaved() { synchronized (this) { for (Document document : documents) { if (document instanceof MutableDocument) { MutableDocument md = (MutableDocument) document; if (!md.isSaved()) { return false; } } } } return true; } @Override public int getDocumentCount() { synchronized (this) { return documents.size(); } } @Override public Document getDocumentAt(int index) { synchronized (this) { return documents.elementAt(index); } } @Override public int getIndexOfDocument(Document document) { synchronized (this) { return documents.indexOf(document); } } @Override public Iterator<Document> iterator() { synchronized (this) { return documents.iterator(); } } @Override public Document getDocumentByFile(File file) { synchronized (this) { return documentsByFile.get(file); } } /** * Adds a {@link Document document} to this manager. * In order to do it: * <ul> * <li>if the document {@link FileBackedDocument has a backing file} it is * added to the map associating files with the documents backed with them * </li> * <li>adds the document to the vector of all documents,</li> * <li>adds the document to the vector of documents of a specified * {@link ManagedDocumentType type}.</li> * </ul> */ @Override public void addDocument(Document document) { synchronized (this) { if (documents.contains(document)) { logger.info("Document already in the manager"); return; } if (document instanceof FileBackedDocument) { FileBackedDocument fbd = (FileBackedDocument) document; File file = fbd.getBackingFile(); if (file != null) { File absFile = file.getAbsoluteFile(); if (documentsByFile.containsKey(absFile)) { throw new SanityCheckException("Sanity check failed, the same path already open"); } documentsByFile.put(absFile, document); } } documents.add(document); ManagedDocumentType type = ManagedDocumentType.getForClass(document.getClass()); int inTypeIndex = -1; if (type != null) { Vector<Document> vector = documentVectorsByType.get(type); if (vector == null) { vector = new Vector<Document>(100); documentVectorsByType.put(type, vector); } vector.add(document); inTypeIndex = vector.indexOf(document); } fireDocumentAdded(document, documents.indexOf(document), inTypeIndex); } } @Override public void removeDocumentAt(int index) { synchronized (this) { removeDocumentInternal(documents.elementAt(index)); } } @Override public void removeDocument(Document document) { synchronized (this) { if (!documents.contains(document)) { return; } removeDocumentInternal(document); } } /** * Removes a {@link Document document} from this manager: * <ul> * <li>if the document {@link FileBackedDocument has a backing file} * it is removed form the map associating files with the documents backed * with them,</li> * <li>removes the document from the vector of documents of a specified * {@link ManagedDocumentType type},</li> * <li>removes the document from a vector of all documents in this manager. * </li> * </ul> * @param document the document to be removed */ private void removeDocumentInternal(Document document) { if (document instanceof FileBackedDocument) { FileBackedDocument fbd = (FileBackedDocument) document; File file = fbd.getBackingFile(); if (file != null) { documentsByFile.remove(file.getAbsoluteFile()); } } int index = documents.indexOf(document); ManagedDocumentType type = ManagedDocumentType.getForClass(document.getClass()); int inTypeIndex = -1; if (type != null) { Vector<Document> vector = documentVectorsByType.get(type); if (vector != null) { inTypeIndex = vector.indexOf(document); vector.remove(document); } } documents.remove(document); fireDocumentRemoved(document, index, inTypeIndex); } @Override public void onDocumentPathChange(Document document, File oldFile, File newFile) { synchronized (this) { if (!documents.contains(document)) { return; } if (oldFile != null) { documentsByFile.remove(oldFile.getAbsoluteFile()); } if (newFile != null) { documentsByFile.put(newFile.getAbsoluteFile(), document); } ManagedDocumentType type = ManagedDocumentType.getForClass(document.getClass()); int inTypeIndex = -1; if (type != null) { Vector<Document> vector = documentVectorsByType.get(type); if (vector != null) { inTypeIndex = vector.indexOf(document); } } fireDocumentPathChanged(document, documents.indexOf(document), inTypeIndex); } } @Override public int getDocumentCount(ManagedDocumentType type) { synchronized (this) { Vector<Document> vector = documentVectorsByType.get(type); if (vector != null) { return vector.size(); } } return 0; } @Override public Document getDocumentAt(ManagedDocumentType type, int index) { synchronized (this) { Vector<Document> vector = documentVectorsByType.get(type); if (vector != null) { return vector.elementAt(index); } } return null; } @Override public int getIndexOfDocument(ManagedDocumentType type, Document document) { synchronized (this) { Vector<Document> vector = documentVectorsByType.get(type); if (vector != null) { return vector.indexOf(document); } } return -1; } /** * Informs all {@link DocumentManagerListener listeners} that the * {@link Document document} was added. * @param document the added document * @param index the index of the document in the * collection of all documents in this manager * @param inTypeIndex the index of the document in the * collection of documents of a specified {@link ManagedDocumentType type} */ protected void fireDocumentAdded(Document document, int index, int inTypeIndex) { Object[] listeners = listenerList.getListenerList(); DocumentManagerEvent e = null; for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==DocumentManagerListener.class) { if (e == null) { e = new DocumentManagerEvent(this,document,index,inTypeIndex); } ((DocumentManagerListener)listeners[i+1]).documentAdded(e); } } } /** * Informs all {@link DocumentManagerListener listeners} that the * {@link Document document} was removed. * @param document the removed document * @param index the index of the document in the * collection of all documents in this manager * @param inTypeIndex the index of the document in the * collection of documents of a specified {@link ManagedDocumentType type} */ protected void fireDocumentRemoved(Document document, int index, int inTypeIndex) { Object[] listeners = listenerList.getListenerList(); DocumentManagerEvent e = null; for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==DocumentManagerListener.class) { if (e == null) { e = new DocumentManagerEvent(this,document,index,inTypeIndex); } ((DocumentManagerListener)listeners[i+1]).documentRemoved(e); } } } /** * Informs all {@link DocumentManagerListener listeners} that the path to * the {@link Document document} has changed. * @param document the document with the changed path * @param index the index of the document in the * collection of all documents in this manager * @param inTypeIndex the index of the document in the * collection of documents of a specified {@link ManagedDocumentType type} */ protected void fireDocumentPathChanged(Document document, int index, int inTypeIndex) { Object[] listeners = listenerList.getListenerList(); DocumentManagerEvent e = null; for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==DocumentManagerListener.class) { if (e == null) { e = new DocumentManagerEvent(this,document,index,inTypeIndex); } ((DocumentManagerListener)listeners[i+1]).documentPathChanged(e); } } } @Override public void addDocumentManagerListener(DocumentManagerListener listener) { synchronized (this) { listenerList.add(DocumentManagerListener.class, listener); } } @Override public void removeDocumentManagerListener(DocumentManagerListener listener) { synchronized (this) { listenerList.remove(DocumentManagerListener.class, listener); } } }