/* * DBeaver - Universal Database Manager * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jkiss.dbeaver.ui.search.metadata; import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.viewers.*; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.events.*; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.*; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.core.CoreMessages; import org.jkiss.dbeaver.core.DBeaverCore; import org.jkiss.dbeaver.core.DBeaverUI; import org.jkiss.dbeaver.model.DBPDataSource; import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore; import org.jkiss.dbeaver.model.DBUtils; import org.jkiss.dbeaver.model.navigator.*; import org.jkiss.dbeaver.model.runtime.DBRProgressListener; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.runtime.DBRRunnableWithProgress; import org.jkiss.dbeaver.model.struct.*; import org.jkiss.dbeaver.ui.DBeaverIcons; import org.jkiss.dbeaver.ui.UIUtils; import org.jkiss.dbeaver.ui.navigator.NavigatorUtils; import org.jkiss.dbeaver.ui.navigator.database.DatabaseNavigatorTree; import org.jkiss.dbeaver.ui.navigator.database.load.TreeLoadNode; import org.jkiss.dbeaver.ui.search.AbstractSearchPage; import org.jkiss.utils.CommonUtils; import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.List; public class SearchMetadataPage extends AbstractSearchPage { private static final String PROP_MASK = "search.metadata.mask"; //$NON-NLS-1$ private static final String PROP_CASE_SENSITIVE = "search.metadata.case-sensitive"; //$NON-NLS-1$ private static final String PROP_MAX_RESULT = "search.metadata.max-results"; //$NON-NLS-1$ private static final String PROP_MATCH_INDEX = "search.metadata.match-index"; //$NON-NLS-1$ private static final String PROP_HISTORY = "search.metadata.history"; //$NON-NLS-1$ private static final String PROP_OBJECT_TYPE = "search.metadata.object-type"; //$NON-NLS-1$ private static final String PROP_SOURCES = "search.metadata.object-source"; //$NON-NLS-1$ private Table typesTable; private Combo searchText; private DatabaseNavigatorTree dataSourceTree; private String nameMask; private boolean caseSensitive; private int maxResults; private int matchTypeIndex; private Set<DBSObjectType> checkedTypes = new HashSet<>(); private Set<String> searchHistory = new LinkedHashSet<>(); private Set<String> savedTypeNames = new HashSet<>(); private List<DBNNode> sourceNodes = new ArrayList<>(); public SearchMetadataPage() { super("Database objects search"); } @Override public void createControl(Composite parent) { super.createControl(parent); initializeDialogUnits(parent); Composite searchGroup = new Composite(parent, SWT.NONE); searchGroup.setLayoutData(new GridData(GridData.FILL_BOTH)); searchGroup.setLayout(new GridLayout(3, false)); setControl(searchGroup); UIUtils.createControlLabel(searchGroup, CoreMessages.dialog_search_objects_label_object_name); searchText = new Combo(searchGroup, SWT.DROP_DOWN); searchText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); if (nameMask != null) { searchText.setText(nameMask); } for (String history : searchHistory) { searchText.add(history); } searchText.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { nameMask = searchText.getText(); updateEnablement(); } }); Composite optionsGroup = new SashForm(searchGroup, SWT.NONE); GridLayout layout = new GridLayout(2, true); layout.marginHeight = 0; layout.marginWidth = 0; optionsGroup.setLayout(layout); GridData gd = new GridData(GridData.FILL_BOTH); gd.horizontalSpan = 3; optionsGroup.setLayoutData(gd); { final DBeaverCore core = DBeaverCore.getInstance(); Group sourceGroup = UIUtils.createControlGroup(optionsGroup, CoreMessages.dialog_search_objects_group_objects_source, 1, GridData.FILL_BOTH, 0); gd = new GridData(GridData.FILL_BOTH); //gd.heightHint = 300; sourceGroup.setLayoutData(gd); final DBNProject projectNode = core.getNavigatorModel().getRoot().getProject(core.getProjectRegistry().getActiveProject()); DBNNode rootNode = projectNode == null ? core.getNavigatorModel().getRoot() : projectNode.getDatabases(); dataSourceTree = new DatabaseNavigatorTree(sourceGroup, rootNode, SWT.SINGLE); gd = new GridData(GridData.FILL_BOTH); gd.heightHint = 300; dataSourceTree.setLayoutData(gd); dataSourceTree.getViewer().addFilter(new ViewerFilter() { @Override public boolean select(Viewer viewer, Object parentElement, Object element) { if (element instanceof TreeLoadNode) { return true; } if (element instanceof DBNNode) { if (element instanceof DBNDatabaseFolder) { DBNDatabaseFolder folder = (DBNDatabaseFolder)element; Class<? extends DBSObject> folderItemsClass = folder.getChildrenClass(); return folderItemsClass != null && DBSObjectContainer.class.isAssignableFrom(folderItemsClass); } if (element instanceof DBNLocalFolder || element instanceof DBNProjectDatabases || element instanceof DBNDataSource || (element instanceof DBSWrapper && ((DBSWrapper)element).getObject() instanceof DBSObjectContainer)) { return true; } } return false; } }); dataSourceTree.getViewer().addSelectionChangedListener( new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { fillObjectTypes(); updateEnablement(); IStructuredSelection structSel = (IStructuredSelection) event.getSelection(); Object object = structSel.isEmpty() ? null : structSel.getFirstElement(); if (object instanceof DBNNode) { for (DBNNode node = (DBNNode)object; node != null; node = node.getParentNode()) { if (node instanceof DBNDataSource) { DBNDataSource dsNode = (DBNDataSource) node; dsNode.initializeNode(null, new DBRProgressListener() { @Override public void onTaskFinished(IStatus status) { if (status.isOK()) { DBeaverUI.asyncExec(new Runnable() { @Override public void run() { if (!dataSourceTree.isDisposed()) { fillObjectTypes(); } } }); } } }); break; } } } } } ); } { Group settingsGroup = UIUtils.createControlGroup(optionsGroup, "Settings", 2, GridData.FILL_BOTH, 0); gd = new GridData(GridData.FILL_BOTH); gd.heightHint = 300; settingsGroup.setLayoutData(gd); { //new Label(searchGroup, SWT.NONE); UIUtils.createControlLabel(settingsGroup, CoreMessages.dialog_search_objects_label_name_match); final Combo matchCombo = new Combo(settingsGroup, SWT.DROP_DOWN | SWT.READ_ONLY); matchCombo.add(CoreMessages.dialog_search_objects_combo_starts_with, SearchMetadataConstants.MATCH_INDEX_STARTS_WITH); matchCombo.add(CoreMessages.dialog_search_objects_combo_contains, SearchMetadataConstants.MATCH_INDEX_CONTAINS); matchCombo.add(CoreMessages.dialog_search_objects_combo_like, SearchMetadataConstants.MATCH_INDEX_LIKE); matchCombo.select(0); matchCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); if (matchTypeIndex >= 0) { matchCombo.select(matchTypeIndex); } matchCombo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { matchTypeIndex = matchCombo.getSelectionIndex(); } }); matchCombo.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING)); if (maxResults <= 0) { maxResults = 100; } final Spinner maxResultsSpinner = UIUtils.createLabelSpinner(settingsGroup, CoreMessages.dialog_search_objects_spinner_max_results, maxResults, 1, 10000); maxResultsSpinner.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); maxResultsSpinner.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { maxResults = maxResultsSpinner.getSelection(); } }); maxResultsSpinner.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING)); final Button caseCheckbox = UIUtils.createLabelCheckbox(settingsGroup, CoreMessages.dialog_search_objects_case_sensitive, caseSensitive); caseCheckbox.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { caseSensitive = caseCheckbox.getSelection(); } }); caseCheckbox.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); } Label otLabel = UIUtils.createControlLabel(settingsGroup, CoreMessages.dialog_search_objects_group_object_types); otLabel.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); typesTable = new Table(settingsGroup, SWT.CHECK | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION); typesTable.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { //checkedTypes.clear(); for (TableItem item : typesTable.getItems()) { DBSObjectType objectType = (DBSObjectType) item.getData(); if (item.getChecked()) { checkedTypes.add(objectType); } else { checkedTypes.remove(objectType); } } updateEnablement(); } }); typesTable.addMouseListener(new MouseAdapter() { @Override public void mouseDoubleClick(MouseEvent e) { TableItem tableItem = typesTable.getSelection()[0]; tableItem.setChecked(!tableItem.getChecked()); } }); typesTable.setLayoutData(new GridData(GridData.FILL_BOTH)); UIUtils.createTableColumn(typesTable, SWT.LEFT, CoreMessages.dialog_search_objects_column_type); UIUtils.createTableColumn(typesTable, SWT.LEFT, CoreMessages.dialog_search_objects_column_description); } try { DBeaverUI.runInProgressDialog(new DBRRunnableWithProgress() { @Override public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException { monitor.beginTask("Load database nodes", 1); try { monitor.subTask("Load tree state"); sourceNodes = loadTreeState(monitor, DBeaverCore.getGlobalPreferenceStore(), PROP_SOURCES); } finally { monitor.done(); } } }); } catch (InvocationTargetException e) { UIUtils.showErrorDialog(getShell(), "Data sources load", "Error loading settings", e.getTargetException()); } if (!sourceNodes.isEmpty()) { dataSourceTree.getViewer().setSelection( new StructuredSelection(sourceNodes)); dataSourceTree.getViewer().reveal(NavigatorUtils.getDataSourceNode(sourceNodes.get(0))); } else { updateEnablement(); } } private DBNNode getSelectedNode() { IStructuredSelection selection = (IStructuredSelection) dataSourceTree.getViewer().getSelection(); if (!selection.isEmpty()) { return (DBNNode) selection.getFirstElement(); } return null; } private DBPDataSource getSelectedDataSource() { DBNNode node = getSelectedNode(); if (node instanceof DBSWrapper) { DBSObject object = ((DBSWrapper)node).getObject(); if (object != null && object.getDataSource() != null) { return object.getDataSource(); } } return null; } private DBSStructureAssistant getSelectedStructureAssistant() { return DBUtils.getAdapter(DBSStructureAssistant.class, getSelectedDataSource()); } private void fillObjectTypes() { DBSStructureAssistant assistant = getSelectedStructureAssistant(); typesTable.removeAll(); if (assistant == null) { // No structure assistant - no object types } else { for (DBSObjectType objectType : assistant.getSupportedObjectTypes()) { TableItem item = new TableItem(typesTable, SWT.NONE); item.setText(objectType.getTypeName()); if (objectType.getImage() != null) { item.setImage(0, DBeaverIcons.getImage(objectType.getImage())); } if (!CommonUtils.isEmpty(objectType.getDescription())) { item.setText(1, objectType.getDescription()); } item.setData(objectType); if (checkedTypes.contains(objectType)) { item.setChecked(true); } else if (savedTypeNames.contains(objectType.getTypeClass().getName())) { item.setChecked(true); checkedTypes.add(objectType); savedTypeNames.remove(objectType.getTypeClass().getName()); } } } for (TableColumn column : typesTable.getColumns()) { column.pack(); } updateEnablement(); } @Override public SearchMetadataQuery createQuery() throws DBException { DBNNode selectedNode = getSelectedNode(); DBSObjectContainer parentObject = null; if (selectedNode instanceof DBSWrapper && ((DBSWrapper)selectedNode).getObject() instanceof DBSObjectContainer) { parentObject = (DBSObjectContainer) ((DBSWrapper)selectedNode).getObject(); } DBPDataSource dataSource = getSelectedDataSource(); DBSStructureAssistant assistant = getSelectedStructureAssistant(); if (dataSource == null || assistant == null) { throw new IllegalStateException("No active datasource"); } java.util.List<DBSObjectType> objectTypes = new ArrayList<>(); for (TableItem item : typesTable.getItems()) { if (item.getChecked()) { objectTypes.add((DBSObjectType) item.getData()); } } String objectNameMask = nameMask; // Save search query if (!searchHistory.contains(objectNameMask)) { searchHistory.add(objectNameMask); searchText.add(objectNameMask); } if (matchTypeIndex == SearchMetadataConstants.MATCH_INDEX_STARTS_WITH) { if (!objectNameMask.endsWith("%")) { //$NON-NLS-1$ objectNameMask = objectNameMask + "%"; //$NON-NLS-1$ } } else if (matchTypeIndex == SearchMetadataConstants.MATCH_INDEX_CONTAINS) { if (!objectNameMask.startsWith("%")) { //$NON-NLS-1$ objectNameMask = "%" + objectNameMask; //$NON-NLS-1$ } if (!objectNameMask.endsWith("%")) { //$NON-NLS-1$ objectNameMask = objectNameMask + "%"; //$NON-NLS-1$ } } SearchMetadataParams params = new SearchMetadataParams(); params.setParentObject(parentObject); params.setObjectTypes(objectTypes); params.setObjectNameMask(objectNameMask); params.setCaseSensitive(caseSensitive); params.setMaxResults(maxResults); return SearchMetadataQuery.createQuery(dataSource, params); } @Override public void loadState(DBPPreferenceStore store) { nameMask = store.getString(PROP_MASK); caseSensitive = store.getBoolean(PROP_CASE_SENSITIVE); maxResults = store.getInt(PROP_MAX_RESULT); matchTypeIndex = store.getInt(PROP_MATCH_INDEX); for (int i = 0; ;i++) { String history = store.getString(PROP_HISTORY + "." + i); //$NON-NLS-1$ if (CommonUtils.isEmpty(history)) { break; } searchHistory.add(history); } { String type = store.getString(PROP_OBJECT_TYPE); if (!CommonUtils.isEmpty(type)) { StringTokenizer st = new StringTokenizer(type, "|"); //$NON-NLS-1$ while (st.hasMoreTokens()) { savedTypeNames.add(st.nextToken()); } } } } @Override public void saveState(DBPPreferenceStore store) { store.setValue(PROP_MASK, nameMask); store.setValue(PROP_CASE_SENSITIVE, caseSensitive); store.setValue(PROP_MAX_RESULT, maxResults); store.setValue(PROP_MATCH_INDEX, matchTypeIndex); saveTreeState(store, PROP_SOURCES, dataSourceTree); { // Search history int historyIndex = 0; for (String history : searchHistory) { if (historyIndex >= 20) { break; } store.setValue(PROP_HISTORY + "." + historyIndex, history); //$NON-NLS-1$ historyIndex++; } } { // Object types StringBuilder typesString = new StringBuilder(); for (DBSObjectType type : checkedTypes) { if (typesString.length() > 0) { typesString.append("|"); //$NON-NLS-1$ } typesString.append(type.getTypeClass().getName()); } store.setValue(PROP_OBJECT_TYPE, typesString.toString()); } } protected void updateEnablement() { boolean enabled = false; if (getSelectedDataSource() != null) { enabled = !checkedTypes.isEmpty(); } if (CommonUtils.isEmpty(nameMask)) { enabled = false; } container.setPerformActionEnabled(enabled); } protected static void saveTreeState(DBPPreferenceStore store, String propName, DatabaseNavigatorTree tree) { // Object sources StringBuilder sourcesString = new StringBuilder(); Object[] nodes = ((IStructuredSelection)tree.getViewer().getSelection()).toArray(); for (Object obj : nodes) { DBNNode node = (DBNNode) obj; if (sourcesString.length() > 0) { sourcesString.append("|"); //$NON-NLS-1$ } sourcesString.append(node.getNodeItemPath()); } store.setValue(propName, sourcesString.toString()); } }