KickJava   Java API By Example, From Geeks To Geeks.

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


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: MethodResolver.java,v 1.31 2004/02/11 05:26:23 minchau Exp $
18  */

19 package org.apache.xalan.extensions;
20
21 import java.lang.reflect.Constructor JavaDoc;
22 import java.lang.reflect.Method JavaDoc;
23 import java.lang.reflect.Modifier JavaDoc;
24
25 import javax.xml.transform.TransformerException JavaDoc;
26
27 import org.apache.xalan.res.XSLMessages;
28 import org.apache.xalan.res.XSLTErrorResources;
29 import org.apache.xml.dtm.DTM;
30 import org.apache.xml.dtm.DTMIterator;
31 import org.apache.xml.dtm.ref.DTMNodeIterator;
32 import org.apache.xpath.objects.XObject;
33 import org.apache.xpath.objects.XRTreeFrag;
34 import org.apache.xpath.objects.XString;
35 import org.w3c.dom.Node JavaDoc;
36 import org.w3c.dom.NodeList JavaDoc;
37 import org.w3c.dom.traversal.NodeIterator;
38
39 /**
40  * Utility class to help resolve method overloading with Xalan XSLT
41  * argument types.
42  */

43 public class MethodResolver
44 {
45
46   /**
47    * Specifies a search for static methods only.
48    */

49   public static final int STATIC_ONLY = 1;
50
51   /**
52    * Specifies a search for instance methods only.
53    */

54   public static final int INSTANCE_ONLY = 2;
55
56   /**
57    * Specifies a search for both static and instance methods.
58    */

59   public static final int STATIC_AND_INSTANCE = 3;
60
61   /**
62    * Specifies a Dynamic method search. If the method being
63    * evaluated is a static method, all arguments are used.
64    * Otherwise, it is an instance method and only arguments
65    * beginning with the second argument are used.
66    */

67   public static final int DYNAMIC = 4;
68
69   /**
70    * Given a class, figure out the resolution of
71    * the Java Constructor from the XSLT argument types, and perform the
72    * conversion of the arguments.
73    * @param classObj the Class of the object to be constructed.
74    * @param argsIn An array of XSLT/XPath arguments.
75    * @param argsOut An array of the exact size as argsIn, which will be
76    * populated with converted arguments if a suitable method is found.
77    * @return A constructor that will work with the argsOut array.
78    * @throws TransformerException may be thrown for Xalan conversion
79    * exceptions.
80    */

81   public static Constructor JavaDoc getConstructor(Class JavaDoc classObj,
82                                            Object JavaDoc[] argsIn,
83                                            Object JavaDoc[][] argsOut,
84                                            ExpressionContext exprContext)
85     throws NoSuchMethodException JavaDoc,
86            SecurityException JavaDoc,
87            TransformerException JavaDoc
88   {
89     Constructor JavaDoc bestConstructor = null;
90     Class JavaDoc[] bestParamTypes = null;
91     Constructor JavaDoc[] constructors = classObj.getConstructors();
92     int nMethods = constructors.length;
93     int bestScore = Integer.MAX_VALUE;
94     int bestScoreCount = 0;
95     for(int i = 0; i < nMethods; i++)
96     {
97       Constructor JavaDoc ctor = constructors[i];
98       Class JavaDoc[] paramTypes = ctor.getParameterTypes();
99       int numberMethodParams = paramTypes.length;
100       int paramStart = 0;
101       boolean isFirstExpressionContext = false;
102       int scoreStart;
103       // System.out.println("numberMethodParams: "+numberMethodParams);
104
// System.out.println("argsIn.length: "+argsIn.length);
105
// System.out.println("exprContext: "+exprContext);
106
if(numberMethodParams == (argsIn.length+1))
107       {
108         Class JavaDoc javaClass = paramTypes[0];
109         // System.out.println("first javaClass: "+javaClass.getName());
110
if(ExpressionContext.class.isAssignableFrom(javaClass))
111         {
112           isFirstExpressionContext = true;
113           scoreStart = 0;
114           paramStart++;
115           // System.out.println("Incrementing paramStart: "+paramStart);
116
}
117         else
118           continue;
119       }
120       else
121           scoreStart = 1000;
122       
123       if(argsIn.length == (numberMethodParams - paramStart))
124       {
125         // then we have our candidate.
126
int score = scoreMatch(paramTypes, paramStart, argsIn, scoreStart);
127         // System.out.println("score: "+score);
128
if(-1 == score)
129           continue;
130         if(score < bestScore)
131         {
132           // System.out.println("Assigning best ctor: "+ctor);
133
bestConstructor = ctor;
134           bestParamTypes = paramTypes;
135           bestScore = score;
136           bestScoreCount = 1;
137         }
138         else if (score == bestScore)
139           bestScoreCount++;
140       }
141     }
142
143     if(null == bestConstructor)
144     {
145       throw new NoSuchMethodException JavaDoc(errString("function", "constructor", classObj,
146                                                                         "", 0, argsIn));
147     }
148     /*** This is commented out until we can do a better object -> object scoring
149     else if (bestScoreCount > 1)
150       throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_MORE_MATCH_CONSTRUCTOR, new Object[]{classObj.getName()})); //"More than one best match for constructor for "
151                                                                    + classObj.getName());
152     ***/

153     else
154       convertParams(argsIn, argsOut, bestParamTypes, exprContext);
155     
156     return bestConstructor;
157   }
158
159   
160   /**
161    * Given the name of a method, figure out the resolution of
162    * the Java Method from the XSLT argument types, and perform the
163    * conversion of the arguments.
164    * @param classObj The Class of the object that should have the method.
165    * @param name The name of the method to be invoked.
166    * @param argsIn An array of XSLT/XPath arguments.
167    * @param argsOut An array of the exact size as argsIn, which will be
168    * populated with converted arguments if a suitable method is found.
169    * @return A method that will work with the argsOut array.
170    * @throws TransformerException may be thrown for Xalan conversion
171    * exceptions.
172    */

173   public static Method JavaDoc getMethod(Class JavaDoc classObj,
174                                  String JavaDoc name,
175                                  Object JavaDoc[] argsIn,
176                                  Object JavaDoc[][] argsOut,
177                                  ExpressionContext exprContext,
178                                  int searchMethod)
179     throws NoSuchMethodException JavaDoc,
180            SecurityException JavaDoc,
181            TransformerException JavaDoc
182   {
183     // System.out.println("---> Looking for method: "+name);
184
// System.out.println("---> classObj: "+classObj);
185
if (name.indexOf("-")>0)
186       name = replaceDash(name);
187     Method JavaDoc bestMethod = null;
188     Class JavaDoc[] bestParamTypes = null;
189     Method JavaDoc[] methods = classObj.getMethods();
190     int nMethods = methods.length;
191     int bestScore = Integer.MAX_VALUE;
192     int bestScoreCount = 0;
193     boolean isStatic;
194     for(int i = 0; i < nMethods; i++)
195     {
196       Method JavaDoc method = methods[i];
197       // System.out.println("looking at method: "+method);
198
int xsltParamStart = 0;
199       if(method.getName().equals(name))
200       {
201         isStatic = Modifier.isStatic(method.getModifiers());
202         switch(searchMethod)
203         {
204           case STATIC_ONLY:
205             if (!isStatic)
206             {
207               continue;
208             }
209             break;
210
211           case INSTANCE_ONLY:
212             if (isStatic)
213             {
214               continue;
215             }
216             break;
217
218           case STATIC_AND_INSTANCE:
219             break;
220
221           case DYNAMIC:
222             if (!isStatic)
223               xsltParamStart = 1;
224         }
225         int javaParamStart = 0;
226         Class JavaDoc[] paramTypes = method.getParameterTypes();
227         int numberMethodParams = paramTypes.length;
228         boolean isFirstExpressionContext = false;
229         int scoreStart;
230         // System.out.println("numberMethodParams: "+numberMethodParams);
231
// System.out.println("argsIn.length: "+argsIn.length);
232
// System.out.println("exprContext: "+exprContext);
233
int argsLen = (null != argsIn) ? argsIn.length : 0;
234         if(numberMethodParams == (argsLen-xsltParamStart+1))
235         {
236           Class JavaDoc javaClass = paramTypes[0];
237           if(ExpressionContext.class.isAssignableFrom(javaClass))
238           {
239             isFirstExpressionContext = true;
240             scoreStart = 0;
241             javaParamStart++;
242           }
243           else
244           {
245             continue;
246           }
247         }
248         else
249             scoreStart = 1000;
250         
251         if((argsLen - xsltParamStart) == (numberMethodParams - javaParamStart))
252         {
253           // then we have our candidate.
254
int score = scoreMatch(paramTypes, javaParamStart, argsIn, scoreStart);
255           // System.out.println("score: "+score);
256
if(-1 == score)
257             continue;
258           if(score < bestScore)
259           {
260             // System.out.println("Assigning best method: "+method);
261
bestMethod = method;
262             bestParamTypes = paramTypes;
263             bestScore = score;
264             bestScoreCount = 1;
265           }
266           else if (score == bestScore)
267             bestScoreCount++;
268         }
269       }
270     }
271     
272     if (null == bestMethod)
273     {
274       throw new NoSuchMethodException JavaDoc(errString("function", "method", classObj,
275                                                                 name, searchMethod, argsIn));
276     }
277     /*** This is commented out until we can do a better object -> object scoring
278     else if (bestScoreCount > 1)
279       throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_MORE_MATCH_METHOD, new Object[]{name})); //"More than one best match for method " + name);
280     ***/

281     else
282       convertParams(argsIn, argsOut, bestParamTypes, exprContext);
283     
284     return bestMethod;
285   }
286   
287   /**
288    * To support EXSLT extensions, convert names with dash to allowable Java names:
289    * e.g., convert abc-xyz to abcXyz.
290    * Note: dashes only appear in middle of an EXSLT function or element name.
291    */

292   private static String JavaDoc replaceDash(String JavaDoc name)
293   {
294     char dash = '-';
295     StringBuffer JavaDoc buff = new StringBuffer JavaDoc("");
296     for (int i=0; i<name.length(); i++)
297     {
298       if (name.charAt(i) == dash)
299       {}
300       else if (i > 0 && name.charAt(i-1) == dash)
301         buff.append(Character.toUpperCase(name.charAt(i)));
302       else
303         buff.append(name.charAt(i));
304     }
305     return buff.toString();
306   }
307   
308   /**
309    * Given the name of a method, figure out the resolution of
310    * the Java Method
311    * @param classObj The Class of the object that should have the method.
312    * @param name The name of the method to be invoked.
313    * @return A method that will work to be called as an element.
314    * @throws TransformerException may be thrown for Xalan conversion
315    * exceptions.
316    */

317   public static Method JavaDoc getElementMethod(Class JavaDoc classObj,
318                                         String JavaDoc name)
319     throws NoSuchMethodException JavaDoc,
320            SecurityException JavaDoc,
321            TransformerException JavaDoc
322   {
323     // System.out.println("---> Looking for element method: "+name);
324
// System.out.println("---> classObj: "+classObj);
325
Method JavaDoc bestMethod = null;
326     Method JavaDoc[] methods = classObj.getMethods();
327     int nMethods = methods.length;
328     int bestScoreCount = 0;
329     for(int i = 0; i < nMethods; i++)
330     {
331       Method JavaDoc method = methods[i];
332       // System.out.println("looking at method: "+method);
333
if(method.getName().equals(name))
334       {
335         Class JavaDoc[] paramTypes = method.getParameterTypes();
336         if ( (paramTypes.length == 2)
337            && paramTypes[1].isAssignableFrom(org.apache.xalan.templates.ElemExtensionCall.class)
338                                          && paramTypes[0].isAssignableFrom(org.apache.xalan.extensions.XSLProcessorContext.class) )
339         {
340           if ( ++bestScoreCount == 1 )
341             bestMethod = method;
342           else
343             break;
344         }
345       }
346     }
347     
348     if (null == bestMethod)
349     {
350       throw new NoSuchMethodException JavaDoc(errString("element", "method", classObj,
351                                                                         name, 0, null));
352     }
353     else if (bestScoreCount > 1)
354       throw new TransformerException JavaDoc(XSLMessages.createMessage(XSLTErrorResources.ER_MORE_MATCH_ELEMENT, new Object JavaDoc[]{name})); //"More than one best match for element method " + name);
355

356     return bestMethod;
357   }
358   
359
360   /**
361    * Convert a set of parameters based on a set of paramTypes.
362    * @param argsIn An array of XSLT/XPath arguments.
363    * @param argsOut An array of the exact size as argsIn, which will be
364    * populated with converted arguments.
365    * @param paramTypes An array of class objects, of the exact same
366    * size as argsIn and argsOut.
367    * @throws TransformerException may be thrown for Xalan conversion
368    * exceptions.
369    */

370   public static void convertParams(Object JavaDoc[] argsIn,
371                                    Object JavaDoc[][] argsOut, Class JavaDoc[] paramTypes,
372                                    ExpressionContext exprContext)
373     throws javax.xml.transform.TransformerException JavaDoc
374   {
375     // System.out.println("In convertParams");
376
if (paramTypes == null)
377       argsOut[0] = null;
378     else
379     {
380       int nParams = paramTypes.length;
381       argsOut[0] = new Object JavaDoc[nParams];
382       int paramIndex = 0;
383       if((nParams > 0)
384          && ExpressionContext.class.isAssignableFrom(paramTypes[0]))
385       {
386         argsOut[0][0] = exprContext;
387         // System.out.println("Incrementing paramIndex in convertParams: "+paramIndex);
388
paramIndex++;
389       }
390
391       if (argsIn != null)
392       {
393         for(int i = argsIn.length - nParams + paramIndex ; paramIndex < nParams; i++, paramIndex++)
394         {
395           // System.out.println("paramTypes[i]: "+paramTypes[i]);
396
argsOut[0][paramIndex] = convert(argsIn[i], paramTypes[paramIndex]);
397         }
398       }
399     }
400   }
401   
402   /**
403    * Simple class to hold information about allowed conversions
404    * and their relative scores, for use by the table below.
405    */

406   static class ConversionInfo
407   {
408     ConversionInfo(Class JavaDoc cl, int score)
409     {
410       m_class = cl;
411       m_score = score;
412     }
413     
414     Class JavaDoc m_class; // Java class to convert to.
415
int m_score; // Match score, closer to zero is more matched.
416
}
417   
418   private static final int SCOREBASE=1;
419   
420   /**
421    * Specification of conversions from XSLT type CLASS_UNKNOWN
422    * (i.e. some unknown Java object) to allowed Java types.
423    */

424   static ConversionInfo[] m_javaObjConversions = {
425     new ConversionInfo(Double.TYPE, 11),
426     new ConversionInfo(Float.TYPE, 12),
427     new ConversionInfo(Long.TYPE, 13),
428     new ConversionInfo(Integer.TYPE, 14),
429     new ConversionInfo(Short.TYPE, 15),
430     new ConversionInfo(Character.TYPE, 16),
431     new ConversionInfo(Byte.TYPE, 17),
432     new ConversionInfo(java.lang.String JavaDoc.class, 18)
433   };
434   
435   /**
436    * Specification of conversions from XSLT type CLASS_BOOLEAN
437    * to allowed Java types.
438    */

439   static ConversionInfo[] m_booleanConversions = {
440     new ConversionInfo(Boolean.TYPE, 0),
441     new ConversionInfo(java.lang.Boolean JavaDoc.class, 1),
442     new ConversionInfo(java.lang.Object JavaDoc.class, 2),
443     new ConversionInfo(java.lang.String JavaDoc.class, 3)
444   };
445
446   /**
447    * Specification of conversions from XSLT type CLASS_NUMBER
448    * to allowed Java types.
449    */

450   static ConversionInfo[] m_numberConversions = {
451     new ConversionInfo(Double.TYPE, 0),
452     new ConversionInfo(java.lang.Double JavaDoc.class, 1),
453     new ConversionInfo(Float.TYPE, 3),
454     new ConversionInfo(Long.TYPE, 4),
455     new ConversionInfo(Integer.TYPE, 5),
456     new ConversionInfo(Short.TYPE, 6),
457     new ConversionInfo(Character.TYPE, 7),
458     new ConversionInfo(Byte.TYPE, 8),
459     new ConversionInfo(Boolean.TYPE, 9),
460     new ConversionInfo(java.lang.String JavaDoc.class, 10),
461     new ConversionInfo(java.lang.Object JavaDoc.class, 11)
462   };
463
464   /**
465    * Specification of conversions from XSLT type CLASS_STRING
466    * to allowed Java types.
467    */

468   static ConversionInfo[] m_stringConversions = {
469     new ConversionInfo(java.lang.String JavaDoc.class, 0),
470     new ConversionInfo(java.lang.Object JavaDoc.class, 1),
471     new ConversionInfo(Character.TYPE, 2),
472     new ConversionInfo(Double.TYPE, 3),
473     new ConversionInfo(Float.TYPE, 3),
474     new ConversionInfo(Long.TYPE, 3),
475     new ConversionInfo(Integer.TYPE, 3),
476     new ConversionInfo(Short.TYPE, 3),
477     new ConversionInfo(Byte.TYPE, 3),
478     new ConversionInfo(Boolean.TYPE, 4)
479   };
480
481   /**
482    * Specification of conversions from XSLT type CLASS_RTREEFRAG
483    * to allowed Java types.
484    */

485   static ConversionInfo[] m_rtfConversions = {
486     new ConversionInfo(org.w3c.dom.traversal.NodeIterator.class, 0),
487     new ConversionInfo(org.w3c.dom.NodeList JavaDoc.class, 1),
488     new ConversionInfo(org.w3c.dom.Node JavaDoc.class, 2),
489     new ConversionInfo(java.lang.String JavaDoc.class, 3),
490     new ConversionInfo(java.lang.Object JavaDoc.class, 5),
491     new ConversionInfo(Character.TYPE, 6),
492     new ConversionInfo(Double.TYPE, 7),
493     new ConversionInfo(Float.TYPE, 7),
494     new ConversionInfo(Long.TYPE, 7),
495     new ConversionInfo(Integer.TYPE, 7),
496     new ConversionInfo(Short.TYPE, 7),
497     new ConversionInfo(Byte.TYPE, 7),
498     new ConversionInfo(Boolean.TYPE, 8)
499   };
500   
501   /**
502    * Specification of conversions from XSLT type CLASS_NODESET
503    * to allowed Java types. (This is the same as for CLASS_RTREEFRAG)
504    */

505   static ConversionInfo[] m_nodesetConversions = {
506     new ConversionInfo(org.w3c.dom.traversal.NodeIterator.class, 0),
507     new ConversionInfo(org.w3c.dom.NodeList JavaDoc.class, 1),
508     new ConversionInfo(org.w3c.dom.Node JavaDoc.class, 2),
509     new ConversionInfo(java.lang.String JavaDoc.class, 3),
510     new ConversionInfo(java.lang.Object JavaDoc.class, 5),
511     new ConversionInfo(Character.TYPE, 6),
512     new ConversionInfo(Double.TYPE, 7),
513     new ConversionInfo(Float.TYPE, 7),
514     new ConversionInfo(Long.TYPE, 7),
515     new ConversionInfo(Integer.TYPE, 7),
516     new ConversionInfo(Short.TYPE, 7),
517     new ConversionInfo(Byte.TYPE, 7),
518     new ConversionInfo(Boolean.TYPE, 8)
519   };
520   
521   /**
522    * Order is significant in the list below, based on
523    * XObject.CLASS_XXX values.
524    */

525   static ConversionInfo[][] m_conversions =
526   {
527     m_javaObjConversions, // CLASS_UNKNOWN = 0;
528
m_booleanConversions, // CLASS_BOOLEAN = 1;
529
m_numberConversions, // CLASS_NUMBER = 2;
530
m_stringConversions, // CLASS_STRING = 3;
531
m_nodesetConversions, // CLASS_NODESET = 4;
532
m_rtfConversions // CLASS_RTREEFRAG = 5;
533
};
534   
535   /**
536    * Score the conversion of a set of XSLT arguments to a
537    * given set of Java parameters.
538    * If any invocations of this function for a method with
539    * the same name return the same positive value, then a conflict
540    * has occured, and an error should be signaled.
541    * @param javaParamTypes Must be filled with valid class names, and
542    * of the same length as xsltArgs.
543    * @param xsltArgs Must be filled with valid object instances, and
544    * of the same length as javeParamTypes.
545    * @return -1 for no allowed conversion, or a positive score
546    * that is closer to zero for more preferred, or further from
547    * zero for less preferred.
548    */

549   public static int scoreMatch(Class JavaDoc[] javaParamTypes, int javaParamsStart,
550                                Object JavaDoc[] xsltArgs, int score)
551   {
552     if ((xsltArgs == null) || (javaParamTypes == null))
553       return score;
554     int nParams = xsltArgs.length;
555     for(int i = nParams - javaParamTypes.length + javaParamsStart, javaParamTypesIndex = javaParamsStart;
556         i < nParams;
557         i++, javaParamTypesIndex++)
558     {
559       Object JavaDoc xsltObj = xsltArgs[i];
560       int xsltClassType = (xsltObj instanceof XObject)
561                           ? ((XObject)xsltObj).getType()
562                             : XObject.CLASS_UNKNOWN;
563       Class JavaDoc javaClass = javaParamTypes[javaParamTypesIndex];
564       
565       // System.out.println("Checking xslt: "+xsltObj.getClass().getName()+
566
// " against java: "+javaClass.getName());
567

568       if(xsltClassType == XObject.CLASS_NULL)
569       {
570         // In Xalan I have objects of CLASS_NULL, though I'm not
571
// sure they're used any more. For now, do something funky.
572
if(!javaClass.isPrimitive())
573         {
574           // Then assume that a null can be used, but give it a low score.
575
score += 10;
576           continue;
577         }
578         else
579           return -1; // no match.
580
}
581       
582       ConversionInfo[] convInfo = m_conversions[xsltClassType];
583       int nConversions = convInfo.length;
584       int k;
585       for(k = 0; k < nConversions; k++)
586       {
587         ConversionInfo cinfo = convInfo[k];
588         if(javaClass.isAssignableFrom(cinfo.m_class))
589         {
590           score += cinfo.m_score;
591           break; // from k loop
592
}
593       }
594
595       if (k == nConversions)
596       {
597         // If we get here, we haven't made a match on this parameter using
598
// the ConversionInfo array. We now try to handle the object -> object
599
// mapping which we can't handle through the array mechanism. To do this,
600
// we must determine the class of the argument passed from the stylesheet.
601

602         // If we were passed a subclass of XObject, representing one of the actual
603
// XSLT types, and we are here, we reject this extension method as a candidate
604
// because a match should have been made using the ConversionInfo array. If we
605
// were passed an XObject that encapsulates a non-XSLT type or we
606
// were passed a non-XSLT type directly, we continue.
607

608         // The current implementation (contributed by Kelly Campbell <camk@channelpoint.com>)
609
// checks to see if we were passed an XObject from the XSLT stylesheet. If not,
610
// we use the class of the object that was passed and make sure that it will
611
// map to the class type of the parameter in the extension function.
612
// If we were passed an XObject, we attempt to get the class of the actual
613
// object encapsulated inside the XObject. If the encapsulated object is null,
614
// we judge this method as a match but give it a low score.
615
// If the encapsulated object is not null, we use its type to determine
616
// whether this java method is a valid match for this extension function call.
617
// This approach eliminates the NullPointerException in the earlier implementation
618
// that resulted from passing an XObject encapsulating the null java object.
619

620         // TODO: This needs to be improved to assign relative scores to subclasses,
621
// etc.
622

623         if (XObject.CLASS_UNKNOWN == xsltClassType)
624         {
625           Class JavaDoc realClass = null;
626
627           if (xsltObj instanceof XObject)
628           {
629             Object JavaDoc realObj = ((XObject) xsltObj).object();
630             if (null != realObj)
631             {
632               realClass = realObj.getClass();
633             }
634             else
635             {
636               // do the same as if we were passed XObject.CLASS_NULL
637
score += 10;
638               continue;
639             }
640           }
641           else
642           {
643             realClass = xsltObj.getClass();
644           }
645
646           if (javaClass.isAssignableFrom(realClass))
647           {
648             score += 0; // TODO: To be assigned based on subclass "distance"
649
}
650           else
651             return -1;
652         }
653         else
654           return -1;
655       }
656     }
657     return score;
658   }
659   
660   /**
661    * Convert the given XSLT object to an object of
662    * the given class.
663    * @param xsltObj The XSLT object that needs conversion.
664    * @param javaClass The type of object to convert to.
665    * @returns An object suitable for passing to the Method.invoke
666    * function in the args array, which may be null in some cases.
667    * @throws TransformerException may be thrown for Xalan conversion
668    * exceptions.
669    */

670   static Object JavaDoc convert(Object JavaDoc xsltObj, Class JavaDoc javaClass)
671     throws javax.xml.transform.TransformerException JavaDoc
672   {
673     if(xsltObj instanceof XObject)
674     {
675       XObject xobj = ((XObject)xsltObj);
676       int xsltClassType = xobj.getType();
677
678       switch(xsltClassType)
679       {
680       case XObject.CLASS_NULL:
681         return null;
682         
683       case XObject.CLASS_BOOLEAN:
684         {
685           if(javaClass == java.lang.String JavaDoc.class)
686             return xobj.str();
687           else
688             return new Boolean JavaDoc(xobj.bool());
689         }
690         // break; Unreachable
691
case XObject.CLASS_NUMBER:
692         {
693           if(javaClass == java.lang.String JavaDoc.class)
694             return xobj.str();
695           else if(javaClass == Boolean.TYPE)
696             return new Boolean JavaDoc(xobj.bool());
697           else
698           {
699             return convertDoubleToNumber(xobj.num(), javaClass);
700           }
701         }
702         // break; Unreachable
703

704       case XObject.CLASS_STRING:
705         {
706           if((javaClass == java.lang.String JavaDoc.class) ||
707              (javaClass == java.lang.Object JavaDoc.class))
708             return xobj.str();
709           else if(javaClass == Character.TYPE)
710           {
711             String JavaDoc str = xobj.str();
712             if(str.length() > 0)
713               return new Character JavaDoc(str.charAt(0));
714             else
715               return null; // ??
716
}
717           else if(javaClass == Boolean.TYPE)
718             return new Boolean JavaDoc(xobj.bool());
719           else
720           {
721             return convertDoubleToNumber(xobj.num(), javaClass);
722           }
723         }
724         // break; Unreachable
725

726       case XObject.CLASS_RTREEFRAG:
727         {
728           // GLP: I don't see the reason for the isAssignableFrom call
729
// instead of an == test as is used everywhere else.
730
// Besides, if the javaClass is a subclass of NodeIterator
731
// the condition will be true and we'll create a NodeIterator
732
// which may not match the javaClass, causing a RuntimeException.
733
// if((NodeIterator.class.isAssignableFrom(javaClass)) ||
734
if ( (javaClass == NodeIterator.class) ||
735                (javaClass == java.lang.Object JavaDoc.class) )
736           {
737             DTMIterator dtmIter = ((XRTreeFrag) xobj).asNodeIterator();
738             return new DTMNodeIterator(dtmIter);
739           }
740           else if (javaClass == NodeList JavaDoc.class)
741           {
742             return ((XRTreeFrag) xobj).convertToNodeset();
743           }
744           // Same comment as above
745
// else if(Node.class.isAssignableFrom(javaClass))
746
else if(javaClass == Node JavaDoc.class)
747           {
748             DTMIterator iter = ((XRTreeFrag) xobj).asNodeIterator();
749             int rootHandle = iter.nextNode();
750             DTM dtm = iter.getDTM(rootHandle);
751             return dtm.getNode(dtm.getFirstChild(rootHandle));
752           }
753           else if(javaClass == java.lang.String JavaDoc.class)
754           {
755             return xobj.str();
756           }
757           else if(javaClass == Boolean.TYPE)
758           {
759             return new Boolean JavaDoc(xobj.bool());
760           }
761           else if(javaClass.isPrimitive())
762           {
763             return convertDoubleToNumber(xobj.num(), javaClass);
764           }
765           else
766           {
767             DTMIterator iter = ((XRTreeFrag) xobj).asNodeIterator();
768             int rootHandle = iter.nextNode();
769             DTM dtm = iter.getDTM(rootHandle);
770             Node JavaDoc child = dtm.getNode(dtm.getFirstChild(rootHandle));
771
772             if(javaClass.isAssignableFrom(child.getClass()))
773               return child;
774             else
775               return null;
776           }
777         }
778         // break; Unreachable
779

780       case XObject.CLASS_NODESET:
781         {
782           // GLP: I don't see the reason for the isAssignableFrom call
783
// instead of an == test as is used everywhere else.
784
// Besides, if the javaClass is a subclass of NodeIterator
785
// the condition will be true and we'll create a NodeIterator
786
// which may not match the javaClass, causing a RuntimeException.
787
// if((NodeIterator.class.isAssignableFrom(javaClass)) ||
788
if ( (javaClass == NodeIterator.class) ||
789                (javaClass == java.lang.Object JavaDoc.class) )
790           {
791             return xobj.nodeset();
792           }
793           // Same comment as above
794
// else if(NodeList.class.isAssignableFrom(javaClass))
795
else if(javaClass == NodeList JavaDoc.class)
796           {
797             return xobj.nodelist();
798           }
799           // Same comment as above
800
// else if(Node.class.isAssignableFrom(javaClass))
801
else if(javaClass == Node JavaDoc.class)
802           {
803             // Xalan ensures that iter() always returns an
804
// iterator positioned at the beginning.
805
DTMIterator ni = xobj.iter();
806             int handle = ni.nextNode();
807             if (handle != DTM.NULL)
808               return ni.getDTM(handle).getNode(handle); // may be null.
809
else
810               return null;
811           }
812           else if(javaClass == java.lang.String JavaDoc.class)
813           {
814             return xobj.str();
815           }
816           else if(javaClass == Boolean.TYPE)
817           {
818             return new Boolean JavaDoc(xobj.bool());
819           }
820           else if(javaClass.isPrimitive())
821           {
822             return convertDoubleToNumber(xobj.num(), javaClass);
823           }
824           else
825           {
826             DTMIterator iter = xobj.iter();
827             int childHandle = iter.nextNode();
828             DTM dtm = iter.getDTM(childHandle);
829             Node JavaDoc child = dtm.getNode(childHandle);
830             if(javaClass.isAssignableFrom(child.getClass()))
831               return child;
832             else
833               return null;
834           }
835         }
836         // break; Unreachable
837

838         // No default:, fall-through on purpose
839
} // end switch
840
xsltObj = xobj.object();
841       
842     } // end if if(xsltObj instanceof XObject)
843

844     // At this point, we have a raw java object, not an XObject.
845
if (null != xsltObj)
846     {
847       if(javaClass == java.lang.String JavaDoc.class)
848       {
849         return xsltObj.toString();
850       }
851       else if(javaClass.isPrimitive())
852       {
853         // Assume a number conversion
854
XString xstr = new XString(xsltObj.toString());
855         double num = xstr.num();
856         return convertDoubleToNumber(num, javaClass);
857       }
858       else if(javaClass == java.lang.Class JavaDoc.class)
859       {
860         return xsltObj.getClass();
861       }
862       else
863       {
864         // Just pass the object directly, and hope for the best.
865
return xsltObj;
866       }
867                 }
868     else
869     {
870       // Just pass the object directly, and hope for the best.
871
return xsltObj;
872     }
873   }
874   
875   /**
876    * Do a standard conversion of a double to the specified type.
877    * @param num The number to be converted.
878    * @param javaClass The class type to be converted to.
879    * @return An object specified by javaClass, or a Double instance.
880    */

881   static Object JavaDoc convertDoubleToNumber(double num, Class JavaDoc javaClass)
882   {
883     // In the code below, I don't check for NaN, etc., instead
884
// using the standard Java conversion, as I think we should
885
// specify. See issue-runtime-errors.
886
if((javaClass == Double.TYPE) ||
887        (javaClass == java.lang.Double JavaDoc.class))
888       return new Double JavaDoc(num);
889     else if(javaClass == Float.TYPE)
890       return new Float JavaDoc(num);
891     else if(javaClass == Long.TYPE)
892     {
893       // Use standard Java Narrowing Primitive Conversion
894
// See http://java.sun.com/docs/books/jls/html/5.doc.html#175672
895
return new Long JavaDoc((long)num);
896     }
897     else if(javaClass == Integer.TYPE)
898     {
899       // Use standard Java Narrowing Primitive Conversion
900
// See http://java.sun.com/docs/books/jls/html/5.doc.html#175672
901
return new Integer JavaDoc((int)num);
902     }
903     else if(javaClass == Short.TYPE)
904     {
905       // Use standard Java Narrowing Primitive Conversion
906
// See http://java.sun.com/docs/books/jls/html/5.doc.html#175672
907
return new Short JavaDoc((short)num);
908     }
909     else if(javaClass == Character.TYPE)
910     {
911       // Use standard Java Narrowing Primitive Conversion
912
// See http://java.sun.com/docs/books/jls/html/5.doc.html#175672
913
return new Character JavaDoc((char)num);
914     }
915     else if(javaClass == Byte.TYPE)
916     {
917       // Use standard Java Narrowing Primitive Conversion
918
// See http://java.sun.com/docs/books/jls/html/5.doc.html#175672
919
return new Byte JavaDoc((byte)num);
920     }
921     else // Some other type of object
922
{
923       return new Double JavaDoc(num);
924     }
925   }
926
927
928   /**
929    * Format the message for the NoSuchMethodException containing
930    * all the information about the method we're looking for.
931    */

932   private static String JavaDoc errString(String JavaDoc callType, // "function" or "element"
933
String JavaDoc searchType, // "method" or "constructor"
934
Class JavaDoc classObj,
935                                   String JavaDoc funcName,
936                                   int searchMethod,
937                                   Object JavaDoc[] xsltArgs)
938   {
939     String JavaDoc resultString = "For extension " + callType
940                                               + ", could not find " + searchType + " ";
941     switch (searchMethod)
942     {
943       case STATIC_ONLY:
944         return resultString + "static " + classObj.getName() + "."
945                             + funcName + "([ExpressionContext,] " + errArgs(xsltArgs, 0) + ").";
946
947       case INSTANCE_ONLY:
948         return resultString + classObj.getName() + "."
949                             + funcName + "([ExpressionContext,] " + errArgs(xsltArgs, 0) + ").";
950
951       case STATIC_AND_INSTANCE:
952         return resultString + classObj.getName() + "." + funcName + "([ExpressionContext,] " + errArgs(xsltArgs, 0) + ").\n"
953                             + "Checked both static and instance methods.";
954
955       case DYNAMIC:
956         return resultString + "static " + classObj.getName() + "." + funcName
957                             + "([ExpressionContext, ]" + errArgs(xsltArgs, 0) + ") nor\n"
958                             + classObj + "." + funcName + "([ExpressionContext,] " + errArgs(xsltArgs, 1) + ").";
959
960       default:
961         if (callType.equals("function")) // must be a constructor
962
{
963           return resultString + classObj.getName()
964                                   + "([ExpressionContext,] " + errArgs(xsltArgs, 0) + ").";
965         }
966         else // must be an element call
967
{
968           return resultString + classObj.getName() + "." + funcName
969                     + "(org.apache.xalan.extensions.XSLProcessorContext, "
970                     + "org.apache.xalan.templates.ElemExtensionCall).";
971         }
972     }
973     
974   }
975
976
977   private static String JavaDoc errArgs(Object JavaDoc[] xsltArgs, int startingArg)
978   {
979     StringBuffer JavaDoc returnArgs = new StringBuffer JavaDoc();
980     for (int i = startingArg; i < xsltArgs.length; i++)
981     {
982       if (i != startingArg)
983         returnArgs.append(", ");
984       if (xsltArgs[i] instanceof XObject)
985         returnArgs.append(((XObject) xsltArgs[i]).getTypeString());
986       else
987         returnArgs.append(xsltArgs[i].getClass().getName());
988     }
989     return returnArgs.toString();
990   }
991
992 }
993
Popular Tags