KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > javaeditor > ClipboardOperationAction


1 /*******************************************************************************
2  * Copyright (c) 2000, 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.jdt.internal.ui.javaeditor;
12
13 import java.io.ByteArrayInputStream JavaDoc;
14 import java.io.ByteArrayOutputStream JavaDoc;
15 import java.io.DataInputStream JavaDoc;
16 import java.io.DataOutputStream JavaDoc;
17 import java.io.IOException JavaDoc;
18 import java.util.ArrayList JavaDoc;
19 import java.util.HashSet JavaDoc;
20 import java.util.List JavaDoc;
21 import java.util.ResourceBundle JavaDoc;
22
23 import org.eclipse.core.runtime.Assert;
24 import org.eclipse.core.runtime.CoreException;
25
26 import org.eclipse.swt.SWTError;
27 import org.eclipse.swt.custom.BusyIndicator;
28 import org.eclipse.swt.dnd.ByteArrayTransfer;
29 import org.eclipse.swt.dnd.Clipboard;
30 import org.eclipse.swt.dnd.DND;
31 import org.eclipse.swt.dnd.RTFTransfer;
32 import org.eclipse.swt.dnd.TextTransfer;
33 import org.eclipse.swt.dnd.Transfer;
34 import org.eclipse.swt.dnd.TransferData;
35 import org.eclipse.swt.widgets.Display;
36 import org.eclipse.swt.widgets.Shell;
37
38 import org.eclipse.jface.viewers.ISelection;
39
40 import org.eclipse.jface.text.IRewriteTarget;
41 import org.eclipse.jface.text.ITextOperationTarget;
42 import org.eclipse.jface.text.ITextSelection;
43 import org.eclipse.jface.text.Region;
44
45 import org.eclipse.ui.IWorkbenchPartSite;
46 import org.eclipse.ui.texteditor.IAbstractTextEditorHelpContextIds;
47 import org.eclipse.ui.texteditor.ITextEditor;
48 import org.eclipse.ui.texteditor.IWorkbenchActionDefinitionIds;
49 import org.eclipse.ui.texteditor.TextEditorAction;
50
51 import org.eclipse.jdt.core.ICompilationUnit;
52 import org.eclipse.jdt.core.IJavaElement;
53 import org.eclipse.jdt.core.Signature;
54 import org.eclipse.jdt.core.dom.ASTNode;
55 import org.eclipse.jdt.core.dom.CompilationUnit;
56 import org.eclipse.jdt.core.dom.IBinding;
57 import org.eclipse.jdt.core.dom.ITypeBinding;
58 import org.eclipse.jdt.core.dom.Name;
59 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
60
61 import org.eclipse.jdt.internal.corext.codemanipulation.ImportReferencesCollector;
62 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
63 import org.eclipse.jdt.internal.corext.dom.Bindings;
64 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
65
66 import org.eclipse.jdt.ui.PreferenceConstants;
67
68 import org.eclipse.jdt.internal.ui.JavaPlugin;
69
70
71 /**
72  * Action for cut/copy and paste with support for adding imports on paste.
73  */

