/******************************************************************************* * Copyright (c) 2000, 2009 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.wst.jsdt.internal.ui.refactoring.reorg; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import org.eclipse.core.filebuffers.ITextFileBuffer; import org.eclipse.core.resources.IContainer; 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.IWorkspaceRunnable; 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.OperationCanceledException; import org.eclipse.core.runtime.SubProgressMonitor; 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.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.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.text.edits.TextEdit; 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.wst.jsdt.core.IIncludePathEntry; import org.eclipse.wst.jsdt.core.IJavaScriptElement; import org.eclipse.wst.jsdt.core.IJavaScriptProject; import org.eclipse.wst.jsdt.core.IJavaScriptUnit; import org.eclipse.wst.jsdt.core.IPackageFragment; import org.eclipse.wst.jsdt.core.IPackageFragmentRoot; import org.eclipse.wst.jsdt.core.IType; import org.eclipse.wst.jsdt.core.JavaScriptCore; import org.eclipse.wst.jsdt.core.JavaScriptModelException; import org.eclipse.wst.jsdt.core.ToolFactory; import org.eclipse.wst.jsdt.core.compiler.IScanner; import org.eclipse.wst.jsdt.core.compiler.ITerminalSymbols; import org.eclipse.wst.jsdt.core.compiler.InvalidInputException; import org.eclipse.wst.jsdt.core.dom.AST; import org.eclipse.wst.jsdt.core.dom.ASTNode; import org.eclipse.wst.jsdt.core.dom.ASTParser; import org.eclipse.wst.jsdt.core.dom.AbstractTypeDeclaration; import org.eclipse.wst.jsdt.core.dom.BodyDeclaration; import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit; import org.eclipse.wst.jsdt.core.dom.Modifier; import org.eclipse.wst.jsdt.core.dom.PackageDeclaration; import org.eclipse.wst.jsdt.core.dom.rewrite.ASTRewrite; import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodes; import org.eclipse.wst.jsdt.internal.corext.refactoring.Checks; import org.eclipse.wst.jsdt.internal.corext.refactoring.TypedSource; import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.CompilationUnitChange; import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.TextChangeCompatibility; import org.eclipse.wst.jsdt.internal.corext.refactoring.reorg.IConfirmQuery; import org.eclipse.wst.jsdt.internal.corext.refactoring.reorg.IReorgQueries; import org.eclipse.wst.jsdt.internal.corext.refactoring.reorg.JavaElementTransfer; import org.eclipse.wst.jsdt.internal.corext.refactoring.reorg.ParentChecker; import org.eclipse.wst.jsdt.internal.corext.refactoring.reorg.ReorgUtils; import org.eclipse.wst.jsdt.internal.corext.refactoring.structure.ASTNodeSearchUtil; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.RefactoringFileBuffers; import org.eclipse.wst.jsdt.internal.corext.refactoring.util.ResourceUtil; import org.eclipse.wst.jsdt.internal.corext.util.JavaModelUtil; import org.eclipse.wst.jsdt.internal.corext.util.JdtFlags; import org.eclipse.wst.jsdt.internal.corext.util.Messages; import org.eclipse.wst.jsdt.internal.ui.IJavaHelpContextIds; import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin; import org.eclipse.wst.jsdt.internal.ui.refactoring.RefactoringExecutionHelper; import org.eclipse.wst.jsdt.internal.ui.refactoring.RefactoringMessages; import org.eclipse.wst.jsdt.internal.ui.refactoring.RefactoringSaveHelper; import org.eclipse.wst.jsdt.internal.ui.util.BusyIndicatorRunnableContext; import org.eclipse.wst.jsdt.internal.ui.util.ExceptionHandler; import org.eclipse.wst.jsdt.internal.ui.util.SelectionUtil; import org.eclipse.wst.jsdt.internal.ui.wizards.buildpaths.BuildPathsBlock; import org.eclipse.wst.jsdt.internal.ui.workingsets.OthersWorkingSetUpdater; import org.eclipse.wst.jsdt.ui.JavaScriptUI; import org.eclipse.wst.jsdt.ui.PreferenceConstants; import org.eclipse.wst.jsdt.ui.actions.SelectionDispatchAction; public class PasteAction extends SelectionDispatchAction{ private final Clipboard fClipboard; public PasteAction(IWorkbenchSite site, Clipboard clipboard) { super(site); Assert.isNotNull(clipboard); fClipboard= clipboard; setText(ReorgMessages.PasteAction_4); setDescription(ReorgMessages.PasteAction_5); ISharedImages workbenchImages= JavaScriptPlugin.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); } /* (non-Javadoc) * @see org.eclipse.wst.jsdt.ui.actions.SelectionDispatchAction#selectionChanged(org.eclipse.jface.viewers.IStructuredSelection) */ 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) throws JavaScriptModelException { Paster paster; Shell shell = getShell(); List result= new ArrayList(2); paster= new ProjectPaster(shell, fClipboard); if (paster.canEnable(availableDataTypes)) result.add(paster); paster= new JavaElementAndResourcePaster(shell, fClipboard); if (paster.canEnable(availableDataTypes)) result.add(paster); paster= new TypedSourcePaster(shell, fClipboard); if (paster.canEnable(availableDataTypes)) result.add(paster); paster= new FilePaster(shell, fClipboard); if (paster.canEnable(availableDataTypes)) result.add(paster); paster= new WorkingSetPaster(shell, fClipboard); if (paster.canEnable(availableDataTypes)) result.add(paster); paster= new TextPaster(shell, fClipboard); if (paster.canEnable(availableDataTypes)) result.add(paster); return (Paster[]) 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() { 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; } public void run(IStructuredSelection selection) { try { TransferData[] availableTypes= fClipboard.getAvailableTypes(); List elements= selection.toList(); IResource[] resources= ReorgUtils.getResources(elements); IJavaScriptElement[] javaElements= ReorgUtils.getJavaElements(elements); IWorkingSet[] workingSets= ReorgUtils.getWorkingSets(elements); Paster[] pasters= createEnabledPasters(availableTypes); for (int i= 0; i < pasters.length; i++) { if (pasters[i].canPasteOn(javaElements, resources, workingSets)) { pasters[i].paste(javaElements, resources, workingSets, availableTypes); return;// one is enough } } String msg= resources.length + javaElements.length + workingSets.length == 0 ? ReorgMessages.PasteAction_cannot_no_selection : ReorgMessages.PasteAction_cannot_selection; MessageDialog.openError(JavaScriptPlugin.getActiveWorkbenchShell(), ReorgMessages.PasteAction_name, msg); } catch (JavaScriptModelException 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) { // OK } } 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 IJavaScriptElement[] getClipboardJavaElements(TransferData[] availableDataTypes) { Transfer transfer= JavaElementTransfer.getInstance(); if (isAvailable(transfer, availableDataTypes)) { return (IJavaScriptElement[])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(IJavaScriptElement[], IResource[], IWorkingSet[])}). * @param availableTypes transfer types * @return whether the paste action can be enabled * @throws JavaScriptModelException */ public abstract boolean canEnable(TransferData[] availableTypes) throws JavaScriptModelException; /** * Only called if {@link #canEnable(TransferData[])} returns <code>true</code>. * @param selectedJavaElements * @param selectedResources * @param selectedWorkingSets * @return whether the paste action can be enabled * @throws JavaScriptModelException */ public abstract boolean canPasteOn(IJavaScriptElement[] selectedJavaElements, IResource[] selectedResources, IWorkingSet[] selectedWorkingSets) throws JavaScriptModelException; /** * only called if {@link #canPasteOn(IJavaScriptElement[], IResource[], IWorkingSet[])} returns <code>true</code> * @param selectedJavaElements * @param selectedResources * @param selectedWorkingSets * @param availableTypes * @throws JavaScriptModelException * @throws InterruptedException * @throws InvocationTargetException */ public abstract void paste(IJavaScriptElement[] selectedJavaElements, IResource[] selectedResources, IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) throws JavaScriptModelException, InterruptedException, InvocationTargetException; } private static class TextPaster extends Paster { private static class ParsedCu { private final String fText; private final String fTypeName; private final String fPackageName; public static ParsedCu[] parse(IJavaScriptProject javaProject, String text) { IScanner scanner= ToolFactory.createScanner(false, false, false, false); scanner.setSource(text.toCharArray()); ArrayList cus= new ArrayList(); int start= 0; boolean tokensScanned= false; 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 && tokensScanned) { int packageStart= scanner.getCurrentTokenStartPosition(); ParsedCu cu= parseCu(javaProject, text.substring(start, packageStart)); if (cu != null) { cus.add(cu); start= packageStart; } } else if (tok == ITerminalSymbols.TokenNameEOF) { ParsedCu cu= parseCu(javaProject, text.substring(start, text.length())); if (cu != null) { cus.add(cu); } break; } tokensScanned= true; } return (ParsedCu[]) cus.toArray(new ParsedCu[cus.size()]); } private static ParsedCu parseCu(IJavaScriptProject javaProject, String text) { String packageName= IPackageFragment.DEFAULT_PACKAGE_NAME; ASTParser parser= ASTParser.newParser(AST.JLS3); parser.setProject(javaProject); parser.setSource(text.toCharArray()); parser.setStatementsRecovery(true); JavaScriptUnit unit= (JavaScriptUnit) parser.createAST(null); if (unit == null) return null; int typesCount= unit.types().size(); String typeName= null; if (typesCount > 0) { // get first most visible type: int maxVisibility= Modifier.PRIVATE; for (ListIterator iter= unit.types().listIterator(typesCount); iter.hasPrevious();) { AbstractTypeDeclaration type= (AbstractTypeDeclaration) iter.previous(); int visibility= JdtFlags.getVisibilityCode(type); if (! JdtFlags.isHigherVisibility(maxVisibility, visibility)) { maxVisibility= visibility; typeName= type.getName().getIdentifier(); } } } if (typeName == null) return null; PackageDeclaration pack= unit.getPackage(); if (pack != null) { packageName= pack.getName().getFullyQualifiedName(); } return new ParsedCu(text, typeName, packageName); } private ParsedCu(String text, String typeName, String packageName) { fText= text; fTypeName= typeName; fPackageName= packageName; } public String getTypeName() { return fTypeName; } public String getPackageName() { return fPackageName; } public String getText() { return fText; } } private IPackageFragmentRoot fDestination; /** * destination pack iff pasted 1 CU to package fragment or compilation unit, <code>null</code> otherwise */ private IPackageFragment fDestinationPack; private ParsedCu[] fParsedCus; private TransferData[] fAvailableTypes; protected TextPaster(Shell shell, Clipboard clipboard) { super(shell, clipboard); } public boolean canEnable(TransferData[] availableTypes) { fAvailableTypes= availableTypes; return PasteAction.isAvailable(TextTransfer.getInstance(), availableTypes); } public boolean canPasteOn(IJavaScriptElement[] javaElements, IResource[] resources, IWorkingSet[] selectedWorkingSets) throws JavaScriptModelException { if (selectedWorkingSets.length != 0) return false; if (resources.length != 0) return false; //alternative: create text file? if (javaElements.length > 1) return false; String text= getClipboardText(fAvailableTypes); IJavaScriptProject javaProject= null; IJavaScriptElement destination= null; if (javaElements.length == 1) { destination= javaElements[0]; javaProject= destination.getJavaScriptProject(); } fParsedCus= ParsedCu.parse(javaProject, text); if (fParsedCus.length == 0) return false; if (destination == null) return true; /* * 1 CU: paste into package, adapt package declaration * 2+ CUs: always paste into source folder */ IPackageFragmentRoot packageFragmentRoot; IPackageFragment destinationPack; switch (destination.getElementType()) { case IJavaScriptElement.JAVASCRIPT_PROJECT : IPackageFragmentRoot[] packageFragmentRoots= ((IJavaScriptProject) destination).getPackageFragmentRoots(); for (int i= 0; i < packageFragmentRoots.length; i++) { packageFragmentRoot= packageFragmentRoots[i]; if (isWritable(packageFragmentRoot)) { fDestination= packageFragmentRoot; return true; } } return false; case IJavaScriptElement.PACKAGE_FRAGMENT_ROOT : packageFragmentRoot= (IPackageFragmentRoot) destination; if (isWritable(packageFragmentRoot)) { fDestination= packageFragmentRoot; return true; } return false; case IJavaScriptElement.PACKAGE_FRAGMENT : destinationPack= (IPackageFragment) destination; packageFragmentRoot= (IPackageFragmentRoot) destinationPack.getParent(); if (isWritable(packageFragmentRoot)) { fDestination= packageFragmentRoot; if (fParsedCus.length == 1) { fDestinationPack= destinationPack; } return true; } return false; case IJavaScriptElement.JAVASCRIPT_UNIT : destinationPack= (IPackageFragment) destination.getParent(); packageFragmentRoot= (IPackageFragmentRoot) destinationPack.getParent(); if (isWritable(packageFragmentRoot)) { fDestination= packageFragmentRoot; if (fParsedCus.length == 1) { fDestinationPack= destinationPack; } 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 (JavaScriptModelException e) { return false; } } public void paste(IJavaScriptElement[] javaElements, IResource[] resources, IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) throws JavaScriptModelException, InterruptedException, InvocationTargetException{ final IEditorPart[] editorPart= new IEditorPart[1]; IRunnableWithProgress op= new IRunnableWithProgress() { private IPath fVMPath; private String fCompilerCompliance; public void run(IProgressMonitor monitor) throws InvocationTargetException { final ArrayList cus= new ArrayList(); try { JavaScriptCore.run(new IWorkspaceRunnable() { 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; IJavaScriptUnit cu= pasteCU(fParsedCus[i], new SubProgressMonitor(pm, 1), confirmQuery); if (cu != null) cus.add(cu); } } }, monitor); } catch (OperationCanceledException e) { // cancelling is fine } catch (CoreException e) { throw new InvocationTargetException(e); } finally { monitor.done(); } IResource[] cuResources= ResourceUtil.getFiles((IJavaScriptUnit[]) cus.toArray(new IJavaScriptUnit[cus.size()])); SelectionUtil.selectAndReveal(cuResources, PlatformUI.getWorkbench().getActiveWorkbenchWindow()); } private IJavaScriptUnit pasteCU(ParsedCu parsedCu, SubProgressMonitor pm, IConfirmQuery confirmQuery) throws CoreException, OperationCanceledException { pm.beginTask("", 4); //$NON-NLS-1$ try { IPackageFragment destinationPack; if (fDestinationPack != null) { destinationPack= fDestinationPack; pm.worked(1); } else { String packageName= parsedCu.getPackageName(); destinationPack= fDestination.getPackageFragment(packageName); if (! destinationPack.exists()) { JavaModelUtil.getPackageFragmentRoot(destinationPack).createPackageFragment(packageName, true, new SubProgressMonitor(pm, 1)); } else { pm.worked(1); } } final String cuName= parsedCu.getTypeName() + JavaModelUtil.DEFAULT_CU_SUFFIX; IJavaScriptUnit cu= destinationPack.getJavaScriptUnit(cuName); boolean alreadyExists= cu.exists(); if (alreadyExists) { String msg= Messages.format(ReorgMessages.PasteAction_TextPaster_exists, new Object[] {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 } destinationPack.createCompilationUnit(cuName, parsedCu.getText(), true, new SubProgressMonitor(pm, 1)); if (! alreadyExists) { editorPart[0]= openCu(cu); } if (fDestinationPack != null && ! fDestinationPack.getElementName().equals(parsedCu.getPackageName())) { if (! alreadyExists && editorPart[0] != null) editorPart[0].doSave(new SubProgressMonitor(pm, 1)); //avoid showing error marker due to missing/wrong package declaration else pm.worked(1); } else { pm.worked(1); } return cu; } finally { pm.done(); } } 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) "" : Integer.valueOf(i)); //$NON-NLS-1$ project= JavaScriptPlugin.getWorkspace().getRoot().getProject(name); i++; } while (project.exists()); BuildPathsBlock.createProject(project, null, new SubProgressMonitor(pm, 3)); BuildPathsBlock.addJavaNature(project, new SubProgressMonitor(pm, 1)); IJavaScriptProject javaProject= JavaScriptCore.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 options= javaProject.getOptions(false); JavaModelUtil.setCompilanceOptions(options, fCompilerCompliance); JavaModelUtil.setDefaultClassfileOptions(options, fCompilerCompliance); javaProject.setOptions(options); } IIncludePathEntry srcEntry= JavaScriptCore.newSourceEntry(srcFolder.getFullPath()); IIncludePathEntry jreEntry= JavaScriptCore.newContainerEntry(fVMPath); //IPath outputLocation= BuildPathsBlock.getDefaultOutputLocation(javaProject); IIncludePathEntry[] cpes= new IIncludePathEntry[] { srcEntry, jreEntry }; javaProject.setRawIncludepath(cpes, new SubProgressMonitor(pm, 1)); return javaProject.getPackageFragmentRoot(srcFolder); } }; IRunnableContext context= JavaScriptPlugin.getActiveWorkbenchWindow(); if (context == null) { context= new BusyIndicatorRunnableContext(); } PlatformUI.getWorkbench().getProgressService().runInUI(context, op, JavaScriptPlugin.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(IJavaScriptUnit cu) { try { return JavaScriptUI.openInEditor(cu, true, true); } catch (PartInitException e) { JavaScriptPlugin.log(e); return null; } catch (JavaScriptModelException e) { JavaScriptPlugin.log(e); return null; } } } private static class WorkingSetPaster extends Paster { protected WorkingSetPaster(Shell shell, Clipboard clipboard) { super(shell, clipboard); } public void paste(IJavaScriptElement[] selectedJavaElements, IResource[] selectedResources, IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) throws JavaScriptModelException, InterruptedException, InvocationTargetException { IWorkingSet workingSet= selectedWorkingSets[0]; Set elements= new HashSet(Arrays.asList(workingSet.getElements())); IJavaScriptElement[] 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 realJavaElements= new ArrayList(); List realResource= new ArrayList(); ReorgUtils.splitIntoJavaElementsAndResources(resources, realJavaElements, realResource); for (Iterator iter= realJavaElements.iterator(); iter.hasNext();) { IJavaScriptElement element= (IJavaScriptElement)iter.next(); if (!ReorgUtils.containsElementOrParent(elements, element)) elements.add(element); } for (Iterator iter= realResource.iterator(); iter.hasNext();) { IResource element= (IResource)iter.next(); if (!ReorgUtils.containsElementOrParent(elements, element)) elements.add(element); } } workingSet.setElements((IAdaptable[])elements.toArray(new IAdaptable[elements.size()])); } public boolean canEnable(TransferData[] availableTypes) throws JavaScriptModelException { return isAvailable(ResourceTransfer.getInstance(), availableTypes) || isAvailable(JavaElementTransfer.getInstance(), availableTypes); } public boolean canPasteOn(IJavaScriptElement[] selectedJavaElements, IResource[] selectedResources, IWorkingSet[] selectedWorkingSets) throws JavaScriptModelException { if (selectedResources.length != 0 || selectedJavaElements.length != 0 || selectedWorkingSets.length != 1) return false; IWorkingSet ws= selectedWorkingSets[0]; return !OthersWorkingSetUpdater.ID.equals(ws.getId()); } } private static class ProjectPaster extends Paster{ protected ProjectPaster(Shell shell, Clipboard clipboard) { super(shell, clipboard); } 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); } public void paste(IJavaScriptElement[] 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); IJavaScriptElement[] javaElements= getClipboardJavaElements(availableTypes); Set 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 (IProject[]) result.toArray(new IProject[result.size()]); } public boolean canPasteOn(IJavaScriptElement[] javaElements, IResource[] resources, IWorkingSet[] selectedWorkingSets) { return selectedWorkingSets.length == 0; // Can't paste on working sets here } private boolean canPasteJavaProjects(TransferData[] availableDataTypes) { IJavaScriptElement[] javaElements= getClipboardJavaElements(availableDataTypes); return javaElements != null && javaElements.length != 0 && ! ReorgUtils.hasElementsNotOfType(javaElements, IJavaScriptElement.JAVASCRIPT_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); } public void paste(IJavaScriptElement[] javaElements, IResource[] resources, IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) throws JavaScriptModelException { 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(IJavaScriptElement[] 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); } public boolean canPasteOn(IJavaScriptElement[] javaElements, IResource[] resources, IWorkingSet[] selectedWorkingSets) throws JavaScriptModelException { Object target= getTarget(javaElements, resources); return target != null && canPasteFilesOn(getAsContainer(target)) && selectedWorkingSets.length == 0; } public boolean canEnable(TransferData[] availableDataTypes) throws JavaScriptModelException { return isAvailable(FileTransfer.getInstance(), availableDataTypes); } private boolean canPasteFilesOn(Object target) { boolean isPackageFragment= target instanceof IPackageFragment; boolean isJavaProject= target instanceof IJavaScriptProject; boolean isPackageFragmentRoot= target instanceof IPackageFragmentRoot; boolean isContainer= target instanceof IContainer; if (!(isPackageFragment || isJavaProject || isPackageFragmentRoot || isContainer)) return false; if (isContainer) { return true; } else { IJavaScriptElement element= (IJavaScriptElement)target; return !element.isReadOnly(); } } private IContainer getAsContainer(Object target) throws JavaScriptModelException{ if (target == null) return null; if (target instanceof IContainer) return (IContainer)target; if (target instanceof IFile) return ((IFile)target).getParent(); return getAsContainer(((IJavaScriptElement)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(IJavaScriptElement[] 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; public void paste(IJavaScriptElement[] javaElements, IResource[] resources, IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) throws JavaScriptModelException, InterruptedException, InvocationTargetException{ IResource[] clipboardResources= getClipboardResources(availableTypes); if (clipboardResources == null) clipboardResources= new IResource[0]; IJavaScriptElement[] clipboardJavaElements= getClipboardJavaElements(availableTypes); if (clipboardJavaElements == null) clipboardJavaElements= new IJavaScriptElement[0]; Object destination= getTarget(javaElements, resources); if (destination instanceof IJavaScriptElement) ReorgCopyStarter.create(clipboardJavaElements, clipboardResources, (IJavaScriptElement)destination).run(getShell()); else if (destination instanceof IResource) ReorgCopyStarter.create(clipboardJavaElements, clipboardResources, (IResource)destination).run(getShell()); } private Object getTarget(IJavaScriptElement[] 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(IJavaScriptElement[] javaElements, IResource[] resources) { return new ParentChecker(resources, javaElements).getCommonParent(); } public boolean canPasteOn(IJavaScriptElement[] javaElements, IResource[] resources, IWorkingSet[] selectedWorkingSets) throws JavaScriptModelException { if (selectedWorkingSets.length != 0) return false; IResource[] clipboardResources= getClipboardResources(fAvailableTypes); if (clipboardResources == null) clipboardResources= new IResource[0]; IJavaScriptElement[] clipboardJavaElements= getClipboardJavaElements(fAvailableTypes); if (clipboardJavaElements == null) clipboardJavaElements= new IJavaScriptElement[0]; Object destination= getTarget(javaElements, resources); if (destination instanceof IJavaScriptElement) return ReorgCopyStarter.create(clipboardJavaElements, clipboardResources, (IJavaScriptElement)destination) != null; if (destination instanceof IResource) return ReorgCopyStarter.create(clipboardJavaElements, clipboardResources, (IResource)destination) != null; return false; } 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; public boolean canEnable(TransferData[] availableTypes) throws JavaScriptModelException { fAvailableTypes= availableTypes; return isAvailable(TypedSourceTransfer.getInstance(), availableTypes); } public boolean canPasteOn(IJavaScriptElement[] selectedJavaElements, IResource[] selectedResources, IWorkingSet[] selectedWorkingSets) throws JavaScriptModelException { if (selectedResources.length != 0 || selectedWorkingSets.length != 0) return false; TypedSource[] typedSources= getClipboardTypedSources(fAvailableTypes); Object destination= getTarget(selectedJavaElements, selectedResources); if (destination instanceof IJavaScriptElement) return ReorgTypedSourcePasteStarter.create(typedSources, (IJavaScriptElement)destination) != null; return false; } public void paste(IJavaScriptElement[] selectedJavaElements, IResource[] selectedResources, IWorkingSet[] selectedWorkingSets, TransferData[] availableTypes) throws JavaScriptModelException, InterruptedException, InvocationTargetException { TypedSource[] typedSources= getClipboardTypedSources(availableTypes); IJavaScriptElement destination= getTarget(selectedJavaElements, selectedResources); ReorgTypedSourcePasteStarter.create(typedSources, destination).run(getShell()); } private static IJavaScriptElement getTarget(IJavaScriptElement[] 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 IJavaScriptElement) return getAsTypeOrCu((IJavaScriptElement)parent); return null; } private static IJavaScriptElement getAsTypeOrCu(IJavaScriptElement element) { //try to get type first if (element.getElementType() == IJavaScriptElement.JAVASCRIPT_UNIT || element.getElementType() == IJavaScriptElement.TYPE) return element; IJavaScriptElement ancestorType= element.getAncestor(IJavaScriptElement.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, IJavaScriptElement 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 IJavaScriptElement fDestination; static PasteTypedSourcesRefactoring create(TypedSource[] sources){ if (! isAvailable(sources)) return null; return new PasteTypedSourcesRefactoring(sources); } public RefactoringStatus setDestination(IJavaScriptElement 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(IJavaScriptElement 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, IJavaScriptElement destination) { IType ancestorType= getAncestorType(destination); if (ancestorType != null) return canPasteToType(elementType); return canPasteToCu(elementType); } private static boolean canPasteToType(int elementType) { return elementType == IJavaScriptElement.TYPE || elementType == IJavaScriptElement.FIELD || elementType == IJavaScriptElement.INITIALIZER || elementType == IJavaScriptElement.METHOD; } private static boolean canPasteToCu(int elementType) { return elementType == IJavaScriptElement.TYPE || elementType == IJavaScriptElement.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; } public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException { return new RefactoringStatus(); } public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException { RefactoringStatus result= Checks.validateModifiesFiles( ResourceUtil.getFiles(new IJavaScriptUnit[]{getDestinationCu()}), getValidationContext()); return result; } public Change createChange(IProgressMonitor pm) throws CoreException { ASTParser p= ASTParser.newParser(AST.JLS3); p.setSource(getDestinationCu()); JavaScriptUnit cuNode= (JavaScriptUnit) 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 JavaScriptUnit) insertToCu(rewrite, createNewNodeToInsertToCu(source, rewrite), (JavaScriptUnit) 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.getJavaScriptProject().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.TYPE_DECLARATION: case ASTNode.FUNCTION_DECLARATION: case ASTNode.FIELD_DECLARATION: case ASTNode.INITIALIZER: rewrite.getListRewrite(typeDeclaration, typeDeclaration.getBodyDeclarationsProperty()).insertAt(node, ASTNodes.getInsertionIndex((BodyDeclaration) node, typeDeclaration.bodyDeclarations()), null); break; default: Assert.isTrue(false, String.valueOf(node.getNodeType())); } } private static void insertToCu(ASTRewrite rewrite, ASTNode node, JavaScriptUnit cuNode) { switch (node.getNodeType()) { case ASTNode.TYPE_DECLARATION: rewrite.getListRewrite(cuNode, JavaScriptUnit.TYPES_PROPERTY).insertAt(node, ASTNodes.getInsertionIndex((AbstractTypeDeclaration) node, cuNode.types()), null); break; case ASTNode.IMPORT_DECLARATION: rewrite.getListRewrite(cuNode, JavaScriptUnit.IMPORTS_PROPERTY).insertLast(node, null); break; case ASTNode.PACKAGE_DECLARATION: // only insert if none exists if (cuNode.getPackage() == null) rewrite.set(cuNode, JavaScriptUnit.PACKAGE_PROPERTY, node, null); break; default: Assert.isTrue(false, String.valueOf(node.getNodeType())); } } /** * @return an AbstractTypeDeclaration, a JavaScriptUnit, or null */ private ASTNode getDestinationNodeForSourceElement(IJavaScriptElement destination, int kind, JavaScriptUnit unit) throws JavaScriptModelException { final IType ancestor= getAncestorType(destination); if (ancestor != null) return ASTNodeSearchUtil.getAbstractTypeDeclarationNode(ancestor, unit); if (kind == IJavaScriptElement.TYPE || kind == IJavaScriptElement.IMPORT_DECLARATION || kind == IJavaScriptElement.IMPORT_CONTAINER) return unit; return null; } private static IType getAncestorType(IJavaScriptElement destinationElement) { return destinationElement.getElementType() == IJavaScriptElement.TYPE ? (IType)destinationElement: (IType)destinationElement.getAncestor(IJavaScriptElement.TYPE); } private ASTNode createNewNodeToInsertToCu(TypedSource source, ASTRewrite rewrite) { switch(source.getType()){ case IJavaScriptElement.TYPE: return rewrite.createStringPlaceholder(source.getSource(), ASTNode.TYPE_DECLARATION); case IJavaScriptElement.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 IJavaScriptElement.TYPE: return rewrite.createStringPlaceholder(source.getSource(), ASTNode.TYPE_DECLARATION); case IJavaScriptElement.METHOD: return rewrite.createStringPlaceholder(source.getSource(), ASTNode.FUNCTION_DECLARATION); case IJavaScriptElement.FIELD: return rewrite.createStringPlaceholder(source.getSource(), ASTNode.FIELD_DECLARATION); case IJavaScriptElement.INITIALIZER: return rewrite.createStringPlaceholder(source.getSource(), ASTNode.INITIALIZER); default: Assert.isTrue(false); return null; } } private IJavaScriptUnit getDestinationCu() { return ReorgUtils.getCompilationUnit(fDestination); } public String getName() { return ReorgMessages.PasteAction_name; } } } }