KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xalan > extensions > ExtensionHandlerJavaClass


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 /*
17  * $Id: ExtensionHandlerJavaClass.java,v 1.19 2004/02/11 05:26:23 minchau Exp $
18  */

19
20 package org.apache.xalan.extensions;
21
22 import java.io.IOException JavaDoc;
23 import java.lang.reflect.Constructor JavaDoc;
24 import java.lang.reflect.InvocationTargetException JavaDoc;
25 import java.lang.reflect.Method JavaDoc;
26 import java.lang.reflect.Modifier JavaDoc;
27 import java.util.Vector JavaDoc;
28
29 import javax.xml.transform.TransformerException JavaDoc;
30
31 import org.apache.xalan.templates.ElemTemplateElement;
32 import org.apache.xalan.templates.Stylesheet;
33 import org.apache.xalan.trace.ExtensionEvent;
34 import org.apache.xalan.transformer.TransformerImpl;
35 import org.apache.xpath.functions.FuncExtFunction;
36 import org.apache.xpath.objects.XObject;
37
38 /**
39  * Represents an extension namespace for XPath that handles java classes.
40  * It is recommended that the class URI be of the form:
41  * <pre>
42  * xalan://fully.qualified.class.name
43  * </pre>
44  * However, we do not enforce this. If the class name contains a
45  * a /, we only use the part to the right of the rightmost slash.
46  * In addition, we ignore any "class:" prefix.
47  * Provides functions to test a function's existence and call a function.
48  * Also provides functions to test an element's existence and call an
49  * element.
50  *
51  * @author <a HREF="mailto:garyp@firstech.com">Gary L Peskin</a>
52  * @xsl.usage internal
53  */

