/*******************************************************************************
* Copyright (c) 2006, 2007 Spring IDE Developers
* 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:
* Spring IDE Developers - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.beans.ui.search;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.resource.ImageDescriptor;
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.custom.CLabel;
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.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.IWorkingSetManager;
import org.eclipse.ui.PlatformUI;
import org.springframework.ide.eclipse.beans.ui.search.internal.BeansSearchMessages;
import org.springframework.ide.eclipse.beans.ui.search.internal.BeansSearchScope;
import org.springframework.ide.eclipse.beans.ui.search.internal.queries.BeanChildQuery;
import org.springframework.ide.eclipse.beans.ui.search.internal.queries.BeanClassQuery;
import org.springframework.ide.eclipse.beans.ui.search.internal.queries.BeanNameQuery;
import org.springframework.ide.eclipse.beans.ui.search.internal.queries.BeanPropertyQuery;
import org.springframework.ide.eclipse.beans.ui.search.internal.queries.BeanReferenceQuery;
/**
* Implementation of {@link ISearchPage} which provides the UI for searching the
* BeansCoreModel.
* @author Torsten Juergeleit
* @author Christian Dupuis
*/
public class BeansSearchPage extends DialogPage implements ISearchPage {
public static final String EXTENSION_POINT_ID = "org.springframework."
+ "ide.eclipse.beans.ui.search.searchPage";
public static final int SEARCH_FOR_BEAN_NAME = 0;
public static final int SEARCH_FOR_BEAN_REFERENCE = 1;
public static final int SEARCH_FOR_BEAN_CLASS = 2;
public static final int SEARCH_FOR_BEAN_CHILD = 3;
public static final int SEARCH_FOR_BEAN_PROPERTY = 4;
private static final int HISTORY_SIZE = 12;
// Dialog store id constants
private final static String PAGE_NAME = "BeansSearchPage";
private final static String STORE_CASE_SENSITIVE = "CASE_SENSITIVE";
private static final String STORE_REG_EX_SEARCH = "REG_EX_SEARCH";
private final static String STORE_HISTORY = "HISTORY";
private final static String STORE_HISTORY_SIZE = "HISTORY_SIZE";
private final List<SearchData> previousSearchPatterns = new
ArrayList<SearchData>();
private boolean firstTime = true;
private IDialogSettings dialogSettings;
private boolean isCaseSensitive;
private boolean isRegExSearch;
private Combo expressionCombo;
private ISearchPageContainer searchContainer;
private Button regExCheckbox;
private CLabel statusLabel;
private Button caseSensitiveCheckbox;
private Button[] searchForButtons;
private String[] searchForText = {
BeansSearchMessages.SearchPage_searchFor_name,
BeansSearchMessages.SearchPage_searchFor_reference,
BeansSearchMessages.SearchPage_searchFor_class,
BeansSearchMessages.SearchPage_searchFor_child,
BeansSearchMessages.SearchPage_searchFor_property
};
public BeansSearchPage() {
// required
}
public BeansSearchPage(String title) {
super(title);
}
public BeansSearchPage(String title, ImageDescriptor image) {
super(title, image);
}
private String[] getPreviousSearchPatterns() {
// Search results are not persistent
int patternCount = previousSearchPatterns.size();
String[] patterns = new String[patternCount];
for (int i = 0; i < patternCount; i++) {
patterns[i] = (previousSearchPatterns.get(i))
.getPattern();
}
return patterns;
}
private int getSearchFor() {
for (int i = 0; i < searchForButtons.length; i++) {
if (searchForButtons[i].getSelection()) {
return i;
}
}
return -1;
}
private String getPattern() {
return expressionCombo.getText();
}
private SearchData findInPrevious(String pattern) {
for (SearchData element : previousSearchPatterns) {
if (pattern.equals(element.getPattern())) {
return element;
}
}
return null;
}
/**
* Returns search pattern data and update previous searches. An existing
* entry will be updated.
*/
private SearchData getPatternData() {
String pattern = getPattern();
SearchData match = findInPrevious(pattern);
if (match != null) {
previousSearchPatterns.remove(match);
}
match = new SearchData(getSearchFor(), pattern, caseSensitiveCheckbox
.getSelection(), regExCheckbox.getSelection(), searchContainer
.getSelectedScope(), searchContainer.getSelectedWorkingSets());
previousSearchPatterns.add(0, match); // insert on top
return match;
}
public void setContainer(ISearchPageContainer container) {
searchContainer = container;
}
@Override
public void setVisible(boolean visible) {
if (visible && expressionCombo != null) {
if (firstTime) {
firstTime = false;
// Set item and text here to prevent page from resizing
expressionCombo.setItems(getPreviousSearchPatterns());
initSelections();
}
expressionCombo.setFocus();
}
updateOKStatus();
super.setVisible(visible);
}
public boolean performAction() {
SearchData data = getPatternData();
// Setup search scope
BeansSearchScope scope;
switch (searchContainer.getSelectedScope()) {
case ISearchPageContainer.SELECTION_SCOPE:
scope = BeansSearchScope.newSearchScope(searchContainer
.getSelection(), false);
break;
case ISearchPageContainer.WORKING_SET_SCOPE:
scope = BeansSearchScope.newSearchScope(searchContainer
.getSelectedWorkingSets());
break;
case ISearchPageContainer.SELECTED_PROJECTS_SCOPE:
scope = BeansSearchScope.newSearchScope(searchContainer
.getSelection(), true);
break;
default:
scope = BeansSearchScope.newSearchScope();
}
ISearchQuery query = null;
switch (data.getSearchFor()) {
case SEARCH_FOR_BEAN_NAME:
query = new BeanNameQuery(scope, data.getPattern(), data
.isCaseSensitive(), data.isRegExp());
break;
case SEARCH_FOR_BEAN_REFERENCE:
query = new BeanReferenceQuery(scope, data.getPattern(), data
.isCaseSensitive(), data.isRegExp());
break;
case SEARCH_FOR_BEAN_CLASS:
query = new BeanClassQuery(scope, data.getPattern(),
isCaseSensitive, isRegExSearch);
break;
case SEARCH_FOR_BEAN_CHILD:
query = new BeanChildQuery(scope, data.getPattern(),
isCaseSensitive, isRegExSearch);
break;
case SEARCH_FOR_BEAN_PROPERTY:
query = new BeanPropertyQuery(scope, data.getPattern(),
isCaseSensitive, isRegExSearch);
break;
}
NewSearchUI.activateSearchResultView();
NewSearchUI.runQueryInBackground(query);
return true;
}
/**
* Returns the page settings for this search page.
*
* @return the page settings to be used
*/
private IDialogSettings getDialogSettings() {
IDialogSettings settings = BeansSearchPlugin.getDefault()
.getDialogSettings();
dialogSettings = settings.getSection(PAGE_NAME);
if (dialogSettings == null) {
dialogSettings = settings.addNewSection(PAGE_NAME);
}
return dialogSettings;
}
/**
* Initializes itself from the stored page settings.
*/
private void readConfiguration() {
IDialogSettings s = getDialogSettings();
isCaseSensitive = s.getBoolean(STORE_CASE_SENSITIVE);
isRegExSearch = s.getBoolean(STORE_REG_EX_SEARCH);
try {
int historySize = s.getInt(STORE_HISTORY_SIZE);
for (int i = 0; i < historySize; i++) {
IDialogSettings histSettings = s.getSection(STORE_HISTORY + i);
if (histSettings != null) {
SearchData data = SearchData.create(histSettings);
if (data != null) {
previousSearchPatterns.add(data);
}
}
}
} catch (NumberFormatException e) {
// ignore
}
}
/**
* Stores it current configuration in the dialog store.
*/
private void writeConfiguration() {
IDialogSettings s = getDialogSettings();
s.put(STORE_CASE_SENSITIVE, isCaseSensitive);
s.put(STORE_REG_EX_SEARCH, isRegExSearch);
int historySize = Math.min(previousSearchPatterns.size(), HISTORY_SIZE);
s.put(STORE_HISTORY_SIZE, historySize);
for (int i = 0; i < historySize; i++) {
IDialogSettings histSettings = s.addNewSection(STORE_HISTORY + i);
SearchData data = (previousSearchPatterns.get(i));
data.store(histSettings);
}
}
/**
* Creates the page's content.
*/
public void createControl(Composite parent) {
initializeDialogUnits(parent);
readConfiguration();
Composite result = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout(2, false);
layout.horizontalSpacing = 10;
result.setLayout(layout);
Control expressionComposite = createExpression(result);
expressionComposite.setLayoutData(new GridData(GridData.FILL,
GridData.CENTER, true, false, 2, 1));
Label separator = new Label(result, SWT.NONE);
separator.setVisible(false);
GridData data = new GridData(GridData.FILL, GridData.FILL, false,
false, 2, 1);
data.heightHint = convertHeightInCharsToPixels(1) / 3;
separator.setLayoutData(data);
Control searchFor = createSearchFor(result);
searchFor.setLayoutData(new GridData(GridData.FILL, GridData.FILL,
false, false, 1, 1));
setControl(result);
Dialog.applyDialogFont(result);
}
private Control createExpression(Composite parent) {
// Group with grid layout with 2 columns
Composite group = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout(2, false);
layout.marginWidth = 0;
layout.marginHeight = 0;
group.setLayout(layout);
// Expression text + info
Label label = new Label(group, SWT.LEFT);
label.setText(BeansSearchMessages.SearchPage_expression);
label.setLayoutData(new GridData(GridData.FILL, GridData.FILL, false,
false, 2, 1));
// Expression combo
expressionCombo = new Combo(group, SWT.SINGLE | SWT.BORDER);
expressionCombo.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
handlePatternSelected();
updateOKStatus();
}
});
expressionCombo.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
doPatternModified();
updateOKStatus();
}
});
GridData data = new GridData(GridData.FILL, GridData.FILL, true, false,
1, 1);
data.widthHint = convertWidthInCharsToPixels(50);
expressionCombo.setLayoutData(data);
// Ignore case checkbox
caseSensitiveCheckbox = new Button(group, SWT.CHECK);
caseSensitiveCheckbox
.setText(BeansSearchMessages.SearchPage_caseSensitive);
caseSensitiveCheckbox.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
isCaseSensitive = caseSensitiveCheckbox.getSelection();
}
});
caseSensitiveCheckbox.setLayoutData(new GridData(GridData.FILL,
GridData.FILL, false, false, 1, 1));
// Text line which explains the special characters
statusLabel = new CLabel(group, SWT.LEAD);
statusLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,
false, 1, 1));
statusLabel.setFont(group.getFont());
statusLabel.setAlignment(SWT.LEFT);
statusLabel.setText(BeansSearchMessages.SearchPage_expressionHint);
// RegEx checkbox
regExCheckbox = new Button(group, SWT.CHECK);
regExCheckbox.setText(BeansSearchMessages.SearchPage_regularExpression);
regExCheckbox.setSelection(isRegExSearch);
regExCheckbox.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
isRegExSearch = regExCheckbox.getSelection();
updateOKStatus();
writeConfiguration();
}
});
regExCheckbox.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false,
false, 1, 1));
regExCheckbox.setFont(group.getFont());
return group;
}
final void updateOKStatus() {
boolean isValid = isValidSearchPattern();
searchContainer.setPerformActionEnabled(isValid);
}
private boolean isValidSearchPattern() {
if (getPattern().length() == 0) {
return false;
}
// TODO return SearchPattern.createPattern(getPattern(), getSearchFor(), getLimitTo(), SearchPattern.R_EXACT_MATCH) != null;
return true;
}
@Override
public void dispose() {
writeConfiguration();
super.dispose();
}
private void doPatternModified() {
// TODO Is this method necessary?
}
private void handlePatternSelected() {
int selectionIndex = expressionCombo.getSelectionIndex();
if (selectionIndex < 0
|| selectionIndex >= previousSearchPatterns.size()) {
return;
}
SearchData data = previousSearchPatterns
.get(selectionIndex);
setSearchFor(data.getSearchFor());
expressionCombo.setText(data.getPattern());
isCaseSensitive = data.isCaseSensitive();
caseSensitiveCheckbox.setSelection(data.isCaseSensitive());
regExCheckbox.setSelection(data.isRegExp());
if (data.getWorkingSets() != null) {
searchContainer.setSelectedWorkingSets(data.getWorkingSets());
} else {
searchContainer.setSelectedScope(data.getScope());
}
}
private void setSearchFor(int searchFor) {
for (int i = 0; i < searchForButtons.length; i++) {
searchForButtons[i].setSelection(searchFor == i);
}
}
private Control createSearchFor(Composite parent) {
Group group = new Group(parent, SWT.NONE);
group.setText(BeansSearchMessages.SearchPage_searchFor);
group.setLayout(new GridLayout(2, true));
SelectionAdapter selectionListener = new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
doPatternModified();
}
};
searchForButtons = new Button[searchForText.length];
for (int i = 0; i < searchForText.length; i++) {
Button button = new Button(group, SWT.RADIO);
button.setText(searchForText[i]);
button.setSelection(i == SEARCH_FOR_BEAN_CLASS);
button.setLayoutData(new GridData());
button.addSelectionListener(selectionListener);
searchForButtons[i] = button;
}
return group;
}
private void initSelections() {
SearchData initData = null;
// TODO handle seclection
// ISelection sel = searchContainer.getSelection();
// if (sel instanceof IStructuredSelection) {
// initData = tryStructuredSelection((IStructuredSelection) sel);
// } else if (sel instanceof ITextSelection) {
// initData = trySimpleTextSelection((ITextSelection) sel);
// }
if (initData == null) {
initData = getDefaultInitValues();
}
expressionCombo.setText(initData.getPattern());
caseSensitiveCheckbox.setSelection(initData.isCaseSensitive());
regExCheckbox.setSelection(initData.isRegExp());
setSearchFor(initData.getSearchFor());
}
// private SearchData tryStructuredSelection(IStructuredSelection selection) {
// if (selection == null || selection.size() > 1) {
// return null;
// }
// Object o = selection.getFirstElement();
// SearchData res = null;
// if (res == null && o instanceof IAdaptable) {
// IWorkbenchAdapter adapter = (IWorkbenchAdapter)
// ((IAdaptable)o).getAdapter(IWorkbenchAdapter.class);
// if (adapter != null) {
// return new SearchData(SEARCH_FOR_BEAN_CLASS, adapter.getLabel(o),
// isCaseSensitive, false);
// }
// }
// return res;
// }
// private SearchData trySimpleTextSelection(ITextSelection selection) {
// String selectedText= selection.getText();
// if (selectedText != null && selectedText.length() > 0) {
// int i = 0;
// TODO while (i < selectedText.length() && !StringUtils.isLineDelimiterChar(selectedText.charAt(i))) {
// while (i < selectedText.length()) {
// i++;
// }
// if (i > 0) {
// return new SearchData(SEARCH_FOR_BEAN_CLASS,
// selectedText.substring(0, i), isCaseSensitive, false);
// }
// }
// return null;
// }
private SearchData getDefaultInitValues() {
if (!previousSearchPatterns.isEmpty()) {
return previousSearchPatterns.get(0);
}
return new SearchData(SEARCH_FOR_BEAN_NAME, "", isCaseSensitive, false);
}
private static class SearchData {
private int searchFor;
private String pattern;
private boolean isCaseSensitive;
private boolean isRegExp;
private int scope;
private IWorkingSet[] workingSets;
public SearchData(int searchFor, String pattern,
boolean isCaseSensitive, boolean isRegExp) {
this(searchFor, pattern, isCaseSensitive, isRegExp,
ISearchPageContainer.WORKSPACE_SCOPE, null);
}
public SearchData(int searchFor, String pattern,
boolean isCaseSensitive, boolean isRegExp, int scope,
IWorkingSet[] workingSets) {
this.searchFor = searchFor;
this.pattern = pattern;
this.isCaseSensitive = isCaseSensitive;
this.isRegExp = isRegExp;
this.scope = scope;
this.workingSets = workingSets;
}
public String getPattern() {
return pattern;
}
public boolean isCaseSensitive() {
return isCaseSensitive;
}
public boolean isRegExp() {
return isRegExp;
}
public int getSearchFor() {
return searchFor;
}
public IWorkingSet[] getWorkingSets() {
return workingSets;
}
public int getScope() {
return scope;
}
public void store(IDialogSettings settings) {
settings.put("searchFor", searchFor);
settings.put("pattern", pattern);
settings.put("isCaseSensitive", isCaseSensitive);
settings.put("isRegExp", isRegExp);
settings.put("scope", scope);
if (workingSets != null) {
String[] wsIds = new String[workingSets.length];
for (int i = 0; i < workingSets.length; i++) {
wsIds[i] = workingSets[i].getId();
}
settings.put("workingSets", wsIds);
} else {
settings.put("workingSets", new String[0]);
}
}
public static SearchData create(IDialogSettings settings) {
String pattern = settings.get("pattern");
if (pattern.length() == 0) {
return null;
}
String[] wsIds = settings.getArray("workingSets");
IWorkingSet[] workingSets = null;
if (wsIds != null && wsIds.length > 0) {
IWorkingSetManager workingSetManager = PlatformUI
.getWorkbench().getWorkingSetManager();
workingSets = new IWorkingSet[wsIds.length];
for (int i = 0; workingSets != null && i < wsIds.length; i++) {
workingSets[i] = workingSetManager.getWorkingSet(wsIds[i]);
if (workingSets[i] == null) {
workingSets = null;
}
}
}
try {
int searchFor = settings.getInt("searchFor");
int scope = settings.getInt("scope");
boolean isCaseSensitive = settings
.getBoolean("isCaseSensitive");
boolean isRegExp = settings.getBoolean("isRegExp");
return new SearchData(searchFor, pattern, isCaseSensitive,
isRegExp, scope, workingSets);
} catch (NumberFormatException e) {
return null;
}
}
}
}