001: package net.refractions.udig.catalog.ui.search;
002:
003: import java.io.IOException;
004: import java.lang.reflect.Method;
005: import java.net.URL;
006: import java.text.MessageFormat;
007: import java.util.Arrays;
008: import java.util.List;
009:
010: import net.refractions.udig.catalog.CatalogPlugin;
011: import net.refractions.udig.catalog.ICatalog;
012: import net.refractions.udig.catalog.ICatalogInfo;
013: import net.refractions.udig.catalog.IGeoResource;
014: import net.refractions.udig.catalog.IGeoResourceInfo;
015: import net.refractions.udig.catalog.IResolve;
016: import net.refractions.udig.catalog.IResolveChangeEvent;
017: import net.refractions.udig.catalog.IResolveChangeListener;
018: import net.refractions.udig.catalog.IService;
019: import net.refractions.udig.catalog.IServiceInfo;
020: import net.refractions.udig.catalog.IResolveChangeEvent.Type;
021: import net.refractions.udig.catalog.internal.ui.ImageConstants;
022: import net.refractions.udig.catalog.internal.ui.Images;
023: import net.refractions.udig.catalog.ui.CatalogTreeViewer;
024: import net.refractions.udig.catalog.ui.CatalogUIPlugin;
025: import net.refractions.udig.catalog.ui.ResolveContentProvider;
026: import net.refractions.udig.catalog.ui.ResolveLabelProviderSimple;
027: import net.refractions.udig.catalog.ui.ResolveTitlesDecorator;
028: import net.refractions.udig.catalog.ui.StatusLineMessageBoardAdapter;
029: import net.refractions.udig.catalog.ui.internal.Messages;
030: import net.refractions.udig.internal.ui.UiPlugin;
031: import net.refractions.udig.ui.SearchPart;
032: import net.refractions.udig.ui.UDIGDragDropUtilities;
033:
034: import org.eclipse.core.runtime.IProgressMonitor;
035: import org.eclipse.jface.action.Action;
036: import org.eclipse.jface.action.GroupMarker;
037: import org.eclipse.jface.action.IAction;
038: import org.eclipse.jface.action.IMenuListener;
039: import org.eclipse.jface.action.IMenuManager;
040: import org.eclipse.jface.action.MenuManager;
041: import org.eclipse.jface.action.Separator;
042: import org.eclipse.jface.viewers.DecoratingLabelProvider;
043: import org.eclipse.jface.viewers.IBaseLabelProvider;
044: import org.eclipse.jface.viewers.IStructuredContentProvider;
045: import org.eclipse.jface.viewers.IStructuredSelection;
046: import org.eclipse.jface.viewers.StructuredViewer;
047: import org.eclipse.swt.SWT;
048: import org.eclipse.swt.custom.SashForm;
049: import org.eclipse.swt.events.SelectionEvent;
050: import org.eclipse.swt.events.SelectionListener;
051: import org.eclipse.swt.graphics.Color;
052: import org.eclipse.swt.layout.FormAttachment;
053: import org.eclipse.swt.layout.FormData;
054: import org.eclipse.swt.layout.FormLayout;
055: import org.eclipse.swt.widgets.Button;
056: import org.eclipse.swt.widgets.Composite;
057: import org.eclipse.swt.widgets.Display;
058: import org.eclipse.swt.widgets.Label;
059: import org.eclipse.swt.widgets.Menu;
060: import org.eclipse.swt.widgets.Text;
061: import org.eclipse.ui.IEditorPart;
062: import org.eclipse.ui.IWorkbenchActionConstants;
063: import org.eclipse.ui.IWorkbenchWindow;
064: import org.eclipse.ui.actions.ActionFactory;
065: import org.eclipse.ui.part.PageBook;
066:
067: import com.vividsolutions.jts.geom.Envelope;
068:
069: /**
070: * Search view is a distinct, simple implementation focused on Catalog searches.
071: * <p>
072: * We are unable to make use of the usual eclipse search facilities (ie
073: * org.eclipse.search.searchPages) as this would cause our application to be dependent on
074: * org.eclipse.core.resources - aka IResource. This would represent a signigicant jump in download
075: * size etc...
076: * </p>
077: * <p>
078: * Of Note:
079: * <ul>
080: * <li>At this time Search only works against the Local Catalog
081: * <li>As more Catalogs are implemented they will be made available in a manner similar to existing
082: * Filter specification.
083: * </ul>
084: * </p>
085: */
086: public class SearchView extends SearchPart {
087: /** ID for this view */
088: public static final String VIEW_ID = "net.refractions.udig.catalog.ui.search.searchView"; //$NON-NLS-1$
089:
090: public Text text;
091: private Button bbox;
092: private Label label;
093:
094: /** Cache for info used by details */
095: private Info cache;
096:
097: private Text summary;
098:
099: private PageBook book;
100: private Action refreshAction;
101:
102: /**
103: * @param dialogSettings
104: */
105: public SearchView() {
106: super (CatalogUIPlugin.getDefault().getDialogSettings());
107: }
108:
109: static class Query {
110: String text; // match against everything we can
111: Envelope bbox; // latlong bbox
112: List<ICatalog> scope; // list of catalogs to search
113: }
114:
115: @Override
116: protected void setOrientation(Orientation orientation) {
117: super .setOrientation(orientation);
118:
119: if (splitter.getOrientation() == SWT.HORIZONTAL) {
120: label.setText(Messages.SearchView_prompt);
121: bbox.setText(Messages.SearchView_bbox);
122: } else {
123: label.setText(""); //$NON-NLS-1$
124: bbox.setText(""); //$NON-NLS-1$
125: }
126: parent.layout();
127: }
128:
129: @Override
130: public void createPartControl(Composite aParent) {
131: label = new Label(aParent, SWT.NONE);
132: label.setText(Messages.SearchView_prompt);
133:
134: text = new Text(aParent, SWT.BORDER);
135: text.setText(Messages.SearchView_default);
136: text.setEditable(true);
137: text.addSelectionListener(new SelectionListener() {
138: public void widgetDefaultSelected(SelectionEvent e) {
139: search(createQuery()); // seach according to filter
140: }
141:
142: public void widgetSelected(SelectionEvent e) {
143: quick(text.getText());
144: }
145: });
146:
147: // Create bbox button
148: bbox = new Button(aParent, SWT.CHECK);
149: bbox.setText(Messages.SearchView_bbox);
150: bbox.setToolTipText(Messages.SearchView_bboxTooltip);
151: bbox.setSelection(true);
152:
153: super .createPartControl(aParent);
154:
155: // Layout using Form Layout (+ indicates FormAttachment)
156: // +
157: // +label+text+bbox+
158: // +
159: // contents
160: // +
161: FormLayout layout = new FormLayout();
162: layout.marginHeight = 0;
163: layout.marginWidth = 0;
164: layout.spacing = 0;
165: aParent.setLayout(layout);
166:
167: FormData dLabel = new FormData(); // bind to left & text
168: dLabel.left = new FormAttachment(0);
169: dLabel.top = new FormAttachment(text, 5, SWT.CENTER);
170: label.setLayoutData(dLabel);
171:
172: FormData dText = new FormData(); // bind to top, label, bbox
173: dText.top = new FormAttachment(1);
174: dText.left = new FormAttachment(label, 5);
175: dText.right = new FormAttachment(bbox, -5);
176: text.setLayoutData(dText);
177:
178: FormData dBbox = new FormData(); // text & right
179: dBbox.right = new FormAttachment(100);
180: dBbox.top = new FormAttachment(text, 0, SWT.CENTER);
181: bbox.setLayoutData(dBbox);
182:
183: FormData dsashForm = new FormData(100, 100); // text & bottom
184: dsashForm.right = new FormAttachment(100); // bind to right of form
185: dsashForm.left = new FormAttachment(0); // bind to left of form
186: dsashForm.top = new FormAttachment(text, 2); // attach with 5 pixel offset
187: dsashForm.bottom = new FormAttachment(100); // bind to bottom of form
188:
189: splitter.setWeights(new int[] { 60, 40 });
190: splitter.setLayoutData(dsashForm);
191: createContextMenu();
192: }
193:
194: @Override
195: protected void initDragAndDrop() {
196:
197: UDIGDragDropUtilities.addDragSupport(this .viewer.getControl(),
198: viewer);
199: }
200:
201: @Override
202: protected Composite createDetails(SashForm parent) {
203: book = new PageBook(parent, SWT.NONE);
204: summary = new Text(book, SWT.V_SCROLL | SWT.READ_ONLY
205: | SWT.WRAP | SWT.BORDER);
206: summary.setText(""); //$NON-NLS-1$
207: Color white = new Color(parent.getDisplay(), 255, 255, 255);
208: summary.setBackground(white);
209: white.dispose();
210:
211: CatalogPlugin.addListener(sync);
212: book.showPage(summary);
213: return book;
214: }
215:
216: IResolveChangeListener sync = new IResolveChangeListener() {
217: public void changed(IResolveChangeEvent event) {
218: // changes made to the search results reported in the event
219: final IResolve res = event.getResolve();
220: Type type = event.getType();
221:
222: // we only care about changes to it
223: if (type != IResolveChangeEvent.Type.POST_CHANGE) {
224: return;
225: }
226: if (res == null) {
227: return;
228: }
229: if (Display.getCurrent() == null) {
230: Display.getDefault().asyncExec(new Runnable() {
231: public void run() {
232: refresh(res);
233: }
234: });
235: } else {
236: refresh(res);
237: }
238: }
239:
240: /**
241: * Attempts to resolve the URL from the IResolve parameter If cached data shares same URL
242: * with changed data found, and the Info can be resolved,then we update the cached data and
243: * redraw the infoDisplay text field.
244: *
245: * @param res
246: */
247: private void refresh(IResolve res) {
248: URL resolveURL = res.getIdentifier();
249: URL cachedURL = null;
250: if (cache != null) {
251: cachedURL = cache.getId();
252: }
253:
254: if (resolveURL != null && resolveURL.equals(cachedURL)) {
255: if (res.canResolve(ICatalogInfo.class)) {
256: try {
257: showInfo(new Info(cache.getId(), res.resolve(
258: ICatalogInfo.class, searchMonitor)));
259: } catch (IOException e) {
260: CatalogUIPlugin.log(null, e);
261: }
262: }
263:
264: if (res.canResolve(IServiceInfo.class)) {
265: try {
266: showInfo(new Info(cache.getId(), res.resolve(
267: IServiceInfo.class, searchMonitor)));
268: } catch (IOException e) {
269: CatalogUIPlugin.log(null, e);
270: }
271: }
272:
273: if (res.canResolve(IGeoResourceInfo.class)) {
274: try {
275: showInfo(new Info(cache.getId(), res.resolve(
276: IGeoResourceInfo.class, searchMonitor)));
277: } catch (IOException e) {
278: CatalogUIPlugin.log(null, e);
279: }
280: }
281: }
282:
283: }// end refresh
284: };
285:
286: private void createContextMenu() {
287: final MenuManager contextMenu = new MenuManager();
288:
289: refreshAction = new Action() {
290: public void run() {
291: IStructuredSelection sel = (IStructuredSelection) viewer
292: .getSelection();
293: sel.getFirstElement();
294: viewer.refresh();
295: }
296: };
297:
298: Messages.initAction(refreshAction, "action_refresh"); //$NON-NLS-1$
299: contextMenu.setRemoveAllWhenShown(true);
300: contextMenu.addMenuListener(new IMenuListener() {
301:
302: public void menuAboutToShow(IMenuManager mgr) {
303: contextMenu.add(new GroupMarker(
304: IWorkbenchActionConstants.MB_ADDITIONS));
305: contextMenu.add(new Separator());
306:
307: refreshAction.setImageDescriptor(Images
308: .getDescriptor(ImageConstants.REFRESH_CO));
309:
310: // contextMenu.add(refreshAction);
311: IWorkbenchWindow window = getSite()
312: .getWorkbenchWindow();
313: IAction action = ActionFactory.IMPORT.create(window);
314: contextMenu.add(action);
315: contextMenu.add(new Separator());
316: contextMenu.add(UiPlugin.getDefault()
317: .getOperationMenuFactory().getContextMenu(
318: viewer.getSelection()));
319: }
320:
321: });
322:
323: // Create menu.
324: Menu menu = contextMenu.createContextMenu(viewer.getControl());
325: viewer.getControl().setMenu(menu);
326:
327: // Register menu for extension.
328: getSite().registerContextMenu(contextMenu, viewer);
329:
330: }
331:
332: @Override
333: protected StructuredViewer createViewer(Composite parent) {
334: CatalogTreeViewer catalogTreeViewer = new CatalogTreeViewer(
335: parent, true);
336: catalogTreeViewer
337: .setMessageBoard(new StatusLineMessageBoardAdapter(
338: getViewSite().getActionBars()
339: .getStatusLineManager()));
340:
341: return catalogTreeViewer;
342: }
343:
344: /**
345: * Construct a query based on the state of the user interface controls, and possibly workbecnh.
346: *
347: * @return A catalog query
348: */
349: Query createQuery() {
350: Query filter = new Query();
351: filter.text = text.getText();
352: filter.bbox = new Envelope();
353: if (bbox.getSelection()) {
354: try {
355: IEditorPart editor = getSite().getPage()
356: .getActiveEditor();
357: if (editor != null) {
358: Object obj = editor.getEditorInput();
359: Class<? extends Object> mapType = obj.getClass();
360: Method get = mapType.getMethod(
361: "getExtent", new Class[0]); //$NON-NLS-1$
362: Object value = get.invoke(obj, new Object[0]);
363: filter.bbox = (Envelope) value;
364: double minx = filter.bbox.getMinX();
365: double miny = filter.bbox.getMinY();
366: double maxx = filter.bbox.getMaxX();
367: double maxy = filter.bbox.getMaxY();
368: if (minx < -180)
369: minx = -180;
370: if (maxx > 180)
371: maxx = 180;
372: if (miny < -90)
373: miny = -90;
374: if (maxy > 90)
375: maxy = 90;
376: filter.bbox = new Envelope(minx, maxx, miny, maxy);
377: }
378: } catch (Throwable t) {
379: // ha ha
380: CatalogUIPlugin.log("ha ha", t); //$NON-NLS-1$
381: }
382: }
383: return filter;
384: }
385:
386: @Override
387: protected IBaseLabelProvider createLabelProvider() {
388: ResolveLabelProviderSimple base = new ResolveLabelProviderSimple();
389: return new DecoratingLabelProvider(base,
390: new ResolveTitlesDecorator(base));
391: }
392:
393: protected void showInfo(Info info) {
394: book.showPage(summary);
395: if (cache != null) {
396: if (info.getId().equals(cache.getId())) {
397: return;
398: }
399: }
400: cache = info;
401: summary.setText(""); //$NON-NLS-1$
402:
403: URL url = cache.getId();
404: String serverName = ""; //$NON-NLS-1$
405: if (url != null) {
406: serverName = url.getHost();
407: // summary.append("Server: " + serverName + "\n\n");
408: }
409:
410: if (cache.title != null && !(cache.title.equals(""))) { //$NON-NLS-1$
411: summary.append(MessageFormat.format(
412: Messages.SearchView_title,
413: new Object[] { cache.title })
414: + "\n\n"); //$NON-NLS-1$
415: }
416:
417: if (cache.name != null && !(cache.name.equals(""))) { //$NON-NLS-1$
418: summary.append(MessageFormat.format(
419: Messages.SearchView_name, new Object[] {
420: cache.name, serverName })
421: + ")\n\n"); //$NON-NLS-1$
422: } else {
423: if (serverName != null && !serverName.equals("")) { //$NON-NLS-1$
424: summary.append(MessageFormat.format(
425: Messages.SearchView_server,
426: new Object[] { serverName })
427: + "\n\n"); //$NON-NLS-1$
428: }
429: }
430:
431: if (cache.keys != null && cache.keys.length > 0) {
432: summary.append(Messages.SearchView_keywords);
433: for (int i = 0; i < cache.keys.length; i++) {
434: String keyword = (cache.keys[i]).trim();
435:
436: if (keyword != null && !(keyword).equalsIgnoreCase("")) { //$NON-NLS-1$
437: if (i == cache.keys.length - 1) {
438: summary.append(keyword + "\n\n"); //$NON-NLS-1$
439: } else {
440: summary.append(keyword + ", "); //$NON-NLS-1$
441: }
442: }
443: }
444: }
445:
446: if (cache.description != null
447: && !(cache.description.equals(""))) { //$NON-NLS-1$
448: summary.append(MessageFormat.format(
449: Messages.SearchView_description,
450: new Object[] { cache.description }));
451: }
452: }
453:
454: @Override
455: protected void showDetail(Object selection) {
456: if (!(selection instanceof IResolve)) {
457: return;
458: }
459: IResolve record = (IResolve) selection;
460: if (record instanceof ICatalog) {
461: ICatalog catalog = (ICatalog) record;
462: try {
463: ICatalogInfo info = catalog.getInfo(searchMonitor);
464: showInfo(new Info(catalog.getIdentifier(), info));
465: } catch (IOException e) {
466: CatalogUIPlugin.log("No information for catalog", e); //$NON-NLS-1$
467: }
468: } else if (record instanceof IService) {
469: IService service = (IService) record;
470: IServiceInfo info;
471: try {
472: info = service.getInfo(searchMonitor);
473: showInfo(new Info(service.getIdentifier(), info));
474: } catch (IOException e) {
475: CatalogUIPlugin.log("No information for service", e); //$NON-NLS-1$
476: }
477:
478: } else if (record instanceof IGeoResource) {
479: IGeoResource layer = (IGeoResource) record;
480: try {
481: IGeoResourceInfo info = layer.getInfo(searchMonitor);
482: showInfo(new Info(layer.getIdentifier(), info));
483: } catch (IOException e) {
484: CatalogUIPlugin.log("No information for layer", e); //$NON-NLS-1$
485: }
486: }
487: }
488:
489: protected IStructuredContentProvider createContentProvider() {
490: return new ResolveContentProvider();
491: }
492:
493: /** Status message ... */
494: public void setStatus(String message) {
495: // change book to status and show message
496: }
497:
498: /**
499: * Search the catalog for text and update view contents
500: *
501: * @param pattern
502: */
503: @Override
504: protected void searchImplementation(Object filter,
505: IProgressMonitor monitor, ResultSet results) {
506: Query query = (Query) filter;
507: if (query == null)
508: return;
509:
510: if ((query.bbox == null || query.bbox.isNull())
511: && (query.text == null || query.text.trim().length() == 0)) {
512: return; // no actual query
513: }
514: if (query.bbox == null) {
515: query.bbox = new Envelope();
516: }
517: if (query.scope == null) {
518: query.scope = Arrays.asList(CatalogPlugin.getDefault()
519: .getCatalogs());
520: }
521: monitor.beginTask(Messages.SearchView_searching, query.scope
522: .size() * 3);
523: int work = 0;
524: for (ICatalog catalog : query.scope) {
525: String name = null;
526: try {
527: name = catalog.getInfo(null).getTitle();
528: if (name == null && catalog.getIdentifier() != null)
529: name = "url: " + catalog.getIdentifier(); //$NON-NLS-1$
530: if (name == null)
531: name = catalog.getClass().getSimpleName();
532: } catch (Exception unknown) {
533: name = catalog.getClass().getSimpleName();
534: }
535: List<IResolve> records = null;
536: try {
537: monitor.subTask(MessageFormat.format(
538: Messages.SearchView_searching_for,
539: new Object[] { query.text, name }));
540: monitor.worked(++work);
541: records = catalog.search(query.text, query.bbox,
542: monitor);
543: if (records != null && !records.isEmpty()) {
544: results.addAll(records);
545: }
546: } catch (Throwable t) {
547: CatalogUIPlugin
548: .log("Search for " + name + " failed", t); //$NON-NLS-1$ //$NON-NLS-2$
549: }
550: }
551: }
552: }
553:
554: class Info {
555: public String title;
556: public String name;
557: public String description;
558: public String[] keys;
559: private URL id;
560:
561: public URL getId() {
562: return id;
563: }
564:
565: public Info(URL id, IServiceInfo info) {
566: title = info.getTitle();
567: name = null;
568: description = info.getDescription();
569: keys = info.getKeywords();
570: this .id = id;
571: }
572:
573: public Info(URL id, ICatalogInfo info) {
574: title = info.getTitle();
575: name = null;
576: description = info.getDescription();
577: keys = info.getKeywords();
578: this .id = id;
579: }
580:
581: public Info(URL id, IGeoResourceInfo info) {
582: title = info.getTitle();
583: name = info.getName();
584: description = info.getDescription();
585: keys = info.getKeywords();
586: this.id = id;
587: }
588: }
|