KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > go > teatools > TeaToolsUtils


1 /* ====================================================================
2  * TeaTools - Copyright (c) 1997-2000 GO.com
3  * ====================================================================
4  * The Tea Software License, Version 1.0
5  *
6  * Copyright (c) 2000 GO.com. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by GO.com
23  * (http://opensource.go.com/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "GO.com" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact opensource@go.com.
31  *
32  * 5. Products derived from this software may not be called "Tea",
33  * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
34  * "Kettle" or "Trove" appear in their name, without prior written
35  * permission of GO.com.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL GO.COM OR ITS CONTRIBUTORS BE LIABLE
41  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
43  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
44  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
45  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
46  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
47  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  * ====================================================================
49  *
50  * For more information about Tea, please see http://opensource.go.com/.
51  */

52
53 package com.go.teatools;
54
55 import com.go.tea.runtime.*;
56 import com.go.tea.util.*;
57
58 import com.go.trove.classfile.AccessFlags;
59 import com.go.trove.util.*;
60
61
62 import java.beans.*;
63 import java.io.*;
64 import java.lang.reflect.*;
65 import java.util.*;
66 import java.text.BreakIterator JavaDoc;
67
68 /******************************************************************************
69  * A Tea Tool's best friend. This class has several useful methods for writing
70  * tools that work with Tea. Many of these methods were taken from Kettle
71  * 3.0.x so that they could be reused in future versions and in other
72  * applications.
73  * <p>
74  * This class was written with the intent that it could be used as a tea
75  * context class. It provides a collection of functions to make introspection
76  * possible from Tea.
77  *
78  * @author Mark Masse
79  * @version
80  * <!--$$Revision:--> 15 <!-- $-->, <!--$$JustDate:--> 12/06/00 <!-- $-->
81  */

