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.compare;
  
  import java.lang.reflect.InvocationTargetException;
  import java.util.Map;
  
  import org.eclipse.compare.HistoryItem;
  import org.eclipse.compare.ITypedElement;
  import org.eclipse.compare.ResourceNode;
  import org.eclipse.core.filebuffers.ITextFileBuffer;
  import org.eclipse.core.resources.IFile;
  import org.eclipse.core.resources.IFileState;
  import org.eclipse.core.runtime.CoreException;
  import org.eclipse.core.runtime.IProgressMonitor;
  import org.eclipse.core.runtime.NullProgressMonitor;
  import org.eclipse.jface.action.IAction;
  import org.eclipse.jface.dialogs.MessageDialog;
  import org.eclipse.jface.operation.IRunnableWithProgress;
  import org.eclipse.jface.text.BadLocationException;
  import org.eclipse.jface.text.IDocument;
  import org.eclipse.jface.text.RewriteSessionEditProcessor;
  import org.eclipse.jface.viewers.ISelection;
  import org.eclipse.jface.viewers.IStructuredSelection;
  import org.eclipse.jface.viewers.StructuredSelection;
  import org.eclipse.swt.widgets.Shell;
  import org.eclipse.text.edits.MultiTextEdit;
  import org.eclipse.text.edits.TextEdit;
  import org.eclipse.ui.IEditorPart;
  import org.eclipse.ui.IWorkbench;
  import org.eclipse.ui.IWorkbenchPage;
  import org.eclipse.ui.IWorkbenchWindow;
  import org.eclipse.ui.PlatformUI;
  import org.eclipse.ui.part.FileEditorInput;
  import org.eclipse.ui.texteditor.IDocumentProvider;
  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.IMember;
  import org.eclipse.wst.jsdt.core.ISourceRange;
  import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  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.JavaScriptUnit;
  import org.eclipse.wst.jsdt.core.dom.TypeDeclaration;
  import org.eclipse.wst.jsdt.core.dom.rewrite.ASTRewrite;
  import org.eclipse.wst.jsdt.internal.corext.dom.NodeFinder;
  import org.eclipse.wst.jsdt.internal.corext.util.Strings;
  import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
  import org.eclipse.wst.jsdt.internal.ui.actions.SelectionConverter;
  import org.eclipse.wst.jsdt.internal.ui.javaeditor.JavaEditor;
  
  /**
   * Base class for the "Replace with local history"
   * and "Add from local history" actions.
   */
  abstract class JavaHistoryActionImpl /* extends Action implements IActionDelegate*/ { 
  	
  	private boolean fModifiesFile;
  	private ISelection fSelection;	
  
  	JavaHistoryActionImpl(boolean modifiesFile) {
  		fModifiesFile= modifiesFile;
  	}
  	
  	ISelection getSelection() {
  		return fSelection;
  	}
  		
  	final IFile getFile(Object input) {
  		return JavaElementHistoryPageSource.getInstance().getFile(input);
  	}
  	
  	final ITypedElement[] buildEditions(ITypedElement target, IFile file) {
  
  		// setup array of editions
  		IFileState[] states= null;		
  		// add available editions
  		try {
  			states= file.getHistory(null);
  		} catch (CoreException ex) {
  			JavaScriptPlugin.log(ex);
  		}
  		
  		int count= 1;
  		if (states != null)
  			count+= states.length;
  
  		ITypedElement[] editions= new ITypedElement[count];
  		editions[0]= new ResourceNode(file);
  		if (states != null)
  			for (int i= 0; i < states.length; i++)
  				editions[i+1]= new HistoryItem(target, states[i]);
  		return editions;
  	}
  	
  	final Shell getShell() {
  		if (fEditor != null)
  			return fEditor.getEditorSite().getShell();
  		return JavaScriptPlugin.getActiveWorkbenchShell();
  	}
  	
  	/**
  	 * Tries to find the given element in a working copy.
  	 */
  	final IJavaScriptElement getWorkingCopy(IJavaScriptElement input) {
  		// TODO: With new working copy story: original == working copy.
  		// Note that the previous code could result in a reconcile as side effect. Should check if that
  		// is still required.
  		return input;
  	}
  	
  	final ASTNode getBodyContainer(JavaScriptUnit root, IMember parent) throws JavaScriptModelException {
  		ISourceRange sourceRange= parent.getNameRange();
  		ASTNode parentNode= NodeFinder.perform(root, sourceRange);
  		do {
  			if (parentNode instanceof TypeDeclaration )
  				return parentNode;
  			parentNode= parentNode.getParent();
  		} while (parentNode != null);
  		return null;
  	}
  	
  	/**
  	 * Returns true if the given file is open in an editor.
  	 */
  	final boolean beingEdited(IFile file) {
  		IDocumentProvider dp= JavaScriptPlugin.getDefault().getCompilationUnitDocumentProvider();
  		FileEditorInput input= new FileEditorInput(file);	
  		return dp.getDocument(input) != null;
  	}
  
  	/**
  	 * Returns an IMember or null.
  	 */
  	final IMember getEditionElement(ISelection selection) {
  		
  		if (selection instanceof IStructuredSelection) {
  			IStructuredSelection ss= (IStructuredSelection) selection;
  			if (ss.size() == 1) {
  				Object o= ss.getFirstElement();
  				if (o instanceof IMember) {
  					IMember m= (IMember) o;
  					if (m.exists() && !m.isBinary() && JavaStructureCreator.hasEdition(m))
  						return m;
  				}
  			}
  		}
  		return null;
  	}
  	
  	final boolean isEnabled(IFile file) {
  		if (file == null || ! file.exists())
  			return false;
  		if (fModifiesFile) {
  			// without validate/edit we would do this:
  			//    return !file.isReadOnly();
  			// with validate/edit we have to return true
  			return true;
  		}
  		return true;
  	}
  	
  	boolean isEnabled(ISelection selection) {
  		IMember m= getEditionElement(selection);
  		if (m == null)
  			return false;
  		IFile file= getFile(m);
  		if (!isEnabled(file))
  			return false;
  		return true;
  	}
  	
  	void applyChanges(ASTRewrite rewriter, final IDocument document, final ITextFileBuffer textFileBuffer, Shell shell, boolean inEditor, Map options)
  							throws CoreException, InvocationTargetException, InterruptedException {
  
  		
  		MultiTextEdit edit= new MultiTextEdit();
  		try {
  			TextEdit res= rewriter.rewriteAST(document, options);
  			edit.addChildren(res.removeChildren());
  		} catch (IllegalArgumentException e) {
  			JavaScriptPlugin.log(e);
  		}
  			
  		try {
  			new RewriteSessionEditProcessor(document, edit, TextEdit.UPDATE_REGIONS).performEdits();
  		} catch (BadLocationException e) {
  			JavaScriptPlugin.log(e);
  		}
  		
  		IRunnableWithProgress r= new IRunnableWithProgress() {
  			public void run(IProgressMonitor pm) throws InvocationTargetException {
  				try {
  					textFileBuffer.commit(pm, false);
  				} catch (CoreException ex) {
  					throw new InvocationTargetException(ex);
  				}
  			}
  		};
  
  		if (inEditor) {
  			// we don't show progress
  			r.run(new NullProgressMonitor());
  		} else {
  			PlatformUI.getWorkbench().getProgressService().run(true, false, r);
  		}
  	}
  
  	static String trimTextBlock(String content, String delimiter, IJavaScriptProject currentProject) {
  		if (content != null) {
  			String[] lines= Strings.convertIntoLines(content);
  			if (lines != null) {
  				Strings.trimIndentation(lines, currentProject);
  				return Strings.concatenate(lines, delimiter);
  			}
  		}
  		return null;
  	}
  	
  	final JavaEditor getEditor(IFile file) {
  		FileEditorInput fei= new FileEditorInput(file);
  		IWorkbench workbench= JavaScriptPlugin.getDefault().getWorkbench();
  		IWorkbenchWindow[] windows= workbench.getWorkbenchWindows();
  		for (int i= 0; i < windows.length; i++) {
  			IWorkbenchPage[] pages= windows[i].getPages();
  			for (int x= 0; x < pages.length; x++) {
  				IEditorPart[] editors= pages[x].getDirtyEditors();
  				for (int z= 0; z < editors.length; z++) {
  					IEditorPart ep= editors[z];
  					if (ep instanceof JavaEditor) {
  						JavaEditor je= (JavaEditor) ep;
  						if (fei.equals(je.getEditorInput()))
  							return (JavaEditor) ep;
  					}
  				}
  			}
  		}
  		return null;
  	}
  
  	/**
  	 * Executes this action with the given selection.
  	 */
  	public abstract void run(ISelection selection);
  
  	//---- Action
  	
  	private JavaEditor fEditor;
  	private String fTitle;
  	private String fMessage;
  
  	void init(JavaEditor editor, String title, String message) {
  		fEditor= editor;
  		fTitle= title;
  		fMessage= message;
  	}
  	
  	final JavaEditor getEditor() {
  		return fEditor;
  	}
  
  	final public void runFromEditor(IAction uiProxy) {
  		
  		// this run is called from Editor
  		IJavaScriptElement element= null;
  		try {
  			element= SelectionConverter.getElementAtOffset(fEditor);
  		} catch (JavaScriptModelException e) {
  			// ignored
  		}
  		
  		fSelection= element != null
  						? new StructuredSelection(element)
  						: StructuredSelection.EMPTY;
  		boolean isEnabled= isEnabled(fSelection);
  		uiProxy.setEnabled(isEnabled);
  		
  		if (!isEnabled) {
  			MessageDialog.openInformation(getShell(), fTitle, fMessage);
  			return;
  		}
  		run(fSelection);
  	}
  
  	boolean checkEnabled() {
  		IJavaScriptUnit unit= SelectionConverter.getInputAsCompilationUnit(fEditor);
  		IFile file= getFile(unit);
  		return isEnabled(file);
  	}	
  
  	final public void update(IAction uiProxy) {
  		uiProxy.setEnabled(checkEnabled());
  	}
  	
   	//---- IActionDelegate
  	
  	final public void selectionChanged(IAction uiProxy, ISelection selection) {
  		fSelection= selection;
  		uiProxy.setEnabled(isEnabled(selection));
  	}
  	
  	final public void run(IAction action) {
  		run(fSelection);
  	}
  	
  	static JavaScriptUnit parsePartialCompilationUnit(IJavaScriptUnit unit) {
  				
  		if (unit == null) {
  			throw new IllegalArgumentException();
  		}
  		try {
  			ASTParser c= ASTParser.newParser(AST.JLS3);
  			c.setSource(unit);
  			c.setFocalPosition(0);
  			c.setResolveBindings(false);
  			c.setWorkingCopyOwner(null);
  			ASTNode result= c.createAST(null);
  			return (JavaScriptUnit) result;
  		} catch (IllegalStateException e) {
  			// convert ASTParser's complaints into old form
  			throw new IllegalArgumentException();
  		}
  	}
  }
  