KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > operations > NonLocalUndoUserApprover


1 /*******************************************************************************
2  * Copyright (c) 2005, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.ui.operations;
12
13 import java.util.ArrayList JavaDoc;
14
15 import org.eclipse.core.commands.operations.IAdvancedUndoableOperation;
16 import org.eclipse.core.commands.operations.IOperationApprover;
17 import org.eclipse.core.commands.operations.IOperationHistory;
18 import org.eclipse.core.commands.operations.IUndoContext;
19 import org.eclipse.core.commands.operations.IUndoableOperation;
20 import org.eclipse.core.runtime.IAdaptable;
21 import org.eclipse.core.runtime.IStatus;
22 import org.eclipse.core.runtime.Status;
23 import org.eclipse.jface.dialogs.IDialogConstants;
24 import org.eclipse.jface.dialogs.MessageDialog;
25 import org.eclipse.osgi.util.NLS;
26 import org.eclipse.ui.IEditorPart;
27 import org.eclipse.ui.internal.Workbench;
28 import org.eclipse.ui.internal.WorkbenchMessages;
29 import org.eclipse.ui.internal.util.Util;
30
31 /**
32  * <p>
33  * An operation approver that prompts the user to see if a non-local undo should
34  * proceed inside an editor. A non-local undo is detected when an operation
35  * being undone or redone affects elements other than those described by the
36  * editor itself. Clients can optionally specify a class, the preferred
37  * comparison class, that should be used when comparing objects affected by the
38  * editor with objects affected by an undo or redo operation. Comparisons
39  * between the affected objects inside the editor and those described by the
40  * operation will first be done by simply performing an equality check, using
41  * {@link java.lang.Object#equals(java.lang.Object)}. If an object described by
42  * an operation is not equal to one of the objects affected by the editor, and
43  * if it is not an instance of the preferred comparison class, but is an
44  * instance of {@link org.eclipse.core.runtime.IAdaptable}, then the operation
45  * approver will also attempt to retrieve an adapter on that object for the
46  * preferred comparison class and perform a second equality check using the
47  * adapter.
48  * </p>
49  * <p>
50  * This class may be instantiated by clients.
51  * </p>
52  *
53  *
54  * @since 3.1
55  */