82 public class TeaToolsUtils extends DefaultContext
83 implements TeaToolsConstants {
84
85
86     /** Zero-length array of properties */
87     private final static PropertyDescriptor[] NO_PROPERTIES =
88         new PropertyDescriptor[0];
89     
90     /** Finite set of primative Class objects. Mapping of primitive
91         name to Class (i.e. "int" -> int.class) */

92     private final static Hashtable cPrimativeClasses = new Hashtable();
93
94     /** The DescriptorComparator used to sort FeatureDescriptors */
95     private final static DescriptorComparator DESCRIPTOR_COMPARATOR =
96         new DescriptorComparator();
97
98     /**
99      * Test program
100      */

101     public static void main(String JavaDoc[] args) throws Exception JavaDoc {
102         new Tester(args);
103     }
104
105     static {
106         
107         Class JavaDoc[] primativeClasses =
108             new Class JavaDoc[] {
109                 void.class,
110                 boolean.class,
111                 char.class,
112                 byte.class,
113                 short.class,
114                 int.class,
115                 float.class,
116                 double.class,
117                 long.class
118             };
119         
120         for (int i = 0; i < primativeClasses.length; i++) {
121             cPrimativeClasses.put(primativeClasses[i].getName(),
122                                   primativeClasses[i]);
123         }
124     }
125
126     /**
127      * Creates a new TeaToolsUtils
128      */

129     public TeaToolsUtils() {
130     }
131
132     // DefaultContext method; implemented to do nothing
133
public void print(Object JavaDoc obj) throws Exception JavaDoc {
134     }
135
136     /**
137      * Returns a TypeDescription object to wrap and describe the
138      * specified type.
139      */

140     public TypeDescription createTypeDescription(Class JavaDoc type) {
141         return new TypeDescription(type, this);
142     }
143
144     /**
145      * Returns an array of PropertyDescriptions to wrap and describe the
146      * specified PropertyDescriptors.
147      */

148     public PropertyDescription[] createPropertyDescriptions(
149                                                    PropertyDescriptor[] pds) {
150
151         if (pds == null) {
152             return null;
153         }
154
155         PropertyDescription[] descriptions =
156             new PropertyDescription[pds.length];
157
158         for (int i = 0; i < pds.length; i++) {
159             descriptions[i] = new PropertyDescription(pds[i], this);
160         }
161
162         return descriptions;
163     }
164
165     /**
166      * Returns an array of MethodDescriptions to wrap and describe the
167      * specified MethodDescriptors.
168      */

169     public MethodDescription[] createMethodDescriptions(
170                                                      MethodDescriptor[] mds) {
171         if (mds == null) {
172             return null;
173         }
174
175         MethodDescription[] descriptions =
176             new MethodDescription[mds.length];
177
178         for (int i = 0; i < mds.length; i++) {
179             descriptions[i] = new MethodDescription(mds[i], this);
180         }
181
182         return descriptions;
183     }
184
185     /**
186      * Returns an array of ParameterDescriptions to wrap and describe the
187      * parameters of the specified MethodDescriptor.
188      */

189     public ParameterDescription[] createParameterDescriptions(
190                                                      MethodDescriptor md) {
191         if (md == null) {
192             return null;
193         }
194
195         Method method = md.getMethod();
196         Class JavaDoc[] paramClasses = method.getParameterTypes();
197         int descriptionCount = paramClasses.length;
198         if (acceptsSubstitution(md)) {
199             descriptionCount--;
200         }
201
202         ParameterDescriptor[] pds = md.getParameterDescriptors();
203         ParameterDescription[] descriptions =
204             new ParameterDescription[descriptionCount];
205
206         for (int i = 0; i < descriptions.length; i++) {
207                         
208             TypeDescription type = createTypeDescription(paramClasses[i]);
209             ParameterDescriptor pd = null;
210             if (pds != null && i < pds.length) {
211                 pd = pds[i];
212             }
213
214             descriptions[i] = new ParameterDescription(type, pd, this);
215         }
216         
217         return descriptions;
218     }
219
220
221     /**
222      * Returns a AccessFlags instance that can be used to check the modifier
223      * int returned by the Class.getModifiers or Member.getModifiers
224      * method.
225      */

226     public AccessFlags getAccessFlags(int modifier) {
227         return new AccessFlags(modifier);
228     }
229
230     /**
231      * Returns a Class object for a given name. Primitive classes can be
232      * loaded via thier normal names (i.e. "float"). Array classes can be
233      * loaded using either the normal Java name (i.e. int[][]) or the VM
234      * name (i.e. [[I).
235      * <p>
236      * Note this method swallows all exceptions and simply returns null if
237      * the class could not be loaded.
238      *
239      * @param className the name of the Class
240      */

241     public Class JavaDoc getClassForName(String JavaDoc className) {
242         return getClassForName(className, null);
243     }
244
245     /**
246      * Returns a Class object for a given name. Primitive classes can be
247      * loaded via thier normal names (i.e. "float"). Array classes can be
248      * loaded using either the normal Java name (i.e. int[][]) or the VM
249      * name (i.e. [[I).
250      * <p>
251      * Note this method swallows all exceptions and simply returns null if
252      * the class could not be loaded.
253      *
254      * @param className the name of the Class
255      * @param classLoader the ClassLoader to use
256      */

257     public Class JavaDoc getClassForName(String JavaDoc className, ClassLoader JavaDoc classLoader) {
258
259         if (className == null) {
260             return null;
261         }
262
263         int bracketIndex = className.indexOf('[');
264         if (bracketIndex > 0) {
265             // Convert foo[][] to [[Lfoo;
266
className = convertArrayClassName(className, bracketIndex);
267         }
268
269         if (className == null) {
270             // Array class name could not be converted (should only happen
271
// for void[]
272
return null;
273         }
274         
275         Class JavaDoc clazz = (Class JavaDoc) cPrimativeClasses.get(className);
276         if (clazz != null) {
277             // Return the primitive class (i.e. int.class for "int")
278
return clazz;
279         }
280
281         Throwable JavaDoc error = null;
282         try {
283             if (classLoader == null) {
284                 classLoader = getClass().getClassLoader();
285             }
286
287             if (classLoader != null) {
288                 clazz = classLoader.loadClass(className);
289             }
290         }
291         catch (Throwable JavaDoc t) {
292             error = t;
293         }
294
295         if (clazz == null) {
296             // Try Class.forName
297
try {
298                 clazz = Class.forName(className);
299             }
300             catch (Throwable JavaDoc t) {
301                 if (error == null) {
302                     t.printStackTrace();
303                 }
304                 else {
305                     error.printStackTrace();
306                 }
307             }
308         }
309
310         return clazz;
311     }
312     
313     /**
314      * Returns the full class name of the specified class. This method
315      * provides special formatting for array and inner classes.
316      */

317     public String JavaDoc getFullClassName(Class JavaDoc clazz) {
318         return getFullClassName(getArrayClassName(clazz));
319     }
320
321     /**
322      * Returns the full class name of the specified class. This method
323      * provides special formatting for inner classes.
324      */

325     public String JavaDoc getFullClassName(String JavaDoc fullClassName) {
326
327         String JavaDoc[] parts = parseClassName(fullClassName);
328
329         String JavaDoc className = getInnerClassName(parts[1]);
330         
331         if (parts[0] != null) {
332             // Return the package name plus the converted class name
333
return parts[0] + '.' + className;
334         }
335         else {
336             return className;
337         }
338     }
339
340     /**
341      * Returns the class name of the specified class. The class name returned
342      * does not include the package. This method provides special formatting
343      * for array and inner classes.
344      */

345     public String JavaDoc getClassName(Class JavaDoc clazz) {
346         return getClassName(getArrayClassName(clazz));
347     }
348
349     /**
350      * Returns the class name of the specified class. The class name returned
351      * does not include the package. This method provides special formatting
352      * for inner classes.
353      */

354     public String JavaDoc getClassName(String JavaDoc fullClassName) {
355         return getInnerClassName(parseClassName(fullClassName)[1]);
356     }
357
358     /**
359      * Returns the package name of the specified class. Returns "" if the
360      * class has no package.
361      */

362     public String JavaDoc getClassPackage(Class JavaDoc clazz) {
363         return getClassPackage(getArrayClassName(clazz));
364     }
365
366     /**
367      * Returns the package name of the specified class. Returns null if the
368      * class has no package.
369      */

370     public String JavaDoc getClassPackage(String JavaDoc fullClassName) {
371         return parseClassName(fullClassName)[0];
372     }
373
374     /**
375      * Returns the type of the specified class.
376      * <p>
377      * <UL>
378      * <LI>A Class returns "class"
379      * <LI>An Interface returns "interface"
380      * <LI>An array returns null
381      * <LI>A primitive returns null
382      * </UL>
383      */

384     public String JavaDoc getClassTypeName(Class JavaDoc clazz) {
385         
386         String JavaDoc type = null;
387
388         if (clazz.isInterface()) {
389             type = "interface";
390         }
391         else if (clazz.isPrimitive()) {
392             type = null;
393         }
394         else if (clazz.isArray()) {
395             type = null;
396         }
397         else {
398             type = "class";
399         }
400
401         return type;
402     }
403
404     /**
405      * Create a version information string based on what the build process
406      * provided. The string is of the form "M.m.r" or
407      * "M.m.r.bbbb" (i.e. 1.1.0.0004) if the build number can be retrieved.
408      * Returns <code>null</code> if the version string cannot be retrieved.
409      */

410     public String JavaDoc getPackageVersion(String JavaDoc packageName) {
411
412         if (packageName == null || packageName.trim().length() == 0) {
413             return null;
414         }
415         
416         if (!packageName.endsWith(".")) {
417             packageName = packageName + ".";
418         }
419         
420         String JavaDoc className = packageName + "PackageInfo";
421
422         Class JavaDoc packageInfoClass = getClassForName(className);
423         if (packageInfoClass == null) {
424             return null;
425         }
426
427         String JavaDoc productVersion = null;
428         String JavaDoc buildNumber = null;
429
430         try {
431
432             Method getProductVersionMethod =
433                 packageInfoClass.getMethod("getProductVersion",
434                                            EMPTY_CLASS_ARRAY);
435
436             productVersion =
437                 (String JavaDoc) getProductVersionMethod.invoke(null,
438                                                         EMPTY_OBJECT_ARRAY);
439             
440             Method getBuildNumberMethod =
441                 packageInfoClass.getMethod("getBuildNumber",
442                                            EMPTY_CLASS_ARRAY);
443
444             buildNumber =
445                 (String JavaDoc) getBuildNumberMethod.invoke(null,
446                                                      EMPTY_OBJECT_ARRAY);
447
448         }
449         catch (Throwable JavaDoc t) {
450             // Just eat it
451
}
452
453         if (productVersion != null && productVersion.length() > 0 &&
454             buildNumber != null && buildNumber.length() > 0) {
455
456             productVersion += '.' + buildNumber;
457         }
458
459         return productVersion;
460     }
461
462
463     /**
464      * Splits a class name into two strings.
465      * <br>
466      * [0] = package name (or null if the class is unpackaged) <br>
467      * [1] = class name
468      */

469     public String JavaDoc[] parseClassName(String JavaDoc fullClassName) {
470
471         int dotIndex = fullClassName.lastIndexOf(".");
472         String JavaDoc packageName = null;
473         String JavaDoc className = fullClassName;
474
475         if (dotIndex > 0) {
476             packageName = fullClassName.substring(0, dotIndex);
477             className = fullClassName.substring(dotIndex + 1);
478         }
479
480         return new String JavaDoc[] { packageName, className };
481     }
482
483     /**
484      * Formats the class name with trailing square brackets.
485      */

486     public String JavaDoc getArrayClassName(Class JavaDoc clazz) {
487
488         if (clazz.isArray()) {
489             return getArrayClassName(clazz.getComponentType()) + "[]";
490         }
491
492         return clazz.getName();
493     }
494
495     /**
496      * Returns the array type. Returns the specified class if it is not an
497      * array.
498      */

499     public Class JavaDoc getArrayType(Class JavaDoc clazz) {
500
501         if (clazz.isArray()) {
502             return getArrayType(clazz.getComponentType());
503         }
504
505         return clazz;
506     }
507
508     /**
509      * Returns the array dimensions.
510      * Returns 0 if the specified class is not an array.
511      */

512     public int getArrayDimensions(Class JavaDoc clazz) {
513
514         if (clazz.isArray()) {
515             return getArrayDimensions(clazz.getComponentType()) + 1;
516         }
517         
518         return 0;
519     }
520
521     /**
522      * Returns the array dimensions String (i.e. "[][][]").
523      * Returns "" (empty string) if the specified class is not an array.
524      */

525     public String JavaDoc getArrayDimensionsString(Class JavaDoc clazz) {
526         return createPatternString("[]", getArrayDimensions(clazz));
527     }
528
529     /**
530      * Converts the user-friendly array class name to the VM friendly one.
531      * For example:
532      * <UL>
533      * <LI><code>java.lang.String[]</code> becomes
534      * <code>[Ljava.lang.String;</code>
535      * <LI><code>int[][]</code> becomes <code>[[I</code>
536      * <LI><code>byte[]</code> becomes <code>[B</code>
537      * </UL>
538      *
539      * @param className the name of the array class
540      * @param bracketIndex the index (withing className) of the first '['
541      * character
542      */

543     public String JavaDoc convertArrayClassName(String JavaDoc className,
544                                         int bracketIndex) {
545
546         String JavaDoc dimensions = "";
547
548         String JavaDoc brackets = className.substring(bracketIndex).trim();
549         char[] chars = brackets.toCharArray();
550         for (int i = 0; i < chars.length; i++) {
551             if (chars[i] == '[') {
552                 dimensions = dimensions + '[';
553             }
554         }
555
556         String JavaDoc arrayType = className.substring(0, bracketIndex).trim();
557         char prefixChar = 'L';
558
559         Class JavaDoc clazz = (Class JavaDoc) cPrimativeClasses.get(arrayType);
560         if (clazz != null) {
561
562             arrayType = "";
563
564             if (clazz == boolean.class) {
565                 prefixChar = 'Z';
566             }
567             else if (clazz == char.class) {
568                 prefixChar = 'C';
569             }
570             else if (clazz == byte.class) {
571                 prefixChar = 'B';
572             }
573             else if (clazz == short.class) {
574                 prefixChar = 'S';
575             }
576             else if (clazz == int.class) {
577                 prefixChar = 'I';
578             }
579             else if (clazz == float.class) {
580                 prefixChar = 'F';
581             }
582             else if (clazz == double.class) {
583                 prefixChar = 'D';
584             }
585             else if (clazz == long.class) {
586                 prefixChar = 'J';
587             }
588             else if (clazz == void.class) {
589                 return null;
590             }
591         }
592         else {
593             arrayType = arrayType + ';';
594         }
595
596         return dimensions + prefixChar + arrayType;
597     }
598
599     /**
600      * Returns the className with '$'s changed to '.'s
601      */

602     public String JavaDoc getInnerClassName(String JavaDoc className) {
603         return className.replace('$', '.');
604     }
605
606
607     /**
608      * Introspects a Java bean to learn about all its properties,
609      * exposed methods, and events.
610      *
611      * @param beanClass the bean class to be analyzed
612      */

613     public BeanInfo getBeanInfo(Class JavaDoc beanClass)
614     throws IntrospectionException {
615         return Introspector.getBeanInfo(beanClass);
616     }
617
618     /**
619      * Introspects a Java bean to learn all about its properties, exposed
620      * methods, below a given "stop" point.
621      *
622      * @param beanClass the bean class to be analyzed
623      * @param stopClass the base class at which to stop the analysis.
624      * Any methods/properties/events in the stopClass or in its baseclasses
625      * will be ignored in the analysis
626      */

627     public BeanInfo getBeanInfo(Class JavaDoc beanClass, Class JavaDoc stopClass)
628     throws IntrospectionException {
629         return Introspector.getBeanInfo(beanClass, stopClass);
630     }
631
632     /**
633      * Retrieves the value of the named FeatureDescriptor attribute.
634      *
635      * @return The value of the attribute. May be null if the attribute
636      * is unknown.
637      */

638     public Object JavaDoc getAttributeValue(FeatureDescriptor feature,
639                                     String JavaDoc attributeName) {
640
641         if (feature == null || attributeName == null) {
642             return null;
643         }
644
645         return feature.getValue(attributeName);
646     }
647
648     /**
649      * Converts a string to normal Java variable name capitalization.
650      * This normally means converting the first character from upper case
651      * to lower case, but in the (unusual) special case when there is more
652      * than one character and both the first and second characters are upper
653      * case, we leave it alone.
654      * <p>
655      * Thus "FooBar" becomes "fooBar" and "X" becomes "x", but "URL" stays
656      * as "URL".
657      *
658      * @param name the string to be decapitalized
659      *
660      * @return the decapitalized version of the string
661      */

662     public String JavaDoc decapitalize(String JavaDoc name) {
663
664         if (name == null) {
665             return "";
666         }
667
668         return Introspector.decapitalize(name);
669     }
670
671
672     /**
673      * Returns the first sentence of the specified paragraph. Uses
674      * <code>java.text.BreakIterator.getSentenceInstance()</code>
675      */

676     public String JavaDoc getFirstSentence(String JavaDoc paragraph) {
677
678         if (paragraph == null || paragraph.length() == 0) {
679             return "";
680         }
681
682         BreakIterator JavaDoc sentenceBreaks = BreakIterator.getSentenceInstance();
683         sentenceBreaks.setText(paragraph);
684         int start = sentenceBreaks.first();
685         int end = sentenceBreaks.next();
686
687         String JavaDoc sentence = paragraph;
688         
689         if (start >= 0 && end <= paragraph.length()) {
690             sentence = paragraph.substring(start, end);
691         }
692
693         return sentence.trim();
694     }
695
696
697     /**
698      * Creates a String with the specified pattern repeated length
699      * times.
700      */

701     public String JavaDoc createPatternString(String JavaDoc pattern, int length) {
702         if (pattern == null) {
703             return null;
704         }
705
706         int totalLength = pattern.length() * length;
707         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(totalLength);
708         for (int i = 0; i < length; i++) {
709             sb.append(pattern);
710         }
711         return sb.toString();
712     }
713
714     /**
715      * Creates a String of spaces with the specified length.
716      */

717     public String JavaDoc createWhitespaceString(int length) {
718         return createPatternString(" ", length);
719     }
720
721     /**
722      * Returns the FeatureDescriptor's shortDescription or "" if the
723      * shortDescription is the same as the displayName.
724      */

725     public String JavaDoc getDescription(FeatureDescriptor feature) {
726         String JavaDoc description = feature.getShortDescription();
727         
728         if (description == null ||
729             description.equals(feature.getDisplayName())) {
730             
731             description = "";
732         }
733
734         return description;
735     }
736
737     /**
738      * Returns the first sentence of the FeatureDescriptor's
739      * shortDescription. Returns "" if the shortDescription is the same as
740      * the displayName (the default for reflection-generated
741      * FeatureDescriptors).
742      */

743     public String JavaDoc getDescriptionFirstSentence(FeatureDescriptor feature) {
744         return getFirstSentence(getDescription(feature));
745     }
746
747     /**
748      * Sorts an array of FeatureDescriptors based on the method name and
749      * if these descriptors are MethodDescriptors, by param count as well.
750      * To prevent damage to the original array, a clone is made, sorted,
751      * and returned from this method.
752      */

753     public FeatureDescriptor[] sortDescriptors(FeatureDescriptor[] fds) {
754         /*
755          * the variable dolly is used here in reference to the sheep cloned
756          * a few years back by Scottish scientists. - Jonathan
757          */

758         FeatureDescriptor[] dolly = (FeatureDescriptor[])fds.clone();
759         Arrays.sort(dolly, DESCRIPTOR_COMPARATOR);
760         return dolly;
761     }
762     
763     /**
764      * Sorts an array of MethodDescriptors based on the method name and
765      * param count.
766      */

767     public void sortMethodDescriptors(MethodDescriptor[] mds) {
768         Arrays.sort(mds, DESCRIPTOR_COMPARATOR);
769     }
770
771     /**
772      * Sorts an array of PropertyDescriptors based on name.
773      */

774     public void sortPropertyDescriptors(PropertyDescriptor[] pds) {
775         Arrays.sort(pds, DESCRIPTOR_COMPARATOR);
776     }
777
778
779     //
780
// Tea-specific utilities
781
//
782

783
784     /**
785      * Merges several classes together, producing a new class that has all
786      * of the methods of the combined classes. All methods in the combined
787      * class delegate to instances of the source classes. If multiple classes
788      * implement the same method, the first one provided is used.
789      * The merged class implements all of the interfaces provided by the
790      * source classes or interfaces.
791      * <p>
792      * This method uses com.go.trove.util.MergedClass
793      */

794     public Class JavaDoc createContextClass(ClassLoader JavaDoc loader,
795                                     ContextClassEntry[] contextClasses)
796     throws Exception JavaDoc {
797
798         Object JavaDoc[] ret = loadContextClasses(loader, contextClasses);
799         Class JavaDoc[] classes = (Class JavaDoc[]) ret[0];
800         String JavaDoc[] prefixNames = (String JavaDoc[]) ret[1];
801
802         return createContextClass(loader, classes, prefixNames);
803     }
804
805     /**
806      * Merges several classes together, producing a new class that has all
807      * of the methods of the combined classes. All methods in the combined
808      * class delegate to instances of the source classes. If multiple classes
809      * implement the same method, the first one provided is used.
810      * The merged class implements all of the interfaces provided by the
811      * source classes or interfaces.
812      * <p>
813      * This method uses com.go.trove.util.MergedClass
814      */

815     public Class JavaDoc createContextClass(ClassLoader JavaDoc loader,
816                                     Class JavaDoc[] classes,
817                                     String JavaDoc[] prefixNames)
818     throws Exception JavaDoc {
819                            
820
821         ClassInjector classInjector =
822             new ClassInjector(loader, (java.io.File JavaDoc) null, null);
823         
824         Constructor constructor = null;
825         
826         try {
827             constructor = MergedClass.getConstructor(classInjector,
828                                                      classes,
829                                                      prefixNames);
830         }
831         catch (Throwable JavaDoc t) {
832             System.err.println(t);
833             t.printStackTrace();
834             throw new Exception JavaDoc(t.getMessage());
835         }
836
837         Class JavaDoc mergedContextClass = constructor.getDeclaringClass();
838
839         return mergedContextClass;
840     }
841
842     /**
843      * Loads and returns the runtime context classes specified by the
844      * ContextClassEntry array.
845      *
846      * @return a 2 element Object array: <br>
847      * <pre>
848      * index [0] is the Class[] containing the context classes.
849      * index [1] is the String[] containing the context prefix names.
850      */

851     public Object JavaDoc[] loadContextClasses(ClassLoader JavaDoc loader,
852                                        ContextClassEntry[] contextClasses)
853     throws Exception JavaDoc {
854
855         if (loader == null) {
856             throw new IllegalArgumentException JavaDoc("ClassLoader is null");
857         }
858
859         if (contextClasses == null || contextClasses.length == 0) {
860             throw new IllegalArgumentException JavaDoc("No Context Classes");
861         }
862                         
863         Vector classVector = new Vector(contextClasses.length);
864         String JavaDoc[] prefixNames = new String JavaDoc[contextClasses.length];
865         
866         Hashtable uniqueNames = new Hashtable();
867         
868         String JavaDoc className = null;
869         try {
870             for (int i = 0; i < contextClasses.length; i++) {
871                 className = contextClasses[i].getContextClassName();
872                 Class JavaDoc contextClass = loader.loadClass(className);
873                 
874                 classVector.addElement(contextClass);
875                 String JavaDoc prefixName = contextClasses[i].getPrefixName();
876                 if (prefixName != null) {
877                     
878                     if (uniqueNames.get(prefixName) != null) {
879                         throw new IllegalArgumentException JavaDoc(
880                             "Duplicate Context name: " + prefixName);
881                     }
882                     
883                     uniqueNames.put(prefixName, prefixName);
884                     prefixNames[i] = prefixName + "$";
885                 }
886                 else {
887                     prefixNames[i] = null;
888                 }
889             }
890         }
891         catch (ClassNotFoundException JavaDoc cnfe) {
892             throw new ClassNotFoundException JavaDoc("Context class \"" + className +
893                                              "\" was not found in the " +
894                                              "current Classpath.");
895         }
896         
897         Class JavaDoc[] classes = new Class JavaDoc[classVector.size()];
898         classVector.copyInto(classes);
899         
900         return new Object JavaDoc[] {classes, prefixNames};
901     }
902
903
904     /**
905      * Returns true if it is likely that the specified class serves as
906      * a Tea runtime context class.
907      */

908     public boolean isLikelyContextClass(Class JavaDoc clazz) {
909         return (OutputReceiver.class.isAssignableFrom(clazz) ||
910                 getClassName(clazz).toLowerCase().endsWith("context"));
911     }
912
913    
914     /**
915      * Gets the MethodDescriptors of the specified context class including
916      * all of the MethodDescriptors for methods declared in the class's
917      * superclass and interfaces
918      *
919      * @param contextClass the Tea context Class to introspect (any class will
920      * work fine)
921      */

922     public MethodDescriptor[] getTeaContextMethodDescriptors(
923                                                          Class JavaDoc contextClass) {
924
925         return getTeaContextMethodDescriptors(contextClass, false);
926     }
927
928     /**
929      * Gets the MethodDescriptors of the specified context class
930      *
931      * @param contextClass the Tea context Class to introspect (any class will
932      * work fine)
933      * @param specifiedClassOnly true indicates that this function should
934      * only return MethodDescriptors declared by the specified Class.
935      */

936     public MethodDescriptor[] getTeaContextMethodDescriptors(
937                                                  Class JavaDoc contextClass,
938                                                  boolean specifiedClassOnly) {
939
940         Vector v = new Vector();
941
942         MethodDescriptor[] methodDescriptors = null;
943         try {
944             BeanInfo beanInfo = getBeanInfo(contextClass);
945             methodDescriptors = beanInfo.getMethodDescriptors();
946         }
947         catch (Throwable JavaDoc e) {
948             e.printStackTrace();
949         }
950
951         if (methodDescriptors != null) {
952             
953             for (int i = 0; i < methodDescriptors.length; i++) {
954                 
955                 MethodDescriptor md = methodDescriptors[i];
956                 Class JavaDoc declaringClass = md.getMethod().getDeclaringClass();
957
958                 if (declaringClass != Object JavaDoc.class &&
959                     !md.isHidden() &&
960                     (!specifiedClassOnly || declaringClass == contextClass)) {
961                     
962                     v.addElement(md);
963                 }
964             }
965         }
966         
967         methodDescriptors = new MethodDescriptor[v.size()];
968         v.copyInto(methodDescriptors);
969         
970         sortMethodDescriptors(methodDescriptors);
971
972         return methodDescriptors;
973     }
974
975
976     /**
977      * Gets the complete, combined set of MethodDescriptors for the specified
978      * context classes.
979      *
980      * @param contextClasses the Tea context classes to introspect
981      */

982     public MethodDescriptor[] getTeaContextMethodDescriptors(
983                                                  Class JavaDoc[] contextClasses) {
984
985         Vector v = new Vector();
986         Hashtable methods = new Hashtable();
987
988         if (contextClasses != null) {
989
990             // Combine all of the MethodDescriptors
991

992             for (int i = 0; i < contextClasses.length; i++) {
993                 Class JavaDoc c = contextClasses[i];
994                 if (c == null) {
995                     continue;
996                 }
997
998                 MethodDescriptor[] mds =
999                     getTeaContextMethodDescriptors(c, false);
1000                
1001                for (int j = 0; j < mds.length; j++) {
1002                    MethodDescriptor md = mds[j];
1003                    Method m = md.getMethod();
1004                    // Check uniqueness of method
1005
if (methods.get(m) == null) {
1006                        methods.put(m, m);
1007                        v.addElement(md);
1008                    }
1009                }
1010            }
1011        }
1012
1013        MethodDescriptor[] methodDescriptors = new MethodDescriptor[v.size()];
1014        v.copyInto(methodDescriptors);
1015
1016        // Sort the combined results
1017
sortMethodDescriptors(methodDescriptors);
1018
1019        return methodDescriptors;
1020    }
1021
1022    
1023
1024    /**
1025     * A function that returns an array of all the available properties on
1026     * a given class.
1027     * <p>
1028     * <b>NOTE:</b> If possible, the results of this method should be cached
1029     * by the caller.
1030     *
1031     * @param beanClass the bean class to introspect
1032     *
1033     * @return an array of all the available properties on the specified class.
1034     */

1035    public PropertyDescriptor[] getTeaBeanPropertyDescriptors(
1036                                                           Class JavaDoc beanClass) {
1037
1038        // Code taken from KettleUtilities.getPropertyDescriptors(Class)
1039

1040        if (beanClass == null) {
1041            return NO_PROPERTIES;
1042        }
1043
1044        PropertyDescriptor[] properties = null;
1045
1046        Map allProps = null;
1047        try {
1048            allProps = BeanAnalyzer.getAllProperties(beanClass);
1049        }
1050        catch (Throwable JavaDoc t) {
1051            return NO_PROPERTIES;
1052        }
1053
1054        Collection cleanProps = new ArrayList(allProps.size());
1055
1056        Iterator JavaDoc it = allProps.entrySet().iterator();
1057        while (it.hasNext()) {
1058
1059            Map.Entry entry = (Map.Entry) it.next();
1060            String JavaDoc name = (String JavaDoc) entry.getKey();
1061            PropertyDescriptor desc = (PropertyDescriptor) entry.getValue();
1062            
1063            // Discard properties that have no name or should be hidden.
1064
if (name == null || name.length() == 0 || "class".equals(name)) {
1065                continue;
1066            }
1067            
1068            if (desc instanceof KeyedPropertyDescriptor) {
1069
1070                KeyedPropertyDescriptor keyed = (KeyedPropertyDescriptor) desc;
1071                Class JavaDoc type = keyed.getKeyedPropertyType();
1072
1073                try {
1074                    // Convert the KeyedPropertyDescriptor to a
1075
// ArrayIndexPropertyDescriptor
1076
desc = new ArrayIndexPropertyDescriptor(beanClass, type);
1077                }
1078                catch (Throwable JavaDoc t) {
1079                    continue;
1080                }
1081            }
1082            else if (!beanClass.isArray() && desc.getReadMethod() == null) {
1083                continue;
1084            }
1085            
1086            cleanProps.add(desc);
1087        }
1088
1089        properties = (PropertyDescriptor[]) cleanProps.toArray
1090            (new PropertyDescriptor[cleanProps.size()]);
1091        
1092        // Sort 'em!
1093
sortPropertyDescriptors(properties);
1094        
1095        return properties;
1096    }
1097
1098
1099    /**
1100     * Returns the full class name of the specified class. This method
1101     * provides special formatting for array and inner classes. If the
1102     * specified class is implicitly imported by Tea, then its package is
1103     * omitted in the returned name.
1104     */

1105    public String JavaDoc getTeaFullClassName(Class JavaDoc clazz) {
1106
1107        if (isImplicitTeaImport(clazz)) {
1108            return getClassName(clazz);
1109        }
1110        
1111        return getFullClassName(clazz);
1112    }
1113
1114    /**
1115     * Returns true if the specified class is
1116     * implicitly imported by Tea.
1117     * <p>
1118     * Returns true if the specified class represents a primitive type or
1119     * a class or interface defined in one of the IMPLICIT_TEA_IMPORTS
1120     * packages. This method also works for array types.
1121     */

1122    public boolean isImplicitTeaImport(Class JavaDoc clazz) {
1123        if (getArrayType(clazz).isPrimitive()) {
1124            return true;
1125        }
1126
1127        return isImplicitTeaImport(getClassPackage(clazz));
1128    }
1129
1130    /**
1131     * Returns true if the specified class or package is
1132     * implicitly imported by Tea.
1133     */

1134    public boolean isImplicitTeaImport(String JavaDoc classOrPackageName) {
1135
1136        if (classOrPackageName == null) {
1137            return false;
1138        }
1139
1140        if (cPrimativeClasses.get(classOrPackageName) != null) {
1141            return true;
1142        }
1143        
1144        for (int i = 0; i < IMPLICIT_TEA_IMPORTS.length; i++) {
1145            String JavaDoc prefix = IMPLICIT_TEA_IMPORTS[i];
1146
1147            if (classOrPackageName.startsWith(prefix)) {
1148                return true;
1149            }
1150        }
1151
1152        return false;
1153    }
1154
1155    
1156    /**
1157     * Returns true if the specified method accepts a
1158     * <code>Substitution</code> as its last parameter.
1159     */

1160    public boolean acceptsSubstitution(MethodDescriptor md) {
1161        return acceptsSubstitution(md.getMethod());
1162    }
1163
1164    /**
1165     * Returns true if the specified method accepts a
1166     * <code>Substitution</code> as its last parameter.
1167     */

1168    public boolean acceptsSubstitution(Method m) {
1169        Class JavaDoc[] paramTypes = m.getParameterTypes();
1170        if (paramTypes.length > 0) {
1171            // Check if last param is a Substitution
1172
Class JavaDoc lastParam = paramTypes[paramTypes.length - 1];
1173            return Substitution.class.isAssignableFrom(lastParam);
1174        }
1175        return false;
1176    }
1177
1178    /**
1179     * Returns true if the specifed class is compatible with Tea's
1180     * <code>foreach</code> statement. Compatibility implies that the
1181     * class can be iterated on by the <code>foreach</code>.
1182     */

1183    public boolean isForeachCompatible(Class JavaDoc clazz) {
1184        if (clazz == null) {
1185            return false;
1186        }
1187
1188        return (clazz.isArray() ||
1189                Collection.class.isAssignableFrom(clazz));
1190    }
1191
1192    /**
1193     * Returns true if the specifed class is compatible with Tea's <code>if
1194     * </code> statement. Only Boolean.class and boolean.class qualify.
1195     */

1196    public boolean isIfCompatible(Class JavaDoc clazz) {
1197
1198        if (clazz == null) {
1199            return false;
1200        }
1201
1202        return (clazz == boolean.class || clazz == Boolean JavaDoc.class);
1203    }
1204
1205    
1206    //
1207
// File name utilities
1208
//
1209

1210
1211    /**
1212     * Removes the file extension (all text after the last '.' character)
1213     * from the specified fileName.
1214     *
1215     * @param fileName the String with an (optional) extension
1216     *
1217     * @return the String param sans extension
1218     */

1219    public String JavaDoc removeFileExtension(String JavaDoc fileName) {
1220        
1221        int dotIndex = fileName.lastIndexOf(".");
1222        if (dotIndex > 0) {
1223            fileName = fileName.substring(0, dotIndex);
1224        }
1225        
1226        return fileName;
1227    }
1228
1229    /**
1230     * Returns true if the specifed fileName is a Tea file.
1231     */

1232    public boolean isTeaFileName(String JavaDoc fileName) {
1233        return compareFileExtension(fileName, TEA_FILE_EXTENSION);
1234    }
1235    
1236    /**
1237     * Returns true if the specified fileName ends with the specified
1238     * file extension.
1239     */

1240    public boolean compareFileExtension(String JavaDoc fileName,
1241                                        String JavaDoc extension) {
1242        
1243        if (fileName == null || extension == null) {
1244            return false;
1245        }
1246        
1247        fileName = fileName.toLowerCase().trim();
1248        extension = extension.toLowerCase();
1249        return (fileName.endsWith(extension));
1250    }
1251    
1252    //
1253
//
1254
// Non-public interface
1255
//
1256
//
1257

1258    /**************************************************************************
1259     * This inner class is used to compare or sort FeatureDescriptors by name
1260     * if the names are the same and the FeatureDescriptor is an instance of
1261     * MethodDescriptor it also sorts by parameter count.
1262     *
1263     * @author Mark Masse
1264     * @version
1265     * <!--$$Revision:--> 15 <!-- $-->, <!--$$JustDate:--> 12/06/00 <!-- $-->
1266     */

1267    private static class DescriptorComparator implements Comparator {
1268
1269        /**
1270         * The compare method determines whether x is less than, greater
1271         * than, or equal to y.
1272         *
1273         * @return less than zero if x < y; zero if x = y; greater than zero
1274         * if x > y.
1275         */

1276        public int compare(Object JavaDoc x, Object JavaDoc y) {
1277            int nameResult = compareNames(x,y);
1278            
1279            if (nameResult == 0 && x != null && y != null &&
1280                x instanceof MethodDescriptor &&
1281                y instanceof MethodDescriptor) {
1282
1283                MethodDescriptor mdX = (MethodDescriptor) x;
1284                MethodDescriptor mdY = (MethodDescriptor) y;
1285
1286                Method mX = mdX.getMethod();
1287                Method mY = mdY.getMethod();
1288                
1289                return (getParamCount(mX) - getParamCount(mY));
1290            }
1291            return nameResult;
1292        }
1293        
1294        private int compareNames(Object JavaDoc x, Object JavaDoc y) {
1295        
1296            if (x != null && y != null &&
1297                x instanceof FeatureDescriptor &&
1298                y instanceof FeatureDescriptor) {
1299
1300                FeatureDescriptor fdX = (FeatureDescriptor) x;
1301                FeatureDescriptor fdY = (FeatureDescriptor) y;
1302
1303                return fdX.getName().compareTo(fdY.getName());
1304            }
1305
1306            return 0;
1307        }
1308
1309        private int getParamCount(Method method) {
1310
1311            int paramCount = 0;
1312
1313            Class JavaDoc[] paramClasses = method.getParameterTypes();
1314            if (paramClasses != null) {
1315                paramCount = paramClasses.length;
1316            }
1317
1318            return paramCount;
1319        }
1320    }
1321
1322    /**************************************************************************
1323     * Slight variation of Tea's KeyedPropertyDescriptor. Does essentially
1324     * the same thing, but provides more user-friendly information.
1325     *
1326     * @author Mark Masse
1327     * @version
1328     * <!--$$Revision:--> 15 <!-- $-->, <!--$$JustDate:--> 12/06/00 <!-- $-->
1329     */

1330    private class ArrayIndexPropertyDescriptor
1331    extends PropertyDescriptor {
1332        
1333        private Class JavaDoc mPropertyType;
1334
1335        public ArrayIndexPropertyDescriptor(Class JavaDoc beanClass,
1336                                            Class JavaDoc propertyType)
1337        throws IntrospectionException {
1338            
1339            super(BeanAnalyzer.KEYED_PROPERTY_NAME, beanClass, null, null);
1340
1341            mPropertyType = propertyType;
1342
1343            String JavaDoc typeName = getTeaFullClassName(propertyType);
1344
1345            setShortDescription("An indexed property with " +
1346                                typeName + " elements");
1347            
1348        }
1349
1350        public Class JavaDoc getPropertyType() {
1351            return mPropertyType;
1352        }
1353    }
1354    
1355
1356
1357    /**************************************************************************
1358     * TeaToolsUtils test class
1359     *
1360     * @author Mark Masse
1361     * @version
1362     * <!--$$Revision:--> 15 <!-- $-->, <!--$$JustDate:--> 12/06/00 <!-- $-->
1363     */

1364    private static class Tester {
1365        
1366        Tester(String JavaDoc[] args) throws Exception JavaDoc {
1367
1368            TeaToolsUtils utils = new TeaToolsUtils();
1369            
1370            if (args == null || args.length == 0) {
1371                System.err.println("No classes specified, using defaults");
1372                args =
1373                    new String JavaDoc[] {
1374                        int.class.getName(),
1375                        Object JavaDoc.class.getName(),
1376                        String JavaDoc[].class.getName(),
1377                        int[].class.getName(),
1378                        int[][].class.getName(),
1379                        boolean[].class.getName(),
1380                        Tester.class.getName() };
1381            }
1382            
1383            for (int i = 0; i < args.length; i++) {
1384                
1385                String JavaDoc className = args[i];
1386                System.out.println("\nTesting with class: " + className);
1387
1388                System.out.println("Class getClassForName(String)");
1389
1390                Class JavaDoc clazz = utils.getClassForName(className);
1391                if (clazz == null) {
1392                    System.err.println("Failed to load class!");
1393                    continue;
1394                }
1395
1396                System.out.println("BeanInfo getBeanInfo(Class)");
1397                BeanInfo info = utils.getBeanInfo(clazz);
1398                
1399                System.out.println("String getDescriptionFirstSentence(FeatureDescriptor)");
1400                System.out.println(utils.getDescriptionFirstSentence(info.getBeanDescriptor()));
1401
1402                System.out.println("String getFirstSentence(String)");
1403
1404                System.out.println(utils.getFirstSentence("Hello World. Hi!"));
1405                
1406
1407                System.out.println("String getFullClassName(Class)");
1408
1409                System.out.println(utils.getFullClassName(clazz));
1410
1411                System.out.println("String getFullClassName(String)");
1412                System.out.println(utils.getFullClassName(className));
1413
1414                System.out.println("String getClassName(Class)");
1415
1416                System.out.println(utils.getClassName(clazz));
1417
1418                System.out.println("String getClassName(String)");
1419                System.out.println(utils.getClassName(className));
1420
1421                System.out.println("String getClassPackage(Class)");
1422                System.out.println(utils.getClassPackage(clazz));
1423
1424                System.out.println("String getClassPackage(String)");
1425                System.out.println(utils.getClassPackage(className));
1426
1427                System.out.println("String getTeaFullClassName(Class)");
1428                System.out.println(utils.getTeaFullClassName(clazz));
1429
1430                System.out.println("boolean isImplicitTeaImport(Class)");
1431
1432                System.out.println(utils.isImplicitTeaImport(clazz));
1433
1434                System.out.println("MethodDescriptor[] getTeaContextMethodDescriptors(Class)");
1435
1436                MethodDescriptor[] mds =
1437                    utils.getTeaContextMethodDescriptors(clazz);
1438                
1439                printMethodDescriptors(mds, utils);
1440
1441                System.out.println("MethodDescriptor[] getTeaContextMethodDescriptors(Class, boolean)");
1442
1443
1444                mds = utils.getTeaContextMethodDescriptors(clazz, true);
1445                printMethodDescriptors(mds, utils);
1446
1447                System.out.println("MethodDescriptor[] getTeaBeanPropertyDescriptors(Class)");
1448
1449                PropertyDescriptor[] pds =
1450                    utils.getTeaBeanPropertyDescriptors(clazz);
1451                
1452                printPropertyDescriptors(pds, utils);
1453
1454            }
1455
1456            if (args.length > 1) {
1457                System.out.println("");
1458                System.out.println("");
1459                System.out.println("MethodDescriptor[] getTeaContextMethodDescriptors(Class[])");
1460                
1461                Class JavaDoc[] classes = new Class JavaDoc[args.length];
1462                for (int i = 0; i < classes.length; i++) {
1463                    classes[i] = utils.getClassForName(args[i]);
1464                }
1465                
1466                MethodDescriptor[] mds =
1467                    utils.getTeaContextMethodDescriptors(classes);
1468                printMethodDescriptors(mds, utils);
1469            }
1470        }
1471
1472
1473        private void printMethodDescriptors(MethodDescriptor[] mds,
1474                                            TeaToolsUtils utils) {
1475
1476            for (int i = 0; i < mds.length; i++) {
1477                printMethodDescriptor(mds[i], utils);
1478            }
1479        }
1480
1481        private void printMethodDescriptor(MethodDescriptor md,
1482                                           TeaToolsUtils utils) {
1483
1484            printlnIndented("Method: " + md.getName(), 2);
1485            String JavaDoc desc = utils.getDescriptionFirstSentence(md);
1486            if (desc.length() > 0) {
1487                printlnIndented(desc, 8);
1488            }
1489        }
1490
1491
1492        private void printPropertyDescriptors(PropertyDescriptor[] pds,
1493                                              TeaToolsUtils utils) {
1494
1495            for (int i = 0; i < pds.length; i++) {
1496                printPropertyDescriptor(pds[i], utils);
1497            }
1498        }
1499
1500        private void printPropertyDescriptor(PropertyDescriptor pd,
1501                                             TeaToolsUtils utils) {
1502
1503            printlnIndented("Property: " + pd.getName(), 2);
1504            String JavaDoc desc = utils.getDescriptionFirstSentence(pd);
1505            if (desc.length() > 0) {
1506                printlnIndented(desc, 8);
1507            }
1508        }
1509
1510
1511
1512        private void printlnIndented(Object JavaDoc o, int indent) {
1513            StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1514            for (int i = 0; i < indent; i++) {
1515                sb.append(' ');
1516            }
1517            
1518            System.out.println(sb.toString() + o);
1519        }
1520        
1521    }
1522}
1523
Popular Tags