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