001: package net.refractions.udig.catalog.ui;
002:
003: import java.io.IOException;
004: import java.net.URL;
005: import java.util.List;
006:
007: import net.refractions.udig.catalog.CatalogPlugin;
008: import net.refractions.udig.catalog.ICatalog;
009: import net.refractions.udig.catalog.ICatalogInfo;
010: import net.refractions.udig.catalog.IGeoResource;
011: import net.refractions.udig.catalog.IGeoResourceInfo;
012: import net.refractions.udig.catalog.IResolve;
013: import net.refractions.udig.catalog.IResolveChangeEvent;
014: import net.refractions.udig.catalog.IResolveChangeListener;
015: import net.refractions.udig.catalog.IResolveDelta;
016: import net.refractions.udig.catalog.IService;
017: import net.refractions.udig.catalog.IServiceInfo;
018: import net.refractions.udig.catalog.IResolveChangeEvent.Type;
019: import net.refractions.udig.catalog.IResolveDelta.Kind;
020: import net.refractions.udig.catalog.internal.ui.Images;
021: import net.refractions.udig.catalog.ui.internal.Messages;
022: import net.refractions.udig.catalog.util.HandleListener;
023: import net.refractions.udig.core.internal.CorePlugin;
024:
025: import org.eclipse.core.runtime.IProgressMonitor;
026: import org.eclipse.core.runtime.IStatus;
027: import org.eclipse.core.runtime.Platform;
028: import org.eclipse.core.runtime.Status;
029: import org.eclipse.jface.preference.IPreferenceStore;
030: import org.eclipse.jface.resource.ImageDescriptor;
031: import org.eclipse.jface.resource.ImageRegistry;
032: import org.eclipse.swt.graphics.Image;
033: import org.eclipse.ui.PlatformUI;
034: import org.eclipse.ui.plugin.AbstractUIPlugin;
035: import org.geotools.data.DataStore;
036: import org.geotools.data.FeatureSource;
037: import org.osgi.framework.BundleContext;
038: import org.picocontainer.Disposable;
039: import org.picocontainer.MutablePicoContainer;
040: import org.picocontainer.Startable;
041:
042: /**
043: * Lifecycle & Resource management for RegistryUI.
044: * <p>
045: * The CatalogUIPlugin provides access for shared images descriptors.
046: * </p>
047: * Example use of a shared image descriptor:
048: *
049: * <pre><code>
050: * ImageRegistry images = CatalogUIPlugin.getDefault().getImageRegistry();
051: * ImageDescriptor image = images.getDescriptor(ISharedImages.IMG_DATASTORE_OBJ);
052: * </code></pre>
053: *
054: * </p>
055: * <h3>Implementation Note</h3>
056: * </p>
057: * The CatalogUIPlugin delegates the following resource management tasks:
058: * <ul>
059: * <li>ResourceBundle: Policy</li>
060: * <li>ImageDescriptors: Images</li>
061: * </ul>
062: * These resources are intended for use by classes within this plugin.
063: * </p>
064: *
065: * @author Jody Garnett, Refractions Research, Inc
066: */
067: public class CatalogUIPlugin extends AbstractUIPlugin {
068:
069: /**
070: * The id of the plug-in
071: */
072: public static final String ID = "net.refractions.udig.catalog.ui"; //$NON-NLS-1$
073:
074: // public static final String DECORATOR_ID = "net.refractions.udig.registry.ui.decorator";
075: // //$NON-NLS-1$
076: /** Preference store for the last directory open by the file selection dialog */
077: public static final String PREF_OPEN_DIALOG_DIRECTORY = "udig.preferences.openDialog.lastDirectory"; //$NON-NLS-1$
078:
079: /** Icons path (value "icons/") */
080: public final static String ICONS_PATH = "icons/";//$NON-NLS-1$
081:
082: private static final String LABELS_PREFERENCE_STORE = "CATALOG_LABELS_PREFERENCE_STORAGE"; //$NON-NLS-1$
083:
084: private boolean loaded = false;
085:
086: // The shared instance.
087: private static CatalogUIPlugin plugin;
088:
089: /** Managed Images instance */
090: private Images images = new Images();
091: private volatile static MutablePicoContainer pluginContainer;
092:
093: /**
094: * The constructor.
095: */
096: public CatalogUIPlugin() {
097: super ();
098: plugin = this ;
099: }
100:
101: /**
102: * Set up shared images.
103: *
104: * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
105: * @param context
106: * @throws Exception
107: */
108: public void start(BundleContext context) throws Exception {
109: super .start(context);
110: final URL iconsUrl = context.getBundle().getEntry(ICONS_PATH);
111:
112: images.initializeImages(iconsUrl, getImageRegistry());
113: registerChangeListener();
114: }
115:
116: /**
117: * Registers a listener with the local catalog for reset events so that there is a mechanism to
118: * reset the label cache for an IResolve.
119: */
120: private void registerChangeListener() {
121: CatalogPlugin.addListener(new IResolveChangeListener() {
122:
123: public void changed(IResolveChangeEvent event) {
124: if (PlatformUI.getWorkbench().isClosing())
125: return;
126: // IPreferenceStore p = getPreferenceStore();
127: if (event.getType() == Type.POST_CHANGE
128: && event.getDelta() != null) {
129: // TODO enable when resolve information is available
130: // updateCache(event.getDelta(), p);
131: }
132:
133: }
134:
135: private void updateCache(IResolveDelta delta,
136: IPreferenceStore p) {
137:
138: if (delta.getKind() == Kind.REPLACED
139: || delta.getKind() == Kind.REMOVED
140: || delta.getKind() == Kind.CHANGED) {
141: if (delta.getResolve() != null
142: && delta.getResolve().getIdentifier() != null) {
143: String string = LABELS_PREFERENCE_STORE
144: + delta.getResolve().getIdentifier()
145: .toString();
146: p.setToDefault(string);
147: }
148: }
149: List<IResolveDelta> children = delta.getChildren();
150: for (IResolveDelta delta2 : children) {
151: updateCache(delta2, p);
152: }
153: }
154:
155: });
156: }
157:
158: /**
159: * Cleanup after shared images.
160: *
161: * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
162: * @param context
163: * @throws Exception
164: */
165: public void stop(BundleContext context) throws Exception {
166: try {
167: // storeLabels();
168: images.cleanUp();
169: } catch (Exception e) {
170: log("", e); //$NON-NLS-1$
171: } finally {
172: super .stop(context);
173: }
174: }
175:
176: /**
177: * Returns the shared instance.
178: *
179: * @return CatalogUIPlugin singleton
180: */
181: public static CatalogUIPlugin getDefault() {
182: if (plugin == null || !plugin.loaded) {
183: synchronized (CatalogUIPlugin.class) {
184:
185: if (plugin == null) {
186: plugin = new CatalogUIPlugin();
187: }
188:
189: }
190: }
191: return plugin;
192: }
193:
194: @Override
195: public ImageRegistry getImageRegistry() {
196: return super .getImageRegistry();
197: }
198:
199: /**
200: * Images instance for use with ImageConstants.
201: *
202: * @return Images for use with ImageConstants.
203: */
204: public ISharedImages getImages() {
205: return images;
206: }
207:
208: /**
209: * Logs the Throwable in the plugin's log.
210: * <p>
211: * This will be a user visable ERROR iff:
212: * <ul>
213: * <li>t is an Exception we are assuming it is human readable or if a message is provided
214: * </ul>
215: * </p>
216: *
217: * @param message
218: * @param t
219: */
220: public static void log(String message2, Throwable t) {
221: String message = message2;
222: if (message == null)
223: message = ""; //$NON-NLS-1$
224: int status = t instanceof Exception || message != null ? IStatus.ERROR
225: : IStatus.WARNING;
226: getDefault().getLog().log(
227: new Status(status, ID, IStatus.OK, message, t));
228: }
229:
230: /**
231: * Messages that only engage if getDefault().isDebugging()
232: * <p>
233: * It is much prefered to do this:
234: *
235: * <pre><code>
236: * private static final String RENDERING = "net.refractions.udig.project/render/trace";
237: * if (ProjectUIPlugin.getDefault().isDebugging() && "true".equalsIgnoreCase(RENDERING)) {
238: * System.out.println("your message here");
239: * }
240: * </code></pre>
241: *
242: * </p>
243: *
244: * @param message
245: * @param e
246: */
247: public static void trace(String message, Throwable e) {
248: if (getDefault().isDebugging()) {
249: if (message != null)
250: System.out.println(message);
251: if (e != null)
252: e.printStackTrace();
253: }
254: }
255:
256: public static void trace(String message) {
257: if (getDefault().isDebugging()) {
258: if (message != null)
259: System.out.println(message);
260: }
261: }
262:
263: /**
264: * Performs the Platform.getDebugOption true check on the provided trace
265: * <p>
266: * Note: ProjectUIPlugin.getDefault().isDebugging() must also be on.
267: * <ul>
268: * <li>Trace.RENDER - trace rendering progress
269: * </ul>
270: * </p>
271: *
272: * @param trace currently only RENDER is defined
273: * @return true if -debug is on for this plugin
274: */
275: public static boolean isDebugging(final String trace) {
276: return getDefault().isDebugging()
277: && "true".equalsIgnoreCase(Platform.getDebugOption(trace)); //$NON-NLS-1$
278: }
279:
280: /**
281: * Gets the container for catalog ui.
282: * <p>
283: * This is used by the IResourceLabel decorator to pass titles, and images between threads.
284: * </p>
285: *
286: * @return Container assocaited with the catalog ui
287: */
288: public static MutablePicoContainer getContainer() {
289: if (pluginContainer == null) {
290: synchronized (CatalogUIPlugin.class) {
291: // check so see that you were not queued for double creation
292: if (pluginContainer == null) {
293: // This line does it ... careful to only call it once!
294: pluginContainer = CorePlugin.getBlackBoard()
295: .makeChildContainer();
296: }
297: }
298: }
299: return pluginContainer;
300: }
301:
302: /**
303: * Gets a container associated with this handle.
304: * <p>
305: * As with any container, the contents should not be assumned, etc...
306: * </p>
307: *
308: * @param handle
309: * @return Container associated with display of resolve
310: */
311: public static MutablePicoContainer getContainer(IResolve handle) {
312: Object instance = getContainer().getComponentInstance(handle);
313: if (instance != null) {
314: HandleLifecycle holder = (HandleLifecycle) instance;
315: return holder.getContainer();
316: }
317: synchronized (handle) {
318: instance = getContainer().getComponentInstance(handle);
319: if (instance != null) {
320: HandleLifecycle holder = (HandleLifecycle) instance;
321: return holder.getContainer();
322: }
323: HandleLifecycle holder = new HandleLifecycle(handle);
324: getContainer().registerComponentInstance(handle, holder);
325: return holder.getContainer();
326: }
327: }
328:
329: /**
330: * Quick and dirty label generation based on ID.
331: * <p>
332: * This method does not block and can be safely used to by a LabelProvider. This method does not
333: * make use of any title information available via an info object (because that would require
334: * blocking and be unsafe).
335: * </p>
336: *
337: * @see title
338: * @return Label for provided resource
339: */
340: public static String label(IResolve resource) {
341: final URL identifier = resource.getIdentifier();
342: if (identifier == null)
343: return null;
344: try {
345: if (hasCachedTitle(resource)) {
346: return getDefault().getPreferenceStore()
347: .getString(
348: LABELS_PREFERENCE_STORE
349: + identifier.toString());
350: }
351: if (resource instanceof IService) {
352: return Identifier.labelServer(identifier);
353: } else if (resource instanceof IGeoResource) {
354: return Identifier.labelResource(identifier);
355: } else {
356: return Identifier.labelServer(identifier)
357: + "/" + Identifier.labelResource(identifier); //$NON-NLS-1$
358: }
359: } catch (Throwable t) {
360: return identifier.toExternalForm(); // warning?!
361: }
362: }
363:
364: /**
365: * Quick and dirty image generated based on ID, this image is shared and should not be disposed.
366: * <p>
367: * This method does not block and can be safely used to by a LabelProvider. This method does not
368: * make use of any information available via an info object (because that would require blocking
369: * and be unsafe).
370: * </p>
371: *
372: * @see glyph
373: * @param resource
374: * @return Image representing provided resource
375: */
376: public static Image image(IResolve resolve) {
377: ISharedImages images = CatalogUIPlugin.getDefault().getImages();
378:
379: if (resolve == null) {
380: return null;
381: }
382: if (resolve instanceof IGeoResource) {
383: IGeoResource resource = (IGeoResource) resolve;
384: boolean isFeature = resource
385: .canResolve(FeatureSource.class);
386: URL url = resource.getIdentifier();
387: if (Identifier.isGraphic(url)) {
388: return images.get(ISharedImages.GRAPHIC_OBJ);
389: }
390: if (Identifier.isWMS(url)) {
391: return images.get(ISharedImages.GRID_OBJ);
392: }
393: if (Identifier.isGraphic(url)) {
394: return images.get(ISharedImages.GRAPHIC_OBJ);
395: }
396: if (Identifier.isMemory(url)) {
397: return images.get(ISharedImages.MEMORY_OBJ);
398: }
399: Image image = isFeature ? images
400: .get(ISharedImages.FEATURE_OBJ) : images
401: .get(ISharedImages.GRID_OBJ);
402: return image;
403: } else if (resolve instanceof IService) {
404: IService service = (IService) resolve;
405: boolean isFeature = service.canResolve(DataStore.class);
406: URL url = service.getIdentifier();
407:
408: if (Identifier.isFile(url)) {
409: Image image = isFeature ? images
410: .get(ISharedImages.FEATURE_FILE_OBJ) : images
411: .get(ISharedImages.GRID_FILE_OBJ);
412: return image;
413: }
414: if (Identifier.isGraphic(url)) {
415: return images.get(ISharedImages.MAP_GRAPHICS_OBJ);
416: }
417: if (Identifier.isWMS(url)) {
418: return images.get(ISharedImages.WMS_OBJ);
419: }
420: if (Identifier.isWFS(url)) {
421: return images.get(ISharedImages.WFS_OBJ);
422: }
423: if (Identifier.isJDBC(url)) {
424: return images.get(ISharedImages.DATABASE_OBJ);
425: }
426: if (Identifier.isGraphic(url)) {
427: return images.get(ISharedImages.MAP_GRAPHICS_OBJ);
428: }
429: if (isFeature) {
430: return images.get(ISharedImages.DATASTORE_OBJ);
431: }
432: return images.get(ISharedImages.SERVER_OBJ);
433: } else if (resolve instanceof ICatalog) {
434: return images.get(ISharedImages.CATALOG_OBJ);
435: }
436: return images.get(ISharedImages.RESOURCE_OBJ);
437: }
438:
439: public static ImageDescriptor icon(IResolve resolve)
440: throws IOException {
441:
442: return icon(resolve, null);
443: }
444:
445: /**
446: * Create icon for provided resource, this will block!
447: *
448: * @param resource
449: * @return ImageDescriptor for resource.
450: * @throws IOException
451: */
452: public static ImageDescriptor icon(IResolve resolve,
453: IProgressMonitor monitor) throws IOException {
454:
455: if (resolve.canResolve(ImageDescriptor.class)) {
456: ImageDescriptor descriptor = resolve.resolve(
457: ImageDescriptor.class, monitor);
458: if (descriptor != null)
459: return descriptor;
460: }
461: if (resolve instanceof IGeoResource) {
462: ImageDescriptor icon = icon((IGeoResource) resolve, monitor);
463: return icon != null ? icon : Images
464: .getDescriptor(ISharedImages.FEATURE_OBJ);
465: }
466:
467: if (resolve instanceof IService) {
468: ImageDescriptor icon = icon((IService) resolve, monitor);
469: return icon != null ? icon : Images
470: .getDescriptor(ISharedImages.SERVER_OBJ);
471: }
472:
473: if (resolve instanceof ICatalog)
474: return CatalogUIPlugin.getDefault().getImages()
475: .getImageDescriptor(ISharedImages.CATALOG_OBJ);
476:
477: return CatalogUIPlugin.getDefault().getImages()
478: .getImageDescriptor(ISharedImages.RESOURCE_OBJ);
479: }
480:
481: /**
482: * Retrieve title, this is based on associated metadata (aka LayerPointInfo object).
483: * <p>
484: * This method is *not* suitable for use with a LabelProvider, only a LabelDecorator that works
485: * in its own thread.
486: * </p>
487: *
488: * @return title, or null if not found (consider use of label( resource )
489: */
490: public static String title(IResolve resource,
491: IProgressMonitor monitor) throws IOException {
492: if (resource instanceof IGeoResource) {
493: IGeoResourceInfo info;
494: try {
495: info = resource
496: .resolve(IGeoResourceInfo.class, monitor);
497: } catch (Throwable t) {
498: log("Error obtaining info", t); //$NON-NLS-1$
499: return null;
500: }
501: if (info == null)
502: return null;
503: String title = null;
504:
505: try {
506: title = info.getTitle();
507: } catch (Throwable t) {
508: log("Error obtaining title", t); //$NON-NLS-1$
509: }
510: if (title != null && title.trim().length() != 0)
511: return title;
512:
513: try {
514: title = info.getName();
515: } catch (Throwable t) {
516: log("Error obtaining name", t); //$NON-NLS-1$
517: }
518: if (title != null && title.trim().length() != 0)
519: return title;
520:
521: return null; // could not locate title
522: }
523: if (resource instanceof IService) {
524: IServiceInfo info = resource.resolve(IServiceInfo.class,
525: monitor);
526: if (info == null)
527: return null;
528: String title;
529:
530: title = info.getTitle();
531: if (title != null && title.trim().length() != 0)
532: return title;
533:
534: return null; // could not locate title
535: }
536: if (resource instanceof ICatalog) {
537: ICatalogInfo info = resource.resolve(ICatalogInfo.class,
538: monitor);
539: if (info == null)
540: return null;
541: String title;
542:
543: title = info.getTitle();
544: if (title != null && title.length() != 0)
545: return title;
546:
547: return null; // could not locate title
548: }
549: return null; // not title available
550: }
551:
552: /**
553: * Retrive title, this is based on associated metadata (aka LayerPointInfo object).
554: * <p>
555: * This method is *not* suitable for use with a LabelProvider, only a LabelDecorator that works
556: * in its own thread.
557: * </p>
558: *
559: * @return title, or null if not found (consider use of label( resource )
560: */
561: public static String title(IResolve resource) throws IOException {
562: return title(resource, null);
563: }
564:
565: /**
566: * Create icon for provided resource, this will block!
567: *
568: * @param resource
569: * @return ImageDescriptor for resource.
570: * @throws IOException
571: */
572: private static ImageDescriptor icon(IGeoResource resource,
573: IProgressMonitor monitor) throws IOException {
574:
575: IGeoResourceInfo info;
576: try {
577: info = resource.resolve(IGeoResourceInfo.class, monitor);
578: } catch (Throwable t) {
579: log("Error obtaining info", t); //$NON-NLS-1$
580: return null;
581: }
582:
583: if (info == null)
584: return null;
585:
586: try {
587: return info.getIcon();
588: } catch (Throwable t) {
589: log("Error obtaining icon for IGeoResource", t); //$NON-NLS-1$
590: return null;
591: }
592: }
593:
594: /**
595: * Create icon for provided service, this will block!
596: *
597: * @param resource
598: * @return ImageDescriptor for resource.
599: * @throws IOException
600: */
601: private static ImageDescriptor icon(IService service,
602: IProgressMonitor monitor) throws IOException {
603:
604: IServiceInfo info = service.getInfo(monitor);
605: if (info == null)
606: return null;
607:
608: return info.getIcon();
609: }
610:
611: /**
612: * Returns true if the title to the resolve was cached during a previous run. In this case {@link #label(IResolve)} will have returned
613: * the cached title.
614: *
615: * @param resolve the resolve to check for a title.
616: * @return true if the title to the resolve was cached during a previous run.
617: */
618: public static boolean hasCachedTitle(IResolve resolve) {
619: if (resolve.getIdentifier() == null)
620: return false;
621: IPreferenceStore p = getDefault().getPreferenceStore();
622: String title = p.getString(LABELS_PREFERENCE_STORE
623: + resolve.getIdentifier().toString());
624: return title != null && title.trim().length() > 0;
625: }
626:
627: public static void storeLabel(IResolve element, String text)
628: throws IOException {
629: if (element == null || element.getIdentifier() == null)
630: return;
631: String id = LABELS_PREFERENCE_STORE
632: + element.getIdentifier().toString();
633: IPreferenceStore preferenceStore2 = getDefault()
634: .getPreferenceStore();
635: if (text == null)
636: preferenceStore2.setToDefault(id);
637: else
638: preferenceStore2.setValue(id, text);
639: }
640: }
641:
642: class HandleLifecycle implements Startable, Disposable {
643: HandleListener listener;
644: volatile MutablePicoContainer container;
645:
646: HandleLifecycle(IResolve resolveHandle) {
647: container = null; // until used by getContainer();
648: listener = new HandleListener(resolveHandle) {
649: public void stop(IResolve handle) {
650: HandleLifecycle.this .stop();
651: }
652:
653: public void dispose() {
654: CatalogPlugin.removeListener(this );
655: HandleLifecycle.this .dispose();
656: }
657:
658: public void start(IResolve handle) {
659: HandleLifecycle.this .start();
660: }
661:
662: public void refresh(IResolve handle) {
663: // nop
664: }
665:
666: /** Replace if easy */
667: public void replace(IResolve handle, IResolve newHandle) {
668: if (newHandle != null) {
669: setHandle(newHandle);
670: }
671: reset(handle, null);
672: }
673:
674: /** Clear out if hard */
675: public void reset(IResolve handle, IResolveChangeEvent event) {
676: HandleLifecycle.this .stop();
677: CatalogPlugin.removeListener(this );
678: HandleLifecycle.this .dispose();
679: }
680: };
681: CatalogPlugin.addListener(listener);
682: }
683:
684: /**
685: * A container for collaboration, or null if handle is out of scope.
686: *
687: * @return The container associated with handle
688: */
689: public MutablePicoContainer getContainer() {
690: IResolve resolve = listener.getHandle();
691: if (resolve == null)
692: return null;
693:
694: if (container == null) {
695: synchronized (resolve) {
696: // check so see that you were not queued for double creation
697: if (container == null) {
698: // This line does it ... careful to only call it once!
699: container = makeChildContainer();
700: }
701: }
702: }
703: return container;
704: }
705:
706: /**
707: * Create container, must only be called once
708: *
709: * @return created container
710: */
711: protected MutablePicoContainer makeChildContainer() {
712: if (container != null) {
713: throw new IllegalStateException(
714: Messages.CatalogUIPlugin_childContainerException);
715: }
716: return CatalogUIPlugin.getContainer().makeChildContainer();
717: }
718:
719: /** We have just been created, lets listen to the catalog */
720: public void start() {
721: CatalogPlugin.addListener(listener);
722: }
723:
724: /** We are no longer in use */
725: public void stop() {
726: if (listener != null) {
727: CatalogPlugin.removeListener(listener);
728: listener.dispose();
729: listener = null;
730: }
731: if (container != null) {
732: container.dispose();
733: container = null;
734: }
735: }
736:
737: /** Stop listening, fee references, and turn off the lights */
738: public void dispose() {
739: if (listener != null) {
740: CatalogPlugin.removeListener(listener);
741: listener.dispose();
742: listener = null;
743: }
744: if (container != null) {
745: container.dispose();
746: container = null;
747: }
748: }
749: }
|