56 public final class NonLocalUndoUserApprover implements IOperationApprover {
57
58     private IUndoContext context;
59
60     private IEditorPart part;
61
62     private Object JavaDoc[] elements;
63
64     private Class JavaDoc affectedObjectsClass;
65
66     private ArrayList JavaDoc elementsAndAdapters;
67
68     /**
69      * Create a NonLocalUndoUserApprover associated with the specified editor
70      * and undo context
71      *
72      * @param context
73      * the undo context of operations in question.
74      * @param part
75      * the editor part that is displaying the element
76      * @param affectedObjects
77      * the objects that are affected by the editor and considered to
78      * be objects local to the editor. The objects are typically
79      * instances of the preferredComparisonClass or else provide
80      * adapters for the preferredComparisonClass, although this is
81      * not required.
82      * @param preferredComparisonClass
83      * the preferred class to be used when comparing the editor's
84      * affectedObjects with those provided by the undoable operation
85      * using
86      * {@link org.eclipse.core.commands.operations.IAdvancedUndoableOperation#getAffectedObjects()}.
87      * If the operation's affected objects are not instances of the
88      * specified class, but are instances of
89      * {@link org.eclipse.core.runtime.IAdaptable}, then an adapter
90      * for this class will be requested. The preferredComparisonClass
91      * may be <code>null</code>, which indicates that there is no
92      * expected class or adapter necessary for the comparison.
93      */

94     public NonLocalUndoUserApprover(IUndoContext context, IEditorPart part,
95             Object JavaDoc[] affectedObjects, Class JavaDoc preferredComparisonClass) {
96         super();
97         this.context = context;
98         this.part = part;
99         this.affectedObjectsClass = preferredComparisonClass;
100         this.elements = affectedObjects;
101     }
102
103     /*
104      * (non-Javadoc)
105      *
106      * @see org.eclipse.core.commands.operations.IOperationApprover#proceedRedoing(org.eclipse.core.commands.operations.IUndoableOperation,
107      * org.eclipse.core.commands.operations.IOperationHistory,
108      * org.eclipse.core.runtime.IAdaptable)
109      */

110     public IStatus proceedRedoing(IUndoableOperation operation,
111             IOperationHistory history, IAdaptable uiInfo) {
112
113         // return immediately if the operation is not relevant
114
if (!requiresApproval(operation, uiInfo)) {
115             return Status.OK_STATUS;
116         }
117
118         String JavaDoc message = NLS.bind(
119                 WorkbenchMessages.Operations_nonLocalRedoWarning, operation
120                         .getLabel(), part.getEditorInput().getName());
121         return proceedWithOperation(operation, message, WorkbenchMessages.Operations_discardRedo);
122     }
123
124     /*
125      * (non-Javadoc)
126      *
127      * @see org.eclipse.core.commands.operations.IOperationApprover#proceedUndoing(org.eclipse.core.commands.operations.IUndoableOperation,
128      * org.eclipse.core.commands.operations.IOperationHistory,
129      * org.eclipse.core.runtime.IAdaptable)
130      */

131     public IStatus proceedUndoing(IUndoableOperation operation,
132             IOperationHistory history, IAdaptable uiInfo) {
133
134         // return immediately if the operation is not relevant
135
if (!requiresApproval(operation, uiInfo)) {
136             return Status.OK_STATUS;
137         }
138
139         String JavaDoc message = NLS.bind(
140                 WorkbenchMessages.Operations_nonLocalUndoWarning, operation
141                         .getLabel(), part.getEditorInput().getName());
142         return proceedWithOperation(operation, message, WorkbenchMessages.Operations_discardUndo);
143
144     }
145
146     /*
147      * Determine whether the operation in question affects elements outside of
148      * the editor. If this can be determined and it does affect other elements,
149      * prompt the user as to whether the operation should proceed.
150      */

151     private IStatus proceedWithOperation(IUndoableOperation operation,
152             final String JavaDoc message, final String JavaDoc discardButton) {
153
154         // if the operation cannot tell us about its modified elements, there's
155
// nothing we can do.
156
if (!(operation instanceof IAdvancedUndoableOperation)) {
157             return Status.OK_STATUS;
158         }
159
160         // Obtain the operation's affected objects.
161
Object JavaDoc[] modifiedElements = ((IAdvancedUndoableOperation) operation)
162                 .getAffectedObjects();
163
164         // Since the operation participates in describing its affected objects,
165
// we assume for the rest of this method that an inability to
166
// determine a match implies that a non-local operation is occurring.
167
// This is a conservative assumption that provides more user prompting.
168

169         boolean local;
170         if (modifiedElements == null) {
171             // The operation could not determine which elements are affected.
172
// Consider the operation non-local.
173
local = false;
174         } else {
175             // The operation answered some array of affected objects. Consider
176
// the operation local until a non-match is found. Note that an
177
// empty
178
// array of affected objects is considered a local change.
179
local = true;
180             for (int i = 0; i < modifiedElements.length; i++) {
181                 Object JavaDoc modifiedElement = modifiedElements[i];
182                 if (!elementsContains(modifiedElement)) {
183                     // the modified element is not known by the editor
184
local = false;
185                     // one last try - try to adapt the modified element if a
186
// preferred
187
// comparison class has been provided.
188
if (affectedObjectsClass != null) {
189                         Object JavaDoc adapter = Util.getAdapter(modifiedElement,
190                                 affectedObjectsClass);
191                         if (adapter != null && elementsContains(adapter)) {
192                             local = true;
193                         }
194                     }
195                     // if the element did not match the affected objects, no
196
// need to check any others.
197
if (!local) {
198                         break;
199                     }
200                 }
201             }
202         }
203         if (local) {
204             return Status.OK_STATUS;
205         }
206
207         // The operation affects more than just our element. Find out if
208
// we should proceed, cancel, or discard the undo. Must be done in
209
// a syncExec because operation approval notifications may come from
210
// a background thread.
211
final int[] answer = new int[1];
212         Workbench.getInstance().getDisplay().syncExec(new Runnable JavaDoc() {
213             public void run() {
214                 MessageDialog dialog = new MessageDialog(part.getSite().getShell(), part.getEditorInput().getName(),
215                         null, message, MessageDialog.QUESTION, new String JavaDoc[] { IDialogConstants.OK_LABEL,
216                                 discardButton, IDialogConstants.CANCEL_LABEL }, 0); // yes is the default
217
answer[0] = dialog.open();
218         }});
219         switch (answer[0]) {
220         case 0:
221             return Status.OK_STATUS;
222         case 1:
223             return IOperationHistory.OPERATION_INVALID_STATUS;
224         default:
225             // Cancel by default to include ESC key and shell close,
226
// which return SWT.DEFAULT, and any other unexpected return codes
227
return Status.CANCEL_STATUS;
228         }
229     }
230
231     /*
232      * Answer whether this operation is relevant enough to this operation
233      * approver that it should be examined in detail.
234      */

235     private boolean requiresApproval(IUndoableOperation operation,
236             IAdaptable uiInfo) {
237         // no approval is required if the operation doesn't have our undo
238
// context
239
if (!(operation.hasContext(context))) {
240             return false;
241         }
242
243         // no approval is required if the operation only has our context
244
if (operation.getContexts().length == 1) {
245             return false;
246         }
247
248         // no approval is required if we can ascertain that the operation did
249
// not originate
250
// in our context.
251
if (uiInfo != null) {
252             IUndoContext originatingContext = (IUndoContext) Util.getAdapter(uiInfo,
253                     IUndoContext.class);
254             if (originatingContext != null
255                     && !(originatingContext.matches(context))) {
256                 return false;
257             }
258         }
259
260         return true;
261     }
262
263     /*
264      * Return whether or not the collection of editor elements plus any of their
265      * adapters contains the specified object.
266      */

267     private boolean elementsContains(Object JavaDoc someObject) {
268         if (elements == null) {
269             return false;
270         }
271         if (elementsAndAdapters == null) {
272             // Compute a list of not just the elements, but any adapters they
273
// may provide on the preferred class if they are not instances of
274
// the preferred class. This is done only once.
275
elementsAndAdapters = new ArrayList JavaDoc(elements.length);
276             for (int i = 0; i < elements.length; i++) {
277                 Object JavaDoc element = elements[i];
278                 elementsAndAdapters.add(element);
279                 if (affectedObjectsClass != null
280                         && !affectedObjectsClass.isInstance(element)) {
281                     Object JavaDoc adapter = Util.getAdapter(element, affectedObjectsClass);
282                     if (adapter != null) {
283                         elementsAndAdapters.add(adapter);
284                     }
285                 }
286             }
287         }
288         for (int i = 0; i < elementsAndAdapters.size(); i++) {
289             if (elementsAndAdapters.get(i).equals(someObject)) {
290                 return true;
291             }
292         }
293         return false;
294     }
295 }
296
Popular Tags