X-Git-Url: http://git.uio.no/git/?a=blobdiff_plain;f=case-study%2Fjdt-before%2Fui%2Forg%2Feclipse%2Fjdt%2Finternal%2Fui%2Fjavaeditor%2FClipboardOperationAction.java;fp=case-study%2Fjdt-before%2Fui%2Forg%2Feclipse%2Fjdt%2Finternal%2Fui%2Fjavaeditor%2FClipboardOperationAction.java;h=1e0e7ff50c2394cb3b94ac7fea9912258218481c;hb=1b2798f607d741df30e5197f427381cbff326adc;hp=0000000000000000000000000000000000000000;hpb=246231e4bd9b24345490f369747c0549ca308c4d;p=ifi-stolz-refaktor.git 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 index 00000000..1e0e7ff5 --- /dev/null +++ b/case-study/jdt-before/ui/org/eclipse/jdt/internal/ui/javaeditor/ClipboardOperationAction.java @@ -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 ResourceAction constructor), or + * null 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 true 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 datas= new ArrayList(3); + ArrayList transfers= new ArrayList(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 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 typeImportsRefs= new ArrayList(); + ArrayList staticImportsRefs= new ArrayList(); + + ImportReferencesCollector.collect(astRoot, inputElement.getJavaProject(), new Region(offset, length), typeImportsRefs, staticImportsRefs); + + if (typeImportsRefs.isEmpty() && staticImportsRefs.isEmpty()) { + return null; + } + + HashSet namesToImport= new HashSet(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 staticsToImport= new HashSet(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(); + } + +}