001: /* ====================================================================
002: * Tea - Copyright (c) 1997-2000 Walt Disney Internet Group
003: * ====================================================================
004: * The Tea Software License, Version 1.1
005: *
006: * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: *
012: * 1. Redistributions of source code must retain the above copyright
013: * notice, this list of conditions and the following disclaimer.
014: *
015: * 2. Redistributions in binary form must reproduce the above copyright
016: * notice, this list of conditions and the following disclaimer in
017: * the documentation and/or other materials provided with the
018: * distribution.
019: *
020: * 3. The end-user documentation included with the redistribution,
021: * if any, must include the following acknowledgment:
022: * "This product includes software developed by the
023: * Walt Disney Internet Group (http://opensource.go.com/)."
024: * Alternately, this acknowledgment may appear in the software itself,
025: * if and wherever such third-party acknowledgments normally appear.
026: *
027: * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
028: * not be used to endorse or promote products derived from this
029: * software without prior written permission. For written
030: * permission, please contact opensource@dig.com.
031: *
032: * 5. Products derived from this software may not be called "Tea",
033: * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
034: * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
035: * written permission of the Walt Disney Internet Group.
036: *
037: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
038: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
039: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
040: * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
041: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
042: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
043: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
044: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
045: * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
046: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
047: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
048: * ====================================================================
049: *
050: * For more information about Tea, please see http://opensource.go.com/.
051: */
052:
053: package com.go.tea.runtime;
054:
055: import java.io.*;
056: import java.util.*;
057: import java.lang.reflect.*;
058: import com.go.tea.compiler.JavaClassGenerator;
059:
060: /******************************************************************************
061: * TemplateLoader manages the loading and execution of Tea templates. To
062: * reload templates, create a new TemplateLoader with a new ClassLoader.
063: *
064: * @author Brian S O'Neill
065: * @version
066: * <!--$$Revision:--> 15 <!-- $-->, <!--$$JustDate:--> 9/07/00 <!-- $-->
067: * @see com.go.tea.util.ClassInjector
068: */
069: public class TemplateLoader {
070: private ClassLoader mBaseLoader;
071: private String mPackagePrefix;
072:
073: // Maps full template names to Templates.
074: private Map mTemplates;
075:
076: /**
077: * Creates a TemplateLoader that uses the current ClassLoader as a base.
078: * It is recommended that templates be compiled to a base package and that
079: * a TemplateLoader be constructed with the package prefix. This way,
080: * the TemplateLoader can easily distinguish between template classes and
081: * normal classes, only loading the templates.
082: */
083: public TemplateLoader() {
084: this (null);
085: }
086:
087: /**
088: * Creates a TemplateLoader that uses the current ClassLoader as a base.
089: *
090: * @param packagePrefix Package that templates should be loaded from
091: */
092: public TemplateLoader(String packagePrefix) {
093: init(getClass().getClassLoader(), packagePrefix);
094: }
095:
096: /**
097: * Creates a TemplateLoader that uses the given ClassLoader as a base. A
098: * base ClassLoader is used to load both template and non-template classes.
099: * The base ClassLoader can use the package prefix for determining
100: * whether or not it is loading a template.
101: *
102: * @param baseLoader Base ClassLoader
103: * @param packagePrefix Package that templates should be loaded from
104: */
105: public TemplateLoader(ClassLoader baseLoader, String packagePrefix) {
106: init(baseLoader, packagePrefix);
107: }
108:
109: private void init(ClassLoader baseLoader, String packagePrefix) {
110: mBaseLoader = baseLoader;
111:
112: if (packagePrefix == null) {
113: packagePrefix = "";
114: } else if (!packagePrefix.endsWith(".")) {
115: packagePrefix += '.';
116: }
117: mPackagePrefix = packagePrefix.trim();
118:
119: mTemplates = new HashMap();
120: }
121:
122: /**
123: * Get or load a template by its full name. The full name of a template
124: * has '.' characters to separate name parts, and it does not include a
125: * Java package prefix.
126: *
127: * @throws ClassNotFoundException when template not found
128: * @throws NoSuchMethodException when the template is invalid
129: */
130: public final synchronized Template getTemplate(String name)
131: throws ClassNotFoundException, NoSuchMethodException,
132: LinkageError {
133: Template template = (Template) mTemplates.get(name);
134: if (template == null) {
135: template = loadTemplate(name);
136: mTemplates.put(name, template);
137: }
138: return template;
139: }
140:
141: /**
142: * Returns all the templates that have been loaded thus far.
143: */
144: public final synchronized Template[] getLoadedTemplates() {
145: return (Template[]) mTemplates.values().toArray(
146: new Template[mTemplates.size()]);
147: }
148:
149: protected Template loadTemplate(String name)
150: throws ClassNotFoundException, NoSuchMethodException,
151: LinkageError {
152: return new TemplateImpl(name, mBaseLoader
153: .loadClass(mPackagePrefix + name));
154: }
155:
156: /**************************************************************************
157: * A ready-to-use Tea template.
158: *
159: * @author Brian S O'Neill
160: * @version
161: * <!--$$Revision:--> 15 <!-- $--> 4 <!-- $$JustDate:--> 9/07/00 <!-- $-->
162: */
163: public static interface Template {
164: public TemplateLoader getTemplateLoader();
165:
166: /**
167: * Returns the full name of this template.
168: */
169: public String getName();
170:
171: /**
172: * Returns the class that defines this template.
173: */
174: public Class getTemplateClass();
175:
176: /**
177: * Returns the type of runtime context that this template accepts.
178: *
179: * @see com.go.tea.runtime.Context
180: */
181: public Class getContextType();
182:
183: /**
184: * Returns the parameter names that this template accepts. The length
185: * of the returned array is the same as returned by getParameterTypes.
186: * If any template parameter names is unknown, the array entry is null.
187: */
188: public String[] getParameterNames();
189:
190: /**
191: * Returns the parameter types that this template accepts. The length
192: * of the returned array is the same as returned by getParameterNames.
193: */
194: public Class[] getParameterTypes();
195:
196: /**
197: * Executes this template using the given runtime context instance and
198: * parameters.
199: *
200: * @param context Must be assignable to the type returned by
201: * {@link #getContextType()}.
202: * @param parameters Must have same length and types as returned by
203: * {@link #getParameterTypes()}.
204: */
205: public void execute(Context context, Object[] parameters)
206: throws Exception;
207:
208: /**
209: * Returns the template signature.
210: */
211: public String toString();
212: }
213:
214: private class TemplateImpl implements Template {
215: private String mName;
216: private Class mClass;
217:
218: private transient Method mExecuteMethod;
219: private transient Class mReturnType;
220: private transient String[] mParameterNames;
221: private transient Class[] mParameterTypes;
222:
223: private TemplateImpl(String name, Class clazz)
224: throws NoSuchMethodException {
225: mName = name;
226: mClass = clazz;
227: doReflection();
228: }
229:
230: public TemplateLoader getTemplateLoader() {
231: return TemplateLoader.this ;
232: }
233:
234: public String getName() {
235: return mName;
236: }
237:
238: public Class getTemplateClass() {
239: return mClass;
240: }
241:
242: public Class getContextType() {
243: return mExecuteMethod.getParameterTypes()[0];
244: }
245:
246: public String[] getParameterNames() {
247: return (String[]) mParameterNames.clone();
248: }
249:
250: public Class[] getParameterTypes() {
251: return (Class[]) mParameterTypes.clone();
252: }
253:
254: public void execute(Context context, Object[] parameters)
255: throws Exception {
256: int length = parameters.length;
257: Object[] args = new Object[1 + length];
258: args[0] = context;
259: for (int i = 0; i < length; i++) {
260: args[i + 1] = parameters[i];
261: }
262:
263: try {
264: Object ret = mExecuteMethod.invoke(null, args);
265: if (mReturnType != void.class) {
266: context.print(ret);
267: }
268: } catch (InvocationTargetException e) {
269: Throwable t = e.getTargetException();
270: if (t instanceof Exception) {
271: throw (Exception) t;
272: } else if (t instanceof Error) {
273: throw (Error) t;
274: } else {
275: throw e;
276: }
277: }
278: }
279:
280: public String toString() {
281: StringBuffer buf = new StringBuffer(80);
282:
283: buf.append("template ");
284: buf.append(getName());
285: buf.append('(');
286:
287: buf.append(getContextType().getName());
288:
289: String[] paramNames = getParameterNames();
290: Class[] paramTypes = getParameterTypes();
291: int length = paramTypes.length;
292: for (int i = 0; i < length; i++) {
293: buf.append(", ");
294: buf.append(paramTypes[i].getName());
295: if (paramNames[i] != null) {
296: buf.append(' ');
297: buf.append(paramNames[i]);
298: }
299: }
300:
301: buf.append(')');
302:
303: return buf.toString();
304: }
305:
306: private void doReflection() throws NoSuchMethodException {
307: // Bind to first execute method found; there should be one.
308: Method[] methods = getTemplateClass().getMethods();
309:
310: int foundCount = 0;
311: for (int i = 0; i < methods.length; i++) {
312: Method m = methods[i];
313: if (m.getName().equals(
314: JavaClassGenerator.EXECUTE_METHOD_NAME)
315: && Modifier.isStatic(m.getModifiers())) {
316:
317: mExecuteMethod = m;
318: break;
319: }
320: }
321:
322: if (mExecuteMethod == null) {
323: throw new NoSuchMethodException(
324: "No execute method found in class "
325: + "for template \"" + getName() + "\"");
326: }
327:
328: mReturnType = mExecuteMethod.getReturnType();
329:
330: Class[] methodParams = mExecuteMethod.getParameterTypes();
331: if (methodParams.length == 0
332: || !Context.class.isAssignableFrom(methodParams[0])) {
333:
334: throw new NoSuchMethodException(
335: "Execute method does not accept a context "
336: + "for template \"" + getName() + "\"");
337: }
338:
339: int length = methodParams.length - 1;
340: mParameterNames = new String[length];
341: mParameterTypes = new Class[length];
342:
343: for (int i = 0; i < length; i++) {
344: mParameterTypes[i] = methodParams[i + 1];
345: }
346:
347: try {
348: Method namesMethod = getTemplateClass().getMethod(
349: JavaClassGenerator.PARAMETER_METHOD_NAME, null);
350:
351: String[] names = (String[]) namesMethod.invoke(null,
352: null);
353: if (names != null) {
354: // Copy, just in case the length differs.
355: for (int i = 0; i < length; i++) {
356: mParameterNames[i] = names[i];
357: }
358: }
359: } catch (Exception e) {
360: // No big deal, we just don't set paramater names.
361: }
362: }
363: }
364: }
|