blob: c96ed13e23a9235e99c0eb71bd54db01a49d1bb9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2008 QNX Software Systems 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:
* QNX - Initial API and implementation
* IBM Corporation
* UIUC
*******************************************************************************/
package org.eclipse.photran.internal.ui.search;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.photran.internal.core.util.Pair;
import org.eclipse.photran.internal.ui.WorkbenchSelectionInfo;
import org.eclipse.photran.internal.ui.vpg.Activator;
import org.eclipse.search.ui.ISearchPage;
import org.eclipse.search.ui.ISearchPageContainer;
import org.eclipse.search.ui.ISearchQuery;
import org.eclipse.search.ui.NewSearchUI;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.ui.IWorkingSet;
/**
* A generic search page with "Search For" and "Limit To" panes.
* <p>
* Based on org.eclipse.cdt.internal.ui.search.PDOMSearchPage
*
* @author Doug Schaefer
* @author Jeff Dammeyer, Andrew Deason, Joe Digiovanna, Nick Sexmith
* @author Jeff Overbey - Rephraser Engine generalization
*/
public abstract class SearchPage extends DialogPage implements ISearchPage
{
// Dialog store id constants
/** @return a string used as a key to store dialog settings; should be unique per search page */
protected abstract String PAGE_NAME();
private final static String STORE_PREVIOUS_PATTERNS = "previousPatterns"; //$NON-NLS-1$
private final static String STORE_REGEX_SEARCH = "regexSearch"; //$NON-NLS-1$
private final static String STORE_SEARCH_FLAGS = "searchFlags"; //$NON-NLS-1$
private Combo patternCombo;
private String[] previousPatterns;
private MessageBox errorBox;
private Button regexButton;
private boolean isRegex;
private boolean firstTime = true;
private IStructuredSelection structuredSelection;
private ITextSelection textSelection;
private ISearchPageContainer pageContainer;
private Label patternLabel;
private final String globPatternString = Messages.SearchPage_AnyStringAnyCharLabel;
/**
* This method receives the workbench selection as an argument and returns the corresponding
* {@link IResource}, if any.
* <p>
* This implementation can only identify a resource if the selection <i>is</i> an
* {@link IResource}, or if an editor is open and it is editing an {@link IFile}. Override this
* method to support other types of selections. (E.g., Photran overrides this method to handle
* the case where an <code>ICElement</code> -- a C model element -- is selected.)
*/
protected IResource getResource(Object obj)
{
if (obj instanceof IResource)
{
return (IResource)obj;
}
else if (obj instanceof IAdaptable)
{
Object res = ((IAdaptable)obj).getAdapter(IResource.class);
return res == null ? null : (IResource)res;
}
else return null;
}
/**
* Performs the search when the Search button is clicked
*/
public boolean performAction()
{
String patternStr = patternCombo.getText();
// get the list of elements for the scope
List<IResource> scope = new ArrayList<IResource>();
String scopeDescription = determineScope(scope);
int searchFlags = getSearchFlagsFromSelectedButtons();
ISearchQuery job;
try
{
patternStr = patternStr.trim();
job = createSearchQuery(scope, scopeDescription, patternStr, convertPattern(isRegex, patternStr), searchFlags);
}
catch (PatternSyntaxException e)
{
errorBox = new MessageBox(this.getShell(), SWT.ICON_ERROR | SWT.OK);
errorBox.setText(Messages.SearchPage_InvalidSearchPatternTitle);
errorBox.setMessage(Messages.SearchPage_SearchPatternIsInvalid + e.getMessage());
errorBox.open();
return false;
}
NewSearchUI.activateSearchResultView();
NewSearchUI.runQueryInBackground(job);
saveDialogSettings(patternStr, searchFlags);
return true;
}
/**
* @param isRegex is this a regular expression or not
* @param patternStr the string to be converted
* @return the converted string
* If the input string is a regex, compile it and return the same string.
* If the input string is not a regex (e.g. a Glob), convert the Glob to regex syntax
* and pass
*/
protected String convertPattern(boolean isRegex, String patternStr) throws PatternSyntaxException
{
if (isRegex)
{
Pattern.compile(patternStr);
return patternStr;
}
else
{
return convertGlobToRegex(patternStr);
}
}
public static String convertGlobToRegex(String patternStr)
{
StringBuffer buff = new StringBuffer();
for (int i = 0; i < patternStr.length(); ++i) {
char c = patternStr.charAt(i);
switch (c) {
case '*':
buff.append(".*"); //$NON-NLS-1$
break;
case '?':
buff.append("."); //$NON-NLS-1$
break;
case '$':
buff.append("\\$"); //$NON-NLS-1$
break;
default:
if (!Character.toString(c).matches("[0-9a-zA-Z._]")) { //$NON-NLS-1$
throw new PatternSyntaxException(Messages.SearchPage_IllegalCharacterInPatternString, patternStr, i+1);
}
buff.append(Messages.SearchPage_11 + c);
}
}
return buff.toString();
}
protected abstract ISearchQuery createSearchQuery(
List<IResource> scope,
String scopeDescription,
String patternDescription,
String patternRegex,
int searchFlags);
/**
* @param scope The list of resources to be populated. This function populates the list.
* @return the description of the search Scope.
*/
private String determineScope(List<IResource> scope)
{
String scopeDescription;
switch (getContainer().getSelectedScope())
{
case ISearchPageContainer.SELECTED_PROJECTS_SCOPE:
scopeDescription = Messages.SearchPage_ScopeDescription_EnclosingProjects;
if (structuredSelection != null)
{
for (Iterator< ? > i = structuredSelection.iterator(); i.hasNext();)
{
IResource res = getResource(i.next());
if (res != null)
{
scope.add(res);
}
}
}
else
{
IFile fileInEditor = new WorkbenchSelectionInfo().getFileInEditor();
if (fileInEditor != null) scope.add(fileInEditor.getProject());
}
break;
case ISearchPageContainer.SELECTION_SCOPE:
scopeDescription = Messages.SearchPage_ScopeDescription_SelectedResources;
if (structuredSelection != null)
{
for (Iterator< ? > i = structuredSelection.iterator(); i.hasNext();)
{
IResource res = getResource(i.next());
if (res != null)
{
scope.add(res);
}
}
}
break;
case ISearchPageContainer.WORKING_SET_SCOPE:
IWorkingSet[] workingSets = getContainer().getSelectedWorkingSets();
scopeDescription = Messages.SearchPage_ScopeDescription_WorkingSet + toString(workingSets); // CSearchUtil.toString(workingSets);
for (int i = 0; i < workingSets.length; ++i)
{
IAdaptable[] wsElements = workingSets[i].getElements();
for (IAdaptable elem : wsElements)
{
IResource res = getResource(elem);
if (res != null)
{
scope.add(res);
}
}
}
break;
case ISearchPageContainer.WORKSPACE_SCOPE:
default:
scope.add(ResourcesPlugin.getWorkspace().getRoot());
scopeDescription = Messages.SearchPage_ScopeDescription_Workspace;
break;
}
return scopeDescription;
}
/**
* @param patternStr The search string
* @param searchFlags The search flags (e.g. The check-boxes and radio buttons)
* Saves all the settings in search page, so that when the user re-opens the page,
* the same settings are restored.
*/
private void saveDialogSettings(String patternStr, int searchFlags)
{
IDialogSettings settings = getDialogSettings();
settings.put(STORE_REGEX_SEARCH, isRegex);
if (previousPatterns == null)
previousPatterns = new String[] { patternStr };
else
{
// Add only if we don't have it already
boolean addit = true;
for (int i = 0; i < previousPatterns.length; ++i)
{
if (patternStr.equals(previousPatterns[i]))
{
// move used pattern to the top of the list
String tmpPattern = previousPatterns[i];
System.arraycopy(previousPatterns, 0, previousPatterns, 1, i);
previousPatterns[0] = tmpPattern;
addit = false;
break;
}
}
if (addit)
{
// Insert it into the beginning of the list
String[] newPatterns = new String[previousPatterns.length + 1];
System.arraycopy(previousPatterns, 0, newPatterns, 1, previousPatterns.length);
newPatterns[0] = patternStr;
previousPatterns = newPatterns;
}
}
settings.put(STORE_PREVIOUS_PATTERNS, previousPatterns);
settings.put(STORE_SEARCH_FLAGS, searchFlags);
}
public void createControl(Composite parent)
{
initializeDialogUnits(parent);
GridData gd;
Composite result = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout(2, false);
layout.horizontalSpacing = 10;
result.setLayout(layout);
result.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
gd = new GridData();
gd.horizontalAlignment = GridData.FILL;
gd.verticalAlignment = GridData.VERTICAL_ALIGN_BEGINNING | GridData.VERTICAL_ALIGN_FILL;
gd.horizontalSpan = 2;
Label label = new Label(result, SWT.LEFT);
label.setText(Messages.SearchPage_SearchPatternLabel);
label.setLayoutData(gd);
// Pattern combo
patternCombo = new Combo(result, SWT.SINGLE | SWT.BORDER);
patternCombo.addModifyListener(new ModifyListener()
{
public void modifyText(ModifyEvent e)
{
setPerformActionEnabled();
}
});
gd = new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL);
regexButton = new Button(result, SWT.CHECK);
regexButton.setText(Messages.SearchPage_RegularExpressionLabel);
gd = new GridData();
regexButton.setLayoutData(gd);
regexButton.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
isRegex = regexButton.getSelection();
setPerformActionEnabled();
}
});
gd = new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL);
gd.horizontalIndent = -gd.horizontalIndent;
patternCombo.setLayoutData(gd);
gd = new GridData();
gd.horizontalAlignment = GridData.FILL;
gd.verticalAlignment = GridData.VERTICAL_ALIGN_BEGINNING | GridData.VERTICAL_ALIGN_FILL;
gd.horizontalSpan = 2;
patternLabel = new Label(result, SWT.LEFT);
patternLabel.setText(globPatternString);
patternLabel.setLayoutData(gd);
Composite groupsComposite = new Composite(result, SWT.NONE);
layout = new GridLayout(2, false);
groupsComposite.setLayout(layout);
gd = new GridData(GridData.FILL_HORIZONTAL);
gd.horizontalSpan = 2;
groupsComposite.setLayoutData(gd);
prepareToCreateSearchAndLimitButtons(groupsComposite);
Group group = new Group(groupsComposite, SWT.NONE);
group.setText(Messages.SearchPage_SearchForLabel);
layout = new GridLayout();
layout.numColumns = 2;
group.setLayout(layout);
group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_BEGINNING));
addSelectionListener(createSearchForButtons(group));
group = new Group(groupsComposite, SWT.NONE);
group.setText(Messages.SearchPage_LimitToLabel);
layout = new GridLayout();
layout.numColumns = 1;
group.setLayout(layout);
group.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_BEGINNING));
addSelectionListener(createLimitToButtons(group));
createAdditionalGroups(groupsComposite);
setControl(result);
Dialog.applyDialogFont(result);
}
private void addSelectionListener(Button[] buttons)
{
for (Button button : buttons)
{
button.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
setPerformActionEnabled();
}
});
}
}
/**
* If any groups other than "Search For" and "Limit To" should appear in this dialog,
* this method should be overridden and those groups created here.
*/
protected void createAdditionalGroups(Composite groupsComposite)
{
}
public void setContainer(ISearchPageContainer container)
{
pageContainer = container;
}
private ISearchPageContainer getContainer()
{
return pageContainer;
}
protected void setPerformActionEnabled()
{
boolean enable = true;
// if regex button is checked, remove description.
patternLabel.setVisible(!isRegex);
// Need a text string to search
if (patternCombo.getText().length() == 0) enable = false;
// Need a type
if (!isAtLeastOneSearchForButtonChecked()) enable = false;
getContainer().setPerformActionEnabled(enable);
}
private IDialogSettings getDialogSettings()
{
IDialogSettings settings = Activator.getDefault().getDialogSettings();
IDialogSettings searchSettings = settings.getSection(PAGE_NAME());
if (searchSettings == null) searchSettings = settings.addNewSection(PAGE_NAME());
return searchSettings;
}
@Override
public void setVisible(boolean visible)
{
if (visible)
{
if (firstTime)
{
firstTime = false;
restoreDialogSettings();
}
patternCombo.setFocus();
setPerformActionEnabled();
}
super.setVisible(visible);
}
private void restoreDialogSettings()
{
IDialogSettings settings = getDialogSettings();
int searchFlags = defaultSearchFlags();
try
{
searchFlags = settings.getInt(STORE_SEARCH_FLAGS);
}
catch (NumberFormatException e)
{
// was uninitialized, assume the defaults
}
previousPatterns = settings.getArray(STORE_PREVIOUS_PATTERNS);
if (previousPatterns != null) patternCombo.setItems(previousPatterns);
// Initialize the selection
ISelection selection = getContainer().getSelection();
if (selection instanceof IStructuredSelection)
{
structuredSelection = (IStructuredSelection)selection;
}
else if (selection instanceof ITextSelection)
{
textSelection = (ITextSelection)selection;
patternCombo.setText(textSelection.getText());
}
checkButtonsCorrespondingTo(searchFlags);
isRegex = settings.getBoolean(STORE_REGEX_SEARCH);
regexButton.setSelection(isRegex);
}
protected abstract int defaultSearchFlags();
// Copy of CSearchUtil#toString
public static String toString(IWorkingSet[] workingSets)
{
if (workingSets != null && workingSets.length > 0)
{
String string = new String();
for (int i = 0; i < workingSets.length; i++)
{
if (i > 0) string += ", "; //$NON-NLS-1$
string += workingSets[i].getName();
}
return string;
}
return null;
}
////////////////////////////// SEARCH FOR, LIMIT TO BUTTONS //////////////////////////////
// If this is too restrictive (i.e., check boxes and option buttons aren't sufficient),
// override the protected methods in this section to create custom controls and give
// the "search flags" integer a custom meaning.
private String[] searchForText;
private Integer[] searchForData;
private String[] limitToText;
private Integer[] limitToData;
private Button[] searchForButtons;
private Button[] limitToButtons;
protected void prepareToCreateSearchAndLimitButtons(Composite groupsComposite)
{
List<Pair<String, Integer>> searchFor = searchFor();
searchForText = new String[searchFor.size()];
searchForData = new Integer[searchFor.size()];
int i = 0;
for (Pair<String, Integer> pair : searchFor)
{
searchForText[i] = pair.fst;
searchForData[i] = pair.snd;
i++;
}
List<Pair<String, Integer>> limitTo = limitTo();
limitToText = new String[limitTo.size()];
limitToData = new Integer[limitTo.size()];
i = 0;
for (Pair<String, Integer> pair : limitTo)
{
limitToText[i] = pair.fst;
limitToData[i] = pair.snd;
i++;
}
}
protected Button[] createSearchForButtons(Group group)
{
searchForButtons = new Button[searchForText.length];
for (int i = 0; i < searchForText.length; i++)
{
Button button = new Button(group, SWT.CHECK);
button.setText(searchForText[i]);
button.setData(searchForData[i]);
searchForButtons[i] = button;
}
return searchForButtons;
}
protected Button[] createLimitToButtons(Group group)
{
limitToButtons = new Button[limitToText.length];
for (int i = 0; i < limitToText.length; i++)
{
Button button = new Button(group, SWT.RADIO);
button.setText(limitToText[i]);
button.setData(limitToData[i]);
limitToButtons[i] = button;
}
return limitToButtons;
}
protected boolean isAtLeastOneSearchForButtonChecked()
{
for (int i = 0; i < searchForButtons.length; ++i)
if (searchForButtons[i].getSelection())
return true;
return false;
}
protected int getSearchFlagsFromSelectedButtons()
{
int searchFlags = 0;
for (int i = 0; i < searchForButtons.length; ++i) {
if (searchForButtons[i].getSelection())
searchFlags |= ((Integer)searchForButtons[i].getData()).intValue();
}
for (int i = 0; i < limitToButtons.length; ++i) {
if (limitToButtons[i].getSelection())
searchFlags |= ((Integer)limitToButtons[i].getData()).intValue();
}
return searchFlags;
}
protected void checkButtonsCorrespondingTo(int searchFlags)
{
checkAllSearchForButtonsCorrespondingTo(searchFlags);
selectFirstLimitToButtonCorrespondingTo(searchFlags);
}
protected void checkAllSearchForButtonsCorrespondingTo(int searchFlags)
{
for (int i = 0; i < searchForButtons.length; ++i)
{
boolean shouldCheck = (searchFlags & ((Integer)searchForButtons[i].getData()).intValue()) != 0;
searchForButtons[i].setSelection(shouldCheck);
}
}
protected void selectFirstLimitToButtonCorrespondingTo(int searchFlags)
{
boolean buttonHasBeenSelected = false;
for (int i = 0; i < limitToButtons.length; ++i)
{
if (buttonHasBeenSelected)
{
limitToButtons[i].setSelection(false);
}
else
{
buttonHasBeenSelected = (searchFlags & ((Integer)limitToButtons[i].getData()).intValue()) != 0;
limitToButtons[i].setSelection(buttonHasBeenSelected);
}
}
}
protected abstract List<Pair<String, Integer>> searchFor();
protected abstract List<Pair<String, Integer>> limitTo();
}