/* DefaultMRUDRegistry.java created 2007-09-12 * */ package org.signalml.app.document.mrud; import java.io.File; import java.io.IOException; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.Vector; import javax.swing.event.EventListenerList; import org.signalml.app.config.MRUDConfiguration; import org.signalml.app.document.ManagedDocumentType; import org.signalml.plugin.export.signal.Document; import com.thoughtworks.xstream.XStream; /** * Implementation of {@link MRUDRegistry}. * The initial capacity of it is 50 (this cache can remember 50 * {@link MRUDEntry entries}), but can be changed through * {@link #setRegistrySize(int)}. * Each entry is stored in three collections: * <ul> * <li>the vector with all entries,</li> * <li>the vector containing entries for a specified {@link ManagedDocumentType * type} of a document,</li> * <li>the map associating files with the {@link MRUDEntry entries} in which * they are described.</li> * </ul> * This class contains also {@link MRUDRegistryListener listeners} listening * for addition and removal of entries. * * @author Michal Dobaczewski © 2007-2008 CC Otwarte Systemy Komputerowe Sp. z o.o. */ public class DefaultMRUDRegistry implements MRUDRegistry { /** * the vector with all {@link MRUDEntry entries} in this registry */ private Vector<MRUDEntry> entries = new Vector<MRUDEntry>(100); /** * the map associating {@link ManagedDocumentType types} of {@link Document * documents} with vectors of {@link MRUDEntry entries} for these types */ private Map<ManagedDocumentType,Vector<MRUDEntry>> entryVectorsByType = new HashMap<ManagedDocumentType,Vector<MRUDEntry>>(10); /** * the map associating files with the {@link MRUDEntry entries} in which * they are described */ private Map<File,MRUDEntry> entriesByFile = new HashMap<File,MRUDEntry>(100); /** * the number of entries that can be stored in this registry */ private int registrySize = 50; /** * {@link MRUDComparator comparator} of {@link MRUDEntry entries} */ private MRUDComparator comparator; /** * true if {@link #entries} is sorted, false otherwise */ private boolean isMainSorted = true; /** * the profile directory */ private File profileDir; /** * the stream used to read stored {@link MRUDEntry entires} from file */ private XStream streamer; /** * the listeners listening on changes (registering an removing * {@link MRUDEntry entries}) in this registry */ private EventListenerList listenerList = new EventListenerList(); /** * Constructor. Sets the default comparator. */ public DefaultMRUDRegistry() { comparator = new MRUDComparator(); } /** * Returns the number of {@link MRUDEntry entries} that can be stored * in this registry. * @return the number of entries that can be stored in this registry */ public int getRegistrySize() { synchronized (this) { return registrySize; } } /** * Sets the number of {@link MRUDEntry entries} that can be stored in this * registry. * @param registrySize the number of entries that can be stored in this * registry */ public void setRegistrySize(int registrySize) { synchronized (this) { this.registrySize = registrySize; } } @Override public int getMRUDEntryCount() { synchronized (this) { return entries.size(); } } @Override public MRUDEntry getMRUDEntryAt(int index) { synchronized (this) { return entries.elementAt(index); } } @Override public int getIndexOfMRUDEntry(MRUDEntry mrud) { synchronized (this) { return entries.indexOf(mrud); } } @Override public int getMRUDEntryCount(ManagedDocumentType type) { synchronized (this) { Vector<MRUDEntry> vector = entryVectorsByType.get(type); if (vector != null) { return vector.size(); } } return 0; } @Override public MRUDEntry getMRUDEntryAt(ManagedDocumentType type, int index) { synchronized (this) { Vector<MRUDEntry> vector = entryVectorsByType.get(type); if (vector != null) { return vector.elementAt(index); } } return null; } @Override public int getIndexOfMRUDEntry(ManagedDocumentType type, MRUDEntry mrud) { synchronized (this) { Vector<MRUDEntry> vector = entryVectorsByType.get(type); if (vector != null) { return vector.indexOf(mrud); } } return -1; } /** * Removes all {@link MRUDEntry entries} from this registry. */ private void clear() { entries.clear(); entriesByFile.clear(); entryVectorsByType.clear(); } /** * Adds a new {@link MRUDEntry entry} to this registry. * In order to do that: * <ul> * <li>if there is an entry describing the same file removes it,</li> * <li>if this registry is full (number of entries equals * {@link #registrySize}) the entry with the oldest * {@link MRUDEntry#getLastTimeOpened() last open time} is removed</li> * <li>the entry is added to the {@link #entries vector} with all entries, * </li> * <li>the entry is added to the {@link #entriesByFile map} associating * files with the entries in which they are described,</li> * <li>the entry is added to the vector with entries for a specified * {@link ManagedDocumentType type} of a {@link Document document}. * If such vector doesn't exist it is created and added to * {@link #entryVectorsByType},</li> * </ul> * @param mrud the entry to be added * @return the vector with entries for a specified type of a document */ private Vector<MRUDEntry> registerMRUDEntryInternal(MRUDEntry mrud) { String path = mrud.getPath(); File file = (new File(path)).getAbsoluteFile(); MRUDEntry existingMrud = entriesByFile.get(file); if (existingMrud != null) { int exIndex = entries.indexOf(existingMrud); entries.remove(exIndex); Vector<MRUDEntry> exVector = entryVectorsByType.get(existingMrud.getDocumentType()); int exInTypeIndex = -1; if (exVector != null) { exInTypeIndex = exVector.indexOf(existingMrud); exVector.remove(exInTypeIndex); } fireMrudEntryRemoved(existingMrud, exIndex, exInTypeIndex); } if (entries.size() == registrySize) { // something needs to be removed if (!isMainSorted) { Collections.sort(entries,comparator); isMainSorted = true; } MRUDEntry dumpedMrud = entries.lastElement(); int duIndex = entries.indexOf(dumpedMrud); Vector<MRUDEntry> duVector = entryVectorsByType.get(dumpedMrud.getDocumentType()); int duInTypeIndex = -1; if (duVector != null) { duInTypeIndex = duVector.indexOf(dumpedMrud); duVector.remove(duInTypeIndex); } entries.remove(duIndex); fireMrudEntryRemoved(dumpedMrud, duIndex, duInTypeIndex); } entriesByFile.put(file,mrud); entries.add(mrud); isMainSorted = false; Vector<MRUDEntry> vector = entryVectorsByType.get(mrud.getDocumentType()); if (vector == null) { vector = new Vector<MRUDEntry>(100); entryVectorsByType.put(mrud.getDocumentType(), vector); } vector.add(mrud); return vector; } @Override public void registerMRUDEntry(MRUDEntry mrud) { synchronized (this) { if (entries.contains(mrud)) { return; } Vector<MRUDEntry> vector = registerMRUDEntryInternal(mrud); Collections.sort(entries, comparator); isMainSorted = true; Collections.sort(vector,comparator); fireMrudEntryRegistered(mrud, entries.indexOf(mrud), vector.indexOf(mrud)); } } /** * Writes all {@link MRUDEntry entries} to the specified file. * @param file the file * @throws IOException if there is an I/O error while writing * entries to the file */ public void writeToPersistence(File file) throws IOException { MRUDEntry[] mruds; synchronized (this) { mruds = new MRUDEntry[entries.size()]; entries.toArray(mruds); } MRUDConfiguration config = new MRUDConfiguration(mruds); config.writeToXML((file == null) ? config.getStandardFile(profileDir) : file, streamer); } /** * Reads {@link MRUDEntry entries} from the specified file. * If the number of read entries is greater then the * {@link #getRegistrySize() size} of this registry, only first {@code size} * entries is used. * @param file the file * @throws IOException if an I/O error occurs while reading entries from * file */ public void readFromPersistence(File file) throws IOException { MRUDConfiguration config = new MRUDConfiguration();; config.readFromXML((file == null) ? config.getStandardFile(profileDir) : file, streamer); synchronized (this) { clear(); MRUDEntry[] mruds = config.getMruds(); for (int i=0; i<Math.min(mruds.length, registrySize); i++) { registerMRUDEntryInternal(mruds[i]); } Collections.sort(entries, comparator); isMainSorted = true; for (Vector<MRUDEntry> vector : entryVectorsByType.values()) { Collections.sort(vector,comparator); } } } /** * Returns the profile directory. * @return the profile directory */ public File getProfileDir() { return profileDir; } /** * Sets the profile directory. * @param profileDir the profile directory */ public void setProfileDir(File profileDir) { this.profileDir = profileDir; } /** * Returns the stream used to read stored {@link MRUDEntry entires} from * file. * @return the stream used to read stored {@link MRUDEntry entires} from * file */ public XStream getStreamer() { return streamer; } /** * Sets the stream used to read stored {@link MRUDEntry entires} from file. * @param streamer the stream used to read stored {@link MRUDEntry entires} * from file */ public void setStreamer(XStream streamer) { this.streamer = streamer; } /** * Informs all {@link MRUDRegistryListener listeners} that the * {@link MRUDEntry entry} was added to this registry. * @param entry the added entry * @param index the index of this entry in the vector of all entries * @param inTypeIndex the index of this entry in the vector of entries * for a specified {@link ManagedDocumentType type} of a {@link Document * document} */ protected void fireMrudEntryRegistered(MRUDEntry entry, int index, int inTypeIndex) { Object[] listeners = listenerList.getListenerList(); MRUDRegistryEvent e = null; for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==MRUDRegistryListener.class) { if (e == null) { e = new MRUDRegistryEvent(this,entry,index,inTypeIndex); } ((MRUDRegistryListener)listeners[i+1]).mrudEntryRegistered(e); } } } /** * Informs all {@link MRUDRegistryListener listeners} that the * {@link MRUDEntry entry} was removed from this registry. * @param entry the removed entry * @param index the index of this entry in the vector of all entries * @param inTypeIndex the index of this entry in the vector of entries * for a specified {@link ManagedDocumentType type} of a {@link Document * document} */ protected void fireMrudEntryRemoved(MRUDEntry entry, int index, int inTypeIndex) { Object[] listeners = listenerList.getListenerList(); MRUDRegistryEvent e = null; for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==MRUDRegistryListener.class) { if (e == null) { e = new MRUDRegistryEvent(this,entry,index,inTypeIndex); } ((MRUDRegistryListener)listeners[i+1]).mrudEntryRemoved(e); } } } @Override public void addMRUDRegistryListener(MRUDRegistryListener listener) { listenerList.add(MRUDRegistryListener.class, listener); } @Override public void removeMRUDRegistryListener(MRUDRegistryListener listener) { listenerList.remove(MRUDRegistryListener.class, listener); } /** * Compares {@link MRUDEntry entries} due to their last open time. * The entry is smaller then another entry if it was last open after that * entry. */ class MRUDComparator implements Comparator<MRUDEntry> { /** * Compares two {@link MRUDEntry entries}. * @param o1 first entry * @param o2 second entry * @return {@code 0} if the entries have the same last open time, * value grater then {@code 0} if the second entry was last time opened * before first, value smaller then {@code 0} otherwise */ @Override public int compare(MRUDEntry o1, MRUDEntry o2) { // note the unary minus! return -o1.getLastTimeOpened().compareTo(o2.getLastTimeOpened()); } } }