blob: dccf2d5d0edc4db1798a91798c344566b793b2b4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2018 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.refactoring.reorg;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IEncodedStorage;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.DocumentRewriteSession;
import org.eclipse.jface.text.DocumentRewriteSessionType;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension4;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchSite;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.CopyFilesAndFoldersOperation;
import org.eclipse.ui.actions.CopyProjectOperation;
import org.eclipse.ui.part.ResourceTransfer;
import org.eclipse.compare.CompareConfiguration;
import org.eclipse.compare.patch.ApplyPatchOperation;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.core.refactoring.RefactoringCore;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageDeclaration;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.compiler.IScanner;
import org.eclipse.jdt.core.compiler.ITerminalSymbols;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ITrackedNodePosition;
import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
import org.eclipse.jdt.internal.core.manipulation.util.Strings;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
import org.eclipse.jdt.internal.corext.dom.BodyDeclarationRewrite;
import org.eclipse.jdt.internal.corext.dom.IASTSharedValues;
import org.eclipse.jdt.internal.corext.refactoring.Checks;
import org.eclipse.jdt.internal.corext.refactoring.TypedSource;
import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility;
import org.eclipse.jdt.internal.corext.refactoring.reorg.IConfirmQuery;
import org.eclipse.jdt.internal.corext.refactoring.reorg.IReorgQueries;
import org.eclipse.jdt.internal.corext.refactoring.reorg.JavaElementTransfer;
import org.eclipse.jdt.internal.corext.refactoring.reorg.ParentChecker;
import org.eclipse.jdt.internal.corext.refactoring.reorg.ReorgDestinationFactory;
import org.eclipse.jdt.internal.corext.refactoring.reorg.ReorgUtils;
import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;
import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringFileBuffers;
import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.JdtFlags;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.IVMInstall2;
import org.eclipse.jdt.launching.IVMInstallType;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.environments.IExecutionEnvironment;
import org.eclipse.jdt.launching.environments.IExecutionEnvironmentsManager;
import org.eclipse.jdt.ui.CodeGeneration;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jdt.ui.actions.SelectionDispatchAction;
import org.eclipse.jdt.ui.refactoring.RefactoringSaveHelper;
import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.jdt.internal.ui.IJavaStatusConstants;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.JavaUIMessages;
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
import org.eclipse.jdt.internal.ui.refactoring.RefactoringExecutionHelper;
import org.eclipse.jdt.internal.ui.refactoring.RefactoringMessages;
import org.eclipse.jdt.internal.ui.util.BusyIndicatorRunnableContext;
import org.eclipse.jdt.internal.ui.util.ExceptionHandler;
import org.eclipse.jdt.internal.ui.util.SelectionUtil;
import org.eclipse.jdt.internal.ui.wizards.buildpaths.BuildPathsBlock;
import org.eclipse.jdt.internal.ui.workingsets.IWorkingSetIDs;
public class PasteAction extends SelectionDispatchAction{
public static final String ID= "org.eclipse.jdt.internal.ui.refactoring.reorg.PasteAction.id"; //$NON-NLS-1$
private final Clipboard fClipboard;
public PasteAction(IWorkbenchSite site) {
this(site, null);
}
public PasteAction(IWorkbenchSite site, Clipboard clipboard) {
super(site);
fClipboard= clipboard;
setId(ID);
setText(ReorgMessages.PasteAction_4);
setDescription(ReorgMessages.PasteAction_5);
ISharedImages workbenchImages= JavaPlugin.getDefault().getWorkbench().getSharedImages();
setDisabledImageDescriptor(workbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE_DISABLED));
setImageDescriptor(workbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE));
setHoverImageDescriptor(workbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE));
PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IJavaHelpContextIds.PASTE_ACTION);
}
@Override
public void selectionChanged(IStructuredSelection selection) {
// Moved condition checking to run (see http://bugs.eclipse.org/bugs/show_bug.cgi?id=78450)
}
private Paster[] createEnabledPasters(TransferData[] availableDataTypes, Clipboard clipboard) {
Paster paster;
Shell shell = getShell();
List<Paster> result= new ArrayList<>(2);
paster= new ProjectPaster(shell, clipboard);
if (paster.canEnable(availableDataTypes))
result.add(paster);
paster= new JavaElementAndResourcePaster(shell, clipboard);
if (paster.canEnable(availableDataTypes))
result.add(paster);
paster= new TypedSourcePaster(shell, clipboard);
if (paster.canEnable(availableDataTypes))
result.add(paster);
paster= new FilePaster(shell, clipboard);
if (paster.canEnable(availableDataTypes))
result.add(paster);
paster= new WorkingSetPaster(shell, clipboard);
if (paster.canEnable(availableDataTypes))
result.add(paster);
paster= new TextPaster(shell, clipboard);
if (paster.canEnable(availableDataTypes))
result.add(paster);
return result.toArray(new Paster[result.size()]);
}
private static Object getContents(final Clipboard clipboard, final Transfer transfer, Shell shell) {
//see bug 33028 for explanation why we need this
final Object[] result= new Object[1];
shell.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
result[0]= clipboard.getContents(transfer);
}
});
return result[0];
}
private static boolean isAvailable(Transfer transfer, TransferData[] availableDataTypes) {
for (int i= 0; i < availableDataTypes.length; i++) {
if (transfer.isSupportedType(availableDataTypes[i])) return true;
}
return false;
}
@Override
public void run(IStructuredSelection selection) {
Clipboard clipboard;
if (fClipboard != null)
clipboard= fClipboard;
else
clipboard= new Clipboard(getShell().getDisplay());
try {
TransferData[] availableTypes= clipboard.getAvailableTypes();
List<?> elements= selection.toList();
IResource[] resources= ReorgUtils.getResources(elements);
IJavaElement[] javaElements= ReorgUtils.getJavaElements(elements);
IWorkingSet[] workingSets= ReorgUtils.getWorkingSets(elements);
Paster[] pasters= createEnabledPasters(availableTypes, clipboard);
for (int i= 0; i < pasters.length; i++) {
try {
if (pasters[i].canPasteOn(javaElements, resources, workingSets, elements)) {
pasters[i].paste(javaElements, resources, workingSets, availableTypes);
return;// one is enough
}
} catch (JavaModelException e) {
ExceptionHandler.handle(e, RefactoringMessages.OpenRefactoringWizardAction_refactoring, RefactoringMessages.OpenRefactoringWizardAction_exception);
} catch (InvocationTargetException e) {
ExceptionHandler.handle(e, RefactoringMessages.OpenRefactoringWizardAction_refactoring, RefactoringMessages.OpenRefactoringWizardAction_exception);
} catch (InterruptedException e) {
// user canceled the paste operation
return;
}
}
String msg= resources.length + javaElements.length + workingSets.length == 0
? ReorgMessages.PasteAction_cannot_no_selection
: ReorgMessages.PasteAction_cannot_selection;
MessageDialog.openError(JavaPlugin.getActiveWorkbenchShell(), ReorgMessages.PasteAction_name, msg);
} finally {
if (fClipboard == null)
clipboard.dispose();
}
}
private abstract static class Paster{
private final Shell fShell;
private final Clipboard fClipboard2;
protected Paster(Shell shell, Clipboard clipboard){
fShell= shell;
fClipboard2= clipboard;
}
protected final Shell getShell() {
return fShell;
}
protected final Clipboard getClipboard() {
return fClipboard2;
}
protected final IResource[] getClipboardResources(TransferData[] availableDataTypes) {
Transfer transfer= ResourceTransfer.getInstance();
if (isAvailable(transfer, availableDataTypes)) {
return (IResource[])getContents(fClipboard2, transfer, getShell());
}
return null;
}
protected final IJavaElement[] getClipboardJavaElements(TransferData[] availableDataTypes) {
Transfer transfer= JavaElementTransfer.getInstance();
if (isAvailable(transfer, availableDataTypes)) {
return (IJavaElement[])getContents(fClipboard2, transfer, getShell());
}
return null;
}
protected final TypedSource[] getClipboardTypedSources(TransferData[] availableDataTypes) {
Transfer transfer= TypedSourceTransfer.getInstance();
if (isAvailable(transfer, availableDataTypes)) {
return (TypedSource[])getContents(fClipboard2, transfer, getShell());
}
return null;
}
protected final String getClipboardText(TransferData[] availableDataTypes) {
Transfer transfer= TextTransfer.getInstance();
if (isAvailable(transfer, availableDataTypes)) {
return (String) getContents(fClipboard2, transfer, getShell());
}
return null;
}
/**
* Used to be called on selection change, but is only called on execution now (before
* {@link #canPasteOn(IJavaElement[], IResource[], IWorkingSet[], List)}).
*
* @param availableTypes transfer types
* @return whether the paste action can be enabled
*/
public abstract boolean canEnable(TransferData[] availableTypes);
/*
* Only called if {@link #canEnable(TransferData[])} returns <code>true</code>.
*/
public abstract boolean canPasteOn(IJavaElement[] selectedJavaElements, IResource[] selectedResources, IWorkingSet[] selectedWorkingSets, List<?> selectedElements) throws JavaModelException;
/*
* only called if {@link #canPasteOn(IJavaElement[], IResource[], IWorkingSet[])} returns <code>true</code>
*/
public abstract void paste(IJavaElement[] selectedJavaElements, IResource[] selectedResources, IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) throws JavaModelException, InterruptedException, InvocationTargetException;
}
private static class TextPaster extends Paster {
private static class ParsedCu {
private final String fText;
private final int fKind;
private final String fTypeName;
private final String fPackageName;
private final boolean fIsModuleInfo;
public static List<ParsedCu> parseCus(IJavaProject javaProject, String compilerCompliance, String text) {
ASTParser parser= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL);
if (javaProject != null) {
parser.setProject(javaProject);
} else if (compilerCompliance != null) {
Map<String, String> options= JavaCore.getOptions();
JavaModelUtil.setComplianceOptions(options, compilerCompliance);
parser.setCompilerOptions(options);
}
parser.setSource(text.toCharArray());
parser.setStatementsRecovery(true);
CompilationUnit unit= (CompilationUnit) parser.createAST(null);
if (unit.types().size() > 0)
return parseAsTypes(text, unit);
if (javaProject != null) {
parser.setProject(javaProject);
} else if (compilerCompliance != null) {
Map<String, String> options= JavaCore.getOptions();
JavaModelUtil.setComplianceOptions(options, compilerCompliance);
parser.setCompilerOptions(options);
}
parser.setSource(text.toCharArray());
parser.setStatementsRecovery(true);
parser.setUnitName(JavaModelUtil.MODULE_INFO_JAVA);
unit= (CompilationUnit) parser.createAST(null);
if (unit.getModule() != null) {
return Collections.singletonList(new ParsedCu(text, ASTParser.K_COMPILATION_UNIT, null, null, true));
}
parser.setProject(javaProject);
parser.setSource(text.toCharArray());
parser.setStatementsRecovery(true);
parser.setKind(ASTParser.K_CLASS_BODY_DECLARATIONS);
ASTNode root= parser.createAST(null);
if (root instanceof TypeDeclaration) {
List<BodyDeclaration> bodyDeclarations= ((TypeDeclaration) root).bodyDeclarations();
if (bodyDeclarations.size() > 0)
return Collections.singletonList(new ParsedCu(text, ASTParser.K_CLASS_BODY_DECLARATIONS, null, null));
}
parser.setProject(javaProject);
parser.setSource(text.toCharArray());
parser.setStatementsRecovery(true);
parser.setKind(ASTParser.K_STATEMENTS);
root= parser.createAST(null);
if (root instanceof Block) {
List<Statement> statements= ((Block) root).statements();
if (statements.size() > 0)
return Collections.singletonList(new ParsedCu(text, ASTParser.K_STATEMENTS, null, null));
}
return Collections.emptyList();
}
private static List<ParsedCu> parseAsTypes(String text, CompilationUnit unit) {
String packageName= IPackageFragment.DEFAULT_PACKAGE_NAME;
PackageDeclaration pack= unit.getPackage();
if (pack != null) {
packageName= pack.getName().getFullyQualifiedName();
}
ArrayList<ParsedCu> cus= new ArrayList<>();
List<AbstractTypeDeclaration> types= unit.types();
int startOffset= 0;
String typeName= null;
int maxVisibility= JdtFlags.VISIBILITY_CODE_INVALID;
// Public types must be in separate CUs:
for (Iterator<AbstractTypeDeclaration> iter= types.iterator(); iter.hasNext(); ) {
AbstractTypeDeclaration type= iter.next();
if (typeName == null) {
// first in group:
maxVisibility= JdtFlags.getVisibilityCode(type);
typeName= type.getName().getIdentifier();
} else {
int visibility= JdtFlags.getVisibilityCode(type);
if (visibility == Modifier.PUBLIC && maxVisibility == Modifier.PUBLIC) {
// public and there was a public type before => create CU for previous:
int prevEnd= type.getStartPosition();
String source= text.substring(startOffset, prevEnd);
cus.add(new ParsedCu(source, ASTParser.K_COMPILATION_UNIT, typeName, packageName));
// ... and restart:
startOffset= prevEnd;
typeName= type.getName().getIdentifier();
maxVisibility= visibility;
} else {
if (JdtFlags.isHigherVisibility(visibility, maxVisibility)) {
maxVisibility= visibility;
typeName= type.getName().getIdentifier();
}
}
}
}
if (typeName != null) {
// create CU for the rest:
String source= text.substring(startOffset);
cus.add(new ParsedCu(source, ASTParser.K_COMPILATION_UNIT, typeName, packageName));
}
return cus;
}
private ParsedCu(String text, int kind, String typeName, String packageName) {
this(text, kind, typeName, packageName, false);
}
private ParsedCu(String text, int kind, String typeName, String packageName, boolean isModuleInfo) {
fText= text;
fTypeName= typeName;
fPackageName= packageName;
fKind= kind;
fIsModuleInfo= isModuleInfo;
}
public String getTypeName() {
return fTypeName;
}
public String getPackageName() {
return fPackageName;
}
public String getText() {
return fText;
}
public int getKind() {
return fKind;
}
}
private IStorage fPatchStorage;
private IPackageFragmentRoot fDestination;
/**
* destination pack iff pasted 1 CU to package fragment or compilation unit, <code>null</code> otherwise
*/
private IPackageFragment fDestinationPack;
private IType fDestinationType;
private IMethod fDestinationMethod;
private int fPackageDeclCount;
private ParsedCu[] fParsedCus;
private TransferData[] fAvailableTypes;
private IPath fVMPath;
private String fCompilerCompliance;
protected TextPaster(Shell shell, Clipboard clipboard) {
super(shell, clipboard);
}
@Override
public boolean canEnable(TransferData[] availableTypes) {
fAvailableTypes= availableTypes;
return PasteAction.isAvailable(TextTransfer.getInstance(), availableTypes) && ! PasteAction.isAvailable(FileTransfer.getInstance(), availableTypes);
}
@Override
public boolean canPasteOn(IJavaElement[] javaElements, IResource[] resources, IWorkingSet[] selectedWorkingSets, List<?> selectedElements) throws JavaModelException {
final String text= getClipboardText(fAvailableTypes);
IStorage storage= new IEncodedStorage() {
@Override
public <T> T getAdapter(Class<T> adapter) {
return null;
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public String getName() {
return null;
}
@Override
public IPath getFullPath() {
return null;
}
@Override
public InputStream getContents() throws CoreException {
try {
return new ByteArrayInputStream(text.getBytes(getCharset()));
} catch (UnsupportedEncodingException e) {
throw new CoreException(new Status(IStatus.ERROR, JavaPlugin.getPluginId(),
IJavaStatusConstants.INTERNAL_ERROR, JavaUIMessages.JavaPlugin_internal_error, e));
}
}
@Override
public String getCharset() throws CoreException {
return "UTF-8"; //$NON-NLS-1$
}
};
try {
if (ApplyPatchOperation.isPatch(storage)) {
fPatchStorage= storage;
return true;
}
} catch (CoreException e) {
// continue
}
if (selectedWorkingSets.length > 1)
return false;
if (resources.length != 0)
return false; //alternative: create text file?
if (javaElements.length > 1)
return false;
IJavaProject javaProject= null;
IJavaElement destination= null;
if (javaElements.length == 1) {
destination= javaElements[0];
javaProject= destination.getJavaProject();
} else if (selectedWorkingSets.length == 1) {
// OK
} else if (selectedElements.size() != 0) {
return false; // e.g. ClassPathContainer
}
computeLatestVM();
parseCUs(javaProject, text);
if (fParsedCus.length == 0)
return false;
if (destination == null)
return true;
/*
* 0 to 1 package declarations in source: paste into package, adapt package declarations
* 2+ package declarations: always paste into source folder
*/
IPackageFragmentRoot packageFragmentRoot;
IPackageFragment destinationPack;
IJavaElement cu;
switch (destination.getElementType()) {
case IJavaElement.JAVA_PROJECT :
IPackageFragmentRoot[] packageFragmentRoots= ((IJavaProject) destination).getPackageFragmentRoots();
for (int i= 0; i < packageFragmentRoots.length; i++) {
packageFragmentRoot= packageFragmentRoots[i];
if (isWritable(packageFragmentRoot)) {
fDestination= packageFragmentRoot;
return true;
}
}
return false;
case IJavaElement.PACKAGE_FRAGMENT_ROOT :
packageFragmentRoot= (IPackageFragmentRoot) destination;
if (isWritable(packageFragmentRoot)) {
fDestination= packageFragmentRoot;
return true;
}
return false;
case IJavaElement.PACKAGE_FRAGMENT :
destinationPack= (IPackageFragment) destination;
packageFragmentRoot= (IPackageFragmentRoot) destinationPack.getParent();
if (isWritable(packageFragmentRoot)) {
fDestination= packageFragmentRoot;
if (fPackageDeclCount <= 1) {
fDestinationPack= destinationPack;
}
return true;
}
return false;
case IJavaElement.COMPILATION_UNIT :
destinationPack= (IPackageFragment) destination.getParent();
packageFragmentRoot= (IPackageFragmentRoot) destinationPack.getParent();
if (isWritable(packageFragmentRoot)) {
fDestination= packageFragmentRoot;
if (fPackageDeclCount <= 1) {
fDestinationPack= destinationPack;
fDestinationType= ((ICompilationUnit) destination).findPrimaryType();
}
return true;
}
return false;
case IJavaElement.TYPE:
cu= ((IType) destination).getCompilationUnit();
if (cu != null) {
fDestinationType= (IType) destination;
fDestinationPack= (IPackageFragment) cu.getParent();
fDestination= (IPackageFragmentRoot) fDestinationPack.getParent();
return true;
}
return false;
case IJavaElement.METHOD:
cu= ((IMethod) destination).getCompilationUnit();
if (cu != null) {
fDestinationMethod= (IMethod) destination;
fDestinationType= (IType) destination.getParent();
fDestinationPack= (IPackageFragment) cu.getParent();
fDestination= (IPackageFragmentRoot) fDestinationPack.getParent();
return true;
}
return false;
default:
return false;
}
}
private boolean isWritable(IPackageFragmentRoot packageFragmentRoot) {
try {
return packageFragmentRoot.exists() && ! packageFragmentRoot.isArchive() && ! packageFragmentRoot.isReadOnly()
&& packageFragmentRoot.getKind() == IPackageFragmentRoot.K_SOURCE;
} catch (JavaModelException e) {
return false;
}
}
@Override
public void paste(IJavaElement[] javaElements, IResource[] resources, final IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) throws JavaModelException, InterruptedException, InvocationTargetException{
if (fPatchStorage != null) {
IResource resource= null;
if (resources.length > 0) {
resource= resources[0];
} else if (javaElements.length > 0) {
resource= javaElements[0].getResource();
}
// XXX: This will be fixed in 3.7, see https://bugs.eclipse.org/309803
new org.eclipse.team.internal.ui.synchronize.patch.ApplyPatchOperation(null, fPatchStorage, resource, new CompareConfiguration()).openWizard();
return;
}
final IEditorPart[] editorPart= new IEditorPart[1];
IRunnableWithProgress op= new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException {
final ArrayList<ICompilationUnit> cus= new ArrayList<>();
try {
JavaCore.run(new IWorkspaceRunnable() {
@Override
public void run(IProgressMonitor pm) throws CoreException {
pm.beginTask("", 1 + fParsedCus.length); //$NON-NLS-1$
if (fDestination == null) {
fDestination= createNewProject(new SubProgressMonitor(pm, 1));
} else {
pm.worked(1);
}
IConfirmQuery confirmQuery= new ReorgQueries(getShell()).createYesYesToAllNoNoToAllQuery(ReorgMessages.PasteAction_TextPaster_confirmOverwriting, true, IReorgQueries.CONFIRM_OVERWRITING);
for (int i= 0; i < fParsedCus.length; i++) {
if (pm.isCanceled())
break;
ICompilationUnit cu= pasteCU(fParsedCus[i], new SubProgressMonitor(pm, 1), confirmQuery);
if (cu != null)
cus.add(cu);
}
if (selectedWorkingSets.length == 1) {
IWorkingSet ws= selectedWorkingSets[0];
if (!IWorkingSetIDs.OTHERS.equals(ws.getId())) {
ArrayList<IAdaptable> newElements= new ArrayList<>();
newElements.addAll(Arrays.asList(ws.getElements()));
newElements.addAll(Arrays.asList(ws.adaptElements(new IAdaptable[] { fDestination.getJavaProject() })));
ws.setElements(newElements.toArray(new IAdaptable[newElements.size()]));
}
}
}
}, monitor);
} catch (OperationCanceledException e) {
// cancelling is fine
} catch (CoreException e) {
throw new InvocationTargetException(e);
} finally {
monitor.done();
}
IResource[] cuResources= ResourceUtil.getFiles(cus.toArray(new ICompilationUnit[cus.size()]));
SelectionUtil.selectAndReveal(cuResources, PlatformUI.getWorkbench().getActiveWorkbenchWindow());
}
private ICompilationUnit pasteCU(ParsedCu parsedCu, IProgressMonitor pm, IConfirmQuery confirmQuery) throws CoreException, OperationCanceledException {
pm.beginTask("", 4); //$NON-NLS-1$
try {
IPackageFragment destinationPack;
if (parsedCu.fIsModuleInfo) {
destinationPack= fDestination.getPackageFragment(""); //$NON-NLS-1$ // the default package
pm.worked(1);
} else {
if (fDestinationPack != null) {
destinationPack= fDestinationPack;
pm.worked(1);
} else {
String packageName= parsedCu.getPackageName();
if (packageName == null)
packageName= ReorgMessages.PasteAction_snippet_default_package_name;
destinationPack= fDestination.getPackageFragment(packageName);
if (!destinationPack.exists()) {
JavaModelUtil.getPackageFragmentRoot(destinationPack).createPackageFragment(packageName, true, new SubProgressMonitor(pm, 1));
} else {
pm.worked(1);
}
}
}
String parsedText= Strings.trimIndentation(parsedCu.getText(), destinationPack.getJavaProject(), true);
int kind= parsedCu.getKind();
if (kind == ASTParser.K_COMPILATION_UNIT) {
final String cuName;
if (parsedCu.fIsModuleInfo) {
cuName= JavaModelUtil.MODULE_INFO_JAVA;
} else {
cuName= parsedCu.getTypeName() + JavaModelUtil.DEFAULT_CU_SUFFIX;
}
ICompilationUnit cu= destinationPack.getCompilationUnit(cuName);
boolean alreadyExists= cu.exists();
if (alreadyExists) {
String msg= Messages.format(ReorgMessages.PasteAction_TextPaster_exists, new Object[] { BasicElementLabels.getResourceName(cuName)});
boolean overwrite= confirmQuery.confirm(msg);
if (! overwrite)
return null;
editorPart[0]= openCu(cu); //Open editor before overwriting to allow undo to restore original package declaration
}
parsedText= parsedText.replaceAll("\r\n?|\n", StubUtility.getLineDelimiterUsed(cu)); //$NON-NLS-1$
destinationPack.createCompilationUnit(cuName, parsedText, true, new SubProgressMonitor(pm, 1));
if (! alreadyExists) {
editorPart[0]= openCu(cu);
}
if (fDestinationPack != null) {
if (fDestinationPack.getElementName().length() == 0) {
removePackageDeclaration(cu);
} else {
cu.createPackageDeclaration(fDestinationPack.getElementName(), new SubProgressMonitor(pm, 1));
}
} else {
String packageName= destinationPack.getElementName();
if (packageName.length() > 0) {
cu.createPackageDeclaration(packageName, new SubProgressMonitor(pm, 1));
}
}
if (! alreadyExists && editorPart[0] != null)
editorPart[0].doSave(new SubProgressMonitor(pm, 1)); //avoid showing error marker due to missing/wrong package declaration
return cu;
} else if (kind == ASTParser.K_CLASS_BODY_DECLARATIONS || kind == ASTParser.K_STATEMENTS) {
return pasteBodyDeclsOrStatements(destinationPack, parsedText, kind, new SubProgressMonitor(pm, 2));
} else {
throw new IllegalStateException("Unexpected kind: " + kind); //$NON-NLS-1$
}
} finally {
pm.done();
}
}
private ICompilationUnit pasteBodyDeclsOrStatements(IPackageFragment destinationPack, String parsedText, int kind, IProgressMonitor pm) throws CoreException, JavaModelException {
ICompilationUnit cu;
IType type;
String typeName;
if (fDestinationType != null) {
cu= fDestinationType.getCompilationUnit();
type= fDestinationType;
typeName= fDestinationType.getElementName();
} else {
typeName= ReorgMessages.PasteAction_snippet_default_type_name;
cu= destinationPack.getCompilationUnit(typeName + JavaModelUtil.DEFAULT_CU_SUFFIX);
type= cu.getType(typeName);
}
if (cu.exists()) {
editorPart[0]= openCu(cu); //Open editor before pasting to allow undo to restore original contents
ITextFileBuffer fileBuffer= RefactoringFileBuffers.acquire(cu);
try {
IDocument document= fileBuffer.getDocument();
ASTParser parser= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL);
parser.setProject(cu.getJavaProject());
parser.setSource(document.get().toCharArray());
parser.setStatementsRecovery(true);
CompilationUnit cuNode= (CompilationUnit) parser.createAST(pm);
AbstractTypeDeclaration typeDecl= type.exists() ? ASTNodeSearchUtil.getAbstractTypeDeclarationNode(type, cuNode) : null;
MethodDeclaration methodDecl= fDestinationMethod != null ? ASTNodeSearchUtil.getMethodDeclarationNode(fDestinationMethod, cuNode) : null;
ITrackedNodePosition textPosition= createOrFillTypeDeclaration(cuNode, document, cu, typeDecl, typeName, methodDecl, kind, parsedText);
EditorUtility.revealInEditor(editorPart[0], textPosition.getStartPosition() + textPosition.getLength(), 0);
// leave unsaved
} finally {
RefactoringFileBuffers.release(cu);
}
} else {
String delim= StubUtility.getLineDelimiterUsed(cu);
String fileComment= null;
if (StubUtility.doAddComments(cu.getJavaProject())) {
fileComment= CodeGeneration.getFileComment(cu, delim);
}
String cuContent= CodeGeneration.getCompilationUnitContent(cu, fileComment, null, "", delim); //$NON-NLS-1$
if (cuContent == null)
cuContent= ""; //$NON-NLS-1$
IDocument document= new Document(cuContent);
ASTParser parser= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL);
parser.setProject(cu.getJavaProject());
parser.setSource(cuContent.toCharArray());
parser.setStatementsRecovery(true);
CompilationUnit cuNode= (CompilationUnit) parser.createAST(pm);
ITrackedNodePosition textPosition= createOrFillTypeDeclaration(cuNode, document, cu, null, typeName, null, kind, parsedText);
destinationPack.createCompilationUnit(cu.getElementName(), document.get(), false, null);
editorPart[0]= openCu(cu);
EditorUtility.revealInEditor(editorPart[0], textPosition.getStartPosition() + textPosition.getLength(), 0);
}
return cu;
}
private ITrackedNodePosition createOrFillTypeDeclaration(
CompilationUnit cuNode,
IDocument document,
ICompilationUnit cu,
AbstractTypeDeclaration typeDecl,
String typeName,
MethodDeclaration methodDecl,
int kind,
String parsedText) throws CoreException {
AST ast= cuNode.getAST();
ASTRewrite rewrite= ASTRewrite.create(ast);
String delim= StubUtility.getLineDelimiterUsed(cu);
if (typeDecl == null) {
typeDecl= ast.newTypeDeclaration();
typeDecl.getName().setIdentifier(typeName);
if (StubUtility.doAddComments(cu.getJavaProject())) {
String typeComment= CodeGeneration.getTypeComment(cu, typeName, delim);
if (typeComment != null)
typeDecl.setJavadoc((Javadoc) rewrite.createStringPlaceholder(typeComment, ASTNode.JAVADOC));
}
typeDecl.modifiers().add(ast.newModifier(ModifierKeyword.PUBLIC_KEYWORD));
String typeBody= CodeGeneration.getTypeBody(CodeGeneration.CLASS_BODY_TEMPLATE_ID, cu, typeName, delim);
if (typeBody != null) {
BodyDeclaration typeBodyNode= (BodyDeclaration) rewrite.createStringPlaceholder(typeBody, ASTNode.METHOD_DECLARATION);
ListRewrite bodyRewrite= rewrite.getListRewrite(typeDecl, typeDecl.getBodyDeclarationsProperty());
bodyRewrite.insertFirst(typeBodyNode, null);
}
rewrite.getListRewrite(cuNode, CompilationUnit.TYPES_PROPERTY).insertLast(typeDecl, null);
}
ITrackedNodePosition textPosition;
if (kind == ASTParser.K_CLASS_BODY_DECLARATIONS) {
ListRewrite bodyRewrite= rewrite.getListRewrite(typeDecl, typeDecl.getBodyDeclarationsProperty());
BodyDeclaration textNode= (BodyDeclaration) rewrite.createStringPlaceholder(parsedText, ASTNode.METHOD_DECLARATION);
bodyRewrite.insertLast(textNode, null);
textPosition= rewrite.track(textNode);
} else { // kind == ASTParser.K_STATEMENTS
if (methodDecl == null || methodDecl.getBody() == null) {
methodDecl= ast.newMethodDeclaration();
String qualifiedtypeName= fDestinationType != null ? fDestinationType.getFullyQualifiedName('.') : cu.getParent().getElementName() + '.' + typeName;
if (StubUtility.doAddComments(cu.getJavaProject())) {
String methodComment= CodeGeneration.getMethodComment(cu, qualifiedtypeName, methodDecl, null, delim);
if (methodComment != null)
methodDecl.setJavadoc((Javadoc) rewrite.createStringPlaceholder(methodComment, ASTNode.JAVADOC));
}
methodDecl.modifiers().addAll(ast.newModifiers(Modifier.PUBLIC | Modifier.STATIC));
String methodName= "main"; //$NON-NLS-1$
methodDecl.getName().setIdentifier(methodName);
SingleVariableDeclaration param= ast.newSingleVariableDeclaration();
param.setType(ast.newArrayType(ast.newSimpleType(ast.newSimpleName("String")))); //$NON-NLS-1$
param.getName().setIdentifier("args"); //$NON-NLS-1$
methodDecl.parameters().add(param);
Block block= ast.newBlock();
methodDecl.setBody(block);
rewrite.getListRewrite(typeDecl, typeDecl.getBodyDeclarationsProperty()).insertLast(methodDecl, null);
}
Block body= methodDecl.getBody();
ListRewrite statementsRewrite= rewrite.getListRewrite(body, Block.STATEMENTS_PROPERTY);
Statement textNode= (Statement) rewrite.createStringPlaceholder(parsedText, ASTNode.EMPTY_STATEMENT);
statementsRewrite.insertLast(textNode, null);
textPosition= rewrite.track(textNode);
}
DocumentRewriteSession rewriteSession= null;
if (document instanceof IDocumentExtension4)
rewriteSession= ((IDocumentExtension4) document).startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED_SMALL);
TextEdit edit= rewrite.rewriteAST(document, cu.getJavaProject().getOptions(true));
try {
edit.apply(document, TextEdit.UPDATE_REGIONS);
return textPosition;
} catch (MalformedTreeException e) {
JavaPlugin.log(e);
} catch (BadLocationException e) {
JavaPlugin.log(e);
} finally {
if (rewriteSession != null)
((IDocumentExtension4) document).stopRewriteSession(rewriteSession);
}
return null;
}
private IPackageFragmentRoot createNewProject(SubProgressMonitor pm) throws CoreException {
pm.beginTask("", 10); //$NON-NLS-1$
IProject project;
int i= 1;
do {
String name= Messages.format(ReorgMessages.PasteAction_projectName, i == 1 ? (Object) "" : new Integer(i)); //$NON-NLS-1$
project= JavaPlugin.getWorkspace().getRoot().getProject(name);
i++;
} while (project.exists());
BuildPathsBlock.createProject(project, null, new SubProgressMonitor(pm, 3));
BuildPathsBlock.addJavaNature(project, new SubProgressMonitor(pm, 1));
IJavaProject javaProject= JavaCore.create(project);
IResource srcFolder;
IPreferenceStore store= PreferenceConstants.getPreferenceStore();
String sourceFolderName= store.getString(PreferenceConstants.SRCBIN_SRCNAME);
if (store.getBoolean(PreferenceConstants.SRCBIN_FOLDERS_IN_NEWPROJ) && sourceFolderName.length() > 0) {
IFolder folder= project.getFolder(sourceFolderName);
if (! folder.exists()) {
folder.create(false, true, new SubProgressMonitor(pm, 1));
}
srcFolder= folder;
} else {
srcFolder= project;
}
if (fCompilerCompliance != null) {
Map<String, String> options= javaProject.getOptions(false);
JavaModelUtil.setComplianceOptions(options, fCompilerCompliance);
JavaModelUtil.setDefaultClassfileOptions(options, fCompilerCompliance);
javaProject.setOptions(options);
}
IClasspathEntry srcEntry= JavaCore.newSourceEntry(srcFolder.getFullPath());
IClasspathEntry jreEntry= JavaCore.newContainerEntry(fVMPath);
IPath outputLocation= BuildPathsBlock.getDefaultOutputLocation(javaProject);
IClasspathEntry[] cpes= new IClasspathEntry[] { srcEntry, jreEntry };
javaProject.setRawClasspath(cpes, outputLocation, new SubProgressMonitor(pm, 1));
return javaProject.getPackageFragmentRoot(srcFolder);
}
private void removePackageDeclaration(final ICompilationUnit cu) throws JavaModelException, CoreException {
IPackageDeclaration[] packageDeclarations= cu.getPackageDeclarations();
if (packageDeclarations.length != 0) {
ITextFileBuffer buffer= null;
try {
buffer= RefactoringFileBuffers.acquire(cu);
ISourceRange sourceRange= packageDeclarations[0].getSourceRange();
buffer.getDocument().replace(sourceRange.getOffset(), sourceRange.getLength(), ""); //$NON-NLS-1$
} catch (BadLocationException e) {
JavaPlugin.log(e);
} finally {
if (buffer != null)
RefactoringFileBuffers.release(cu);
}
}
}
};
IRunnableContext context= JavaPlugin.getActiveWorkbenchWindow();
if (context == null) {
context= new BusyIndicatorRunnableContext();
}
//TODO: wrap op in workspace runnable and pass to JavaCore.run(..); take project creation out of UI thread.
PlatformUI.getWorkbench().getProgressService().runInUI(context, op, JavaPlugin.getWorkspace().getRoot());
if (editorPart[0] != null)
editorPart[0].getEditorSite().getPage().activate(editorPart[0]); //activate editor again, since runInUI restores previous active part
}
private IEditorPart openCu(ICompilationUnit cu) {
try {
return JavaUI.openInEditor(cu, true, true);
} catch (PartInitException e) {
JavaPlugin.log(e);
return null;
} catch (JavaModelException e) {
JavaPlugin.log(e);
return null;
}
}
private void parseCUs(IJavaProject javaProject, String text) {
IScanner scanner= ToolFactory.createScanner(false, false, false, false);
scanner.setSource(text.toCharArray());
ArrayList<ParsedCu> cus= new ArrayList<>();
int start= 0;
boolean tokensScanned= false;
fPackageDeclCount= 0;
int tok;
while (true) {
try {
tok= scanner.getNextToken();
} catch (InvalidInputException e) {
// Handle gracefully to give the ASTParser a chance to recover,
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=168691
tok= ITerminalSymbols.TokenNameEOF;
}
if (tok == ITerminalSymbols.TokenNamepackage) {
fPackageDeclCount++;
if (tokensScanned) {
int packageStart= scanner.getCurrentTokenStartPosition();
List<ParsedCu> parsed= ParsedCu.parseCus(javaProject, fCompilerCompliance, text.substring(start, packageStart));
if (parsed.size() > 0) {
cus.addAll(parsed);
start= packageStart;
}
}
} else if (tok == ITerminalSymbols.TokenNameEOF) {
List<ParsedCu> parsed= ParsedCu.parseCus(javaProject, fCompilerCompliance, text.substring(start, text.length()));
if (parsed.size() > 0) {
cus.addAll(parsed);
}
break;
}
tokensScanned= true;
}
fParsedCus= cus.toArray(new ParsedCu[cus.size()]);
}
private void computeLatestVM() {
IVMInstall bestVM= JavaRuntime.getDefaultVMInstall();
String bestVersion= getVMVersion(bestVM);
IExecutionEnvironmentsManager eeManager= JavaRuntime.getExecutionEnvironmentsManager();
IExecutionEnvironment bestEE= null;
IExecutionEnvironment[] ees= eeManager.getExecutionEnvironments();
outer: for (int j= 0; j < ees.length; j++) {
IExecutionEnvironment ee= ees[j];
IVMInstall vm= ee.getDefaultVM();
String ver= getVMVersion(vm);
if (ver != null) {
if (bestVersion == null || JavaModelUtil.isVersionLessThan(bestVersion, ver) || bestVersion.equals(ver)) {
bestVersion= ver;
bestEE= ee;
}
} else {
String eeVer= JavaModelUtil.getExecutionEnvironmentCompliance(ee);
IVMInstall[] compatibleVMs= ee.getCompatibleVMs();
for (int i= 0; i < compatibleVMs.length; i++) {
vm= compatibleVMs[i];
ver= getVMVersion(vm);
if (!eeVer.equals(ver))
continue; // don't want to set an EE where there's no strictly compatible VM
if (bestVersion == null || JavaModelUtil.isVersionLessThan(bestVersion, ver) || bestVersion.equals(ver)) {
bestVersion= ver;
bestEE= ee;
continue outer;
}
}
}
}
IVMInstallType[] vmTypes= JavaRuntime.getVMInstallTypes();
for (int i= 0; i < vmTypes.length; i++) {
IVMInstall[] vms= vmTypes[i].getVMInstalls();
for (int j= 0; j < vms.length; j++) {
IVMInstall vm= vms[j];
String ver= getVMVersion(vm);
if (ver != null && (bestVersion == null || JavaModelUtil.isVersionLessThan(bestVersion, ver))) {
bestVersion= ver;
bestVM= vm;
bestEE= null;
}
}
}
if (bestEE != null) {
fVMPath= JavaRuntime.newJREContainerPath(bestEE);
fCompilerCompliance= bestVersion;
} else if (bestVM != null) {
fVMPath= JavaRuntime.newJREContainerPath(bestVM);
fCompilerCompliance= bestVersion;
} else {
fVMPath= JavaRuntime.newDefaultJREContainerPath();
}
}
private String getVMVersion(IVMInstall vm) {
if (vm instanceof IVMInstall2) {
IVMInstall2 vm2= (IVMInstall2) vm;
return JavaModelUtil.getCompilerCompliance(vm2, null);
} else {
return null;
}
}
}
private static class WorkingSetPaster extends Paster {
protected WorkingSetPaster(Shell shell, Clipboard clipboard) {
super(shell, clipboard);
}
@Override
public void paste(IJavaElement[] selectedJavaElements, IResource[] selectedResources, IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) throws JavaModelException, InterruptedException, InvocationTargetException {
IWorkingSet workingSet= selectedWorkingSets[0];
Set<IAdaptable> elements= new HashSet<>(Arrays.asList(workingSet.getElements()));
IJavaElement[] javaElements= getClipboardJavaElements(availableTypes);
if (javaElements != null) {
for (int i= 0; i < javaElements.length; i++) {
if (!ReorgUtils.containsElementOrParent(elements, javaElements[i]))
elements.add(javaElements[i]);
}
}
IResource[] resources= getClipboardResources(availableTypes);
if (resources != null) {
List<IJavaElement> realJavaElements= new ArrayList<>();
List<IResource> realResource= new ArrayList<>();
ReorgUtils.splitIntoJavaElementsAndResources(resources, realJavaElements, realResource);
for (Iterator<IJavaElement> iter= realJavaElements.iterator(); iter.hasNext();) {
IJavaElement element= iter.next();
if (!ReorgUtils.containsElementOrParent(elements, element))
elements.add(element);
}
for (Iterator<IResource> iter= realResource.iterator(); iter.hasNext();) {
IResource element= iter.next();
if (!ReorgUtils.containsElementOrParent(elements, element))
elements.add(element);
}
}
workingSet.setElements(elements.toArray(new IAdaptable[elements.size()]));
}
@Override
public boolean canEnable(TransferData[] availableTypes) {
return isAvailable(ResourceTransfer.getInstance(), availableTypes) ||
isAvailable(JavaElementTransfer.getInstance(), availableTypes);
}
@Override
public boolean canPasteOn(IJavaElement[] selectedJavaElements, IResource[] selectedResources, IWorkingSet[] selectedWorkingSets, List<?> selectedElements) throws JavaModelException {
if (selectedResources.length != 0 || selectedJavaElements.length != 0 || selectedWorkingSets.length != 1)
return false;
IWorkingSet ws= selectedWorkingSets[0];
return !IWorkingSetIDs.OTHERS.equals(ws.getId());
}
}
private static class ProjectPaster extends Paster{
protected ProjectPaster(Shell shell, Clipboard clipboard) {
super(shell, clipboard);
}
@Override
public boolean canEnable(TransferData[] availableDataTypes) {
boolean resourceTransfer= isAvailable(ResourceTransfer.getInstance(), availableDataTypes);
boolean javaElementTransfer= isAvailable(JavaElementTransfer.getInstance(), availableDataTypes);
if (! javaElementTransfer)
return canPasteSimpleProjects(availableDataTypes);
if (! resourceTransfer)
return canPasteJavaProjects(availableDataTypes);
return canPasteJavaProjects(availableDataTypes) && canPasteSimpleProjects(availableDataTypes);
}
@Override
public void paste(IJavaElement[] javaElements, IResource[] resources, IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) {
pasteProjects(availableTypes);
}
private void pasteProjects(TransferData[] availableTypes) {
pasteProjects(getProjectsToPaste(availableTypes));
}
private void pasteProjects(IProject[] projects){
Shell shell= getShell();
for (int i = 0; i < projects.length; i++) {
new CopyProjectOperation(shell).copyProject(projects[i]);
}
}
private IProject[] getProjectsToPaste(TransferData[] availableTypes) {
IResource[] resources= getClipboardResources(availableTypes);
IJavaElement[] javaElements= getClipboardJavaElements(availableTypes);
Set<IResource> result= new HashSet<>();
if (resources != null)
result.addAll(Arrays.asList(resources));
if (javaElements != null)
result.addAll(Arrays.asList(ReorgUtils.getNotNulls(ReorgUtils.getResources(javaElements))));
Assert.isTrue(result.size() > 0);
return result.toArray(new IProject[result.size()]);
}
@Override
public boolean canPasteOn(IJavaElement[] javaElements, IResource[] resources, IWorkingSet[] selectedWorkingSets, List<?> selectedElements) {
return selectedWorkingSets.length == 0; // Can't paste on working sets here
}
private boolean canPasteJavaProjects(TransferData[] availableDataTypes) {
IJavaElement[] javaElements= getClipboardJavaElements(availableDataTypes);
return javaElements != null &&
javaElements.length != 0 &&
! ReorgUtils.hasElementsNotOfType(javaElements, IJavaElement.JAVA_PROJECT);
}
private boolean canPasteSimpleProjects(TransferData[] availableDataTypes) {
IResource[] resources= getClipboardResources(availableDataTypes);
if (resources == null || resources.length == 0) return false;
for (int i= 0; i < resources.length; i++) {
if (resources[i].getType() != IResource.PROJECT || ! ((IProject)resources[i]).isOpen())
return false;
}
return true;
}
}
private static class FilePaster extends Paster{
protected FilePaster(Shell shell, Clipboard clipboard) {
super(shell, clipboard);
}
@Override
public void paste(IJavaElement[] javaElements, IResource[] resources, IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) throws JavaModelException {
String[] fileData= getClipboardFiles(availableTypes);
if (fileData == null)
return;
IContainer container= getAsContainer(getTarget(javaElements, resources));
if (container == null)
return;
new CopyFilesAndFoldersOperation(getShell()).copyFiles(fileData, container);
}
private Object getTarget(IJavaElement[] javaElements, IResource[] resources) {
if (javaElements.length + resources.length == 1){
if (javaElements.length == 1)
return javaElements[0];
else
return resources[0];
} else
return getCommonParent(javaElements, resources);
}
@Override
public boolean canPasteOn(IJavaElement[] javaElements, IResource[] resources, IWorkingSet[] selectedWorkingSets, List<?> selectedElements) throws JavaModelException {
Object target= getTarget(javaElements, resources);
return target != null && canPasteFilesOn(getAsContainer(target)) && selectedWorkingSets.length == 0;
}
@Override
public boolean canEnable(TransferData[] availableDataTypes) {
return isAvailable(FileTransfer.getInstance(), availableDataTypes);
}
private boolean canPasteFilesOn(Object target) {
boolean isPackageFragment= target instanceof IPackageFragment;
boolean isJavaProject= target instanceof IJavaProject;
boolean isPackageFragmentRoot= target instanceof IPackageFragmentRoot;
boolean isContainer= target instanceof IContainer;
if (!(isPackageFragment || isJavaProject || isPackageFragmentRoot || isContainer))
return false;
if (isContainer) {
if (target instanceof IProject)
return ((IProject)target).isOpen();
return true;
} else {
if (isJavaProject && !((IJavaProject) target).getProject().isOpen())
return false;
IJavaElement element= (IJavaElement)target;
return !element.isReadOnly();
}
}
private IContainer getAsContainer(Object target) throws JavaModelException{
if (target == null)
return null;
if (target instanceof IContainer)
return (IContainer)target;
if (target instanceof IFile)
return ((IFile)target).getParent();
return getAsContainer(((IJavaElement)target).getCorrespondingResource());
}
private String[] getClipboardFiles(TransferData[] availableDataTypes) {
Transfer transfer= FileTransfer.getInstance();
if (isAvailable(transfer, availableDataTypes)) {
return (String[])getContents(getClipboard(), transfer, getShell());
}
return null;
}
private Object getCommonParent(IJavaElement[] javaElements, IResource[] resources) {
return new ParentChecker(resources, javaElements).getCommonParent();
}
}
private static class JavaElementAndResourcePaster extends Paster {
protected JavaElementAndResourcePaster(Shell shell, Clipboard clipboard) {
super(shell, clipboard);
}
private TransferData[] fAvailableTypes;
@Override
public void paste(IJavaElement[] javaElements, IResource[] resources, IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) throws JavaModelException, InterruptedException, InvocationTargetException{
IResource[] clipboardResources= getClipboardResources(availableTypes);
if (clipboardResources == null)
clipboardResources= new IResource[0];
IJavaElement[] clipboardJavaElements= getClipboardJavaElements(availableTypes);
if (clipboardJavaElements == null)
clipboardJavaElements= new IJavaElement[0];
Object destination= getTarget(javaElements, resources);
Assert.isNotNull(destination);
Assert.isLegal(clipboardResources.length + clipboardJavaElements.length > 0);
ReorgCopyStarter.create(clipboardJavaElements, clipboardResources, ReorgDestinationFactory.createDestination(destination)).run(getShell());
}
private Object getTarget(IJavaElement[] javaElements, IResource[] resources) {
if (javaElements.length + resources.length == 1){
if (javaElements.length == 1)
return javaElements[0];
else
return resources[0];
} else
return getCommonParent(javaElements, resources);
}
private Object getCommonParent(IJavaElement[] javaElements, IResource[] resources) {
return new ParentChecker(resources, javaElements).getCommonParent();
}
@Override
public boolean canPasteOn(IJavaElement[] javaElements, IResource[] resources, IWorkingSet[] selectedWorkingSets, List<?> selectedElements) throws JavaModelException {
if (selectedWorkingSets.length != 0)
return false;
IResource[] clipboardResources= getClipboardResources(fAvailableTypes);
if (clipboardResources == null)
clipboardResources= new IResource[0];
IJavaElement[] clipboardJavaElements= getClipboardJavaElements(fAvailableTypes);
if (clipboardJavaElements == null)
clipboardJavaElements= new IJavaElement[0];
Object destination= getTarget(javaElements, resources);
return ReorgCopyStarter.create(clipboardJavaElements, clipboardResources, ReorgDestinationFactory.createDestination(destination)) != null;
}
@Override
public boolean canEnable(TransferData[] availableTypes) {
fAvailableTypes= availableTypes;
return isAvailable(JavaElementTransfer.getInstance(), availableTypes) || isAvailable(ResourceTransfer.getInstance(), availableTypes);
}
}
private static class TypedSourcePaster extends Paster{
protected TypedSourcePaster(Shell shell, Clipboard clipboard) {
super(shell, clipboard);
}
private TransferData[] fAvailableTypes;
@Override
public boolean canEnable(TransferData[] availableTypes) {
fAvailableTypes= availableTypes;
return isAvailable(TypedSourceTransfer.getInstance(), availableTypes);
}
@Override
public boolean canPasteOn(IJavaElement[] selectedJavaElements, IResource[] selectedResources, IWorkingSet[] selectedWorkingSets, List<?> selectedElements) throws JavaModelException {
if (selectedResources.length != 0 || selectedWorkingSets.length != 0)
return false;
TypedSource[] typedSources= getClipboardTypedSources(fAvailableTypes);
Object destination= getTarget(selectedJavaElements, selectedResources);
if (destination instanceof IJavaElement)
return ReorgTypedSourcePasteStarter.create(typedSources, (IJavaElement)destination) != null;
return false;
}
@Override
public void paste(IJavaElement[] selectedJavaElements, IResource[] selectedResources, IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) throws JavaModelException, InterruptedException, InvocationTargetException {
TypedSource[] typedSources= getClipboardTypedSources(availableTypes);
IJavaElement destination= getTarget(selectedJavaElements, selectedResources);
ReorgTypedSourcePasteStarter.create(typedSources, destination).run(getShell());
}
private static IJavaElement getTarget(IJavaElement[] selectedJavaElements, IResource[] selectedResources) {
Assert.isTrue(selectedResources.length == 0);
if (selectedJavaElements.length == 1)
return getAsTypeOrCu(selectedJavaElements[0]);
Object parent= new ParentChecker(selectedResources, selectedJavaElements).getCommonParent();
if (parent instanceof IJavaElement)
return getAsTypeOrCu((IJavaElement)parent);
return null;
}
private static IJavaElement getAsTypeOrCu(IJavaElement element) {
//try to get type first
if (element.getElementType() == IJavaElement.COMPILATION_UNIT || element.getElementType() == IJavaElement.TYPE)
return element;
IJavaElement ancestorType= element.getAncestor(IJavaElement.TYPE);
if (ancestorType != null)
return ancestorType;
return ReorgUtils.getCompilationUnit(element);
}
private static class ReorgTypedSourcePasteStarter {
private final PasteTypedSourcesRefactoring fPasteRefactoring;
private ReorgTypedSourcePasteStarter(PasteTypedSourcesRefactoring pasteRefactoring) {
Assert.isNotNull(pasteRefactoring);
fPasteRefactoring= pasteRefactoring;
}
public static ReorgTypedSourcePasteStarter create(TypedSource[] typedSources, IJavaElement destination) {
Assert.isNotNull(typedSources);
Assert.isNotNull(destination);
PasteTypedSourcesRefactoring pasteRefactoring= PasteTypedSourcesRefactoring.create(typedSources);
if (pasteRefactoring == null)
return null;
if (! pasteRefactoring.setDestination(destination).isOK())
return null;
return new ReorgTypedSourcePasteStarter(pasteRefactoring);
}
public void run(Shell parent) throws InterruptedException, InvocationTargetException {
IRunnableContext context= new ProgressMonitorDialog(parent);
new RefactoringExecutionHelper(fPasteRefactoring, RefactoringCore.getConditionCheckingFailedSeverity(), RefactoringSaveHelper.SAVE_NOTHING, parent, context).perform(false, false);
}
}
private static class PasteTypedSourcesRefactoring extends Refactoring {
private final TypedSource[] fSources;
private IJavaElement fDestination;
static PasteTypedSourcesRefactoring create(TypedSource[] sources){
if (! isAvailable(sources))
return null;
return new PasteTypedSourcesRefactoring(sources);
}
public RefactoringStatus setDestination(IJavaElement destination) {
fDestination= destination;
if (ReorgUtils.getCompilationUnit(destination) == null)
return RefactoringStatus.createFatalErrorStatus(ReorgMessages.PasteAction_wrong_destination);
if (! destination.exists())
return RefactoringStatus.createFatalErrorStatus(ReorgMessages.PasteAction_element_doesnot_exist);
if (! canPasteAll(destination))
return RefactoringStatus.createFatalErrorStatus(ReorgMessages.PasteAction_invalid_destination);
return new RefactoringStatus();
}
private boolean canPasteAll(IJavaElement destination) {
for (int i= 0; i < fSources.length; i++) {
if (! canPaste(fSources[i].getType(), destination))
return false;
}
return true;
}
private static boolean canPaste(int elementType, IJavaElement destination) {
IType ancestorType= getAncestorType(destination);
if (ancestorType != null)
return canPasteToType(elementType);
return canPasteToCu(elementType);
}
private static boolean canPasteToType(int elementType) {
return elementType == IJavaElement.TYPE ||
elementType == IJavaElement.FIELD ||
elementType == IJavaElement.INITIALIZER ||
elementType == IJavaElement.METHOD;
}
private static boolean canPasteToCu(int elementType) {
return elementType == IJavaElement.PACKAGE_DECLARATION ||
elementType == IJavaElement.TYPE ||
elementType == IJavaElement.IMPORT_DECLARATION;
}
PasteTypedSourcesRefactoring(TypedSource[] sources){
Assert.isNotNull(sources);
Assert.isTrue(sources.length != 0);
fSources= sources;
}
private static boolean isAvailable(TypedSource[] sources) {
return sources != null && sources.length > 0;
}
@Override
public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
return new RefactoringStatus();
}
@Override
public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException {
RefactoringStatus result= Checks.validateModifiesFiles(
ResourceUtil.getFiles(new ICompilationUnit[]{getDestinationCu()}), getValidationContext());
return result;
}
@Override
public Change createChange(IProgressMonitor pm) throws CoreException {
ASTParser p= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL);
p.setSource(getDestinationCu());
CompilationUnit cuNode= (CompilationUnit) p.createAST(pm);
ASTRewrite rewrite= ASTRewrite.create(cuNode.getAST());
TypedSource source= null;
for (int i= fSources.length - 1; i >= 0; i--) {
source= fSources[i];
final ASTNode destination= getDestinationNodeForSourceElement(fDestination, source.getType(), cuNode);
if (destination != null) {
if (destination instanceof CompilationUnit)
insertToCu(rewrite, createNewNodeToInsertToCu(source, rewrite), (CompilationUnit) destination);
else if (destination instanceof AbstractTypeDeclaration)
insertToType(rewrite, createNewNodeToInsertToType(source, rewrite), (AbstractTypeDeclaration) destination);
}
}
final CompilationUnitChange result= new CompilationUnitChange(ReorgMessages.PasteAction_change_name, getDestinationCu());
try {
ITextFileBuffer buffer= RefactoringFileBuffers.acquire(getDestinationCu());
TextEdit rootEdit= rewrite.rewriteAST(buffer.getDocument(), fDestination.getJavaProject().getOptions(true));
if (getDestinationCu().isWorkingCopy())
result.setSaveMode(TextFileChange.LEAVE_DIRTY);
TextChangeCompatibility.addTextEdit(result, ReorgMessages.PasteAction_edit_name, rootEdit);
} finally {
RefactoringFileBuffers.release(getDestinationCu());
}
return result;
}
private static void insertToType(ASTRewrite rewrite, ASTNode node, AbstractTypeDeclaration typeDeclaration) {
switch (node.getNodeType()) {
case ASTNode.ANNOTATION_TYPE_DECLARATION:
case ASTNode.ENUM_DECLARATION:
case ASTNode.TYPE_DECLARATION:
case ASTNode.METHOD_DECLARATION:
case ASTNode.FIELD_DECLARATION:
case ASTNode.INITIALIZER:
rewrite.getListRewrite(typeDeclaration, typeDeclaration.getBodyDeclarationsProperty()).insertAt(node, BodyDeclarationRewrite.getInsertionIndex((BodyDeclaration) node, typeDeclaration.bodyDeclarations()), null);
break;
default:
Assert.isTrue(false, String.valueOf(node.getNodeType()));
}
}
private static void insertToCu(ASTRewrite rewrite, ASTNode node, CompilationUnit cuNode) {
switch (node.getNodeType()) {
case ASTNode.TYPE_DECLARATION:
case ASTNode.ENUM_DECLARATION:
case ASTNode.ANNOTATION_TYPE_DECLARATION:
rewrite.getListRewrite(cuNode, CompilationUnit.TYPES_PROPERTY).insertAt(node, BodyDeclarationRewrite.getInsertionIndex((AbstractTypeDeclaration) node, cuNode.types()), null);
break;
case ASTNode.IMPORT_DECLARATION:
rewrite.getListRewrite(cuNode, CompilationUnit.IMPORTS_PROPERTY).insertLast(node, null);
break;
case ASTNode.PACKAGE_DECLARATION:
// only insert if none exists
if (cuNode.getPackage() == null)
rewrite.set(cuNode, CompilationUnit.PACKAGE_PROPERTY, node, null);
break;
default:
Assert.isTrue(false, String.valueOf(node.getNodeType()));
}
}
/**
* @param destination the destination element
* @param kind the type of the source to paste
* @param unit the parsed CU
* @return an AbstractTypeDeclaration, a CompilationUnit, or null
* @throws JavaModelException if something fails
*/
private ASTNode getDestinationNodeForSourceElement(IJavaElement destination, int kind, CompilationUnit unit) throws JavaModelException {
final IType ancestor= getAncestorType(destination);
if (ancestor != null)
return ASTNodeSearchUtil.getAbstractTypeDeclarationNode(ancestor, unit);
if (kind == IJavaElement.TYPE || kind == IJavaElement.PACKAGE_DECLARATION || kind == IJavaElement.IMPORT_DECLARATION || kind == IJavaElement.IMPORT_CONTAINER)
return unit;
return null;
}
private static IType getAncestorType(IJavaElement destinationElement) {
return destinationElement.getElementType() == IJavaElement.TYPE ? (IType)destinationElement: (IType)destinationElement.getAncestor(IJavaElement.TYPE);
}
private ASTNode createNewNodeToInsertToCu(TypedSource source, ASTRewrite rewrite) {
switch(source.getType()){
case IJavaElement.TYPE:
return rewrite.createStringPlaceholder(source.getSource(), ASTNode.TYPE_DECLARATION);
case IJavaElement.PACKAGE_DECLARATION:
return rewrite.createStringPlaceholder(source.getSource(), ASTNode.PACKAGE_DECLARATION);
case IJavaElement.IMPORT_DECLARATION:
return rewrite.createStringPlaceholder(source.getSource(), ASTNode.IMPORT_DECLARATION);
default: Assert.isTrue(false, String.valueOf(source.getType()));
return null;
}
}
private ASTNode createNewNodeToInsertToType(TypedSource source, ASTRewrite rewrite) {
switch(source.getType()){
case IJavaElement.TYPE:
return rewrite.createStringPlaceholder(source.getSource(), ASTNode.TYPE_DECLARATION);
case IJavaElement.METHOD:
return rewrite.createStringPlaceholder(source.getSource(), ASTNode.METHOD_DECLARATION);
case IJavaElement.FIELD:
return rewrite.createStringPlaceholder(source.getSource(), ASTNode.FIELD_DECLARATION);
case IJavaElement.INITIALIZER:
return rewrite.createStringPlaceholder(source.getSource(), ASTNode.INITIALIZER);
default: Assert.isTrue(false);
return null;
}
}
private ICompilationUnit getDestinationCu() {
return ReorgUtils.getCompilationUnit(fDestination);
}
@Override
public String getName() {
return ReorgMessages.PasteAction_name;
}
}
}
}