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.actions;
  
  import java.lang.reflect.InvocationTargetException;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.HashSet;
  
  import org.eclipse.core.runtime.CoreException;
  import org.eclipse.core.runtime.IStatus;
  import org.eclipse.core.runtime.MultiStatus;
  import org.eclipse.core.runtime.Status;
  import org.eclipse.jface.dialogs.ErrorDialog;
  import org.eclipse.jface.dialogs.MessageDialog;
  import org.eclipse.jface.text.ITextSelection;
  import org.eclipse.jface.viewers.IStructuredSelection;
  import org.eclipse.ui.IWorkbenchSite;
  import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
  import org.eclipse.wst.jsdt.core.IJavaScriptElement;
  import org.eclipse.wst.jsdt.core.IJavaScriptProject;
  import org.eclipse.wst.jsdt.core.IPackageFragment;
  import org.eclipse.wst.jsdt.core.IPackageFragmentRoot;
  import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringExecutionStarter;
  import org.eclipse.wst.jsdt.internal.corext.util.JavaModelUtil;
  import org.eclipse.wst.jsdt.internal.corext.util.Messages;
  import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
  import org.eclipse.wst.jsdt.internal.ui.browsing.LogicalPackage;
  import org.eclipse.wst.jsdt.internal.ui.fix.ICleanUp;
  import org.eclipse.wst.jsdt.internal.ui.javaeditor.JavaEditor;
  import org.eclipse.wst.jsdt.internal.ui.util.ElementValidator;
  import org.eclipse.wst.jsdt.ui.JavaScriptUI;
  import org.eclipse.wst.jsdt.ui.actions.SelectionDispatchAction;
  
  public abstract class CleanUpAction extends SelectionDispatchAction {
  
  	private JavaEditor fEditor;
  
  	public CleanUpAction(IWorkbenchSite site) {
  		super(site);
  	}
  
  	/**
  	 * Note: This constructor is for internal use only. Clients should not call
  	 * this constructor.
  	 * 
  	 * @param editor
  	 *            the Java editor
  	 */
  	public CleanUpAction(JavaEditor editor) {
  		this(editor.getEditorSite());
  		fEditor= editor;
  		setEnabled(getCompilationUnit(fEditor) != null);
  	}
  
  	/**
  	 * @return the name of this action, not <b>null</b>
  	 */
  	protected abstract String getActionName();
  
  	/**
  	 * @param units
  	 *            the units to clean up
  	 * @return the clean ups to be performed or <b>null</b> if none to be
  	 *         performed
  	 */
  	protected abstract ICleanUp[] createCleanUps(IJavaScriptUnit[] units);
  
  	/**
  	 * @param units
  	 *            to clean up
  	 * @param cleanUps
  	 *            clean ups to execute on units
  	 * 
  	 * @throws JavaScriptModelException
  	 * @throws InvocationTargetException
  	 */
  	protected void performRefactoring(IJavaScriptUnit[] units, ICleanUp[] cleanUps) throws JavaScriptModelException, InvocationTargetException {
  		RefactoringExecutionStarter.startCleanupRefactoring(units, cleanUps, getShell(), false, getActionName());
  	}
  
  	public void run(ITextSelection selection) {
  		IJavaScriptUnit cu= getCompilationUnit(fEditor);
  		if (cu != null) {
  			run(cu);
  		}
  	}
  
  	public void run(IStructuredSelection selection) {
  		IJavaScriptUnit[] cus= getCompilationUnits(selection);
  		if (cus.length == 0) {
  			MessageDialog.openInformation(getShell(), getActionName(), ActionMessages.CleanUpAction_EmptySelection_description);
  		} else if (cus.length == 1) {
  			run(cus[0]);
  		} else {
  			runOnMultiple(cus);
  		}
  	}
  
  	public void selectionChanged(ITextSelection selection) {
  		setEnabled(getCompilationUnit(fEditor) != null);
  	}
  
  	public void selectionChanged(IStructuredSelection selection) {
  		setEnabled(isEnabled(selection));
  	}
  
  	private boolean isEnabled(IStructuredSelection selection) {
  		Object[] selected= selection.toArray();
  		for (int i= 0; i < selected.length; i++) {
  			try {
  				if (selected[i] instanceof IJavaScriptElement) {
  					IJavaScriptElement elem= (IJavaScriptElement)selected[i];
  					if (elem.exists()) {
  						switch (elem.getElementType()) {
  							case IJavaScriptElement.TYPE:
  								return elem.getParent().getElementType() == IJavaScriptElement.JAVASCRIPT_UNIT; // for browsing perspective
  							case IJavaScriptElement.JAVASCRIPT_UNIT:
  								return true;
  							case IJavaScriptElement.IMPORT_CONTAINER:
  								return true;
  							case IJavaScriptElement.PACKAGE_FRAGMENT:
  							case IJavaScriptElement.PACKAGE_FRAGMENT_ROOT:
  								IPackageFragmentRoot root= (IPackageFragmentRoot)elem.getAncestor(IJavaScriptElement.PACKAGE_FRAGMENT_ROOT);
  								return (root.getKind() == IPackageFragmentRoot.K_SOURCE);
  							case IJavaScriptElement.JAVASCRIPT_PROJECT:
  								// https://bugs.eclipse.org/bugs/show_bug.cgi?id=65638
  								return true;
  						}
  					}
  				} else if (selected[i] instanceof LogicalPackage) {
  					return true;
  				}
  			} catch (JavaScriptModelException e) {
  				if (!e.isDoesNotExist()) {
  					JavaScriptPlugin.log(e);
  				}
  			}
  		}
  		return false;
  	}
  
  	private void run(IJavaScriptUnit cu) {
  		if (!ActionUtil.isEditable(fEditor, getShell(), cu))
  			return;
  
  		ICleanUp[] cleanUps= createCleanUps(new IJavaScriptUnit[] {
  			cu
  		});
  		if (cleanUps == null)
  			return;
  
  		if (!ElementValidator.check(cu, getShell(), getActionName(), fEditor != null))
  			return;
  
  		try {
  			performRefactoring(new IJavaScriptUnit[] {
  				cu
  			}, cleanUps);
  		} catch (InvocationTargetException e) {
  			JavaScriptPlugin.log(e);
  			if (e.getCause() instanceof CoreException)
  				showUnexpectedError((CoreException)e.getCause());
  		} catch (JavaScriptModelException e) {
  			showUnexpectedError(e);
  		}
  	}
  
  	private void runOnMultiple(final IJavaScriptUnit[] cus) {
  		ICleanUp[] cleanUps= createCleanUps(cus);
  		if (cleanUps == null)
  			return;
  
  		MultiStatus status= new MultiStatus(JavaScriptUI.ID_PLUGIN, IStatus.OK, ActionMessages.CleanUpAction_MultiStateErrorTitle, null);
  		for (int i= 0; i < cus.length; i++) {
  			IJavaScriptUnit cu= cus[i];
  
  			if (!ActionUtil.isOnBuildPath(cu)) {
  				String cuLocation= cu.getPath().makeRelative().toString();
  				String message= Messages.format(ActionMessages.CleanUpAction_CUNotOnBuildpathMessage, cuLocation);
  				status.add(new Status(IStatus.INFO, JavaScriptUI.ID_PLUGIN, IStatus.ERROR, message, null));
  			}
  		}
  		if (!status.isOK()) {
  			ErrorDialog.openError(getShell(), getActionName(), null, status);
  			return;
  		}
  
  		try {
  			performRefactoring(cus, cleanUps);
  		} catch (InvocationTargetException e) {
  			JavaScriptPlugin.log(e);
  			if (e.getCause() instanceof CoreException)
  				showUnexpectedError((CoreException)e.getCause());
  		} catch (JavaScriptModelException e) {
  			showUnexpectedError(e);
  		}
  	}
  
  	private void showUnexpectedError(CoreException e) {
  		String message2= Messages.format(ActionMessages.CleanUpAction_UnexpectedErrorMessage, e.getStatus().getMessage());
  		IStatus status= new Status(IStatus.ERROR, JavaScriptUI.ID_PLUGIN, IStatus.ERROR, message2, null);
  		ErrorDialog.openError(getShell(), getActionName(), null, status);
  	}
  
  	public IJavaScriptUnit[] getCompilationUnits(IStructuredSelection selection) {
  		HashSet result= new HashSet();
  		Object[] selected= selection.toArray();
  		for (int i= 0; i < selected.length; i++) {
  			try {
  				if (selected[i] instanceof IJavaScriptElement) {
  					IJavaScriptElement elem= (IJavaScriptElement)selected[i];
  					if (elem.exists()) {
  						switch (elem.getElementType()) {
  							case IJavaScriptElement.TYPE:
  								if (elem.getParent().getElementType() == IJavaScriptElement.JAVASCRIPT_UNIT) {
  									result.add(elem.getParent());
  								}
  								break;
  							case IJavaScriptElement.JAVASCRIPT_UNIT:
  								result.add(elem);
  								break;
  							case IJavaScriptElement.IMPORT_CONTAINER:
  								result.add(elem.getParent());
  								break;
  							case IJavaScriptElement.PACKAGE_FRAGMENT:
  								collectCompilationUnits((IPackageFragment)elem, result);
  								break;
  							case IJavaScriptElement.PACKAGE_FRAGMENT_ROOT:
  								collectCompilationUnits((IPackageFragmentRoot)elem, result);
  								break;
  							case IJavaScriptElement.JAVASCRIPT_PROJECT:
  								IPackageFragmentRoot[] roots= ((IJavaScriptProject)elem).getPackageFragmentRoots();
  								for (int k= 0; k < roots.length; k++) {
  									collectCompilationUnits(roots[k], result);
  								}
  								break;
  						}
  					}
  				} else if (selected[i] instanceof LogicalPackage) {
  					IPackageFragment[] packageFragments= ((LogicalPackage)selected[i]).getFragments();
  					for (int k= 0; k < packageFragments.length; k++) {
  						IPackageFragment pack= packageFragments[k];
  						if (pack.exists()) {
  							collectCompilationUnits(pack, result);
  						}
  					}
  				}
  			} catch (JavaScriptModelException e) {
  				if (JavaModelUtil.isExceptionToBeLogged(e))
  					JavaScriptPlugin.log(e);
  			}
  		}
  		return (IJavaScriptUnit[])result.toArray(new IJavaScriptUnit[result.size()]);
  	}
  
  	private void collectCompilationUnits(IPackageFragment pack, Collection result) throws JavaScriptModelException {
  		result.addAll(Arrays.asList(pack.getJavaScriptUnits()));
  	}
  
  	private void collectCompilationUnits(IPackageFragmentRoot root, Collection result) throws JavaScriptModelException {
  		if (root.getKind() == IPackageFragmentRoot.K_SOURCE) {
  			IJavaScriptElement[] children= root.getChildren();
  			for (int i= 0; i < children.length; i++) {
  				collectCompilationUnits((IPackageFragment)children[i], result);
  			}
  		}
  	}
  
  	private static IJavaScriptUnit getCompilationUnit(JavaEditor editor) {
  		IJavaScriptElement element= JavaScriptUI.getEditorInputJavaElement(editor.getEditorInput());
  		if (!(element instanceof IJavaScriptUnit))
  			return null;
  
  		return (IJavaScriptUnit)element;
  	}
  
  }
  