Java tutorial
/* * uDig - User Friendly Desktop Internet GIS client * http://udig.refractions.net * (C) 2012 Refractions Research Inc. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * (http://www.eclipse.org/legal/epl-v10.html), and the Refractions BSD * License v1.0 (http://udig.refractions.net/files/bsd3-v10.html). * */ package net.refractions.udig.catalog.ui.search; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import net.refractions.udig.catalog.CatalogPlugin; import net.refractions.udig.catalog.IResolve; import net.refractions.udig.catalog.ISearch; import net.refractions.udig.catalog.ui.CatalogUIPlugin; import net.refractions.udig.catalog.ui.ResolveContentProvider; import net.refractions.udig.catalog.ui.ResolveLabelProviderSimple; import net.refractions.udig.catalog.ui.ResolveTitlesDecorator; import net.refractions.udig.ui.PlatformGIS; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.viewers.DecoratingLabelProvider; import org.eclipse.jface.viewers.IBaseLabelProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Tree; import org.geotools.data.FeatureSource; import org.geotools.geometry.jts.ReferencedEnvelope; /** * Composite used to provide list of resource to be searched. * <p> * Intended for use in the AddLayer wizard, but packaged as a reusable composite. * <p> * When embedding this composite are interaction is provided using the ISelectionProvider * interface. Since the primary selection of interest is a GeoResource a helper method * has been provided for easy retrieval. * * @author Levi Putna */ public class ResourceSearchComposite extends Composite implements ISelectionProvider { private Text text; private List<ISearch> catalogs; private TreeViewer treeViewer; private static final long DELAY = 500; /** * Listen for key press and issue a search. * <p> * For most keys a small delay is used (to allow the user to type more); for return a * search is issued immediately. */ private KeyAdapter keyListener = new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { doSearch(DELAY); // search in a bit (incase they keep typing) } }; private SelectionListener selectionListener = new SelectionAdapter() { @Override public void widgetDefaultSelected(SelectionEvent e) { doSearch(0); // search now! } }; // Single job used for searching in the background SearchJob search = new SearchJob(); /** * Creates a searchable catalog resource viewer. * * @param parent the parent that this composite will be added to * @param style the stile used on this composite {@see SWT} */ public ResourceSearchComposite(Composite parent, int style) { super(parent, style); setLayout(new GridLayout(1, false)); Label hintLabel = new Label(this, SWT.NONE); hintLabel.setText("Start typing in search term"); text = new Text(this, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH); text.addKeyListener(keyListener); text.addSelectionListener(selectionListener); text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); // Using SWT.SINGLE until we have "join" functionality treeViewer = new TreeViewer(this, SWT.BORDER | SWT.MULTI); // SWT.CHECK ? Tree tree = treeViewer.getTree(); tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); treeViewer.setContentProvider(createContentProvider()); treeViewer.setLabelProvider(createLabelProvider()); catalogs = Arrays.asList(CatalogPlugin.getDefault().getCatalogs()); } public Control getControl() { return treeViewer != null ? treeViewer.getControl() : null; } public IStructuredSelection getSelection() { return (IStructuredSelection) treeViewer.getSelection(); } @Override public void setSelection(ISelection selection) { treeViewer.setSelection(selection); } /** * Adds a listener for selection changes in this selection provider. Has no effect if an * identical listener is already registered. * * @param listener the listener to add */ @Override public void addSelectionChangedListener(ISelectionChangedListener listener) { treeViewer.addSelectionChangedListener(listener); } /** * Removes the given selection change listener from this selection provider. Has no effect if an * identical listener is not registered. * * @param listener the listener to remove */ @Override public void removeSelectionChangedListener(ISelectionChangedListener listener) { treeViewer.removeSelectionChangedListener(listener); } public void setSearchText(final String search) { PlatformGIS.asyncInDisplayThread(new Runnable() { @Override public void run() { if (text != null && !text.isDisposed()) { text.setText(search != null ? search : ""); doSearch(0); } } }, true); } public String getSearchText() { if (text != null && !text.isDisposed()) { return text.getText(); } return null; } /** * LabelProvider; override to take charge of your labels and icons. * * @return LabelProvider for use with the viewer */ protected IBaseLabelProvider createLabelProvider() { ResolveLabelProviderSimple base = new ResolveLabelProviderSimple(); return new DecoratingLabelProvider(base, new ResolveTitlesDecorator(base)); } /** * Default implementation will work for lists, please overide if you are into the whole tree * thing. * * @return */ protected IStructuredContentProvider createContentProvider() { return new ResolveContentProvider(); } /** * Search the catalogs for the input text and create tree items for results. This is ran in a * background thread and will need to call {@link #doSearchCallback(List)} to update UI with * search results. * @param delay a time delay in milliseconds before the search should run */ protected void doSearch(long delay) { if (search.getState() == Job.RUNNING) { search.cancel(); } String searchText = text.getText(); if (!searchText.isEmpty()) { search.setSearchText(searchText); search.schedule(delay); } else { List<IResolve> empty = new ArrayList<IResolve>(); doSearchCallback(empty); } } /** * {@link #doSearch()} callback to update the UI. * * @param resolves */ protected void doSearchCallback(final List<IResolve> resolves) { reviewResults(resolves); Display.getDefault().asyncExec(new Runnable() { public void run() { treeViewer.setInput(resolves); } }); } protected void reviewResults(List<IResolve> resolves) { // do nothing } /** * Search catalogs in a separate job. * * @author Jody Garnett */ class SearchJob extends Job { private String searchText; private ReferencedEnvelope bounds = new ReferencedEnvelope(); public SearchJob() { super("Search Catalog"); //$NON-NLS-1$ } public String getSearchText() { return searchText; } public void setSearchText(String searchText) { this.searchText = searchText; } public ReferencedEnvelope getBounds() { return bounds; } public void setBounds(ReferencedEnvelope bounds) { this.bounds = bounds; } @Override protected IStatus run(IProgressMonitor monitor) { if (monitor == null) monitor = new NullProgressMonitor(); try { monitor.beginTask("Seaching for " + searchText, catalogs.size() * 100); final List<IResolve> resolves = new CopyOnWriteArrayList<IResolve>(); for (ISearch search : catalogs) { try { monitor.subTask("Search " + search.getTitle() + " for " + searchText); List<IResolve> searchResults = search.search(searchText, bounds, null); monitor.worked(10); IProgressMonitor searchMonitor = new SubProgressMonitor(monitor, 10, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK); for (IResolve member : searchResults) { if (member.canResolve(FeatureSource.class)) { resolves.add(member); } } } catch (IOException e) { e.printStackTrace(); } } doSearchCallback(resolves); return Status.OK_STATUS; } catch (Exception e) { String message = "Unable to complete search for '" + searchText + "':" + e; Status failure = new Status(IStatus.ERROR, CatalogUIPlugin.ID, message, e); return failure; } finally { monitor.done(); } } } }