74 public final class ClipboardOperationAction extends TextEditorAction {
75
76     public static class ClipboardData {
77         private String JavaDoc fOriginHandle;
78         private String JavaDoc[] fTypeImports;
79         private String JavaDoc[] fStaticImports;
80
81         public ClipboardData(IJavaElement origin, String JavaDoc[] typeImports, String JavaDoc[] staticImports) {
82             Assert.isNotNull(origin);
83             Assert.isNotNull(typeImports);
84             Assert.isNotNull(staticImports);
85
86             fTypeImports= typeImports;
87             fStaticImports= staticImports;
88             fOriginHandle= origin.getHandleIdentifier();
89         }
90
91         public ClipboardData(byte[] bytes) throws IOException JavaDoc {
92             DataInputStream JavaDoc dataIn = new DataInputStream JavaDoc(new ByteArrayInputStream JavaDoc(bytes));
93             try {
94                 fOriginHandle= dataIn.readUTF();
95                 fTypeImports= readArray(dataIn);
96                 fStaticImports= readArray(dataIn);
97             } finally {
98                 dataIn.close();
99             }
100         }
101
102         private static String JavaDoc[] readArray(DataInputStream JavaDoc dataIn) throws IOException JavaDoc {
103             int count= dataIn.readInt();
104
105             String JavaDoc[] array= new String JavaDoc[count];
106             for (int i = 0; i < count; i++) {
107                 array[i]= dataIn.readUTF();
108             }
109             return array;
110         }
111
112         private static void writeArray(DataOutputStream JavaDoc dataOut, String JavaDoc[] array) throws IOException JavaDoc {
113             dataOut.writeInt(array.length);
114             for (int i = 0; i < array.length; i++) {
115                 dataOut.writeUTF(array[i]);
116             }
117         }
118
119         public String JavaDoc[] getTypeImports() {
120             return fTypeImports;
121         }
122
123         public String JavaDoc[] getStaticImports() {
124             return fStaticImports;
125         }
126
127         public boolean isFromSame(IJavaElement elem) {
128             return fOriginHandle.equals(elem.getHandleIdentifier());
129         }
130
131         public byte[] serialize() throws IOException JavaDoc {
132             ByteArrayOutputStream JavaDoc out = new ByteArrayOutputStream JavaDoc();
133             DataOutputStream JavaDoc dataOut = new DataOutputStream JavaDoc(out);
134             try {
135                 dataOut.writeUTF(fOriginHandle);
136                 writeArray(dataOut, fTypeImports);
137                 writeArray(dataOut, fStaticImports);
138             } finally {
139                 dataOut.close();
140                 out.close();
141             }
142
143             return out.toByteArray();
144         }
145     }
146
147
148     private static class ClipboardTransfer extends ByteArrayTransfer {
149
150         private static final String JavaDoc TYPE_NAME = "source-with-imports-transfer-format" + System.currentTimeMillis(); //$NON-NLS-1$
151

152         private static final int TYPEID = registerType(TYPE_NAME);
153
154         /* (non-Javadoc)
155          * @see org.eclipse.swt.dnd.Transfer#getTypeIds()
156          */

157         protected int[] getTypeIds() {
158             return new int[] { TYPEID };
159         }
160
161         /* (non-Javadoc)
162          * @see org.eclipse.swt.dnd.Transfer#getTypeNames()
163          */

164         protected String JavaDoc[] getTypeNames() {
165             return new String JavaDoc[] { TYPE_NAME };
166         }
167
168         /* (non-Javadoc)
169          * @see org.eclipse.swt.dnd.Transfer#javaToNative(java.lang.Object, org.eclipse.swt.dnd.TransferData)
170          */

171         protected void javaToNative(Object JavaDoc data, TransferData transferData) {
172             if (data instanceof ClipboardData) {
173                 try {
174                     super.javaToNative(((ClipboardData) data).serialize(), transferData);
175                 } catch (IOException JavaDoc e) {
176                     //it's best to send nothing if there were problems
177
}
178             }
179         }
180
181         /* (non-Javadoc)
182          * Method declared on Transfer.
183          */

184         protected Object JavaDoc nativeToJava(TransferData transferData) {
185             byte[] bytes = (byte[]) super.nativeToJava(transferData);
186             if (bytes != null) {
187                 try {
188                     return new ClipboardData(bytes);
189                 } catch (IOException JavaDoc e) {
190                 }
191             }
192             return null;
193         }
194
195     }
196
197     private static final ClipboardTransfer fgTransferInstance = new ClipboardTransfer();
198
199     /** The text operation code */
200     private int fOperationCode= -1;
201     /** The text operation target */
202     private ITextOperationTarget fOperationTarget;
203
204
205     /**
206      * Creates the action.
207      */

208     public ClipboardOperationAction(ResourceBundle JavaDoc bundle, String JavaDoc prefix, ITextEditor editor, int operationCode) {
209         super(bundle, prefix, editor);
210         fOperationCode= operationCode;
211
212         if (operationCode == ITextOperationTarget.CUT) {
213             setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_ACTION);
214             setActionDefinitionId(IWorkbenchActionDefinitionIds.CUT);
215         } else if (operationCode == ITextOperationTarget.COPY) {
216             setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_ACTION);
217             setActionDefinitionId(IWorkbenchActionDefinitionIds.COPY);
218         } else if (operationCode == ITextOperationTarget.PASTE) {
219             setHelpContextId(IAbstractTextEditorHelpContextIds.PASTE_ACTION);
220             setActionDefinitionId(IWorkbenchActionDefinitionIds.PASTE);
221         } else {
222             Assert.isTrue(false, "Invalid operation code"); //$NON-NLS-1$
223
}
224         update();
225     }
226
227     private boolean isReadOnlyOperation() {
228         return fOperationCode == ITextOperationTarget.COPY;
229     }
230
231
232     /* (non-Javadoc)
233      * @see org.eclipse.jface.action.IAction#run()
234      */

