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    }