| /******************************************************************************* |
| * Copyright (c) 2003, 2005 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - Initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.wst.server.ui.internal.editor; |
| |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.io.File; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.commands.ExecutionException; |
| import org.eclipse.core.commands.operations.IUndoableOperation; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.wst.server.core.*; |
| import org.eclipse.wst.server.core.internal.Server; |
| import org.eclipse.wst.server.ui.editor.IServerEditorPartInput; |
| import org.eclipse.wst.server.ui.internal.Messages; |
| import org.eclipse.wst.server.ui.internal.ServerUIPlugin; |
| import org.eclipse.wst.server.ui.internal.Trace; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Shell; |
| /** |
| * |
| */ |
| public class GlobalCommandManager { |
| // maximum number of commands in the history |
| private static final int MAX_HISTORY = 200; |
| |
| class ServerResourceCommand { |
| IUndoableOperation command; |
| String id; |
| } |
| |
| // commands in the undo history |
| protected List undoList = new ArrayList(); |
| |
| // size of the undo stack on last save |
| protected int undoSaveIndex = 0; |
| |
| // commands in the redo history |
| protected List redoList = new ArrayList(); |
| |
| class CommandManagerInfo { |
| // number of open editors on this resource |
| int count; |
| |
| // true if the resource has not been saved since |
| // the last change |
| boolean isDirty; |
| |
| // true if the resource is read-only |
| boolean isReadOnly; |
| |
| // the element id |
| String id; |
| |
| // the working copy |
| IServerWorkingCopy wc; |
| |
| // files and timestamps |
| Map fileMap; |
| |
| int timestamp; |
| } |
| |
| protected Map commandManagers = new HashMap(); |
| |
| // property change listeners |
| protected List propertyListeners; |
| public static final String PROP_DIRTY = "dirtyState"; |
| public static final String PROP_UNDO = "undoAction"; |
| public static final String PROP_REDO = "redoAction"; |
| public static final String PROP_RELOAD = "reload"; |
| |
| protected static GlobalCommandManager instance; |
| |
| public static GlobalCommandManager getInstance() { |
| if (instance == null) |
| instance = new GlobalCommandManager(); |
| return instance; |
| } |
| |
| /** |
| * Add a property change listener to this instance. |
| * |
| * @param listener java.beans.PropertyChangeListener |
| */ |
| public void addPropertyChangeListener(PropertyChangeListener listener) { |
| if (propertyListeners == null) |
| propertyListeners = new ArrayList(); |
| propertyListeners.add(listener); |
| } |
| |
| /** |
| * Remove a property change listener from this instance. |
| * |
| * @param listener java.beans.PropertyChangeListener |
| */ |
| public void removePropertyChangeListener(PropertyChangeListener listener) { |
| if (propertyListeners != null) |
| propertyListeners.remove(listener); |
| } |
| |
| /** |
| * Fire a property change event. |
| */ |
| protected void firePropertyChangeEvent(String propertyName, Object oldValue, Object newValue) { |
| if (propertyListeners == null) |
| return; |
| |
| PropertyChangeEvent event = new PropertyChangeEvent(this, propertyName, oldValue, newValue); |
| //Trace.trace("Firing: " + event + " " + oldValue); |
| try { |
| int size = propertyListeners.size(); |
| PropertyChangeListener[] pcl = new PropertyChangeListener[size]; |
| propertyListeners.toArray(pcl); |
| |
| for (int i = 0; i < size; i++) |
| try { |
| pcl[i].propertyChange(event); |
| } catch (Exception e) { |
| // ignore |
| } |
| } catch (Exception e) { |
| // ignore |
| } |
| } |
| |
| /** |
| * Get the command manager for a given id. |
| * |
| * @param id an id |
| */ |
| public void getCommandManager(String id) { |
| Trace.trace(Trace.FINEST, "Getting command manager for " + id); |
| try { |
| CommandManagerInfo info = (CommandManagerInfo) commandManagers.get(id); |
| if (info != null) { |
| info.count ++; |
| return; |
| } |
| } catch (Exception e) { |
| Trace.trace(Trace.WARNING, "Could not find existing command manager", e); |
| } |
| Trace.trace(Trace.FINEST, "Creating new command manager for " + id); |
| try { |
| CommandManagerInfo info = new CommandManagerInfo(); |
| info.count = 1; |
| info.id = id; |
| IServer server = null; |
| if (id != null) |
| server = ServerCore.findServer(id); |
| if (server != null) |
| info.wc = server.createWorkingCopy(); |
| info.isDirty = false; |
| info.isReadOnly = false; |
| commandManagers.put(id, info); |
| updateTimestamps(id); |
| } catch (Exception e) { |
| Trace.trace(Trace.SEVERE, "Could not obtain command manager", e); |
| } |
| return; |
| } |
| |
| /** |
| * Release the command manager for a given id. |
| * |
| * @param id an id |
| */ |
| public void releaseCommandManager(String id) { |
| Trace.trace(Trace.FINEST, "Releasing command manager for " + id); |
| try { |
| CommandManagerInfo info = (CommandManagerInfo) commandManagers.get(id); |
| if (info != null) { |
| info.count --; |
| if (info.count == 0) { |
| commandManagers.remove(id); |
| clearUndoList(id); |
| clearRedoList(id); |
| } |
| } |
| } catch (Exception e) { |
| Trace.trace(Trace.SEVERE, "Could not release command manager", e); |
| } |
| } |
| |
| /** |
| * Reload the command manager for a given id. |
| * |
| * @param id an id |
| * @param monitor a progress monitor |
| */ |
| public void reload(String id, IProgressMonitor monitor) { |
| try { |
| CommandManagerInfo info = getExistingCommandManagerInfo(id); |
| if (info != null) { |
| IServer server = null; |
| if (id != null) |
| server = ServerCore.findServer(id); |
| if (server != null) |
| info.wc = server.createWorkingCopy(); |
| //info.serverElement = ServerCore.getResourceManager().getServer() |
| //info.serverElement = ServerCore.getEditManager().reloadEditModel(info.file, monitor); |
| firePropertyChangeEvent(PROP_RELOAD, id, null); |
| } |
| } catch (Exception e) { |
| Trace.trace(Trace.SEVERE, "Could not release command manager", e); |
| } |
| } |
| |
| /** |
| * |
| */ |
| protected CommandManagerInfo getExistingCommandManagerInfo(String id) { |
| try { |
| return (CommandManagerInfo) commandManagers.get(id); |
| } catch (Exception e) { |
| Trace.trace(Trace.SEVERE, "Could not find existing command manager info"); |
| } |
| return null; |
| } |
| |
| /** |
| * Returns true if there is only one command manager. |
| * |
| * @param id an id |
| * @return <code>true</code> if the only command manager |
| */ |
| public boolean isOnlyCommandManager(String id) { |
| CommandManagerInfo info = getExistingCommandManagerInfo(id); |
| return (info != null && info.count == 1); |
| } |
| |
| protected IServerEditorPartInput getPartInput(String serverId, ServerResourceCommandManager serverCommandManager) { |
| CommandManagerInfo serverInfo = null; |
| IServerWorkingCopy server = null; |
| boolean serverReadOnly = false; |
| if (serverId != null) { |
| serverInfo = getExistingCommandManagerInfo(serverId); |
| if (serverInfo == null) |
| return null; |
| |
| server = serverInfo.wc; |
| serverReadOnly = serverInfo.isReadOnly; |
| } |
| |
| return new ServerEditorPartInput(serverCommandManager, server, serverReadOnly); |
| } |
| |
| /** |
| * |
| */ |
| protected IServerWorkingCopy getServerResource(String id) { |
| CommandManagerInfo info = getExistingCommandManagerInfo(id); |
| if (info == null) |
| return null; |
| |
| return info.wc; |
| } |
| |
| /** |
| * Execute the given command and place it in the undo stack. |
| * If the command cannot be undone, the user will be notifed |
| * before it is executed. |
| * |
| * @param id an id |
| * @param command a task |
| */ |
| public void executeCommand(String id, IUndoableOperation command) { |
| if (!command.canUndo() && !undoList.isEmpty() && ServerUIPlugin.getPreferences().getPromptBeforeIrreversibleChange()) { |
| try { |
| Display d = Display.getCurrent(); |
| if (d == null) |
| d = Display.getDefault(); |
| |
| Shell shell = d.getActiveShell(); |
| if (!MessageDialog.openConfirm(shell, Messages.editorServerEditor, Messages.editorPromptIrreversible)) |
| return; |
| } catch (Exception e) { |
| // ignore |
| } |
| } |
| |
| ServerResourceCommand src = new ServerResourceCommand(); |
| src.id = id; |
| src.command = command; |
| |
| try { |
| command.execute(new NullProgressMonitor(), null); |
| } catch (ExecutionException ce) { |
| return; |
| } |
| |
| CommandManagerInfo info = getExistingCommandManagerInfo(id); |
| if (info == null) |
| return; |
| |
| if (command.canUndo()) |
| addToUndoList(src); |
| else { |
| undoSaveIndex = -1; |
| clearUndoList(id); |
| } |
| |
| // clear redo list since a new command has been executed. |
| clearRedoList(id); |
| |
| setDirtyState(id, true); |
| } |
| |
| /** |
| * Add a command to the history. |
| */ |
| private void addToUndoList(ServerResourceCommand src) { |
| undoList.add(src); |
| |
| // limit history growth |
| if (undoList.size() > MAX_HISTORY) |
| undoList.remove(0); |
| |
| firePropertyChangeEvent(PROP_UNDO, src.id, null); |
| } |
| |
| /** |
| * Clears the undo of a particular resource. |
| */ |
| private void clearUndoList(String id) { |
| int i = 0; |
| boolean modified = false; |
| while (i < undoList.size()) { |
| ServerResourceCommand src = (ServerResourceCommand) undoList.get(i); |
| if (src.id.equals(id)) { |
| modified = true; |
| undoList.remove(i); |
| } else |
| i++; |
| } |
| if (modified) |
| firePropertyChangeEvent(PROP_UNDO, id, null); |
| } |
| |
| /** |
| * Clears the redo of a particular resource. |
| */ |
| private void clearRedoList(String id) { |
| int i = 0; |
| boolean modified = false; |
| while (i < redoList.size()) { |
| ServerResourceCommand src = (ServerResourceCommand) redoList.get(i); |
| if (src.id.equals(id)) { |
| redoList.remove(i); |
| modified = true; |
| } else |
| i++; |
| } |
| if (modified) |
| firePropertyChangeEvent(PROP_REDO, id, null); |
| } |
| |
| /** |
| * Returns true if there is a command that can be undone. |
| * @return boolean |
| */ |
| protected boolean canUndo(String a, String b) { |
| Iterator iterator = undoList.iterator(); |
| while (iterator.hasNext()) { |
| ServerResourceCommand src = (ServerResourceCommand) iterator.next(); |
| if (src.id == a || src.id == b) |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Returns true if there is a command that can be redone. |
| * @return boolean |
| */ |
| protected boolean canRedo(String a, String b) { |
| Iterator iterator = redoList.iterator(); |
| while (iterator.hasNext()) { |
| ServerResourceCommand src = (ServerResourceCommand) iterator.next(); |
| if (src.id == a || src.id == b) |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Returns the command that would be undone next. |
| * |
| * @param a an id |
| * @return a task |
| */ |
| public IUndoableOperation getUndoCommand(String a) { |
| int size = undoList.size(); |
| for (int i = size - 1; i >= 0; i--) { |
| ServerResourceCommand src = (ServerResourceCommand) undoList.get(i); |
| if (src.id == a) |
| return src.command; |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the command that would be redone next. |
| * |
| * @param a an id |
| * @return a task |
| */ |
| public IUndoableOperation getRedoCommand(String a) { |
| int size = redoList.size(); |
| for (int i = size - 1; i >= 0; i--) { |
| ServerResourceCommand src = (ServerResourceCommand) redoList.get(i); |
| if (src.id == a) |
| return src.command; |
| } |
| return null; |
| } |
| |
| /** |
| * Returns true if the server resource is "dirty". |
| * |
| * @param id an id |
| * @return a task |
| */ |
| public boolean isDirty(String id) { |
| CommandManagerInfo info = getExistingCommandManagerInfo(id); |
| if (info == null) |
| return false; |
| |
| return info.isDirty; |
| } |
| |
| /** |
| * Returns true if the server resource is read-only. |
| * |
| * @param id an id |
| * @return boolean |
| */ |
| public boolean isReadOnly(String id) { |
| CommandManagerInfo info = getExistingCommandManagerInfo(id); |
| if (info == null) |
| return false; |
| return info.isReadOnly; |
| } |
| |
| /** |
| * Sets the server resource read-only flag. |
| * |
| * @param id an id |
| * @param readOnly <code>true</code> to set read-only, <code>false</code> otherwise |
| */ |
| public void setReadOnly(String id, boolean readOnly) { |
| CommandManagerInfo info = getExistingCommandManagerInfo(id); |
| if (info == null) |
| return; |
| |
| if (info.isReadOnly == readOnly) |
| return; |
| info.isReadOnly = readOnly; |
| firePropertyChangeEvent(PROP_RELOAD, id, null); |
| } |
| |
| /** |
| * Returns true if the server resource files are read-only. |
| * |
| * @param id an id |
| * @return <code>true</code> if the files are read-only |
| */ |
| public boolean areFilesReadOnly(String id) { |
| CommandManagerInfo info = getExistingCommandManagerInfo(id); |
| if (info == null) |
| return false; |
| return (getReadOnlyFiles(id).length > 0); |
| } |
| |
| /** |
| * Sets the dirty state and fires an event if needed. |
| * @param dirty boolean |
| */ |
| private void setDirtyState(String id, boolean dirty) { |
| CommandManagerInfo info = getExistingCommandManagerInfo(id); |
| if (info.isDirty == dirty) |
| return; |
| |
| info.isDirty = dirty; |
| firePropertyChangeEvent(PROP_DIRTY, id, null); |
| } |
| |
| /** |
| * Undo the last command. |
| */ |
| protected void undo(String a) { |
| ServerResourceCommand src = null; |
| Iterator iterator = undoList.iterator(); |
| while (iterator.hasNext()) { |
| ServerResourceCommand src2 = (ServerResourceCommand) iterator.next(); |
| if (src2.id == a) |
| src = src2; |
| } |
| if (src == null) |
| return; |
| |
| try { |
| src.command.undo(null, null); |
| } catch (ExecutionException ee) { |
| // do something |
| } |
| |
| undoList.remove(src); |
| firePropertyChangeEvent(PROP_UNDO, src.id, null); |
| redoList.add(src); |
| firePropertyChangeEvent(PROP_REDO, src.id, null); |
| |
| if (undoSaveIndex == undoList.size()) |
| setDirtyState(src.id, false); |
| else |
| setDirtyState(src.id, true); |
| } |
| |
| /** |
| * Redo the last command. |
| */ |
| protected void redo(String a) { |
| ServerResourceCommand src = null; |
| Iterator iterator = redoList.iterator(); |
| while (iterator.hasNext()) { |
| ServerResourceCommand src2 = (ServerResourceCommand) iterator.next(); |
| if (src2.id == a) |
| src = src2; |
| } |
| if (src == null) |
| return; |
| |
| try { |
| src.command.execute(new NullProgressMonitor(), null); |
| } catch (ExecutionException ce) { |
| return; |
| } |
| redoList.remove(src); |
| firePropertyChangeEvent(PROP_REDO, src.id, null); |
| undoList.add(src); |
| firePropertyChangeEvent(PROP_UNDO, src.id, null); |
| |
| if (undoSaveIndex == undoList.size()) |
| setDirtyState(src.id, false); |
| else |
| setDirtyState(src.id, true); |
| } |
| |
| /** |
| * Clears the history list. |
| * |
| * @param id an id |
| */ |
| public void resourceSaved(String id) { |
| undoSaveIndex = undoList.size(); |
| setDirtyState(id, false); |
| } |
| |
| /** |
| * Return an array of read-only files. |
| * |
| * @param server a server |
| * @return a possibly empty array of files |
| */ |
| public static IFile[] getReadOnlyFiles(IServerAttributes server) { |
| try { |
| List list = new ArrayList(); |
| IFile file = ((Server)server).getFile(); |
| |
| if (file != null) |
| list.add(file); |
| |
| //if () |
| //IServerConfiguration config = (IServerConfiguration) element; |
| // TODO: get read-only files |
| IFile[] files = new IFile[list.size()]; |
| list.toArray(files); |
| return files; |
| } catch (Exception e) { |
| Trace.trace(Trace.SEVERE, "getReadOnlyFiles", e); |
| } |
| return null; |
| } |
| |
| /** |
| * |
| */ |
| protected IFile[] getServerResourceFiles(String id) { |
| if (id == null) |
| return new IFile[0]; |
| |
| CommandManagerInfo info = getExistingCommandManagerInfo(id); |
| if (info == null) |
| return new IFile[0]; |
| |
| return getReadOnlyFiles(info.wc); |
| } |
| |
| protected IFile[] getReadOnlyFiles(String id) { |
| List list = new ArrayList(); |
| IFile[] files = getServerResourceFiles(id); |
| int size = files.length; |
| for (int i = 0; i < size; i++) { |
| if (files[i].isReadOnly()) |
| list.add(files[i]); |
| } |
| |
| IFile[] fileList = new IFile[list.size()]; |
| list.toArray(fileList); |
| return fileList; |
| } |
| |
| /** |
| * Update the timestamps. |
| * |
| * @param id an id |
| */ |
| public void updateTimestamps(String id) { |
| CommandManagerInfo info = getExistingCommandManagerInfo(id); |
| if (info == null) |
| return; |
| |
| info.fileMap = new HashMap(); |
| IFile[] files = getServerResourceFiles(id); |
| if (files != null) { |
| int size = files.length; |
| |
| for (int i = 0; i < size; i++) { |
| if (files[i] != null) { |
| File f = files[i].getLocation().toFile(); |
| if (f != null) { |
| long time = f.lastModified(); |
| info.fileMap.put(files[i], new Long(time)); |
| } |
| } |
| } |
| } |
| info.timestamp = getTimestamp(info); |
| } |
| |
| protected static int getTimestamp(CommandManagerInfo info) { |
| IServer server = info.wc.getOriginal(); |
| |
| if (server != null) |
| return ((Server)server).getTimestamp(); |
| return -1; |
| } |
| |
| /** |
| * |
| */ |
| protected boolean hasChanged(String id) { |
| CommandManagerInfo info = getExistingCommandManagerInfo(id); |
| if (info == null) |
| return false; |
| IFile[] files = getServerResourceFiles(id); |
| int size = files.length; |
| |
| int count = 0; |
| for (int i = 0; i < size; i++) { |
| count++; |
| File f = files[i].getLocation().toFile(); |
| try { |
| Long time = (Long) info.fileMap.get(files[i]); |
| if (time.longValue() != f.lastModified()) |
| return true; |
| } catch (Exception e) { |
| return true; |
| } |
| } |
| |
| int timestamp = getTimestamp(info); |
| if (info.timestamp != timestamp) |
| return true; |
| |
| if (count != info.fileMap.size()) |
| return true; |
| return false; |
| } |
| } |