/*******************************************************************************
* Copyright (c) 2007-2012 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is 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:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.seam.ui.actions;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor;
import org.eclipse.jdt.internal.ui.search.SearchMessages;
import org.eclipse.jdt.internal.ui.search.SearchUtil;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IActionDelegate2;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.MultiPageEditorPart;
import org.eclipse.ui.progress.IProgressService;
import org.eclipse.ui.texteditor.AbstractTextEditor;
import org.jboss.tools.common.el.core.model.ELInvocationExpression;
import org.jboss.tools.common.model.ui.editor.EditorPartWrapper;
import org.jboss.tools.common.model.ui.texteditors.xmleditor.XMLTextEditor;
import org.jboss.tools.jst.web.ui.internal.editor.jspeditor.JSPMultiPageEditor;
import org.jboss.tools.seam.core.ISeamContextVariable;
import org.jboss.tools.seam.core.ISeamProject;
import org.jboss.tools.seam.core.SeamCorePlugin;
import org.jboss.tools.seam.internal.core.el.SeamELCompletionEngine;
import org.jboss.tools.seam.ui.SeamGuiPlugin;
import org.jboss.tools.seam.ui.search.SeamSearchQuery;
import org.jboss.tools.seam.ui.search.SeamSearchScope;
/**
* Base class for Seam Find actions
*
* @author Jeremy
*/
abstract public class FindSeamAction extends Action implements IWorkbenchWindowActionDelegate, IActionDelegate2, ISelectionListener
{
/**
* Constructs the Action object
*/
protected FindSeamAction() {
}
/**
* @Override
*/
public void run() {
runWithEvent(null);
}
/*
* Returns editor from a given workbench window
*
* @param window
* @return
*/
private IEditorPart getCurrentEditor(IWorkbenchWindow window) {
if (window == null || window.getActivePage() == null)
return null;
return window.getActivePage().getActiveEditor();
}
/*
* Returns viewer for a given editor
*
* @param editor
* @return
*/
private ISourceViewer getEditorViewer(IWorkbenchPart editor) {
ISourceViewer viewer = null;
if (editor instanceof EditorPartWrapper) {
editor = ((EditorPartWrapper)editor).getEditor();
}
if (editor instanceof JSPMultiPageEditor) {
viewer = ((JSPMultiPageEditor)editor).getJspEditor().getTextViewer();
} else if (editor instanceof XMLTextEditor) {
viewer = ((XMLTextEditor)editor).getTextViewer();
} else if (editor instanceof MultiPageEditorPart) {
IEditorPart activeEditor = getActiveEditor((MultiPageEditorPart)editor);
if (activeEditor instanceof AbstractTextEditor) {
viewer = getSourceViewer((AbstractTextEditor)activeEditor);
}
} else if (editor instanceof AbstractTextEditor ) {
viewer = getSourceViewer((AbstractTextEditor)editor);
}
else if (editor instanceof CompilationUnitEditor) {
viewer = ((CompilationUnitEditor)editor).getViewer();
}
return viewer;
}
/*
* Returns {@link ITextSelection} for a given viewer
*
* @param viewer
* @return
*/
private ITextSelection getTextSelection(ITextViewer viewer) {
if (viewer == null || viewer.getSelectionProvider() == null)
return null;
return getTextSelection(viewer.getSelectionProvider().getSelection());
}
/*
* Returns {@link ITextSelection} for a given {@link ISelection}
*
* @param selection
* @return
*/
private ITextSelection getTextSelection(ISelection selection) {
if (selection == null || selection.isEmpty())
return null;
if (selection instanceof ITextSelection) {
return (ITextSelection)selection;
}
return null;
}
/**
* @Override
*/
public void runWithEvent(Event e) {
IWorkbenchPart activeWorkbenchPart = getCurrentEditor(getActiveWorkbenchWindow());
if (activeWorkbenchPart == null)
return;
IEditorInput input = (activeWorkbenchPart instanceof IEditorPart ?
((IEditorPart)activeWorkbenchPart).getEditorInput() : null);
ISourceViewer viewer = getEditorViewer(activeWorkbenchPart);
if (viewer == null)
return;
ITextSelection selection = getTextSelection(viewer);
if (selection == null)
return;
int selectionOffset = selection.getOffset();
IDocument document = viewer.getDocument();
IFile file = null;
if (input instanceof IFileEditorInput) {
file = ((IFileEditorInput)input).getFile();
}
IProject project = (file == null ? null : file.getProject());
ISeamProject seamProject = SeamCorePlugin.getSeamProject(project, true);
if (seamProject == null)
return;
ELInvocationExpression expression = SeamELCompletionEngine.findExpressionAtOffset(
document, selectionOffset, 0, document.getLength());
if (expression == null)
return; // No EL Operand found
try {
performNewSearch(expression, file);
} catch (JavaModelException jme) {
SeamGuiPlugin.getPluginLog().logError(jme);
} catch (InterruptedException ie) {
SeamGuiPlugin.getPluginLog().logError(ie);
}
return;
}
/**
* Finds the variable names for the selected ELOperandToken tokens
*
* @param seamProject
* @param document
* @param tokens
* @return
*/
public static String[] findVariableNames(ISeamProject seamProject, IDocument document, ELInvocationExpression tokens) {
String[] varNames = null;
if(tokens == null) return varNames;
// Define the Seam project variables to search for declarations
List<ISeamContextVariable> variables = new ArrayList<ISeamContextVariable>();
while(tokens != null) {
try {
int start = tokens.getStartPosition();
int end = tokens.getEndPosition();
String variationText = document.get(start, end - start);
Set<ISeamContextVariable> vars = seamProject.getVariablesByName(variationText);
if (vars != null)
variables.addAll(vars);
} catch (BadLocationException e1) {
SeamGuiPlugin.getPluginLog().logError(e1);
}
tokens = tokens.getLeft();
}
if (!variables.isEmpty()) {
// Some variable/variables are found - perform search for their declarations
varNames = new String[variables.size()];
for (int i = 0; i < variables.size(); i++) {
varNames[i] = variables.get(i).getName();
}
}
return varNames;
}
// IWorkbenchWindowActionDelegate
/**
* @Override
*/
public void run(IAction action) {
run();
}
/**
* @Override
*/
public void dispose() {
// do nothing.
}
/**
* @Override
*/
public void init(IWorkbenchWindow window) {
}
/**
* @Override
*/
public void selectionChanged(IAction action, ISelection selection) {
selectionChanged(getCurrentEditor(getActiveWorkbenchWindow()), selection);
}
// IActionDelegate2
/**
* @Override
*/
public void runWithEvent(IAction action, Event event) {
runWithEvent(event);
}
IAction fDelegatorAction =null;
/**
* @Override
*/
public void init(IAction action) {
fDelegatorAction = action;
}
private SeamSearchQuery createQuery(ELInvocationExpression tokens, IFile sourceFile) throws JavaModelException, InterruptedException {
SeamSearchScope scope = new SeamSearchScope(new IProject[] {sourceFile.getProject()}, getLimitTo());
return new SeamSearchQuery(tokens, sourceFile, scope);
}
/**
* Returns the limitTo flag. The possible values are:
* - SeamSearchScope.SEARCH_FOR_DECLARATIONS
* - SeamSearchScope.SEARCH_FOR_REFERENCES
* @return
*/
abstract protected int getLimitTo();
private void performNewSearch(ELInvocationExpression expression, IFile sourceFile) throws JavaModelException, InterruptedException {
SeamSearchQuery query= createQuery(expression, sourceFile);
if (query.canRunInBackground()) {
/*
* This indirection with Object as parameter is needed to prevent the loading
* of the Search plug-in: the VM verifies the method call and hence loads the
* types used in the method signature, eventually triggering the loading of
* a plug-in (in this case ISearchQuery results in Search plug-in being loaded).
*/
SearchUtil.runQueryInBackground(query);
} else {
IProgressService progressService= PlatformUI.getWorkbench().getProgressService();
/*
* This indirection with Object as parameter is needed to prevent the loading
* of the Search plug-in: the VM verifies the method call and hence loads the
* types used in the method signature, eventually triggering the loading of
* a plug-in (in this case it would be ISearchQuery).
*/
IStatus status= SearchUtil.runQueryInForeground(progressService, query);
if (status.matches(IStatus.ERROR | IStatus.INFO | IStatus.WARNING)) {
ErrorDialog.openError(getShell(), SearchMessages.Search_Error_search_title, SearchMessages.Search_Error_search_message, status);
}
}
}
/**
* Returns the active editor from active MultipageEditor
*
* @param multiPageEditor
* @return
*/
public static IEditorPart getActiveEditor(MultiPageEditorPart multiPageEditor) {
Class editorClass = multiPageEditor.getClass();
while (editorClass != null) {
try {
Method m = editorClass.getDeclaredMethod("getActiveEditor", new Class[] {});
if(m != null) {
m.setAccessible(true);
Object result = m.invoke(multiPageEditor, new Object[]{});
return (result instanceof IEditorPart ? (IEditorPart)result : null);
}
} catch (NoSuchMethodException e) {
// ignore
} catch (IllegalArgumentException e) {
// ignore
} catch (IllegalAccessException e) {
// ignore
} catch (InvocationTargetException e) {
// ignore
}
editorClass = editorClass.getSuperclass();
}
return null;
}
/**
* Returns the source viewer from AbstractTextEditor
*
* @param multiPageEditor
* @return
*/
public static ISourceViewer getSourceViewer(AbstractTextEditor editor) {
Class editorClass = editor.getClass();
while (editorClass != null) {
try {
Method m = editorClass.getDeclaredMethod("getSourceViewer", new Class[] {});
if(m != null) {
m.setAccessible(true);
Object result = m.invoke(editor, new Object[]{});
return (result instanceof ISourceViewer ? (ISourceViewer)result : null);
}
} catch (NoSuchMethodException e) {
// ignore
} catch (IllegalArgumentException e) {
// ignore
} catch (IllegalAccessException e) {
// ignore
} catch (InvocationTargetException e) {
// ignore
}
editorClass = editorClass.getSuperclass();
}
return null;
}
/*
* Returns current Shell
*
* @return
*/
private Shell getShell() {
return PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
}
// IWorkbenchPart fActiveWorkbenchPart = null;
/*
* Returns the workbench
*
* @return
*/
private IWorkbench getWorkbench() {
return PlatformUI.getWorkbench();
}
/*
* Returns active workbench window
* @return
*/
private IWorkbenchWindow getActiveWorkbenchWindow() {
return (getWorkbench() == null ? null : getWorkbench().getActiveWorkbenchWindow());
}
/*
* Updates availability on the action delegate
*
* @param selection
*/
private void update(ISelection selection) {
boolean enabled = false;
try {
IWorkbenchPart activeWorkbenchPart = getCurrentEditor(getActiveWorkbenchWindow());
if (!(activeWorkbenchPart instanceof IEditorPart))
return;
ISourceViewer viewer = getEditorViewer((IEditorPart)activeWorkbenchPart);
if (viewer == null)
return;
enabled = (getTextSelection(selection) != null);
} finally {
setEnabled(enabled);
if (fDelegatorAction != null) {
fDelegatorAction.setEnabled(enabled);
}
}
}
// ISelectionListener
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
update(getTextSelection(getEditorViewer(part)));
}
}