001 /**
002 * <copyright>
003 *
004 * Copyright (c) 2002-2007 IBM Corporation and others.
005 * All rights reserved. This program and the accompanying materials
006 * are made available under the terms of the Eclipse Public License v1.0
007 * which accompanies this distribution, and is available at
008 * http://www.eclipse.org/legal/epl-v10.html
009 *
010 * Contributors:
011 * IBM - Initial API and implementation
012 *
013 * </copyright>
014 *
015 * $Id: EMFPlugin.java,v 1.21 2008/04/08 15:00:50 emerks Exp $
016 */
017 package org.eclipse.emf.common;
018
019
020 import java.io.IOException;
021 import java.io.InputStream;
022 import java.net.MalformedURLException;
023 import java.net.URL;
024 import java.text.MessageFormat;
025 import java.util.MissingResourceException;
026 import java.util.PropertyResourceBundle;
027 import java.util.ResourceBundle;
028 import java.util.jar.Manifest;
029
030 import org.osgi.framework.Bundle;
031
032 import org.eclipse.core.runtime.ILog;
033 import org.eclipse.core.runtime.IStatus;
034 import org.eclipse.core.runtime.Platform;
035 import org.eclipse.core.runtime.Plugin;
036 import org.eclipse.core.runtime.Status;
037
038 import org.eclipse.emf.common.util.DelegatingResourceLocator;
039 import org.eclipse.emf.common.util.Logger;
040 import org.eclipse.emf.common.util.ResourceLocator;
041 import org.eclipse.emf.common.util.URI;
042 import org.eclipse.emf.common.util.WrappedException;
043
044
045 /**
046 * EMF must run
047 * within an Eclipse workbench,
048 * within a headless Eclipse workspace,
049 * or just stand-alone as part of some other application.
050 * To support this, all resource access (e.g., NL strings, images, and so on) is directed to the resource locator methods,
051 * which can redirect the service as appropriate to the runtime.
052 * During Eclipse invocation, the implementation delegates to a plugin implementation.
053 * During stand-alone invocation, no plugin initialization takes place,
054 * so the implementation delegates to a resource JAR on the CLASSPATH.
055 * The resource jar will typically <b>not</b> be on the CLASSPATH during Eclipse invocation.
056 * It will contain things like the icons and the .properties,
057 * which are available in a different way during Eclipse invocation.
058 * @see DelegatingResourceLocator
059 * @see ResourceLocator
060 * @see Logger
061 */
062 public abstract class EMFPlugin extends DelegatingResourceLocator implements ResourceLocator, Logger
063 {
064 public static final boolean IS_ECLIPSE_RUNNING;
065 static
066 {
067 boolean result = false;
068 try
069 {
070 result = Platform.isRunning();
071 }
072 catch (Throwable exception)
073 {
074 // Assume that we aren't running.
075 }
076 IS_ECLIPSE_RUNNING = result;
077 }
078
079 public static final boolean IS_RESOURCES_BUNDLE_AVAILABLE;
080 static
081 {
082 boolean result = false;
083 if (IS_ECLIPSE_RUNNING)
084 {
085 try
086 {
087 Bundle resourcesBundle = Platform.getBundle("org.eclipse.core.resources");
088 result = resourcesBundle != null && (resourcesBundle.getState() & (Bundle.ACTIVE | Bundle.STARTING | Bundle.RESOLVED)) != 0;
089 }
090 catch (Throwable exception)
091 {
092 // Assume that it's not available.
093 }
094 }
095 IS_RESOURCES_BUNDLE_AVAILABLE = result;
096 }
097
098 protected ResourceLocator [] delegateResourceLocators;
099
100 public EMFPlugin(ResourceLocator [] delegateResourceLocators)
101 {
102 this.delegateResourceLocators = delegateResourceLocators;
103 }
104
105 /**
106 * Returns an Eclipse plugin implementation of a resource locator.
107 * @return an Eclipse plugin implementation of a resource locator.
108 */
109 public abstract ResourceLocator getPluginResourceLocator();
110
111 @Override
112 final protected ResourceLocator getPrimaryResourceLocator()
113 {
114 return getPluginResourceLocator();
115 }
116
117 @Override
118 protected ResourceLocator[] getDelegateResourceLocators()
119 {
120 return delegateResourceLocators;
121 }
122
123 /**
124 * Returns an Eclipse plugin implementation of a logger.
125 * @return an Eclipse plugin implementation of a logger.
126 */
127 public Logger getPluginLogger()
128 {
129 return (Logger)getPluginResourceLocator();
130 }
131
132 public String getSymbolicName()
133 {
134 ResourceLocator resourceLocator = getPluginResourceLocator();
135 if (resourceLocator instanceof InternalEclipsePlugin)
136 {
137 return ((InternalEclipsePlugin)resourceLocator).getSymbolicName();
138 }
139 else
140 {
141 String result = getClass().getName();
142 return result.substring(0, result.lastIndexOf('.'));
143 }
144 }
145
146 /*
147 * Javadoc copied from interface.
148 */
149 public void log(Object logEntry)
150 {
151 Logger logger = getPluginLogger();
152 if (logger == null)
153 {
154 if (logEntry instanceof Throwable)
155 {
156 ((Throwable)logEntry).printStackTrace(System.err);
157 }
158 else
159 {
160 System.err.println(logEntry);
161 }
162 }
163 else
164 {
165 logger.log(logEntry);
166 }
167 }
168
169 /**
170 * The actual implementation of an Eclipse <b>Plugin</b>.
171 */
172 public static abstract class EclipsePlugin extends Plugin implements ResourceLocator, Logger, InternalEclipsePlugin
173 {
174 /**
175 * The EMF plug-in APIs are all delegated to this helper, so that code can be shared by plug-in
176 * implementations with a different platform base class (e.g. AbstractUIPlugin).
177 */
178 protected InternalHelper helper;
179
180 /**
181 * Creates an instance.
182 */
183 public EclipsePlugin()
184 {
185 super();
186 helper = new InternalHelper(this);
187 }
188
189 /**
190 * Creates an instance.
191 * @param descriptor the description of the plugin.
192 * @deprecated
193 */
194 @Deprecated
195 public EclipsePlugin(org.eclipse.core.runtime.IPluginDescriptor descriptor)
196 {
197 super(descriptor);
198 helper = new InternalHelper(this);
199 }
200
201 /**
202 * Return the plugin ID.
203 */
204 public String getSymbolicName()
205 {
206 return helper.getSymbolicName();
207 }
208
209 /*
210 * Javadoc copied from interface.
211 */
212 public URL getBaseURL()
213 {
214 return helper.getBaseURL();
215 }
216
217 /*
218 * Javadoc copied from interface.
219 */
220 public Object getImage(String key)
221 {
222 try
223 {
224 return doGetImage(key);
225 }
226 catch (MalformedURLException exception)
227 {
228 throw new WrappedException(exception);
229 }
230 catch (IOException exception)
231 {
232 throw
233 new MissingResourceException
234 (CommonPlugin.INSTANCE.getString("_UI_StringResourceNotFound_exception", new Object [] { key }),
235 getClass().getName(),
236 key);
237 }
238 }
239
240 /**
241 * Does the work of fetching the image associated with the key.
242 * It ensures that the image exists.
243 * @param key the key of the image to fetch.
244 * @exception IOException if an image doesn't exist.
245 * @return the description of the image associated with the key.
246 */
247 protected Object doGetImage(String key) throws IOException
248 {
249 return helper.getImage(key);
250 }
251
252 public String getString(String key)
253 {
254 return helper.getString(key, true);
255 }
256
257 public String getString(String key, boolean translate)
258 {
259 return helper.getString(key, translate);
260 }
261
262 public String getString(String key, Object [] substitutions)
263 {
264 return helper.getString(key, substitutions, true);
265 }
266
267 public String getString(String key, Object [] substitutions, boolean translate)
268 {
269 return helper.getString(key, substitutions, translate);
270 }
271
272 public void log(Object logEntry)
273 {
274 helper.log(logEntry);
275 }
276 }
277
278 /**
279 * This just provides a common interface for the Eclipse plugins supported by EMF.
280 * It is not considered API and should not be used by clients.
281 */
282 public static interface InternalEclipsePlugin
283 {
284 String getSymbolicName();
285 }
286
287 /**
288 * This just provides a common delegate for non-UI and UI plug-in classes.
289 * It is not considered API and should not be used by clients.
290 */
291 public static class InternalHelper
292 {
293 protected Plugin plugin;
294 protected ResourceBundle resourceBundle;
295 protected ResourceBundle untranslatedResourceBundle;
296
297 public InternalHelper(Plugin plugin)
298 {
299 this.plugin = plugin;
300 }
301
302 protected Bundle getBundle()
303 {
304 return plugin.getBundle();
305 }
306
307 protected ILog getLog()
308 {
309 return plugin.getLog();
310 }
311
312 /**
313 * Return the plugin ID.
314 */
315 public String getSymbolicName()
316 {
317 return getBundle().getSymbolicName();
318 }
319
320 public URL getBaseURL()
321 {
322 return getBundle().getEntry("/");
323 }
324
325 /**
326 * Fetches the image associated with the given key. It ensures that the image exists.
327 * @param key the key of the image to fetch.
328 * @exception IOException if an image doesn't exist.
329 * @return the description of the image associated with the key.
330 */
331 public Object getImage(String key) throws IOException
332 {
333 URL url = new URL(getBaseURL() + "icons/" + key + extensionFor(key));
334 InputStream inputStream = url.openStream();
335 inputStream.close();
336 return url;
337 }
338
339 public String getString(String key, boolean translate)
340 {
341 ResourceBundle bundle = translate ? resourceBundle : untranslatedResourceBundle;
342 if (bundle == null)
343 {
344 if (translate)
345 {
346 bundle = resourceBundle = Platform.getResourceBundle(getBundle());
347 }
348 else
349 {
350 String resourceName = getBaseURL().toString() + "plugin.properties";
351 try
352 {
353 InputStream inputStream = new URL(resourceName).openStream();
354 bundle = untranslatedResourceBundle = new PropertyResourceBundle(inputStream);
355 inputStream.close();
356 }
357 catch (IOException ioException)
358 {
359 throw new MissingResourceException("Missing properties: " + resourceName, getClass().getName(), "plugin.properties");
360 }
361 }
362 }
363 return bundle.getString(key);
364 }
365
366 public String getString(String key, Object [] substitutions, boolean translate)
367 {
368 return MessageFormat.format(getString(key, translate), substitutions);
369 }
370
371 public void log(Object logEntry)
372 {
373 IStatus status;
374 if (logEntry instanceof IStatus)
375 {
376 status = (IStatus)logEntry;
377 getLog().log(status);
378 }
379 else
380 {
381 if (logEntry == null)
382 {
383 logEntry = new RuntimeException(getString("_UI_NullLogEntry_exception", true)).fillInStackTrace();
384 }
385
386 if (logEntry instanceof Throwable)
387 {
388 Throwable throwable = (Throwable)logEntry;
389
390 // System.err.println("Logged throwable: --------------------");
391 // throwable.printStackTrace();
392
393 String message = throwable.getLocalizedMessage();
394 if (message == null)
395 {
396 message = "";
397 }
398
399 getLog().log(new Status(IStatus.WARNING, getBundle().getSymbolicName(), 0, message, throwable));
400 }
401 else
402 {
403 // System.err.println("Logged throwable: --------------------");
404 // throwable.printStackTrace();
405
406 getLog().log (new Status (IStatus.WARNING, getBundle().getSymbolicName(), 0, logEntry.toString(), null));
407 }
408 }
409 }
410 }
411
412 public static void main(String[] args)
413 {
414 try
415 {
416 String [] relativePath = { "META-INF", "MANIFEST.MF" };
417 Class<?> theClass = args.length > 0 ? Class.forName(args[0]) : EMFPlugin.class;
418
419 String className = theClass.getName();
420 int index = className.lastIndexOf(".");
421 URL classURL = theClass.getResource((index == -1 ? className : className.substring(index + 1)) + ".class");
422 URI uri = URI.createURI(classURL.toString());
423
424 // Trim off the segments corresponding to the package nesting.
425 //
426 int count = 1;
427 for (int i = 0; (i = className.indexOf('.', i)) != -1; ++i)
428 {
429 ++count;
430 }
431 uri = uri.trimSegments(count);
432
433 URL manifestURL = null;
434
435 // For an archive URI, check for the path in the archive.
436 //
437 if (URI.isArchiveScheme(uri.scheme()))
438 {
439 try
440 {
441 // If we can open an input stream, then the path is there, and we have a good URL.
442 //
443 String manifestURI = uri.appendSegments(relativePath).toString();
444 InputStream inputStream = new URL(manifestURI).openStream();
445 inputStream.close();
446 manifestURL = new URL(manifestURI);
447 }
448 catch (IOException exception)
449 {
450 // If the path isn't within the root of the archive,
451 // create a new URI for the folder location of the archive,
452 // so we can look in the folder that contains it.
453 //
454 uri = URI.createURI(uri.authority()).trimSegments(1);
455 }
456 }
457
458 // If we didn't find the path in the usual place nor in the archive...
459 //
460 if (manifestURL == null)
461 {
462 // Trim off the "bin" or "runtime" segment.
463 //
464 String lastSegment = uri.lastSegment();
465 if ("bin".equals(lastSegment) || "runtime".equals(lastSegment))
466 {
467 uri = uri.trimSegments(1);
468 }
469 uri = uri.appendSegments(relativePath);
470 manifestURL = new URL(uri.toString());
471 }
472
473 Manifest manifest = new Manifest(manifestURL.openStream());
474 String symbolicName = manifest.getMainAttributes().getValue("Bundle-SymbolicName");
475 if (symbolicName != null)
476 {
477 int end = symbolicName.indexOf(";");
478 if (end != -1)
479 {
480 symbolicName = symbolicName.substring(0, end);
481 }
482 System.out.println("Bundle-SymbolicName=" + symbolicName + " Bundle-Version=" + manifest.getMainAttributes().getValue("Bundle-Version"));
483 return;
484 }
485 }
486 catch (Exception exception)
487 {
488 // Just print an error message.
489 }
490
491 System.err.println("No Bundle information found");
492 }
493 }