235     public void run() {
236         if (fOperationCode == -1 || fOperationTarget == null)
237             return;
238
239         ITextEditor editor= getTextEditor();
240         if (editor == null)
241             return;
242
243         if (!isReadOnlyOperation() && !validateEditorInputState())
244             return;
245
246         BusyIndicator.showWhile(getDisplay(), new Runnable JavaDoc() {
247             public void run() {
248                 internalDoOperation();
249             }
250         });
251     }
252
253     private Shell getShell() {
254         ITextEditor editor= getTextEditor();
255         if (editor != null) {
256             IWorkbenchPartSite site= editor.getSite();
257             Shell shell= site.getShell();
258             if (shell != null && !shell.isDisposed()) {
259                 return shell;
260             }
261         }
262         return null;
263     }
264
265     private Display getDisplay() {
266         Shell shell= getShell();
267         if (shell != null) {
268             return shell.getDisplay();
269         }
270         return null;
271     }
272
273
274     protected final void internalDoOperation() {
275         if (PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_IMPORTS_ON_PASTE)) {
276             if (fOperationCode == ITextOperationTarget.PASTE) {
277                 doPasteWithImportsOperation();
278             } else {
279                 doCutCopyWithImportsOperation();
280             }
281         } else {
282             fOperationTarget.doOperation(fOperationCode);
283         }
284     }
285
286     /* (non-Javadoc)
287      * @see org.eclipse.ui.texteditor.IUpdate#update()
288      */

289     public void update() {
290         super.update();
291
292         if (!isReadOnlyOperation() && !canModifyEditor()) {
293             setEnabled(false);
294             return;
295         }
296
297         ITextEditor editor= getTextEditor();
298         if (fOperationTarget == null && editor!= null && fOperationCode != -1)
299             fOperationTarget= (ITextOperationTarget) editor.getAdapter(ITextOperationTarget.class);
300
301         boolean isEnabled= (fOperationTarget != null && fOperationTarget.canDoOperation(fOperationCode));
302         setEnabled(isEnabled);
303     }
304
305     /* (non-Javadoc)
306      * @see org.eclipse.ui.texteditor.TextEditorAction#setEditor(org.eclipse.ui.texteditor.ITextEditor)
307      */

