]> git.uio.no Git - ifi-stolz-refaktor.git/blobdiff - case-study/jdt-before/ui/org/eclipse/jdt/internal/ui/javaeditor/ClipboardOperationAction.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-before / ui / org / eclipse / jdt / internal / ui / javaeditor / ClipboardOperationAction.java
diff --git a/case-study/jdt-before/ui/org/eclipse/jdt/internal/ui/javaeditor/ClipboardOperationAction.java b/case-study/jdt-before/ui/org/eclipse/jdt/internal/ui/javaeditor/ClipboardOperationAction.java
new file mode 100644 (file)
index 0000000..1e0e7ff
--- /dev/null
@@ -0,0 +1,598 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2011 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.jdt.internal.ui.javaeditor;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ResourceBundle;
+
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.dnd.ByteArrayTransfer;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.RTFTransfer;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.dnd.TransferData;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.ISelection;
+
+import org.eclipse.jface.text.IRewriteTarget;
+import org.eclipse.jface.text.ITextOperationTarget;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.Region;
+
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchCommandConstants;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPartSite;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.progress.IProgressService;
+import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
+
+import org.eclipse.ui.texteditor.IAbstractTextEditorHelpContextIds;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.eclipse.ui.texteditor.ITextEditorExtension3;
+import org.eclipse.ui.texteditor.TextEditorAction;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.ITypeRoot;
+import org.eclipse.jdt.core.Signature;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+
+import org.eclipse.jdt.internal.corext.codemanipulation.ImportReferencesCollector;
+import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
+import org.eclipse.jdt.internal.corext.dom.Bindings;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jdt.ui.PreferenceConstants;
+import org.eclipse.jdt.ui.SharedASTProvider;
+
+import org.eclipse.jdt.internal.ui.IJavaStatusConstants;
+import org.eclipse.jdt.internal.ui.JavaPlugin;
+import org.eclipse.jdt.internal.ui.JavaUIMessages;
+
+
+/**
+ * Action for cut/copy and paste with support for adding imports on paste.
+ */
+public final class ClipboardOperationAction extends TextEditorAction {
+
+       public static class ClipboardData {
+               private String fOriginHandle;
+               private String[] fTypeImports;
+               private String[] fStaticImports;
+
+               public ClipboardData(IJavaElement origin, String[] typeImports, String[] staticImports) {
+                       Assert.isNotNull(origin);
+                       Assert.isNotNull(typeImports);
+                       Assert.isNotNull(staticImports);
+
+                       fTypeImports= typeImports;
+                       fStaticImports= staticImports;
+                       fOriginHandle= origin.getHandleIdentifier();
+               }
+
+               public ClipboardData(byte[] bytes) throws IOException {
+                       DataInputStream dataIn = new DataInputStream(new ByteArrayInputStream(bytes));
+                       try {
+                               fOriginHandle= dataIn.readUTF();
+                               fTypeImports= readArray(dataIn);
+                               fStaticImports= readArray(dataIn);
+                       } finally {
+                               dataIn.close();
+                       }
+               }
+
+               private static String[] readArray(DataInputStream dataIn) throws IOException {
+                       int count= dataIn.readInt();
+
+                       String[] array= new String[count];
+                       for (int i = 0; i < count; i++) {
+                               array[i]= dataIn.readUTF();
+                       }
+                       return array;
+               }
+
+               private static void writeArray(DataOutputStream dataOut, String[] array) throws IOException {
+                       dataOut.writeInt(array.length);
+                       for (int i = 0; i < array.length; i++) {
+                               dataOut.writeUTF(array[i]);
+                       }
+               }
+
+               public String[] getTypeImports() {
+                       return fTypeImports;
+               }
+
+               public String[] getStaticImports() {
+                       return fStaticImports;
+               }
+
+               public boolean isFromSame(IJavaElement elem) {
+                       return fOriginHandle.equals(elem.getHandleIdentifier());
+               }
+
+               public byte[] serialize() throws IOException {
+                       ByteArrayOutputStream out = new ByteArrayOutputStream();
+                       DataOutputStream dataOut = new DataOutputStream(out);
+                       try {
+                               dataOut.writeUTF(fOriginHandle);
+                               writeArray(dataOut, fTypeImports);
+                               writeArray(dataOut, fStaticImports);
+                       } finally {
+                               dataOut.close();
+                               out.close();
+                       }
+
+                       return out.toByteArray();
+               }
+       }
+
+
+       private static class ClipboardTransfer extends ByteArrayTransfer {
+
+               private static final String TYPE_NAME = "source-with-imports-transfer-format" + System.currentTimeMillis(); //$NON-NLS-1$
+
+               private static final int TYPEID = registerType(TYPE_NAME);
+
+               /* (non-Javadoc)
+                * @see org.eclipse.swt.dnd.Transfer#getTypeIds()
+                */
+               @Override
+               protected int[] getTypeIds() {
+                       return new int[] { TYPEID };
+               }
+
+               /* (non-Javadoc)
+                * @see org.eclipse.swt.dnd.Transfer#getTypeNames()
+                */
+               @Override
+               protected String[] getTypeNames() {
+                       return new String[] { TYPE_NAME };
+               }
+
+               /* (non-Javadoc)
+                * @see org.eclipse.swt.dnd.Transfer#javaToNative(java.lang.Object, org.eclipse.swt.dnd.TransferData)
+                */
+               @Override
+               protected void javaToNative(Object data, TransferData transferData) {
+                       if (data instanceof ClipboardData) {
+                               try {
+                                       super.javaToNative(((ClipboardData) data).serialize(), transferData);
+                               } catch (IOException e) {
+                                       //it's best to send nothing if there were problems
+                               }
+                       }
+               }
+
+               /* (non-Javadoc)
+                * Method declared on Transfer.
+                */
+               @Override
+               protected Object nativeToJava(TransferData transferData) {
+                       byte[] bytes = (byte[]) super.nativeToJava(transferData);
+                       if (bytes != null) {
+                               try {
+                                       return new ClipboardData(bytes);
+                               } catch (IOException e) {
+                               }
+                       }
+                       return null;
+               }
+
+       }
+
+       private static final ClipboardTransfer fgTransferInstance = new ClipboardTransfer();
+
+       /** The text operation code */
+       private int fOperationCode= -1;
+       /** The text operation target */
+       private ITextOperationTarget fOperationTarget;
+
+
+       /**
+        * Creates the action.
+        * @param bundle the resource bundle
+        * @param prefix a prefix to be prepended to the various resource keys
+        *   (described in <code>ResourceAction</code> constructor), or
+        *   <code>null</code> if none
+        * @param editor the text editor
+        * @param operationCode the operation code
+        */
+       public ClipboardOperationAction(ResourceBundle bundle, String prefix, ITextEditor editor, int operationCode) {
+               super(bundle, prefix, editor);
+               fOperationCode= operationCode;
+
+               if (operationCode == ITextOperationTarget.CUT) {
+                       setHelpContextId(IAbstractTextEditorHelpContextIds.CUT_ACTION);
+                       setActionDefinitionId(IWorkbenchCommandConstants.EDIT_CUT);
+               } else if (operationCode == ITextOperationTarget.COPY) {
+                       setHelpContextId(IAbstractTextEditorHelpContextIds.COPY_ACTION);
+                       setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY);
+               } else if (operationCode == ITextOperationTarget.PASTE) {
+                       setHelpContextId(IAbstractTextEditorHelpContextIds.PASTE_ACTION);
+                       setActionDefinitionId(IWorkbenchCommandConstants.EDIT_PASTE);
+               } else {
+                       Assert.isTrue(false, "Invalid operation code"); //$NON-NLS-1$
+               }
+               update();
+       }
+
+       private boolean isReadOnlyOperation() {
+               return fOperationCode == ITextOperationTarget.COPY;
+       }
+
+
+       /* (non-Javadoc)
+        * @see org.eclipse.jface.action.IAction#run()
+        */
+       @Override
+       public void run() {
+               if (fOperationCode == -1 || fOperationTarget == null)
+                       return;
+
+               ITextEditor editor= getTextEditor();
+               if (editor == null)
+                       return;
+
+               if (!isReadOnlyOperation() && !validateEditorInputState())
+                       return;
+
+               BusyIndicator.showWhile(getDisplay(), new Runnable() {
+                       public void run() {
+                               internalDoOperation();
+                       }
+               });
+       }
+
+       private Shell getShell() {
+               ITextEditor editor= getTextEditor();
+               if (editor != null) {
+                       IWorkbenchPartSite site= editor.getSite();
+                       Shell shell= site.getShell();
+                       if (shell != null && !shell.isDisposed()) {
+                               return shell;
+                       }
+               }
+               return null;
+       }
+
+       private Display getDisplay() {
+               Shell shell= getShell();
+               if (shell != null) {
+                       return shell.getDisplay();
+               }
+               return null;
+       }
+
+       /**
+        * Returns whether the Smart Insert Mode is selected.
+        * 
+        * @return <code>true</code> if the Smart Insert Mode is selected
+        * @since 3.7
+        */
+       private boolean isSmartInsertMode() {
+               IWorkbenchPage page= JavaPlugin.getActivePage();
+               if (page != null) {
+                       IEditorPart part= page.getActiveEditor();
+                       if (part instanceof ITextEditorExtension3) {
+                               ITextEditorExtension3 extension= (ITextEditorExtension3)part;
+                               return extension.getInsertMode() == ITextEditorExtension3.SMART_INSERT;
+                       } else if (part != null && EditorUtility.isCompareEditorInput(part.getEditorInput())) {
+                               ITextEditorExtension3 extension= (ITextEditorExtension3)part.getAdapter(ITextEditorExtension3.class);
+                               if (extension != null)
+                                       return extension.getInsertMode() == ITextEditorExtension3.SMART_INSERT;
+                       }
+               }
+               return false;
+       }
+
+       protected final void internalDoOperation() {
+               if (PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_IMPORTS_ON_PASTE) && isSmartInsertMode()) {
+                       if (fOperationCode == ITextOperationTarget.PASTE) {
+                               doPasteWithImportsOperation();
+                       } else {
+                               doCutCopyWithImportsOperation();
+                       }
+               } else {
+                       fOperationTarget.doOperation(fOperationCode);
+               }
+       }
+
+       /* (non-Javadoc)
+        * @see org.eclipse.ui.texteditor.IUpdate#update()
+        */
+       @Override
+       public void update() {
+               super.update();
+
+               if (!isReadOnlyOperation() && !canModifyEditor()) {
+                       setEnabled(false);
+                       return;
+               }
+
+               ITextEditor editor= getTextEditor();
+               if (fOperationTarget == null && editor!= null && fOperationCode != -1)
+                       fOperationTarget= (ITextOperationTarget) editor.getAdapter(ITextOperationTarget.class);
+
+               boolean isEnabled= (fOperationTarget != null && fOperationTarget.canDoOperation(fOperationCode));
+               setEnabled(isEnabled);
+       }
+
+       /* (non-Javadoc)
+        * @see org.eclipse.ui.texteditor.TextEditorAction#setEditor(org.eclipse.ui.texteditor.ITextEditor)
+        */
+       @Override
+       public void setEditor(ITextEditor editor) {
+               super.setEditor(editor);
+               fOperationTarget= null;
+       }
+
+
+       private void doCutCopyWithImportsOperation() {
+               ITextEditor editor= getTextEditor();
+               ITypeRoot inputElement= JavaUI.getEditorInputTypeRoot(editor.getEditorInput());
+               ISelection selection= editor.getSelectionProvider().getSelection();
+
+               Object clipboardData= null;
+               if (inputElement != null && selection instanceof ITextSelection && !selection.isEmpty()) {
+                       ITextSelection textSelection= (ITextSelection) selection;
+                       if (isNonTrivialSelection(textSelection)) {
+                               clipboardData= getClipboardData(inputElement, textSelection.getOffset(), textSelection.getLength());
+                       }
+               }
+
+               fOperationTarget.doOperation(fOperationCode);
+
+               if (clipboardData != null) {
+                       /*
+                        * We currently make assumptions about what the styled text widget sets,
+                        * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=61876
+                        */
+                       Clipboard clipboard= new Clipboard(getDisplay());
+                       try {
+                               Object textData= clipboard.getContents(TextTransfer.getInstance());
+                               /*
+                                * Don't add if we didn't get any text data from the clipboard, see:
+                                * - https://bugs.eclipse.org/bugs/show_bug.cgi?id=70077
+                                * - https://bugs.eclipse.org/bugs/show_bug.cgi?id=200743
+                                */
+                               if (textData == null)
+                                       return;
+
+                               ArrayList<Object> datas= new ArrayList<Object>(3);
+                               ArrayList<ByteArrayTransfer> transfers= new ArrayList<ByteArrayTransfer>(3);
+                               datas.add(textData);
+                               transfers.add(TextTransfer.getInstance());
+
+                               Object rtfData= clipboard.getContents(RTFTransfer.getInstance());
+                               if (rtfData != null) {
+                                       datas.add(rtfData);
+                                       transfers.add(RTFTransfer.getInstance());
+                               }
+
+                               datas.add(clipboardData);
+                               transfers.add(fgTransferInstance);
+
+                               Transfer[] dataTypes= transfers.toArray(new Transfer[transfers.size()]);
+                               Object[] data= datas.toArray();
+                               setClipboardContents(clipboard, data, dataTypes);
+                       } finally {
+                               clipboard.dispose();
+                       }
+               }
+       }
+
+       private void setClipboardContents(Clipboard clipboard, Object[] datas, Transfer[] transfers) {
+               try {
+                       clipboard.setContents(datas, transfers);
+               } catch (SWTError e) {
+                       if (e.code != DND.ERROR_CANNOT_SET_CLIPBOARD) {
+                               throw e;
+                       }
+                       // silently fail.  see e.g. https://bugs.eclipse.org/bugs/show_bug.cgi?id=65975
+               }
+       }
+
+       private boolean isNonTrivialSelection(ITextSelection selection) {
+               if (selection.getLength() < 30) {
+                       String text= selection.getText();
+                       if (text != null) {
+                               for (int i= 0; i < text.length(); i++) {
+                                       if (!Character.isJavaIdentifierPart(text.charAt(i))) {
+                                               return true;
+                                       }
+                               }
+                       }
+                       return false;
+               }
+               return true;
+       }
+
+
+       private ClipboardData getClipboardData(ITypeRoot inputElement, int offset, int length) {
+               CompilationUnit astRoot= SharedASTProvider.getAST(inputElement, SharedASTProvider.WAIT_ACTIVE_ONLY, null);
+               if (astRoot == null) {
+                       return null;
+               }
+
+               // do process import if selection spans over import declaration or package
+               List<ImportDeclaration> list= astRoot.imports();
+               if (!list.isEmpty()) {
+                       if (offset < ((ASTNode) list.get(list.size() - 1)).getStartPosition()) {
+                               return null;
+                       }
+               } else if (astRoot.getPackage() != null) {
+                       if (offset < ((ASTNode) astRoot.getPackage()).getStartPosition()) {
+                               return null;
+                       }
+               }
+
+               ArrayList<SimpleName> typeImportsRefs= new ArrayList<SimpleName>();
+               ArrayList<SimpleName> staticImportsRefs= new ArrayList<SimpleName>();
+
+               ImportReferencesCollector.collect(astRoot, inputElement.getJavaProject(), new Region(offset, length), typeImportsRefs, staticImportsRefs);
+
+               if (typeImportsRefs.isEmpty() && staticImportsRefs.isEmpty()) {
+                       return null;
+               }
+
+               HashSet<String> namesToImport= new HashSet<String>(typeImportsRefs.size());
+               for (int i= 0; i < typeImportsRefs.size(); i++) {
+                       Name curr= typeImportsRefs.get(i);
+                       IBinding binding= curr.resolveBinding();
+                       if (binding != null && binding.getKind() == IBinding.TYPE) {
+                               ITypeBinding typeBinding= (ITypeBinding) binding;
+                               if (typeBinding.isArray()) {
+                                       typeBinding= typeBinding.getElementType();
+                               }
+                               if (typeBinding.isTypeVariable() || typeBinding.isCapture() || typeBinding.isWildcardType()) { // can be removed when bug 98473 is fixed
+                                       continue;
+                               }
+
+                               if (typeBinding.isMember() || typeBinding.isTopLevel()) {
+                                       String name= Bindings.getRawQualifiedName(typeBinding);
+                                       if (name.length() > 0) {
+                                               namesToImport.add(name);
+                                       }
+                               }
+                       }
+               }
+
+               HashSet<String> staticsToImport= new HashSet<String>(staticImportsRefs.size());
+               for (int i= 0; i < staticImportsRefs.size(); i++) {
+                       Name curr= staticImportsRefs.get(i);
+                       IBinding binding= curr.resolveBinding();
+                       if (binding != null) {
+                               StringBuffer buf= new StringBuffer(Bindings.getImportName(binding));
+                               if (binding.getKind() == IBinding.METHOD) {
+                                       buf.append("()"); //$NON-NLS-1$
+                               }
+                               staticsToImport.add(buf.toString());
+                       }
+               }
+
+
+               if (namesToImport.isEmpty() && staticsToImport.isEmpty()) {
+                       return null;
+               }
+
+               String[] typeImports= namesToImport.toArray(new String[namesToImport.size()]);
+               String[] staticImports= staticsToImport.toArray(new String[staticsToImport.size()]);
+               return new ClipboardData(inputElement, typeImports, staticImports);
+       }
+
+       private void doPasteWithImportsOperation() {
+               ITextEditor editor= getTextEditor();
+               IJavaElement inputElement= JavaUI.getEditorInputTypeRoot(editor.getEditorInput());
+
+               Clipboard clipboard= new Clipboard(getDisplay());
+               try {
+                       ClipboardData importsData= (ClipboardData)clipboard.getContents(fgTransferInstance);
+                       if (importsData != null && inputElement instanceof ICompilationUnit && !importsData.isFromSame(inputElement)) {
+                               // combine operation and adding of imports
+                               IRewriteTarget target= (IRewriteTarget)editor.getAdapter(IRewriteTarget.class);
+                               if (target != null) {
+                                       target.beginCompoundChange();
+                               }
+                               try {
+                                       fOperationTarget.doOperation(fOperationCode);
+                                       addImports((ICompilationUnit)inputElement, importsData);
+                               } catch (CoreException e) {
+                                       JavaPlugin.log(e);
+                               } finally {
+                                       if (target != null) {
+                                               target.endCompoundChange();
+                                       }
+                               }
+                       } else {
+                               fOperationTarget.doOperation(fOperationCode);
+                       }
+               } finally {
+                       clipboard.dispose();
+               }
+       }
+
+       private void addImports(final ICompilationUnit unit, ClipboardData data) throws CoreException {
+               final ImportRewrite rewrite= StubUtility.createImportRewrite(unit, true);
+               String[] imports= data.getTypeImports();
+               for (int i= 0; i < imports.length; i++) {
+                       rewrite.addImport(imports[i]);
+               }
+               String[] staticImports= data.getStaticImports();
+               for (int i= 0; i < staticImports.length; i++) {
+                       String name= Signature.getSimpleName(staticImports[i]);
+                       boolean isField= !name.endsWith("()"); //$NON-NLS-1$
+                       if (!isField) {
+                               name= name.substring(0, name.length() - 2);
+                       }
+                       String qualifier= Signature.getQualifier(staticImports[i]);
+                       rewrite.addStaticImport(qualifier, name, isField);
+               }
+
+               try {
+                       getProgressService().busyCursorWhile(new IRunnableWithProgress() {
+                               public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+                                       try {
+                                               JavaModelUtil.applyEdit(unit, rewrite.rewriteImports(monitor), false, null);
+                                       } catch (CoreException e) {
+                                               throw new InvocationTargetException(e);
+                                       }
+                               }
+                       });
+               } catch (InvocationTargetException e) {
+                       Throwable cause= e.getCause();
+                       if (cause instanceof CoreException)
+                               throw (CoreException) cause;
+                       throw new CoreException(new Status(IStatus.ERROR, JavaUI.ID_PLUGIN, IJavaStatusConstants.INTERNAL_ERROR, JavaUIMessages.JavaPlugin_internal_error, cause));
+               } catch (InterruptedException e) {
+                       // Canceled by the user
+               }
+       }
+
+       private IProgressService getProgressService() {
+               IEditorPart editor= getTextEditor();
+               if (editor != null) {
+                       IWorkbenchPartSite site= editor.getSite();
+                       if (site != null)
+                               return (IWorkbenchSiteProgressService) editor.getSite().getAdapter(IWorkbenchSiteProgressService.class);
+               }
+               return PlatformUI.getWorkbench().getProgressService();
+       }
+
+}