package org.xmind.ui.internal.editor;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.ui.services.IDisposable;
import org.xmind.ui.editor.EditorHistoryItem;
import org.xmind.ui.editor.IEditorHistory;
import org.xmind.ui.editor.IEditorHistoryItem;
import org.xmind.ui.internal.editor.IEditorHistoryLoader.IEditorHistoryLoaderCallback;
/**
* @author Ren Siu
* @author Frank Shaka
* @since 3.6.50
*/
public final class EditorHistoryImpl implements IEditorHistory, IDisposable {
private final IEditorHistoryLoader loader;
private final List<URI> unpinnedInputURIs;
private final List<URI> pinnedInputURIs;
private final Map<URI, URI> inputToThumbnail;
private final Map<URI, IEditorHistoryItem> editorHistoryItems;
private final ListenerList listeners;
public EditorHistoryImpl(IEditorHistoryLoader loader) {
this.loader = loader;
this.unpinnedInputURIs = new ArrayList<URI>();
this.pinnedInputURIs = new ArrayList<URI>();
this.inputToThumbnail = new HashMap<URI, URI>();
this.listeners = new ListenerList();
this.editorHistoryItems = new HashMap<URI, IEditorHistoryItem>();
init();
}
private void init() {
loader.load(new IEditorHistoryLoaderCallback() {
@Override
public void thumbnailURILoaded(URI inputURI, URI thumbnailURI) {
inputToThumbnail.put(inputURI, thumbnailURI);
}
@Override
public void pinnedInputURILoaded(URI inputURI) {
pinnedInputURIs.add(inputURI);
}
@Override
public void inputURILoaded(URI inputURI) {
unpinnedInputURIs.add(inputURI);
}
@Override
public void editorHistoryItemsLoaded(URI inputURI,
IEditorHistoryItem item) {
editorHistoryItems.put(inputURI, item);
}
});
while (unpinnedInputURIs.size() > MAX_UNPINNED_SIZE) {
unpinnedInputURIs.remove(unpinnedInputURIs.size() - 1);
}
}
public URI[] getRecentInputURIs(int unpinnedSize) {
if (unpinnedSize < 0)
unpinnedSize = MAX_UNPINNED_SIZE;
unpinnedSize = Math.max(0,
Math.min(unpinnedSize, unpinnedInputURIs.size()));
ArrayList<URI> recentInputURIs = new ArrayList<URI>();
recentInputURIs.addAll(pinnedInputURIs);
recentInputURIs.addAll(unpinnedInputURIs.subList(0, unpinnedSize));
return recentInputURIs.toArray(new URI[recentInputURIs.size()]);
}
public URI[] getAllInputURIs() {
int unpinnedSize = Math.max(0,
Math.min(MAX_UNPINNED_SIZE, unpinnedInputURIs.size()));
List<URI> allInputURIs = new ArrayList<URI>();
allInputURIs.addAll(pinnedInputURIs);
allInputURIs.addAll(unpinnedInputURIs.subList(0, unpinnedSize));
return allInputURIs.toArray(new URI[unpinnedSize]);
}
public URI[] getPinnedInputURIs() {
return pinnedInputURIs.toArray(new URI[pinnedInputURIs.size()]);
}
public URI[] getUnpinnedInputURIs(int unpinnedSize) {
if (unpinnedSize < 0)
unpinnedSize = MAX_UNPINNED_SIZE;
unpinnedSize = Math.max(0,
Math.min(unpinnedSize, unpinnedInputURIs.size()));
return unpinnedInputURIs.subList(0, unpinnedSize)
.toArray(new URI[unpinnedSize]);
}
public void add(URI inputURI) {
if (inputURI == null)
return;
this.add(inputURI, new EditorHistoryItem(inputURI.getScheme(),
System.currentTimeMillis()));
boolean pinned = pinnedInputURIs.contains(inputURI);
remove(inputURI);
if (pinned) {
pinnedInputURIs.add(0, inputURI);
} else {
unpinnedInputURIs.add(0, inputURI);
while (unpinnedInputURIs.size() > MAX_UNPINNED_SIZE) {
unpinnedInputURIs.remove(unpinnedInputURIs.size() - 1);
}
}
fireChanged();
}
public void remove(URI inputURI) {
if (inputURI == null)
return;
int oldPinnedSize = pinnedInputURIs.size();
Iterator<URI> iter = pinnedInputURIs.iterator();
while (iter.hasNext()) {
URI oldURI = iter.next();
if (inputURI.equals(oldURI)) {
iter.remove();
}
}
int oldUnpinnedSize = unpinnedInputURIs.size();
Iterator<URI> unpinnedIterator = unpinnedInputURIs.iterator();
while (unpinnedIterator.hasNext()) {
URI oldURI = unpinnedIterator.next();
if (inputURI.equals(oldURI)) {
unpinnedIterator.remove();
}
}
//REMOVE THUMBNAIL
removeThumbnail(inputURI);
removeEditorHistoryItem(inputURI);
if (oldPinnedSize != pinnedInputURIs.size()
|| oldUnpinnedSize != unpinnedInputURIs.size()) {
fireChanged();
}
}
private void removeThumbnail(URI inputURI) {
URI thumbnailURI = inputToThumbnail.get(inputURI);
if (thumbnailURI != null) {
File existedThumbnailFile = new File(thumbnailURI);
if (existedThumbnailFile.exists()) {
existedThumbnailFile.delete();
}
}
inputToThumbnail.remove(inputURI);
}
public void clear() {
int oldSize = unpinnedInputURIs.size();
for (URI unpinnedInputURI : unpinnedInputURIs) {
removeThumbnail(unpinnedInputURI);
removeEditorHistoryItem(unpinnedInputURI);
}
unpinnedInputURIs.clear();
if (oldSize != unpinnedInputURIs.size()) {
fireChanged();
}
}
public InputStream loadThumbnailData(URI inputURI) throws IOException {
URI thumbnailURI = getThumbnail(inputURI);
if (thumbnailURI == null)
return null;
return thumbnailURI.toURL().openStream();
}
public void saveThumbnailData(URI inputURI, InputStream thumbnailData)
throws IOException {
//REMOVE EXPIRED THUMBNAIL
removeThumbnail(inputURI);
URI thumbnailURI = loader.saveThumbnail(thumbnailData);
if (thumbnailURI == null)
return;
inputToThumbnail.put(inputURI, thumbnailURI);
fireChanged();
}
public URI getThumbnail(URI inputURI) {
return inputToThumbnail.get(inputURI);
}
@Override
public void add(URI inputURI, IEditorHistoryItem item) {
if (inputURI == null)
return;
boolean pinned = pinnedInputURIs.contains(inputURI);
remove(inputURI);
if (pinned) {
pinnedInputURIs.add(0, inputURI);
} else {
unpinnedInputURIs.add(0, inputURI);
while (unpinnedInputURIs.size() > MAX_UNPINNED_SIZE) {
unpinnedInputURIs.remove(unpinnedInputURIs.size() - 1);
}
}
removeEditorHistoryItem(inputURI);
editorHistoryItems.put(inputURI, item);
fireChanged();
}
private void removeEditorHistoryItem(URI inputURI) {
// do remove EditorHistoryItem by uri.
IEditorHistoryItem historyItemUri = editorHistoryItems.get(inputURI);
if (historyItemUri != null) {
historyItemUri = null;
}
editorHistoryItems.remove(inputURI);
}
@Override
public IEditorHistoryItem getItem(URI inputURI) {
return editorHistoryItems.get(inputURI);
}
public void pin(URI inputURI) {
unpinnedInputURIs.remove(inputURI);
pinnedInputURIs.remove(inputURI);
pinnedInputURIs.add(0, inputURI);
fireChanged();
}
public void unPin(URI inputURI) {
pinnedInputURIs.remove(inputURI);
unpinnedInputURIs.remove(inputURI);
unpinnedInputURIs.add(0, inputURI);
fireChanged();
}
public boolean isPinned(URI inputURI) {
return pinnedInputURIs.contains(inputURI);
}
public void removeEditorHistoryListener(IEditorHistoryListener listener) {
listeners.remove(listener);
}
public void addEditorHistoryListener(IEditorHistoryListener listener) {
listeners.add(listener);
}
private void fireChanged() {
for (final Object listener : listeners.getListeners()) {
SafeRunner.run(new SafeRunnable() {
public void run() throws Exception {
((IEditorHistoryListener) listener).editorHistoryChanged();
}
});
}
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.services.IDisposable#dispose()
*/
@Override
public void dispose() {
loader.dispose();
}
}