308     public void setEditor(ITextEditor editor) {
309         super.setEditor(editor);
310         fOperationTarget= null;
311     }
312
313
314     private void doCutCopyWithImportsOperation() {
315         ITextEditor editor= getTextEditor();
316         IJavaElement inputElement= (IJavaElement) editor.getEditorInput().getAdapter(IJavaElement.class);
317         ISelection selection= editor.getSelectionProvider().getSelection();
318
319         Object JavaDoc clipboardData= null;
320         if (inputElement != null && selection instanceof ITextSelection && !selection.isEmpty()) {
321             ITextSelection textSelection= (ITextSelection) selection;
322             if (isNonTrivialSelection(textSelection)) {
323                 clipboardData= getClipboardData(inputElement, textSelection.getOffset(), textSelection.getLength());
324             }
325         }
326
327         fOperationTarget.doOperation(fOperationCode);
328
329         if (clipboardData != null) {
330             Clipboard clipboard= new Clipboard(getDisplay());
331             try {
332                 /*
333                  * We currently make assumptions about what the styled text widget sets,
334                  * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=61876
335                  */

336                 Object JavaDoc textData= clipboard.getContents(TextTransfer.getInstance());
337                 Object JavaDoc rtfData= clipboard.getContents(RTFTransfer.getInstance());
338
339                 ArrayList JavaDoc datas= new ArrayList JavaDoc(3);
340                 ArrayList JavaDoc transfers= new ArrayList JavaDoc(3);
341                 if (textData != null) {
342                     datas.add(textData);
343                     transfers.add(TextTransfer.getInstance());
344                 }
345                 if (rtfData != null) {
346                     datas.add(rtfData);
347                     transfers.add(RTFTransfer.getInstance());
348                 }
349
350                 /*
351                  * Don't add if we didn't get any data from the clipboard
352                  * see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=70077
353                  */

354                 if (datas.isEmpty())
355                     return;
356
357                 datas.add(clipboardData);
358                 transfers.add(fgTransferInstance);
359
360                 Transfer[] dataTypes= (Transfer[]) transfers.toArray(new Transfer[transfers.size()]);
361                 Object JavaDoc[] data= datas.toArray();
362                 setClipboardContents(clipboard, data, dataTypes);
363             } finally {
364                 clipboard.dispose();
365             }
366         }
367     }
368
369     private void setClipboardContents(Clipboard clipboard, Object JavaDoc[] datas, Transfer[] transfers) {
370         try {
371             clipboard.setContents(datas, transfers);
372         } catch (SWTError e) {
373             if (e.code != DND.ERROR_CANNOT_SET_CLIPBOARD) {
374                 throw e;
375             }
376             // silently fail. see e.g. https://bugs.eclipse.org/bugs/show_bug.cgi?id=65975
377
}
378     }
379
380     private boolean isNonTrivialSelection(ITextSelection selection) {
381         if (selection.getLength() < 30) {
382             String JavaDoc text= selection.getText();
383             if (text != null) {
384                 for (int i= 0; i < text.length(); i++) {
385                     if (!Character.isJavaIdentifierPart(text.charAt(i))) {
386                         return true;
387                     }
388                 }
389             }
390             return false;
391         }
392         return true;
393     }
394
395
396     private ClipboardData getClipboardData(IJavaElement inputElement, int offset, int length) {
397         CompilationUnit astRoot= JavaPlugin.getDefault().getASTProvider().getAST(inputElement, ASTProvider.WAIT_ACTIVE_ONLY, null);
398         if (astRoot == null) {
399             return null;
400         }
401
402         // do process import if selection spans over import declaration or package
403
List JavaDoc list= astRoot.imports();
404         if (!list.isEmpty()) {
405             if (offset < ((ASTNode) list.get(list.size() - 1)).getStartPosition()) {
406                 return null;
407             }
408         } else if (astRoot.getPackage() != null) {
409             if (offset < ((ASTNode) astRoot.getPackage()).getStartPosition()) {
410                 return null;
411             }
412         }
413
414         ArrayList JavaDoc typeImportsRefs= new ArrayList JavaDoc();
415         ArrayList JavaDoc staticImportsRefs= new ArrayList JavaDoc();
416
417         ImportReferencesCollector.collect(astRoot, inputElement.getJavaProject(), new Region(offset, length), typeImportsRefs, staticImportsRefs);
418
419         if (typeImportsRefs.isEmpty() && staticImportsRefs.isEmpty()) {
420             return null;
421         }
422
423         HashSet JavaDoc namesToImport= new HashSet JavaDoc(typeImportsRefs.size());
424         for (int i= 0; i < typeImportsRefs.size(); i++) {
425             Name curr= (Name) typeImportsRefs.get(i);
426             IBinding binding= curr.resolveBinding();
427             if (binding != null && binding.getKind() == IBinding.TYPE) {
428                 ITypeBinding typeBinding= (ITypeBinding) binding;
429                 if (typeBinding.isArray()) {
430                     typeBinding= typeBinding.getElementType();
431                 }
432                 if (typeBinding.isTypeVariable() || typeBinding.isCapture() || typeBinding.isWildcardType()) { // can be removed when bug 98473 is fixed
433
continue;
434                 }
435                 
436                 if (typeBinding.isMember() || typeBinding.isTopLevel()) {
437                     String JavaDoc name= Bindings.getRawQualifiedName(typeBinding);
438                     if (name.length() > 0) {
439                         namesToImport.add(name);
440                     }
441                 }
442             }
443         }
444
445         HashSet JavaDoc staticsToImport= new HashSet JavaDoc(staticImportsRefs.size());
446         for (int i= 0; i < staticImportsRefs.size(); i++) {
447             Name curr= (Name) staticImportsRefs.get(i);
448             IBinding binding= curr.resolveBinding();
449             if (binding != null) {
450                 StringBuffer JavaDoc buf= new StringBuffer JavaDoc(Bindings.getImportName(binding));
451                 if (binding.getKind() == IBinding.METHOD) {
452                     buf.append("()"); //$NON-NLS-1$
453
}
454                 staticsToImport.add(buf.toString());
455             }
456         }
457
458
459         if (namesToImport.isEmpty() && staticsToImport.isEmpty()) {
460             return null;
461         }
462
463         String JavaDoc[] typeImports= (String JavaDoc[]) namesToImport.toArray(new String JavaDoc[namesToImport.size()]);
464         String JavaDoc[] staticImports= (String JavaDoc[]) staticsToImport.toArray(new String JavaDoc[staticsToImport.size()]);
465         return new ClipboardData(inputElement, typeImports, staticImports);
466     }
467
468
469     private void doPasteWithImportsOperation() {
470         ITextEditor editor= getTextEditor();
471         IJavaElement inputElement= (IJavaElement) editor.getEditorInput().getAdapter(IJavaElement.class);
472
473         Clipboard clipboard= new Clipboard(getDisplay());
474         ClipboardData importsData= (ClipboardData) clipboard.getContents(fgTransferInstance);
475         if (importsData != null && inputElement instanceof ICompilationUnit && !importsData.isFromSame(inputElement)) {
476             // combine operation and adding of imports
477
IRewriteTarget target= editor != null ? (IRewriteTarget) editor.getAdapter(IRewriteTarget.class) : null;
478             if (target != null) {
479                 target.beginCompoundChange();
480             }
481             try {
482                 fOperationTarget.doOperation(fOperationCode);
483                 addImports((ICompilationUnit) inputElement, importsData);
484             } catch (CoreException e) {
485                 JavaPlugin.log(e);
486             } finally {
487                 if (target != null) {
488                     target.endCompoundChange();
489                 }
490             }
491         } else {
492             fOperationTarget.doOperation(fOperationCode);
493         }
494     }
495
496
497     private void addImports(ICompilationUnit unit, ClipboardData data) throws CoreException {
498         ImportRewrite rewrite= StubUtility.createImportRewrite(unit, true);
499         String JavaDoc[] imports= data.getTypeImports();
500         for (int i= 0; i < imports.length; i++) {
501             rewrite.addImport(imports[i]);
502         }
503         String JavaDoc[] staticImports= data.getStaticImports();
504         for (int i= 0; i < staticImports.length; i++) {
505             String JavaDoc name= Signature.getSimpleName(staticImports[i]);
506             boolean isField= !name.endsWith("()"); //$NON-NLS-1$
507
if (!isField) {
508                 name= name.substring(0, name.length() - 2);
509             }
510             String JavaDoc qualifier= Signature.getQualifier(staticImports[i]);
511             rewrite.addStaticImport(qualifier, name, isField);
512         }
513
514         JavaModelUtil.applyEdit(unit, rewrite.rewriteImports(null), false, null);
515     }
516
517
518 }
519
Popular Tags