Coverage Report - org.crosswire.common.util.PluginUtil
 
Classes in this File Line Coverage Branch Coverage Complexity
PluginUtil
0%
0/51
0%
0/10
3.167
 
 1  
 /**
 2  
  * Distribution License:
 3  
  * JSword is free software; you can redistribute it and/or modify it under
 4  
  * the terms of the GNU Lesser General Public License, version 2.1 or later
 5  
  * as published by the Free Software Foundation. This program is distributed
 6  
  * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 7  
  * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 8  
  * See the GNU Lesser General Public License for more details.
 9  
  *
 10  
  * The License is available on the internet at:
 11  
  *      http://www.gnu.org/copyleft/lgpl.html
 12  
  * or by writing to:
 13  
  *      Free Software Foundation, Inc.
 14  
  *      59 Temple Place - Suite 330
 15  
  *      Boston, MA 02111-1307, USA
 16  
  *
 17  
  * © CrossWire Bible Society, 2008 - 2016
 18  
  *
 19  
  */
 20  
 package org.crosswire.common.util;
 21  
 
 22  
 import java.io.IOException;
 23  
 import java.io.InputStream;
 24  
 import java.net.MalformedURLException;
 25  
 import java.util.ArrayList;
 26  
 import java.util.HashMap;
 27  
 import java.util.List;
 28  
 import java.util.Map;
 29  
 import java.util.MissingResourceException;
 30  
 
 31  
 import org.crosswire.jsword.JSOtherMsg;
 32  
 import org.slf4j.LoggerFactory;
 33  
 
 34  
 /**
 35  
  * A plugin maps one or more implementations to an interface or abstract class
 36  
  * via a properties file whose suffix is "plugin". When there is more than one
 37  
  * implementation, one is marked as a default.
 38  
  * 
 39  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.
 40  
  * @author DM Smith
 41  
  */
 42  
 public final class PluginUtil {
 43  
     /**
 44  
      * Prevent instantiation
 45  
      */
 46  0
     private PluginUtil() {
 47  0
     }
 48  
 
 49  
     /**
 50  
      * Get the known implementors of some interface or abstract class. This is
 51  
      * currently done by looking up a plugin file by the name of the given
 52  
      * class, and assuming that values are implementors of said class. Those
 53  
      * that are not are warned, but ignored.
 54  
      * 
 55  
      * @param <T> the implementor's type
 56  
      * @param clazz
 57  
      *            The class or interface to find implementors of.
 58  
      * @return The list of implementing classes.
 59  
      */
 60  
     public static <T> Class<T>[] getImplementors(Class<T> clazz) {
 61  
         try {
 62  0
             List<Class<T>> matches = new ArrayList<Class<T>>();
 63  0
             PropertyMap props = getPlugin(clazz);
 64  0
             for (String key : props.keySet()) {
 65  0
                 String name = props.get(key);
 66  
                 try {
 67  0
                     Class<T> impl = (Class<T>) ClassUtil.forName(name);
 68  0
                     if (clazz.isAssignableFrom(impl)) {
 69  0
                         matches.add(impl);
 70  
                     } else {
 71  0
                         log.warn("Class {} does not implement {}. Ignoring.", impl.getName(), clazz.getName());
 72  
                     }
 73  0
                 } catch (ClassNotFoundException ex) {
 74  0
                     log.warn("Failed to add class to list: {}", clazz.getName(), ex);
 75  0
                 }
 76  0
             }
 77  
 
 78  0
             log.debug("Found {} implementors of {}", Integer.toString(matches.size()), clazz.getName());
 79  0
             return matches.toArray(new Class[matches.size()]);
 80  0
         } catch (IOException ex) {
 81  0
             log.error("Failed to get any classes.", ex);
 82  0
             return new Class[0];
 83  
         }
 84  
     }
 85  
 
 86  
     /**
 87  
      * Get a map of known implementors of some interface or abstract class. This
 88  
      * is currently done by looking up a plugins file by the name of the given
 89  
      * class, and assuming that values are implementors of said class. Those
 90  
      * that are not are warned, but ignored. The reply is in the form of a map
 91  
      * of keys=strings, and values=classes in case you need to get at the names
 92  
      * given to the classes in the plugin file.
 93  
      * 
 94  
      * @param <T> the implementor's type
 95  
      * @param clazz
 96  
      *            The class or interface to find implementors of.
 97  
      * @return The map of implementing classes.
 98  
      * @see PluginUtil#getImplementors(Class)
 99  
      */
 100  
     public static <T> Map<String, Class<T>> getImplementorsMap(Class<T> clazz) {
 101  0
         Map<String, Class<T>> matches = new HashMap<String, Class<T>>();
 102  
 
 103  
         try {
 104  0
             PropertyMap props = getPlugin(clazz);
 105  0
             for (String key : props.keySet()) {
 106  
                 try {
 107  0
                     String value = props.get(key);
 108  0
                     Class<T> impl = (Class<T>) ClassUtil.forName(value);
 109  0
                     if (clazz.isAssignableFrom(impl)) {
 110  0
                         matches.put(key, impl);
 111  
                     } else {
 112  0
                         log.warn("Class {} does not implement {}. Ignoring.", impl.getName(), clazz.getName());
 113  
                     }
 114  0
                 } catch (ClassNotFoundException ex) {
 115  0
                     log.warn("Failed to add class to list: {}", clazz.getName(), ex);
 116  0
                 }
 117  
             }
 118  
 
 119  0
             log.debug("Found {} implementors of {}", Integer.toString(matches.size()), clazz.getName());
 120  0
         } catch (IOException ex) {
 121  0
             log.error("Failed to get any classes.", ex);
 122  0
         }
 123  
 
 124  0
         return matches;
 125  
     }
 126  
 
 127  
     /**
 128  
      * Get the preferred implementor of some interface or abstract class. This
 129  
      * is currently done by looking up a plugins file by the name of the given
 130  
      * class, and assuming that the "default" key is an implementation of said
 131  
      * class. Warnings are given otherwise.
 132  
      * 
 133  
      * @param <T> the implementor's type
 134  
      * @param clazz
 135  
      *            The class or interface to find an implementation of.
 136  
      * @return The configured implementing class.
 137  
      * @throws MalformedURLException
 138  
      *             if the plugin file can not be found
 139  
      * @throws IOException
 140  
      *             if there is a problem reading the found file
 141  
      * @throws ClassNotFoundException
 142  
      *             if the read contents are not found
 143  
      * @throws ClassCastException
 144  
      *             if the read contents are not valid
 145  
      * @see PluginUtil#getImplementors(Class)
 146  
      */
 147  
     public static <T> Class<T> getImplementor(Class<T> clazz) throws IOException, ClassNotFoundException, ClassCastException {
 148  0
         PropertyMap props = getPlugin(clazz);
 149  0
         String name = props.get(DEFAULT);
 150  
 
 151  0
         Class<T> impl = (Class<T>) ClassUtil.forName(name);
 152  0
         if (!clazz.isAssignableFrom(impl)) {
 153  0
             throw new ClassCastException(JSOtherMsg.lookupText("Class {0} does not implement {1}.", impl.getName(), clazz.getName()));
 154  
         }
 155  
 
 156  0
         return impl;
 157  
     }
 158  
 
 159  
     /**
 160  
      * Get and instantiate the preferred implementor of some interface or
 161  
      * abstract class.
 162  
      * 
 163  
      * @param <T> the implementor's type
 164  
      * @param clazz
 165  
      *            The class or interface to find an implementation of.
 166  
      * @return The configured implementing class.
 167  
      * @throws MalformedURLException
 168  
      *             if the plugin file can not be found
 169  
      * @throws IOException
 170  
      *             if there is a problem reading the found file
 171  
      * @throws ClassNotFoundException
 172  
      *             if the read contents are not found
 173  
      * @throws ClassCastException
 174  
      *             if the read contents are not valid
 175  
      * @throws InstantiationException
 176  
      *             if the new object can not be instantiated
 177  
      * @throws IllegalAccessException
 178  
      *             if the new object can not be instantiated
 179  
      * @see PluginUtil#getImplementors(Class)
 180  
      */
 181  
     public static <T> T getImplementation(Class<T> clazz) throws MalformedURLException, ClassCastException, IOException, ClassNotFoundException,
 182  
             InstantiationException, IllegalAccessException
 183  
     {
 184  0
         return getImplementor(clazz).newInstance();
 185  
     }
 186  
 
 187  
     /**
 188  
      * Get and load a plugin file by looking it up as a resource.
 189  
      * 
 190  
      * @param <T> the implementor's type
 191  
      * @param clazz
 192  
      *            The name of the desired resource
 193  
      * @return The found and loaded plugin file
 194  
      * @throws IOException
 195  
      *             if the resource can not be loaded
 196  
      * @throws MissingResourceException
 197  
      *             if the resource can not be found
 198  
      */
 199  
     public static <T> PropertyMap getPlugin(Class<T> clazz) throws IOException {
 200  0
         String subject = ClassUtil.getShortClassName(clazz);
 201  
 
 202  
         try {
 203  0
             String lookup = subject + PluginUtil.EXTENSION_PLUGIN;
 204  0
             InputStream in = ResourceUtil.getResourceAsStream(clazz, lookup);
 205  
 
 206  0
             PropertyMap prop = new PropertyMap();
 207  0
             prop.load(in);
 208  0
             return prop;
 209  0
         } catch (MissingResourceException e) {
 210  0
             return new PropertyMap();
 211  
         }
 212  
     }
 213  
 
 214  
     /**
 215  
      * Extension for properties files
 216  
      */
 217  
     public static final String EXTENSION_PLUGIN = ".plugin";
 218  
 
 219  
     /**
 220  
      * The string for default implementations
 221  
      */
 222  
     private static final String DEFAULT = "default";
 223  
 
 224  
     /**
 225  
      * The log stream
 226  
      */
 227  0
     private static final org.slf4j.Logger log = LoggerFactory.getLogger(PluginUtil.class);
 228  
 
 229  
 }