001: /*
002: JSPWiki - a JSP-based WikiWiki clone.
003:
004: Copyright (C) 2001-2003 Janne Jalkanen (Janne.Jalkanen@iki.fi)
005:
006: This program is free software; you can redistribute it and/or modify
007: it under the terms of the GNU Lesser General Public License as published by
008: the Free Software Foundation; either version 2.1 of the License, or
009: (at your option) any later version.
010:
011: This program is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public License
017: along with this program; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package com.ecyrd.jspwiki.util;
021:
022: import java.io.InputStream;
023: import java.lang.reflect.Constructor;
024: import java.lang.reflect.InvocationTargetException;
025: import java.util.*;
026:
027: import org.apache.log4j.Logger;
028: import org.jdom.Document;
029: import org.jdom.Element;
030: import org.jdom.input.SAXBuilder;
031: import org.jdom.xpath.XPath;
032:
033: import com.ecyrd.jspwiki.WikiException;
034:
035: /**
036: * Contains useful utilities for class file manipulation. This is a static class,
037: * so there is no need to instantiate it.
038: *
039: * @author Janne Jalkanen
040: * @since 2.1.29.
041: */
042: public final class ClassUtil {
043: private static final Logger log = Logger.getLogger(ClassUtil.class);
044: /**
045: * The location of the classmappings.xml document. It will be searched for
046: * in the classpath. It's value is "{@value}".
047: */
048: public static final String MAPPINGS = "/ini/classmappings.xml";
049:
050: private static Map c_classMappings = new Hashtable();
051:
052: /**
053: * Initialize the class mappings document.
054: */
055: static {
056: try {
057: InputStream is = ClassUtil.class
058: .getResourceAsStream(MAPPINGS);
059:
060: if (is != null) {
061: Document doc = new SAXBuilder().build(is);
062:
063: XPath xpath = XPath
064: .newInstance("/classmappings/mapping");
065:
066: List nodes = xpath.selectNodes(doc);
067:
068: for (Iterator i = nodes.iterator(); i.hasNext();) {
069: Element f = (Element) i.next();
070:
071: String key = f.getChildText("requestedClass");
072: String className = f.getChildText("mappedClass");
073:
074: c_classMappings.put(key, className);
075:
076: log.debug("Mapped class '" + key + "' to class '"
077: + className + "'");
078: }
079: } else {
080: log.info("Didn't find class mapping document in "
081: + MAPPINGS);
082: }
083: } catch (Exception ex) {
084: log.error("Unable to parse mappings document!", ex);
085: }
086: }
087:
088: /**
089: * Private constructor to prevent direct instantiation.
090: */
091: private ClassUtil() {
092: }
093:
094: /**
095: * Attempts to find a class from a collection of packages. This will first
096: * attempt to find the class based on just the className parameter, but
097: * should that fail, will iterate through the "packages" -list, prefixes
098: * the package name to the className, and then tries to find the class
099: * again.
100: *
101: * @param packages A List of Strings, containing different package names.
102: * @param className The name of the class to find.
103: * @return The class, if it was found.
104: * @throws ClassNotFoundException if this particular class cannot be found
105: * from the list.
106: */
107: public static Class findClass(List packages, String className)
108: throws ClassNotFoundException {
109: ClassLoader loader = ClassUtil.class.getClassLoader();
110:
111: try {
112: return loader.loadClass(className);
113: } catch (ClassNotFoundException e) {
114: for (Iterator i = packages.iterator(); i.hasNext();) {
115: String packageName = (String) i.next();
116:
117: try {
118: return loader.loadClass(packageName + "."
119: + className);
120: } catch (ClassNotFoundException ex) {
121: // This is okay, we go to the next package.
122: }
123: }
124: }
125:
126: throw new ClassNotFoundException("Class '" + className
127: + "' not found in search path!");
128: }
129:
130: /**
131: * A shortcut for findClass when you only have a singular package to search.
132: * It will first attempt to instantiate the class directly from the className,
133: * and will then try to prefix it with the packageName.
134: *
135: * @param packageName A package name (such as "com.ecyrd.jspwiki.plugins").
136: * @param className The class name to find.
137: * @return The class, if it was found.
138: * @throws ClassNotFoundException if this particular class cannot be found.
139: */
140:
141: public static Class findClass(String packageName, String className)
142: throws ClassNotFoundException {
143: ArrayList list = new ArrayList();
144: list.add(packageName);
145:
146: return findClass(list, className);
147: }
148:
149: /**
150: * This method is used to locate and instantiate a mapped class.
151: * You may redefine anything in the resource file which is located in your classpath
152: * under the name <code>ClassUtil.MAPPINGS ({@value #MAPPINGS})</code>.
153: * <p>
154: * This is an extremely powerful system, which allows you to remap many of
155: * the JSPWiki core classes to your own class. Please read the documentation
156: * included in the default <code>{@value #MAPPINGS}</code> file to see
157: * how this method works.
158: *
159: * @param requestedClass The name of the class you wish to instantiate.
160: * @return An instantiated Object.
161: * @throws WikiException If the class cannot be found or instantiated.
162: * @since 2.5.40
163: */
164: public static Object getMappedObject(String requestedClass)
165: throws WikiException {
166: Object[] initargs = {};
167: return getMappedObject(requestedClass, initargs);
168: }
169:
170: /**
171: * This method is used to locate and instantiate a mapped class.
172: * You may redefine anything in the resource file which is located in your classpath
173: * under the name <code>ClassUtil.MAPPINGS ({@value #MAPPINGS})</code>.
174: * <p>
175: * This is an extremely powerful system, which allows you to remap many of
176: * the JSPWiki core classes to your own class. Please read the documentation
177: * included in the default <code>{@value #MAPPINGS}</code> file to see
178: * how this method works.
179: *
180: * @param requestedClass The name of the class you wish to instantiate.
181: * @param arg1 Argument for the constructor.
182: * @return An instantiated Object.
183: * @throws WikiException If the class cannot be found or instantiated.
184: * @since 2.5.40
185: */
186: public static Object getMappedObject(String requestedClass,
187: Object arg1) throws WikiException {
188: Object[] initargs = { arg1 };
189: return getMappedObject(requestedClass, initargs);
190: }
191:
192: /**
193: * This method is used to locate and instantiate a mapped class.
194: * You may redefine anything in the resource file which is located in your classpath
195: * under the name <code>ClassUtil.MAPPINGS ({@value #MAPPINGS})</code>.
196: * <p>
197: * This is an extremely powerful system, which allows you to remap many of
198: * the JSPWiki core classes to your own class. Please read the documentation
199: * included in the default <code>{@value #MAPPINGS}</code> file to see
200: * how this method works.
201: *
202: * @param requestedClass The name of the class you wish to instantiate.
203: * @param arg1 Argument for the constructor
204: * @param arg2 A second argument for the constructor
205: * @return An instantiated Object.
206: * @throws WikiException If the class cannot be found or instantiated.
207: * @since 2.5.40
208: */
209: public static Object getMappedObject(String requestedClass,
210: Object arg1, Object arg2) throws WikiException {
211: Object[] initargs = { arg1, arg2 };
212: return getMappedObject(requestedClass, initargs);
213: }
214:
215: /**
216: * This method is used to locate and instantiate a mapped class.
217: * You may redefine anything in the resource file which is located in your classpath
218: * under the name <code>{@value #MAPPINGS}</code>.
219: * <p>
220: * This is an extremely powerful system, which allows you to remap many of
221: * the JSPWiki core classes to your own class. Please read the documentation
222: * included in the default <code>{@value #MAPPINGS}</code> file to see
223: * how this method works.
224: * <p>
225: * This method takes in an object array for the constructor arguments for classes
226: * which have more than two constructors.
227: *
228: * @param requestedClass The name of the class you wish to instantiate.
229: * @param initargs The parameters to be passed to the constructor. May be <code>null</code>.
230: * @return An instantiated Object.
231: * @throws WikiException If the class cannot be found or instantiated. The error is logged.
232: * @since 2.5.40
233: */
234: public static Object getMappedObject(String requestedClass,
235: Object[] initargs) throws WikiException {
236: try {
237: Class cl = getMappedClass(requestedClass);
238:
239: Constructor[] ctors = cl.getConstructors();
240:
241: //
242: // Try to find the proper constructor by comparing the
243: // initargs array classes and the constructor types.
244: //
245: for (int c = 0; c < ctors.length; c++) {
246: Class[] params = ctors[c].getParameterTypes();
247:
248: if (params.length == initargs.length) {
249: for (int arg = 0; arg < initargs.length; arg++) {
250: if (params[arg].isAssignableFrom(initargs[arg]
251: .getClass())) {
252: //
253: // Ha, found it! Instantiating and returning...
254: //
255: return ctors[c].newInstance(initargs);
256: }
257: }
258: }
259: }
260:
261: //
262: // No arguments, so we can just call a default constructor and
263: // ignore the arguments.
264: //
265: Object o = cl.newInstance();
266:
267: return o;
268: } catch (InstantiationException e) {
269: log.info("Cannot instantiate requested class "
270: + requestedClass, e);
271:
272: throw new WikiException("Failed to instantiate class "
273: + requestedClass);
274: } catch (IllegalAccessException e) {
275: log.info("Cannot access requested class " + requestedClass,
276: e);
277:
278: throw new WikiException("Failed to instantiate class "
279: + requestedClass);
280: } catch (IllegalArgumentException e) {
281: log.info("Illegal arguments when constructing new object",
282: e);
283:
284: throw new WikiException("Failed to instantiate class "
285: + requestedClass);
286: } catch (InvocationTargetException e) {
287: log.info("You tried to instantiate an abstract class "
288: + requestedClass, e);
289:
290: throw new WikiException("Failed to instantiate class "
291: + requestedClass);
292: }
293: }
294:
295: /**
296: * Finds a mapped class from the c_classMappings list. If there is no
297: * mappped class, will use the requestedClass.
298: *
299: * @param requestedClass
300: * @return A Class object which you can then instantiate.
301: * @throws WikiException
302: */
303: private static Class getMappedClass(String requestedClass)
304: throws WikiException {
305: String mappedClass = (String) c_classMappings
306: .get(requestedClass);
307:
308: if (mappedClass == null) {
309: mappedClass = requestedClass;
310: }
311:
312: try {
313: Class cl = Class.forName(mappedClass);
314:
315: return cl;
316: } catch (ClassNotFoundException e) {
317: log.info("Cannot find requested class", e);
318:
319: throw new WikiException("Failed to instantiate class "
320: + requestedClass);
321: }
322: }
323: }
|