001: package net.refractions.udig.catalog;
002:
003: import java.io.File;
004: import java.io.IOException;
005: import java.io.Serializable;
006: import java.io.UnsupportedEncodingException;
007: import java.lang.reflect.Constructor;
008: import java.lang.reflect.Method;
009: import java.net.MalformedURLException;
010: import java.net.URL;
011: import java.net.URLDecoder;
012: import java.net.URLEncoder;
013: import java.util.HashMap;
014: import java.util.Iterator;
015: import java.util.LinkedList;
016: import java.util.List;
017: import java.util.Map;
018: import java.util.MissingResourceException;
019: import java.util.ResourceBundle;
020:
021: import net.refractions.udig.catalog.internal.CatalogImpl;
022: import net.refractions.udig.catalog.internal.Messages;
023: import net.refractions.udig.catalog.internal.ResolveManager;
024: import net.refractions.udig.catalog.internal.ServiceFactoryImpl;
025: import net.refractions.udig.core.internal.CorePlugin;
026: import net.refractions.udig.core.internal.ExtensionPointProcessor;
027: import net.refractions.udig.core.internal.ExtensionPointUtil;
028: import net.refractions.udig.ui.PreShutdownTask;
029: import net.refractions.udig.ui.ProgressManager;
030: import net.refractions.udig.ui.ShutdownTaskList;
031:
032: import org.eclipse.core.runtime.FileLocator;
033: import org.eclipse.core.runtime.IConfigurationElement;
034: import org.eclipse.core.runtime.IExtension;
035: import org.eclipse.core.runtime.IProgressMonitor;
036: import org.eclipse.core.runtime.IStatus;
037: import org.eclipse.core.runtime.Platform;
038: import org.eclipse.core.runtime.Plugin;
039: import org.eclipse.core.runtime.Status;
040: import org.eclipse.core.runtime.SubProgressMonitor;
041: import org.eclipse.core.runtime.preferences.IEclipsePreferences;
042: import org.eclipse.core.runtime.preferences.IPreferencesService;
043: import org.eclipse.core.runtime.preferences.InstanceScope;
044: import org.eclipse.jface.preference.IPreferenceStore;
045: import org.eclipse.ui.IWorkbench;
046: import org.eclipse.ui.preferences.ScopedPreferenceStore;
047: import org.osgi.framework.BundleContext;
048: import org.osgi.service.prefs.BackingStoreException;
049: import org.osgi.service.prefs.Preferences;
050:
051: /**
052: * The main plugin class to be used in the desktop.
053: */
054: public class CatalogPlugin extends Plugin {
055: public static final String ID = "net.refractions.udig.catalog"; //$NON-NLS-1$
056:
057: // The shared instance.
058: private static CatalogPlugin plugin;
059:
060: // Resource bundle.
061: private ResourceBundle resourceBundle;
062:
063: /**
064: * The constructor.
065: */
066: public CatalogPlugin() {
067: super ();
068: plugin = this ;
069: catalogs = new ICatalog[] { new CatalogImpl() };
070: serviceFactory = new ServiceFactoryImpl();
071: }
072:
073: /**
074: * This method is called upon plug-in activation
075: */
076: public void start(BundleContext context) throws Exception {
077: super .start(context);
078: getDefault();
079:
080: ShutdownTaskList.instance().addPreShutdownTask(
081: new PreShutdownTask() {
082:
083: public int getProgressMonitorSteps() {
084: try {
085: return getLocalCatalog().members(
086: ProgressManager.instance().get())
087: .size();
088: } catch (IOException e) {
089: return 0;
090: }
091: }
092:
093: public boolean handlePreShutdownException(
094: Throwable t, boolean forced) {
095: CatalogPlugin.log(
096: "Error storing local catalog", t); //$NON-NLS-1$
097: return true;
098: }
099:
100: public boolean preShutdown(
101: IProgressMonitor monitor,
102: IWorkbench workbench, boolean forced)
103: throws Exception {
104: ICatalog[] toDispose = getCatalogs();
105: monitor.beginTask(
106: Messages.CatalogPlugin_SavingCatalog,
107: 4 + (4 * toDispose.length));
108: SubProgressMonitor subProgressMonitor = new SubProgressMonitor(
109: monitor, 4);
110: storeToPreferences(subProgressMonitor);
111: subProgressMonitor.done();
112: for (ICatalog catalog : toDispose) {
113: subProgressMonitor = new SubProgressMonitor(
114: monitor, 4);
115: catalog.dispose(subProgressMonitor);
116: subProgressMonitor.done();
117: }
118: return true;
119: }
120:
121: });
122:
123: resolveManager = new ResolveManager();
124: preferenceStore = new ScopedPreferenceStore(
125: new InstanceScope(), getBundle().getSymbolicName());
126: }
127:
128: /**
129: * Cleanup after shared images.
130: *
131: * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
132: * @param context
133: * @throws Exception
134: */
135: public void stop(BundleContext context) throws Exception {
136:
137: super .stop(context);
138:
139: plugin = null;
140: resourceBundle = null;
141: }
142:
143: public void restoreFromPreferences() throws BackingStoreException,
144: MalformedURLException {
145: try {
146:
147: ((CatalogImpl) getLocalCatalog()).loadFromFile(
148: getLocalCatalogFile(), getServiceFactory());
149: loadCatalogs();
150: } catch (Throwable t) {
151: CatalogPlugin.log(null, new Exception(t));
152: }
153: }
154:
155: private void loadCatalogs() {
156: final List<ICatalog> cats = new LinkedList<ICatalog>();
157: ExtensionPointUtil
158: .process(
159: getDefault(),
160: "net.refractions.udig.catalog.ICatalog", new ExtensionPointProcessor() { //$NON-NLS-1$
161: public void process(IExtension extension,
162: IConfigurationElement element)
163: throws Exception {
164: cats
165: .add((ICatalog) element
166: .createExecutableExtension("class")); //$NON-NLS-1$
167: }
168: });
169:
170: ICatalog[] ctemp = new ICatalog[cats.size() + 1];
171: ctemp[0] = catalogs[0];
172: Iterator<ICatalog> ii = cats.iterator();
173: for (int i = 1; i < ctemp.length && ii.hasNext(); i++) {
174: ctemp[i] = ii.next();
175: }
176: catalogs = ctemp;
177: }
178:
179: public void storeToPreferences(IProgressMonitor monitor)
180: throws BackingStoreException, IOException {
181: // IPreferencesService prefs = Platform.getPreferencesService();
182: // IEclipsePreferences root = prefs.getRootNode();
183: // Preferences node = root.node(InstanceScope.SCOPE).node(ID + ".services"); //$NON-NLS-1$
184: // ServiceParameterPersister persister=new ServiceParameterPersister(getLocalCatalog(), getServiceFactory());
185: // persister.store(monitor, node, getLocalCatalog().members(monitor));
186:
187: ((CatalogImpl) getLocalCatalog()).saveToFile(
188: getLocalCatalogFile(), getServiceFactory(), monitor);
189: }
190:
191: private File getLocalCatalogFile() throws IOException {
192: File userLocation = new File(FileLocator.toFileURL(
193: Platform.getInstanceLocation().getURL()).getFile());
194: if (!userLocation.exists())
195: userLocation.mkdirs();
196: File catalogLocation = new File(userLocation, ".localCatalog");
197: return catalogLocation;
198: }
199:
200: private ICatalog[] catalogs;
201: private IServiceFactory serviceFactory;
202:
203: private static Boolean loading = Boolean.FALSE;
204:
205: /**
206: * Returns the shared instance.
207: */
208: public static CatalogPlugin getDefault() {
209: if (!plugin.loaded) {
210: synchronized (CatalogPlugin.class) {
211: if (!plugin.loaded)
212: try {
213: if (!plugin.loaded && !loading.booleanValue()) {
214: loading = Boolean.TRUE;
215: plugin.restoreFromPreferences();
216: loading = Boolean.FALSE;
217: }
218: } catch (BackingStoreException e) {
219: CatalogPlugin.log(null, e);
220: plugin.loaded = false;
221: loading = Boolean.FALSE;
222: // nothing ... not loaded
223: } catch (MalformedURLException e) {
224: CatalogPlugin.log(null, e);
225: plugin.loaded = false;
226: loading = Boolean.FALSE;
227: // nothing ... not loaded
228: }
229: plugin.loaded = true;
230: }
231: }
232: return plugin;
233: }
234:
235: private volatile boolean loaded = false;
236:
237: private IPreferenceStore preferenceStore;
238:
239: private volatile IResolveManager resolveManager;
240:
241: /**
242: * Add a catalog listener for changed to this catalog.
243: *
244: * @param listener
245: */
246: public static void addListener(IResolveChangeListener listener) {
247: ((CatalogImpl) getDefault().getLocalCatalog())
248: .addCatalogListener(listener);
249: }
250:
251: /**
252: * Remove a catalog listener that was interested in this catalog.
253: *
254: * @param listener
255: */
256: public static void removeListener(IResolveChangeListener listener) {
257: ((CatalogImpl) getDefault().getLocalCatalog())
258: .removeCatalogListener(listener);
259: }
260:
261: /**
262: * Returns the string from the plugin's resource bundle, or 'key' if not found.
263: */
264: public static String getResourceString(String key) {
265: ResourceBundle bundle = CatalogPlugin.getDefault()
266: .getResourceBundle();
267: try {
268: return (bundle != null) ? bundle.getString(key) : key;
269: } catch (MissingResourceException e) {
270: return key;
271: }
272: }
273:
274: /**
275: * Returns the plugin's resource bundle,
276: */
277: public ResourceBundle getResourceBundle() {
278: try {
279: if (resourceBundle == null)
280: resourceBundle = ResourceBundle
281: .getBundle("net.refractions.udig.catalog.CatalogPluginResources"); //$NON-NLS-1$
282: } catch (MissingResourceException x) {
283: resourceBundle = null;
284: }
285: return resourceBundle;
286: }
287:
288: /**
289: * @return Returns the catalogs found ... local one is slot[0]. TODO hook the extension point
290: * up.
291: */
292: public ICatalog[] getCatalogs() {
293: if (catalogs == null)
294: loadCatalogs();
295:
296: int i = 0;
297: if (catalogs != null)
298: i = catalogs.length;
299: ICatalog[] c = new ICatalog[i];
300: if (catalogs != null)
301: System.arraycopy(catalogs, 0, c, 0, c.length);
302: return c;
303: }
304:
305: /**
306: * @return the local catalog. Equivalent to getCatalogs()[0]
307: */
308: public ICatalog getLocalCatalog() {
309: return getCatalogs()[0];
310: }
311:
312: /**
313: * @return Returns the serviceFactory.
314: */
315: public IServiceFactory getServiceFactory() {
316: return serviceFactory;
317: }
318:
319: /**
320: * Attempts to turn data into a URL. If data is an array or Collection, it will return the first
321: * successful URL it can find. This is a utility method. Feel free to move it to another class.
322: * In the future, it might be nice to have it return a List of the URLs it found, not just the
323: * first one.
324: *
325: * @param data
326: * @return a URL if it can find one, or null otherwise
327: */
328: public static URL locateURL(Object data) {
329: if (data == null)
330: return null;
331:
332: return toURL(data);
333: }
334:
335: private static URL toURL(Object data) {
336: URL url = null;
337: if (data instanceof String) {
338: String string = (String) data;
339: int index = string.indexOf("\n"); //$NON-NLS-1$
340: if (index > -1)
341: string = string.substring(0, index);
342:
343: // try to turn into a string directly
344: try {
345: url = new URL(string);
346: } catch (MalformedURLException e) {
347: // try to go to a file first
348: try {
349: url = new File(string).toURL();
350: } catch (MalformedURLException e1) {
351: // do nothing
352: }
353: }
354:
355: } else if (data instanceof File) {
356: try {
357: url = ((File) data).toURL();
358: } catch (MalformedURLException e) {
359: // do nothing
360: }
361: } else if (data instanceof URL) {
362: url = (URL) data;
363: } else if (data instanceof IGeoResource) {
364: IGeoResource resource = (IGeoResource) data;
365: return resource.getIdentifier();
366: } else if (data instanceof IService) {
367: IService service = (IService) data;
368: return service.getIdentifier();
369: }
370:
371: return url;
372: }
373:
374: /**
375: * Logs the Throwable in the plugin's log.
376: * <p>
377: * This will be a user visable ERROR iff:
378: * <ul>
379: * <li>t is an Exception we are assuming it is human readable or if a message is provided
380: */
381: public static void log(String message2, Throwable t) {
382: String message = message2;
383: if (message == null)
384: message = ""; //$NON-NLS-1$
385: int status = t instanceof Exception || message != null ? IStatus.ERROR
386: : IStatus.WARNING;
387: getDefault().getLog().log(
388: new Status(status, ID, IStatus.OK, message, t));
389: }
390:
391: /**
392: * Messages that only engage if getDefault().isDebugging()
393: * <p>
394: * It is much prefered to do this:
395: *
396: * <pre><code>
397: * private static final String RENDERING = "net.refractions.udig.project/render/trace";
398: * if (ProjectUIPlugin.getDefault().isDebugging() && "true".equalsIgnoreCase(RENDERING)) {
399: * System.out.println("your message here");
400: * }
401: *
402: */
403: public static void trace(String message, Throwable e) {
404: if (getDefault().isDebugging()) {
405: if (message != null)
406: System.out.println(message);
407: if (e != null)
408: e.printStackTrace();
409: }
410: }
411:
412: /**
413: * Performs the Platform.getDebugOption true check on the provided trace
414: * <p>
415: * Note: ProjectUIPlugin.getDefault().isDebugging() must also be on.
416: * <ul>
417: * <li>Trace.RENDER - trace rendering progress
418: * </ul>
419: * </p>
420: *
421: * @param trace currently only RENDER is defined
422: */
423: public static boolean isDebugging(final String trace) {
424: return getDefault().isDebugging()
425: && "true".equalsIgnoreCase(Platform.getDebugOption(trace)); //$NON-NLS-1$
426: }
427:
428: /**
429: * Returns the preference store for this UI plug-in.
430: * This preference store is used to hold persistent settings for this plug-in in
431: * the context of a workbench. Some of these settings will be user controlled,
432: * whereas others may be internal setting that are never exposed to the user.
433: * <p>
434: * If an error occurs reading the preference store, an empty preference store is
435: * quietly created, initialized with defaults, and returned.
436: * </p>
437: * <p>
438: * <strong>NOTE:</strong> As of Eclipse 3.1 this method is
439: * no longer referring to the core runtime compatibility layer and so
440: * plug-ins relying on Plugin#initializeDefaultPreferences
441: * will have to access the compatibility layer themselves.
442: * </p>
443: *
444: * @return the preference store
445: */
446: public IPreferenceStore getPreferenceStore() {
447: return preferenceStore;
448: }
449:
450: public IResolveManager getResolveManager() {
451: return resolveManager;
452: }
453:
454: }
|