content: /*******************************************************************************
   * Copyright (c) 2000, 2008 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.util.ArrayList;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Set;
  
  import org.eclipse.core.resources.IResource;
  import org.eclipse.core.runtime.Assert;
  import org.eclipse.core.runtime.CoreException;
  import org.eclipse.core.runtime.IPath;
  import org.eclipse.jface.dialogs.MessageDialog;
  import org.eclipse.jface.viewers.ILabelProvider;
  import org.eclipse.jface.viewers.IStructuredSelection;
  import org.eclipse.swt.SWTError;
  import org.eclipse.swt.dnd.Clipboard;
  import org.eclipse.swt.dnd.DND;
  import org.eclipse.swt.dnd.FileTransfer;
  import org.eclipse.swt.dnd.TextTransfer;
  import org.eclipse.swt.dnd.Transfer;
  import org.eclipse.swt.widgets.Shell;
  import org.eclipse.ui.ISharedImages;
  import org.eclipse.ui.IWorkbenchSite;
  import org.eclipse.ui.PlatformUI;
  import org.eclipse.ui.part.ResourceTransfer;
  import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
  import org.eclipse.wst.jsdt.core.IJavaScriptElement;
  import org.eclipse.wst.jsdt.core.IType;
  import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.TypedSource;
  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.util.JavaElementUtil;
  import org.eclipse.wst.jsdt.internal.corext.util.JavaModelUtil;
  import org.eclipse.wst.jsdt.internal.ui.IJavaHelpContextIds;
  import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
  import org.eclipse.wst.jsdt.internal.ui.util.ExceptionHandler;
  import org.eclipse.wst.jsdt.ui.JavaScriptElementLabelProvider;
  import org.eclipse.wst.jsdt.ui.actions.SelectionDispatchAction;
  
  
  public class CopyToClipboardAction extends SelectionDispatchAction{
  
  	private final Clipboard fClipboard;
  	private boolean fAutoRepeatOnFailure= false;
  
  	public CopyToClipboardAction(IWorkbenchSite site, Clipboard clipboard) {
  		super(site);
  		setText(ReorgMessages.CopyToClipboardAction_0); 
  		setDescription(ReorgMessages.CopyToClipboardAction_1); 
  		Assert.isNotNull(clipboard);
  		fClipboard= clipboard;
  		ISharedImages workbenchImages= getWorkbenchSharedImages();
  		setDisabledImageDescriptor(workbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_COPY_DISABLED));
  		setImageDescriptor(workbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_COPY));
  		setHoverImageDescriptor(workbenchImages.getImageDescriptor(ISharedImages.IMG_TOOL_COPY));
  		update(getSelection());
  
  		PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IJavaHelpContextIds.COPY_ACTION);
  	}
  
  	public void setAutoRepeatOnFailure(boolean autorepeatOnFailure){
  		fAutoRepeatOnFailure= autorepeatOnFailure;
  	}
  	
  	private static ISharedImages getWorkbenchSharedImages() {
  		return JavaScriptPlugin.getDefault().getWorkbench().getSharedImages();
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.ui.actions.SelectionDispatchAction#selectionChanged(org.eclipse.jface.viewers.IStructuredSelection)
  	 */
  	public void selectionChanged(IStructuredSelection selection) {
  		try {
  			List elements= selection.toList();
  			IResource[] resources= ReorgUtils.getResources(elements);
  			IJavaScriptElement[] javaElements= ReorgUtils.getJavaElements(elements);
  			if (elements.size() != resources.length + javaElements.length)
  				setEnabled(false);
  			else
  				setEnabled(canEnable(resources, javaElements));
  		} catch (JavaScriptModelException e) {
  			//no ui here - this happens on selection changes
  			// http://bugs.eclipse.org/bugs/show_bug.cgi?id=19253
  			if (JavaModelUtil.isExceptionToBeLogged(e))
  				JavaScriptPlugin.log(e);
  			setEnabled(false);
  		}
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.wst.jsdt.ui.actions.SelectionDispatchAction#run(org.eclipse.jface.viewers.IStructuredSelection)
  	 */
  	public void run(IStructuredSelection selection) {
  		try {
  			List elements= selection.toList();
  			IResource[] resources= ReorgUtils.getResources(elements);
  			IJavaScriptElement[] javaElements= ReorgUtils.getJavaElements(elements);
  			if (elements.size() == resources.length + javaElements.length && canEnable(resources, javaElements)) 
  				doRun(resources, javaElements);
  		} catch (CoreException e) {
  			ExceptionHandler.handle(e, getShell(), ReorgMessages.CopyToClipboardAction_2, ReorgMessages.CopyToClipboardAction_3); 
  		}
  	}
  
  	private void doRun(IResource[] resources, IJavaScriptElement[] javaElements) throws CoreException {
  		new ClipboardCopier(resources, javaElements, fClipboard, getShell(), fAutoRepeatOnFailure).copyToClipboard();
  	}
  
  	private boolean canEnable(IResource[] resources, IJavaScriptElement[] javaElements) throws JavaScriptModelException {
  		return new CopyToClipboardEnablementPolicy(resources, javaElements).canEnable();
  	}
  	
  	//----------------------------------------------------------------------------------------//
  	
  	private static class ClipboardCopier{
  		private final boolean fAutoRepeatOnFailure;
  		private final IResource[] fResources;
  		private final IJavaScriptElement[] fJavaElements;
  		private final Clipboard fClipboard;
  		private final Shell fShell;
  		private final ILabelProvider fLabelProvider;
  		
  		private ClipboardCopier(IResource[] resources, IJavaScriptElement[] javaElements, Clipboard clipboard, Shell shell, boolean autoRepeatOnFailure){
  			Assert.isNotNull(resources);
  			Assert.isNotNull(javaElements);
  			Assert.isNotNull(clipboard);
  			Assert.isNotNull(shell);
  			fResources= resources;
  			fJavaElements= javaElements;
  			fClipboard= clipboard;
  			fShell= shell;
  			fLabelProvider= createLabelProvider();
  			fAutoRepeatOnFailure= autoRepeatOnFailure;
  		}
  
  		public void copyToClipboard() throws CoreException{
  			//Set<String> fileNames
  			Set fileNames= new HashSet(fResources.length + fJavaElements.length);
  			StringBuffer namesBuf = new StringBuffer();
  			processResources(fileNames, namesBuf);
  			processJavaElements(fileNames, namesBuf);
  
  			IType[] mainTypes= ReorgUtils.getMainTypes(fJavaElements);
  			IJavaScriptUnit[] cusOfMainTypes= ReorgUtils.getCompilationUnits(mainTypes);
  			IResource[] resourcesOfMainTypes= ReorgUtils.getResources(cusOfMainTypes);
  			addFileNames(fileNames, resourcesOfMainTypes);
  			
  			IResource[] cuResources= ReorgUtils.getResources(getCompilationUnits(fJavaElements));
  			addFileNames(fileNames, cuResources);
  
  			IResource[] resourcesForClipboard= ReorgUtils.union(fResources, ReorgUtils.union(cuResources, resourcesOfMainTypes));
  			IJavaScriptElement[] javaElementsForClipboard= ReorgUtils.union(fJavaElements, cusOfMainTypes);
  			
  			TypedSource[] typedSources= TypedSource.createTypedSources(javaElementsForClipboard);
  			String[] fileNameArray= (String[]) fileNames.toArray(new String[fileNames.size()]);
  			copyToClipboard(resourcesForClipboard, fileNameArray, namesBuf.toString(), javaElementsForClipboard, typedSources, 0);
  		}
  
  		private static IJavaScriptElement[] getCompilationUnits(IJavaScriptElement[] javaElements) {
  			List cus= ReorgUtils.getElementsOfType(javaElements, IJavaScriptElement.JAVASCRIPT_UNIT);
  			return (IJavaScriptUnit[]) cus.toArray(new IJavaScriptUnit[cus.size()]);
  		}
  
  		private void processResources(Set fileNames, StringBuffer namesBuf) {
  			for (int i= 0; i < fResources.length; i++) {
  				IResource resource= fResources[i];
  				addFileName(fileNames, resource);
  
  				if (i > 0)
  					namesBuf.append('\n');
  				namesBuf.append(getName(resource));
  			}
  		}
  
  		private void processJavaElements(Set fileNames, StringBuffer namesBuf) {
  			for (int i= 0; i < fJavaElements.length; i++) {
  				IJavaScriptElement element= fJavaElements[i];
  				switch (element.getElementType()) {
  					case IJavaScriptElement.JAVASCRIPT_PROJECT :
  					case IJavaScriptElement.PACKAGE_FRAGMENT_ROOT :
  					case IJavaScriptElement.PACKAGE_FRAGMENT :
  					case IJavaScriptElement.JAVASCRIPT_UNIT :
  					case IJavaScriptElement.CLASS_FILE :
  						addFileName(fileNames, ReorgUtils.getResource(element));
  						break;
  					default :
  						break;
  				}
  
  				if (fResources.length > 0 || i > 0)
  					namesBuf.append('\n');
  				namesBuf.append(getName(element));
  			}
  		}
  
  		private static void addFileNames(Set fileName, IResource[] resources) {
  			for (int i= 0; i < resources.length; i++) {
  				addFileName(fileName, resources[i]);
  			}
  		}
  
  		private static void addFileName(Set fileName, IResource resource){
  			if (resource == null)
  				return;
  			IPath location = resource.getLocation();
  			if (location != null) {
  				fileName.add(location.toOSString());
  			} else {
  				// not a file system path. skip file.
  			}
  		}
  		
  		private void copyToClipboard(IResource[] resources, String[] fileNames, String names, IJavaScriptElement[] javaElements, TypedSource[] typedSources, int repeat){
  			final int repeat_max_count= 10;
  			try{
  				fClipboard.setContents( createDataArray(resources, javaElements, fileNames, names, typedSources),
  										createDataTypeArray(resources, javaElements, fileNames, typedSources));
  			} catch (SWTError e) {
  				if (e.code != DND.ERROR_CANNOT_SET_CLIPBOARD || repeat >= repeat_max_count)
  					throw e;
  				if (fAutoRepeatOnFailure) {
  					try {
  						Thread.sleep(500);
  					} catch (InterruptedException e1) {
  						// do nothing.
  					}
  				}
  				if (fAutoRepeatOnFailure || MessageDialog.openQuestion(fShell, ReorgMessages.CopyToClipboardAction_4, ReorgMessages.CopyToClipboardAction_5)) 
  					copyToClipboard(resources, fileNames, names, javaElements, typedSources, repeat+1);
  			}
  		}
  		
  		private static Transfer[] createDataTypeArray(IResource[] resources, IJavaScriptElement[] javaElements, String[] fileNames, TypedSource[] typedSources) {
  			List result= new ArrayList(4);
  			if (resources.length != 0)
  				result.add(ResourceTransfer.getInstance());
  			if (javaElements.length != 0)
  				result.add(JavaElementTransfer.getInstance());
  			if (fileNames.length != 0)
  				result.add(FileTransfer.getInstance());
  			if (typedSources.length != 0)
  				result.add(TypedSourceTransfer.getInstance());
  			result.add(TextTransfer.getInstance());			
  			return (Transfer[]) result.toArray(new Transfer[result.size()]);
  		}
  
  		private static Object[] createDataArray(IResource[] resources, IJavaScriptElement[] javaElements, String[] fileNames, String names, TypedSource[] typedSources) {
  			List result= new ArrayList(4);
  			if (resources.length != 0)
  				result.add(resources);
  			if (javaElements.length != 0)
  				result.add(javaElements);
  			if (fileNames.length != 0)
  				result.add(fileNames);
  			if (typedSources.length != 0)
  				result.add(typedSources);
  			result.add(names);
  			return result.toArray();
  		}
  
  		private static ILabelProvider createLabelProvider(){
  			return new JavaScriptElementLabelProvider(
  				JavaScriptElementLabelProvider.SHOW_VARIABLE
  				+ JavaScriptElementLabelProvider.SHOW_PARAMETERS
  				+ JavaScriptElementLabelProvider.SHOW_TYPE
  			);		
  		}
  		private String getName(IResource resource){
  			return fLabelProvider.getText(resource);
  		}
  		private String getName(IJavaScriptElement javaElement){
  			return fLabelProvider.getText(javaElement);
  		}
  	}
  	
  	private static class CopyToClipboardEnablementPolicy {
  		private final IResource[] fResources;
  		private final IJavaScriptElement[] fJavaElements;
  		public CopyToClipboardEnablementPolicy(IResource[] resources, IJavaScriptElement[] javaElements){
  			Assert.isNotNull(resources);
  			Assert.isNotNull(javaElements);
  			fResources= resources;
  			fJavaElements= javaElements;
  		}
  
  		public boolean canEnable() throws JavaScriptModelException{
  			if (fResources.length + fJavaElements.length == 0)
  				return false;
  			if (hasProjects() && hasNonProjects())
  				return false;
  			if (! canCopyAllToClipboard())
  				return false;
  			if (! new ParentChecker(fResources, fJavaElements).haveCommonParent())
  				return false;
  			return true;
  		}
  
  		private boolean canCopyAllToClipboard() throws JavaScriptModelException {
  			for (int i= 0; i < fResources.length; i++) {
  				if (! canCopyToClipboard(fResources[i])) return false;
  			}
  			for (int i= 0; i < fJavaElements.length; i++) {
  				if (! canCopyToClipboard(fJavaElements[i])) return false;
  			}
  			return true;
  		}
  
  		private static boolean canCopyToClipboard(IJavaScriptElement element) throws JavaScriptModelException {
  			if (element == null || ! element.exists())
  				return false;
  				
  			if (JavaElementUtil.isDefaultPackage(element))		
  				return false;
  			
  			return true;
  		}
  
  		private static boolean canCopyToClipboard(IResource resource) {
  			return 	resource != null && 
  					resource.exists() &&
  					! resource.isPhantom() &&
  					resource.getType() != IResource.ROOT;
  		}
  
  		private boolean hasProjects() {
  			for (int i= 0; i < fResources.length; i++) {
  				if (ReorgUtils.isProject(fResources[i])) return true;
  			}
  			for (int i= 0; i < fJavaElements.length; i++) {
  				if (ReorgUtils.isProject(fJavaElements[i])) return true;
  			}
  			return false;
  		}
  
  		private boolean hasNonProjects() {
  			for (int i= 0; i < fResources.length; i++) {
  				if (! ReorgUtils.isProject(fResources[i])) return true;
  			}
  			for (int i= 0; i < fJavaElements.length; i++) {
  				if (! ReorgUtils.isProject(fJavaElements[i])) return true;
  			}
  			return false;
  		}
  	}
  }
  