54
55 public class ExtensionHandlerJavaClass extends ExtensionHandlerJava
56 {
57
58   private Class JavaDoc m_classObj = null;
59
60   /**
61    * Provides a default Instance for use by elements that need to call
62    * an instance method.
63    */

64
65   private Object JavaDoc m_defaultInstance = null;
66
67
68   /**
69    * Construct a new extension namespace handler given all the information
70    * needed.
71    * @param namespaceUri the extension namespace URI that I'm implementing
72    * @param scriptLang language of code implementing the extension
73    * @param className the fully qualified class name of the class
74    */

75   public ExtensionHandlerJavaClass(String JavaDoc namespaceUri,
76                                    String JavaDoc scriptLang,
77                                    String JavaDoc className)
78   {
79     super(namespaceUri, scriptLang, className);
80     try
81     {
82       m_classObj = getClassForName(className);
83     }
84     catch (ClassNotFoundException JavaDoc e)
85     {
86       // For now, just let this go. We'll catch it when we try to invoke a method.
87
}
88   }
89
90
91   /**
92    * Tests whether a certain function name is known within this namespace.
93    * Simply looks for a method with the appropriate name. There is
94    * no information regarding the arguments to the function call or
95    * whether the method implementing the function is a static method or
96    * an instance method.
97    * @param function name of the function being tested
98    * @return true if its known, false if not.
99    */

100
101   public boolean isFunctionAvailable(String JavaDoc function)
102   {
103     Method JavaDoc[] methods = m_classObj.getMethods();
104     int nMethods = methods.length;
105     for (int i = 0; i < nMethods; i++)
106     {
107       if (methods[i].getName().equals(function))
108         return true;
109     }
110     return false;
111   }
112
113
114   /**
115    * Tests whether a certain element name is known within this namespace.
116    * Looks for a method with the appropriate name and signature.
117    * This method examines both static and instance methods.
118    * @param element name of the element being tested
119    * @return true if its known, false if not.
120    */

121
122   public boolean isElementAvailable(String JavaDoc element)
123   {
124     Method JavaDoc[] methods = m_classObj.getMethods();
125     int nMethods = methods.length;
126     for (int i = 0; i < nMethods; i++)
127     {
128       if (methods[i].getName().equals(element))
129       {
130         Class JavaDoc[] paramTypes = methods[i].getParameterTypes();
131         if ( (paramTypes.length == 2)
132           && paramTypes[0].isAssignableFrom(
133                                      org.apache.xalan.extensions.XSLProcessorContext.class)
134           && paramTypes[1].isAssignableFrom(org.apache.xalan.templates.ElemExtensionCall.class) )
135         {
136           return true;
137         }
138       }
139     }
140     return false;
141   }
142   
143   /**
144    * Process a call to a function in the java class represented by
145    * this <code>ExtensionHandlerJavaClass<code>.
146    * There are three possible types of calls:
147    * <pre>
148    * Constructor:
149    * classns:new(arg1, arg2, ...)
150    *
151    * Static method:
152    * classns:method(arg1, arg2, ...)
153    *
154    * Instance method:
155    * classns:method(obj, arg1, arg2, ...)
156    * </pre>
157    * We use the following rules to determine the type of call made:
158    * <ol type="1">
159    * <li>If the function name is "new", call the best constructor for
160    * class represented by the namespace URI</li>
161    * <li>If the first argument to the function is of the class specified
162    * in the namespace or is a subclass of that class, look for the best
163    * method of the class specified in the namespace with the specified
164    * arguments. Compare all static and instance methods with the correct
165    * method name. For static methods, use all arguments in the compare.
166    * For instance methods, use all arguments after the first.</li>
167    * <li>Otherwise, select the best static or instance method matching
168    * all of the arguments. If the best method is an instance method,
169    * call the function using a default object, creating it if needed.</li>
170    * </ol>
171    *
172    * @param funcName Function name.
173    * @param args The arguments of the function call.
174    * @param methodKey A key that uniquely identifies this class and method call.
175    * @param exprContext The context in which this expression is being executed.
176    * @return the return value of the function evaluation.
177    * @throws TransformerException
178    */

179
180   public Object JavaDoc callFunction (String JavaDoc funcName,
181                               Vector JavaDoc args,
182                               Object JavaDoc methodKey,
183                               ExpressionContext exprContext)
184     throws TransformerException JavaDoc
185   {
186
187     Object JavaDoc[] methodArgs;
188     Object JavaDoc[][] convertedArgs;
189     Class JavaDoc[] paramTypes;
190
191     try
192     {
193       if (funcName.equals("new")) { // Handle constructor call
194

195         methodArgs = new Object JavaDoc[args.size()];
196         convertedArgs = new Object JavaDoc[1][];
197         for (int i = 0; i < methodArgs.length; i++)
198         {
199           methodArgs[i] = args.elementAt(i);
200         }
201         Constructor JavaDoc c = (Constructor JavaDoc) getFromCache(methodKey, null, methodArgs);
202         if (c != null && !TransformerImpl.S_DEBUG)
203         {
204           try
205           {
206             paramTypes = c.getParameterTypes();
207             MethodResolver.convertParams(methodArgs, convertedArgs, paramTypes, exprContext);
208             return c.newInstance(convertedArgs[0]);
209           }
210           catch (InvocationTargetException JavaDoc ite)
211           {
212             throw ite;
213           }
214           catch(Exception JavaDoc e)
215           {
216             // Must not have been the right one
217
}
218         }
219         c = MethodResolver.getConstructor(m_classObj,
220                                           methodArgs,
221                                           convertedArgs,
222                                           exprContext);
223         putToCache(methodKey, null, methodArgs, c);
224         if (TransformerImpl.S_DEBUG) {
225             TransformerImpl trans = (TransformerImpl)exprContext.getXPathContext().getOwnerObject();
226             trans.getTraceManager().fireExtensionEvent(new ExtensionEvent(trans, c, convertedArgs[0]));
227             Object JavaDoc result;
228             try {
229                 result = c.newInstance(convertedArgs[0]);
230             } catch (Exception JavaDoc e) {
231                 throw e;
232             } finally {
233                 trans.getTraceManager().fireExtensionEndEvent(new ExtensionEvent(trans, c, convertedArgs[0]));
234             }
235             return result;
236         } else
237             return c.newInstance(convertedArgs[0]);
238       }
239
240       else
241       {
242
243         int resolveType;
244         Object JavaDoc targetObject = null;
245         methodArgs = new Object JavaDoc[args.size()];
246         convertedArgs = new Object JavaDoc[1][];
247         for (int i = 0; i < methodArgs.length; i++)
248         {
249           methodArgs[i] = args.elementAt(i);
250         }
251         Method JavaDoc m = (Method JavaDoc) getFromCache(methodKey, null, methodArgs);
252         if (m != null && !TransformerImpl.S_DEBUG)
253         {
254           try
255           {
256             paramTypes = m.getParameterTypes();
257             MethodResolver.convertParams(methodArgs, convertedArgs, paramTypes, exprContext);
258             if (Modifier.isStatic(m.getModifiers()))
259               return m.invoke(null, convertedArgs[0]);
260             else
261             {
262               // This is tricky. We get the actual number of target arguments (excluding any
263
// ExpressionContext). If we passed in the same number, we need the implied object.
264
int nTargetArgs = convertedArgs[0].length;
265               if (ExpressionContext.class.isAssignableFrom(paramTypes[0]))
266                 nTargetArgs--;
267               if (methodArgs.length <= nTargetArgs)
268                 return m.invoke(m_defaultInstance, convertedArgs[0]);
269               else
270               {
271                 targetObject = methodArgs[0];
272                 
273                 if (targetObject instanceof XObject)
274                   targetObject = ((XObject) targetObject).object();
275                   
276                 return m.invoke(targetObject, convertedArgs[0]);
277               }
278             }
279           }
280           catch (InvocationTargetException JavaDoc ite)
281           {
282             throw ite;
283           }
284           catch(Exception JavaDoc e)
285           {
286             // Must not have been the right one
287
}
288         }
289
290         if (args.size() > 0)
291         {
292           targetObject = methodArgs[0];
293
294           if (targetObject instanceof XObject)
295             targetObject = ((XObject) targetObject).object();
296
297           if (m_classObj.isAssignableFrom(targetObject.getClass()))
298             resolveType = MethodResolver.DYNAMIC;
299           else
300             resolveType = MethodResolver.STATIC_AND_INSTANCE;
301         }
302         else
303         {
304           targetObject = null;
305           resolveType = MethodResolver.STATIC_AND_INSTANCE;
306         }
307
308         m = MethodResolver.getMethod(m_classObj,
309                                      funcName,
310                                      methodArgs,
311                                      convertedArgs,
312                                      exprContext,
313                                      resolveType);
314         putToCache(methodKey, null, methodArgs, m);
315
316         if (MethodResolver.DYNAMIC == resolveType) { // First argument was object type
317
if (TransformerImpl.S_DEBUG) {
318             TransformerImpl trans = (TransformerImpl)exprContext.getXPathContext().getOwnerObject();
319             trans.getTraceManager().fireExtensionEvent(m, targetObject, convertedArgs[0]);
320             Object JavaDoc result;
321             try {
322                 result = m.invoke(targetObject, convertedArgs[0]);
323             } catch (Exception JavaDoc e) {
324                 throw e;
325             } finally {
326                 trans.getTraceManager().fireExtensionEndEvent(m, targetObject, convertedArgs[0]);
327             }
328             return result;
329           } else
330             return m.invoke(targetObject, convertedArgs[0]);
331         }
332         else // First arg was not object. See if we need the implied object.
333
{
334           if (Modifier.isStatic(m.getModifiers())) {
335             if (TransformerImpl.S_DEBUG) {
336               TransformerImpl trans = (TransformerImpl)exprContext.getXPathContext().getOwnerObject();
337               trans.getTraceManager().fireExtensionEvent(m, null, convertedArgs[0]);
338               Object JavaDoc result;
339               try {
340                   result = m.invoke(null, convertedArgs[0]);
341               } catch (Exception JavaDoc e) {
342                 throw e;
343               } finally {
344                 trans.getTraceManager().fireExtensionEndEvent(m, null, convertedArgs[0]);
345               }
346               return result;
347             } else
348               return m.invoke(null, convertedArgs[0]);
349           }
350           else
351           {
352             if (null == m_defaultInstance)
353             {
354               if (TransformerImpl.S_DEBUG) {
355                 TransformerImpl trans = (TransformerImpl)exprContext.getXPathContext().getOwnerObject();
356                 trans.getTraceManager().fireExtensionEvent(new ExtensionEvent(trans, m_classObj));
357                 try {
358                     m_defaultInstance = m_classObj.newInstance();
359                 } catch (Exception JavaDoc e) {
360                     throw e;
361                 } finally {
362                     trans.getTraceManager().fireExtensionEndEvent(new ExtensionEvent(trans, m_classObj));
363                 }
364               } else
365                   m_defaultInstance = m_classObj.newInstance();
366             }
367             if (TransformerImpl.S_DEBUG) {
368               TransformerImpl trans = (TransformerImpl)exprContext.getXPathContext().getOwnerObject();
369               trans.getTraceManager().fireExtensionEvent(m, m_defaultInstance, convertedArgs[0]);
370               Object JavaDoc result;
371               try {
372                 result = m.invoke(m_defaultInstance, convertedArgs[0]);
373               } catch (Exception JavaDoc e) {
374                 throw e;
375               } finally {
376                 trans.getTraceManager().fireExtensionEndEvent(m, m_defaultInstance, convertedArgs[0]);
377               }
378               return result;
379             } else
380               return m.invoke(m_defaultInstance, convertedArgs[0]);
381           }
382         }
383
384       }
385     }
386     catch (InvocationTargetException JavaDoc ite)
387     {
388       Throwable JavaDoc resultException = ite;
389       Throwable JavaDoc targetException = ite.getTargetException();
390  
391       if (targetException instanceof TransformerException JavaDoc)
392         throw ((TransformerException JavaDoc)targetException);
393       else if (targetException != null)
394         resultException = targetException;
395             
396       throw new TransformerException JavaDoc(resultException);
397     }
398     catch (Exception JavaDoc e)
399     {
400       // e.printStackTrace();
401
throw new TransformerException JavaDoc(e);
402     }
403   }
404
405   /**
406    * Process a call to an XPath extension function
407    *
408    * @param extFunction The XPath extension function
409    * @param args The arguments of the function call.
410    * @param exprContext The context in which this expression is being executed.
411    * @return the return value of the function evaluation.
412    * @throws TransformerException
413    */

414   public Object JavaDoc callFunction(FuncExtFunction extFunction,
415                              Vector JavaDoc args,
416                              ExpressionContext exprContext)
417       throws TransformerException JavaDoc
418   {
419     return callFunction(extFunction.getFunctionName(), args,
420                         extFunction.getMethodKey(), exprContext);
421   }
422
423   /**
424    * Process a call to this extension namespace via an element. As a side
425    * effect, the results are sent to the TransformerImpl's result tree.
426    * We invoke the static or instance method in the class represented by
427    * by the namespace URI. If we don't already have an instance of this class,
428    * we create one upon the first call.
429    *
430    * @param localPart Element name's local part.
431    * @param element The extension element being processed.
432    * @param transformer Handle to TransformerImpl.
433    * @param stylesheetTree The compiled stylesheet tree.
434    * @param sourceTree The root of the source tree (but don't assume
435    * it's a Document).
436    * @param sourceNode The current context node.
437    * @param mode The current mode.
438    * @param methodKey A key that uniquely identifies this element call.
439    * @throws IOException if loading trouble
440    * @throws TransformerException if parsing trouble
441    */

442
443   public void processElement(String JavaDoc localPart,
444                              ElemTemplateElement element,
445                              TransformerImpl transformer,
446                              Stylesheet stylesheetTree,
447                              Object JavaDoc methodKey)
448     throws TransformerException JavaDoc, IOException JavaDoc
449   {
450     Object JavaDoc result = null;
451
452     Method JavaDoc m = (Method JavaDoc) getFromCache(methodKey, null, null);
453     if (null == m)
454     {
455       try
456       {
457         m = MethodResolver.getElementMethod(m_classObj, localPart);
458         if ( (null == m_defaultInstance) && !Modifier.isStatic(m.getModifiers()) ) {
459           if (TransformerImpl.S_DEBUG) {
460             transformer.getTraceManager().fireExtensionEvent(new ExtensionEvent(transformer, m_classObj));
461             try {
462               m_defaultInstance = m_classObj.newInstance();
463             } catch (Exception JavaDoc e) {
464               throw e;
465             } finally {
466               transformer.getTraceManager().fireExtensionEndEvent(new ExtensionEvent(transformer, m_classObj));
467             }
468           } else
469             m_defaultInstance = m_classObj.newInstance();
470         }
471       }
472       catch (Exception JavaDoc e)
473       {
474         // e.printStackTrace ();
475
throw new TransformerException JavaDoc (e.getMessage (), e);
476       }
477       putToCache(methodKey, null, null, m);
478     }
479
480     XSLProcessorContext xpc = new XSLProcessorContext(transformer,
481                                                       stylesheetTree);
482
483     try
484     {
485       if (TransformerImpl.S_DEBUG) {
486         transformer.getTraceManager().fireExtensionEvent(m, m_defaultInstance, new Object JavaDoc[] {xpc, element});
487         try {
488           result = m.invoke(m_defaultInstance, new Object JavaDoc[] {xpc, element});
489         } catch (Exception JavaDoc e) {
490           throw e;
491         } finally {
492           transformer.getTraceManager().fireExtensionEndEvent(m, m_defaultInstance, new Object JavaDoc[] {xpc, element});
493         }
494       } else
495         result = m.invoke(m_defaultInstance, new Object JavaDoc[] {xpc, element});
496     }
497     catch (InvocationTargetException JavaDoc e)
498     {
499       Throwable JavaDoc targetException = e.getTargetException();
500       
501       if (targetException instanceof TransformerException JavaDoc)
502         throw (TransformerException JavaDoc)targetException;
503       else if (targetException != null)
504         throw new TransformerException JavaDoc (targetException.getMessage (), targetException);
505       else
506         throw new TransformerException JavaDoc (e.getMessage (), e);
507     }
508     catch (Exception JavaDoc e)
509     {
510       // e.printStackTrace ();
511
throw new TransformerException JavaDoc (e.getMessage (), e);
512     }
513
514     if (result != null)
515     {
516       xpc.outputToResultTree (stylesheetTree, result);
517     }
518  
519   }
520  
521 }
522
Popular Tags