Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/tripi/asp/JavaObjectNode.java


1   /**
2    * ArrowHead ASP Server 
3    * This is a source file for the ArrowHead ASP Server - an 100% Java
4    * VBScript interpreter and ASP server.
5    *
6    * For more information, see http://www.tripi.com/arrowhead
7    *
8    * Copyright (C) 2002  Terence Haddock
9    *
10   * This program is free software; you can redistribute it and/or modify
11   * it under the terms of the GNU General Public License as published by
12   * the Free Software Foundation; either version 2 of the License, or
13   * (at your option) any later version.
14   *
15   * This program is distributed in the hope that it will be useful,
16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   * GNU General Public License for more details.
19   *
20   * You should have received a copy of the GNU General Public License
21   * along with this program; if not, write to the Free Software
22   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23   *
24   */
25  package com.tripi.asp;
26  
27  import java.util.Hashtable;
28  import java.util.Vector;
29  import java.util.Map;
30  import java.lang.reflect.*;
31  import org.apache.log4j.Category;
32  
33  /**
34   * JavaObjectNode class handles the interaction between Java objects
35   * and ASP objects.
36   * @author Terence Haddock
37   * @version 0.9
38   */
39  public class JavaObjectNode extends DefaultNode implements ObjectNode, MapNode
40  {
41      /** Debugging category */
42      private static Category DBG = Category.getInstance(JavaObjectNode.class);
43  
44      /** Java object this ASP node is referencing */
45      Object obj;
46  
47      /**
48       * Constructor
49       * @param obj Java object this ASP object is referencing
50       */
51      public JavaObjectNode(Object obj)
52      {
53          this.obj = obj;
54      }
55  
56      /**
57       * getField obtains a field of a Java object. Returns a JavaFieldNode
58       * reference to this field.
59       * @param ident Identifier of field to obtain    
60       * @see ObjectNode#getField(IdentNode)
61       */
62      public Object getField(IdentNode ident)
63      {
64          if (DBG.isDebugEnabled())
65              DBG.debug("getField: " + ident);
66          String name = ident.ident;
67          return new JavaFieldNode(obj, ident);
68      }
69  
70      /**
71       * Obtains the Java sub-object contained within this ASP node.
72       * @return Java sub-object
73       */
74      public Object getSubObject()
75      {
76          return obj;
77      }
78  
79      /**
80       * Sets the value of this Java object, if the Java object implements
81       * the SimpleReference object.
82       * @param value Value to set
83       * @throws AspException if an error occurs.
84       */
85      public void setValue(Object value) throws AspException
86      {
87          if (obj instanceof SimpleReference) {
88              ((SimpleReference)obj).setValue(value);
89          } else {
90              throw new AspReadOnlyException();
91          }
92      }
93  
94      /**
95       * Obtains an array index, or calls a function, of a Java object.
96       * @param varlist Variable list of parameters.
97       * @param context AspContext under which this Java object was called.
98       * @return Value of the array or return value of the function.
99       * @throws AspException if an exception occurs.
100      * @see MapNode#getIndex(VarListNode,AspContext)
101      */
102     public Object getIndex(VarListNode varlist, AspContext context) throws AspException
103     {
104         /* See if this is a simple type of reference */
105         if (varlist.size() == 0 && obj instanceof SimpleReference)
106         {
107             if (DBG.isDebugEnabled())
108                 DBG.debug("Trying simple reference");
109             Object subValue = ((SimpleReference)obj).getValue();
110             if (DBG.isDebugEnabled())
111             {
112                 DBG.debug("Sub-value: " + subValue);
113                 if (subValue != null)
114                     DBG.debug("Sub-value class: " +
115                         subValue.getClass());
116             }
117             /* Return it as a Node object */
118             return Types.coerceToNode(subValue);
119         }
120         /* Only single dimensional lists supported */
121         if (varlist.size() != 1) {
122             throw new AspInvalidArgumentsException(""+varlist.size());
123         }
124         /* Handle MapNode objects */
125         if (obj instanceof MapNode) {
126             return ((MapNode)obj).getIndex(varlist, context);
127         }
128         /* Handle Java arrays */
129         if (obj.getClass().isArray()) {
130             Vector vec = (Vector)varlist.execute(context);
131             Integer i = Types.coerceToInteger(vec.get(0));
132             Object sObj = Array.get(obj, i.intValue());
133             return Types.coerceToNode(sObj);
134         }
135         /* Map and SimpleMap objects are all that is left */
136         if (!(obj instanceof Map)&&!(obj instanceof SimpleMap)) {
137             throw new AspException("Invalid argument: " + obj.getClass());
138         }
139         Vector vec = (Vector)varlist.execute(context);
140         Object objVal = vec.get(0);
141         if (!(objVal instanceof Integer) && !(objVal instanceof String))
142             objVal = Types.coerceToString(objVal);
143         if (DBG.isDebugEnabled())
144             DBG.debug("getIndex(" + objVal + ")");
145         if (obj instanceof Map) {
146             return new JavaAccessorNode((Map)obj, objVal);
147         } else {
148             return new JavaAccessorNode((SimpleMap)obj, objVal);
149         }
150     }
151 
152     /**
153      * Returns the upper bound of the array held within this JavaObjectNode.
154      * Supports only single-dimensional Java arrays of objects.
155      * @param dimension Which dimension to obtain
156      * @return upper bound
157      * @see MapNode#getUBOUND
158      * @throws AspSubscriptOutOfRangeException if dimension does not equal 1
159      */
160     public int getUBOUND(int dimension) throws AspException
161     {
162         if (dimension != 1) {
163             throw new AspSubscriptOutOfRangeException("UBOUND");
164         }
165         if (obj instanceof Object[]) {
166             return ((Object[])obj).length - 1;
167         }
168         throw new AspException("LBOUND called on non-array");
169     }
170 
171     /**
172      * Returns the lower bound of the array contained within the Java object.
173      * @param dimension Which dimension to obtain.
174      * @return lower bound, usually 0
175      * @see MapNode#getLBOUND
176      * @throws AspSubscriptOutOfRangeExceptoin if dimension does not equal 1
177      */
178     public int getLBOUND(int dimension) throws AspException
179     {
180         if (dimension != 1) {
181             throw new AspSubscriptOutOfRangeException("UBOUND");
182         }
183         if (obj instanceof Object[]) {
184             return 0;
185         }
186         throw new AspException("LBOUND called on non-array");
187     }
188 
189     /**
190      * Converts this JavaObjectNode to a string, for debugging.
191      * @return String representation of this Java object
192      */
193     public String toString()
194     {
195         if (obj == null)
196         {
197             return "{JavaObjectNode=null}";
198         } else {
199             return "{JavaObjectNode=" + obj.toString() + "(" + obj.getClass() + ")}";
200         }
201     }
202 
203     /**
204      * JavaAccessorNode class handles the obtaining of object
205      * within Java objects by strings.
206      */
207     static class JavaAccessorNode extends DefaultNode implements SimpleReference
208     {
209         /** simpleMapObj is used if the sub-object is an instance of the
210           * SimpleMap interface.
211           */
212         SimpleMap simpleMapObj;
213 
214         /**
215          * mapObj isused if the sub-object is an instance of the Map interface.
216           */
217         Map mapObj;
218 
219         /**
220          * The key being obtained from the map.
221          */
222         Object key;
223 
224         /**
225           * Constructor, Map form.
226          * @param obj Map object
227          * @param str String being referenced
228          */
229         public JavaAccessorNode(Map obj, Object key)
230         {
231             this.mapObj = obj;
232             this.key = key;
233         }
234 
235         /**
236          * Constructor, SimpleMap form.
237          * @param obj SimpleMap object.
238          * @param str String being referenced.
239          */
240         public JavaAccessorNode(SimpleMap obj, Object key)
241         {
242             this.simpleMapObj = obj;
243             this.key = key;
244         }
245 
246         /**
247          * getValue is a SimpleReference method which obtains the value
248          * of the current reference.
249           * @return value of the current reference
250           */
251         public Object getValue() throws AspException
252         {
253             Object ret;
254             if (mapObj != null) {
255                 ret = Types.coerceToNode(mapObj.get(key));
256                 if (DBG.isDebugEnabled())
257                     if (ret != null)
258                         DBG.debug("Getting value of " + mapObj +  " = "
259                             + ret + "(" + ret.getClass() + ")");
260                     else
261                         DBG.debug("Getting value of " + simpleMapObj +  " = null");
262             } else {
263                 ret = Types.coerceToNode(simpleMapObj.get(key));
264                 if (DBG.isDebugEnabled())
265                     if (ret != null)
266                         DBG.debug("Getting value of " + simpleMapObj +  " = " +
267                             ret + "(" + ret.getClass() + ")");
268                     else
269                         DBG.debug("Getting value of " + simpleMapObj +  " = null");
270             }
271             return ret;
272         }
273 
274         /**
275          * setValue sets the value of the current reference.
276          * @param val New value
277          */
278         public void setValue(Object val) throws AspException
279         {
280             val = Types.dereference(val);
281             if (mapObj != null) {
282                 mapObj.put(key, val);
283             } else {
284                 simpleMapObj.put(key, val);
285             }
286         }
287     }
288 
289     /**
290      * The JavaFieldNode class handles referencing fields within Java objects.
291      */
292     static class JavaFieldNode extends DefaultNode implements FunctionNode, SimpleReference
293     {
294         /** Global cache of data about classes */
295         static private Hashtable classData = new Hashtable();
296 
297         /** The object being referenced by this class */
298         Object obj;
299 
300         /** The identifier of the field being referenced */
301         IdentNode ident;
302 
303         /** Vector of methods which match the identifier being referenced */
304         Vector matchingMethods;
305 
306         /** Vector of fields which match the identifier being referenced */
307         Vector matchingFields;
308     
309         /**
310           * Constructor.
311          * @param obj Object being referenced
312          * @param field IdentNode identifier of field being referenced
313           */
314         JavaFieldNode(Object obj, IdentNode field)
315         {
316             this.obj = obj;
317             this.ident = field;
318             matchingMethods = getMatchingMethods(obj.getClass(), field);
319             matchingFields = getMatchingFields(obj.getClass(), field);
320         }
321 
322         /**
323          * Executes this field given a parameter list.
324          * @param vars Parameters to execute.
325          * @param context Current context
326          * @see FunctionNode#execute(VarListNode, AspContext)
327          */
328         public Object execute(VarListNode vars, AspContext context)
329             throws AspException
330         {
331             if (DBG.isDebugEnabled())
332                 DBG.debug("Execute " + ident + 
333                     " on " + obj.getClass() + " with " +
334                     vars.size() + " parameters");
335 
336             Vector values = executeAndDereference(vars, context);
337             Method met = findMethod(values);
338 
339             if (met == null) {
340                 if (DBG.isDebugEnabled())
341                     DBG.debug("Associated method " +
342                     "not found, trying field access " +
343                     "on " + obj);
344                 /* Get the sub-object */
345                 Object subObj = getValue();
346                 /* Call the sub-object's function */
347                 if (subObj instanceof MapNode) {
348                     return ((MapNode)subObj).getIndex(vars, context);
349                 }
350                 throw new AspException("Unknown function: " + ident);
351             }
352             Object res = executeMethod(met, values);
353 
354             Class paramTypes[] = met.getParameterTypes();
355             for (int i = 0; i < paramTypes.length; i++)
356             {
357                 if (paramTypes[i] == ByRefValue.class) {
358                     Object finalValue = values.get(i);
359                     Object calledValue = vars.get(i);
360                     if (calledValue instanceof IdentNode) {
361                         context.setValue((IdentNode)calledValue, finalValue);
362                     } else if (calledValue instanceof SimpleReference) {
363                         ((SimpleReference)calledValue).setValue(finalValue);
364                     } else {
365                         /* Cannot set value */
366                     }
367                 }
368             }
369             
370             return res;
371         }
372 
373         /**
374          * Sets the value of a field.
375          * @param setObj new value of field.
376          * @throws AspException if an error occurs
377          * @see SimpleReference#setValue(Object)
378          */
379         public void setValue(Object setObj) throws AspException
380         {
381             if (matchingFields.size() == 0 && matchingMethods.size() == 0)
382             {
383                 throw new AspException("Unknown field name: " + ident);
384             }
385             if (matchingFields.size()<1)
386             {
387                 VarListNode vl = new VarListNode();
388                 vl.append(setObj);
389                 Vector vec = executeAndDereference(vl, null);
390                 Method met = findMethod(vec);
391                 if (met != null) {
392                     executeMethod(met, vec);
393                     return;
394                 }
395                 throw new AspException("Unknown field name: " + ident);
396             }
397             while (setObj instanceof JavaObjectNode) {
398                 if (DBG.isDebugEnabled())
399                     DBG.debug("Dereferencing: " + setObj);
400                 setObj = ((JavaObjectNode)setObj).getSubObject();
401             }
402             setObj = Types.dereference(setObj);
403             if (matchingFields.size()>1)
404             {
405                 throw new AspException("Too many fields match: " + ident);
406             }
407             Field field = (Field)matchingFields.get(0);
408             try {
409                 if (DBG.isDebugEnabled())
410                     DBG.debug("Setting field " + field +
411                         " = " + setObj);
412                 field.set(obj, setObj);
413             } catch (IllegalAccessException ex)
414             {
415                 throw new AspException("Illegal Access Exception: " + ex.toString());
416             }
417         }
418 
419         /**
420          * Obtains the value of a field.
421          * @return the value of the current field.
422          * @throws AspException if an error occurs
423          * @see SimpleReference#getValue
424           */
425         public Object getValue() throws AspException
426         {
427             if (DBG.isDebugEnabled())
428                 DBG.debug("Getvalue of field " + ident +
429                     " of object: " + obj);
430             if (matchingFields.size()<1)
431             {
432                 Vector vec = new Vector();
433                 Method met = findMethod(vec);
434                 if (met != null) {
435                     return executeMethod(met, vec);
436                 }
437                 throw new AspException("Unknown field name: " + ident);
438             }
439             if (matchingFields.size()>1)
440             {
441                 throw new AspException("Too many fields match: " + ident);
442             }
443             Field field = (Field)matchingFields.get(0);
444             if (DBG.isDebugEnabled())
445                 DBG.debug("One field matches: " + field);
446             try {
447                 return Types.coerceToNode(field.get(obj));
448             } catch (IllegalAccessException ex)
449             {
450                 throw new AspException("Illegal Access Exception: " + ex.toString());
451             }
452         }
453 
454         /**
455          * Internal function which executes and dereferences
456          * the list of parameters in a VarListNode.
457          * @param params Parameters to execute and dereference
458           * @param context AspContext under which to execute the variables.
459          * @return The list of parameters executed and dereferenced.
460          * @throws AspException if an error occurs.
461          */
462         static private Vector executeAndDereference(VarListNode params,
463             AspContext context) throws AspException
464         {
465             Vector vec = (Vector)params.execute(context);
466             Vector deref = new Vector();
467             for (int i = 0; i < vec.size(); i++)
468             {
469                 Object value = vec.get(i);
470                 if (value instanceof JavaObjectNode) {
471                     value = ((JavaObjectNode)value).
472                         getSubObject();
473                 }
474                 deref.add(Types.dereference(value));
475             }
476             return deref;
477         }
478 
479         /**
480          * Internal method which finds a method which matches the
481          * parameter list given.
482          * Uses the current field, method name.
483          * @param values Parameters for the method call.
484          * @return Method which matches the given parameters.
485          */
486         private Method findMethod(Vector values)
487         {
488             int curScore = 0;
489             Method curMethod = null;
490             if (DBG.isDebugEnabled()) DBG.debug("Number of methods: " +
491                 matchingMethods.size());
492             for (int i = 0; i < matchingMethods.size(); i++)
493             {
494                 Method method = (Method)matchingMethods.get(i);
495                 Class params[] = method.getParameterTypes();
496                 int score = parametersAreCompatible(params, values);
497                 if (score > curScore)
498                 {
499                     curScore = score;
500                     curMethod = method;
501                 }
502                 if (DBG.isDebugEnabled())
503                 {
504                     DBG.debug("Score: " + score);
505                     DBG.debug("curScore: " + curScore);
506                     DBG.debug("curMethod: " + curMethod);
507                 }
508             }
509             return curMethod;
510         }
511 
512         /**
513          * Internal method which executes a method with the given parameter
514          * list.
515          * @param met Method to execute
516          * @param parameter Parameters for the method.
517          * @return return value of method call
518          * @throws AspException if an error ocurrs.
519          */
520         private Object executeMethod(Method met, Vector params)
521             throws AspException
522         {
523             Class paramTypes[] = met.getParameterTypes();
524 
525             Object varObjs[] = new Object[params.size()];
526             for (int i = 0; i < varObjs.length;i++)
527             {
528                 if (paramTypes[i] == Integer.class ||
529                     paramTypes[i] == int.class) {
530                     varObjs[i] = Types.
531                         coerceToInteger(params.get(i));
532                 } else if (paramTypes[i] == Boolean.class ||
533                     paramTypes[i] == boolean.class) {
534                     varObjs[i] = Types.
535                         coerceToBoolean(params.get(i));
536                 } else if (paramTypes[i] == String.class) {
537                     varObjs[i] = Types.
538                         coerceToString(params.get(i));
539                 } else if (paramTypes[i] == java.util.Date.class) {
540                     Object parm = params.get(i);
541                     if (parm instanceof UndefinedValueNode ||
542                         parm instanceof NullNode) {
543                         varObjs[i] = null;
544                     } else {
545                         varObjs[i] = Types.coerceToDate(parm).toDate();
546                     }
547                 } else if (paramTypes[i] == ByRefValue.class) {
548                     ByRefValue newNode = new ByRefValue(params.get(i));
549                     varObjs[i] = newNode;
550                 } else {
551                     Object parm = params.get(i);
552                     if (parm instanceof UndefinedValueNode ||
553                         parm instanceof NullNode) {
554                         varObjs[i] = null;
555                     } else if (parm instanceof PackedCharArrayNode) {
556                         varObjs[i] = ((PackedCharArrayNode)params.get(i)).
557                                                 internGetValues();
558                     } else if (parm instanceof PackedByteArrayNode) {
559                         varObjs[i] = ((PackedByteArrayNode)params.get(i)).
560                                                 internGetValues();
561                     } else if (parm instanceof ArrayNode) {
562                         varObjs[i] = ((ArrayNode)params.get(i)).toJavaArray();
563                     } else {
564                         varObjs[i] = params.get(i);
565                     }
566                 }
567             }
568             try {
569                 Object retVal = met.invoke(obj, varObjs);
570                 if (DBG.isDebugEnabled())
571                     DBG.debug("Return value = " + retVal);
572                 for (int i = 0; i < params.size(); i++) {
573                     if (paramTypes[i] == ByRefValue.class) {
574                         params.set(i,((ByRefValue)varObjs[i]).getValue());
575                     }
576                 }
577                 return Types.coerceToNode(retVal);
578             } catch (IllegalAccessException ex)
579             {
580                 DBG.error("Error while calling Java object", ex);
581                 throw new AspNestedException(ex);
582             } catch (InvocationTargetException ex)
583             {
584                 Throwable targetEx = ex.getTargetException();
585                 if (targetEx != null)
586                 {
587                     if (targetEx instanceof AspException) {
588                         throw (AspException)targetEx;
589                     }
590                 }
591                 DBG.error("Error while calling Java object", ex);
592                 throw new AspNestedException(ex);
593             }
594         }
595 
596         /**
597          * Internal method which tests how compatible a list of classes
598          * are to a parameters list.
599          * Scores are as follows:
600          * 2 = Exact match
601          * 1 = Coersion possible
602          * 0 = Coersion not possible.
603          * @param types Parameter types to test
604          * @param vars Parameter values
605          * @return score
606          */
607         static private int parametersAreCompatible(Class types[],
608             Vector vars)
609         {
610             int score = 0;
611             if (types.length != vars.size()) return 0;
612             /* Special case, no parameters */
613             if (types.length == 0) return 2;
614             for (int i = 0; i < types.length; i++)
615             {
616                 Object value = vars.get(i);
617                 Class valClass = null;
618 
619                 if (DBG.isDebugEnabled())
620                 {
621                     DBG.debug("Value: " + value);
622                     DBG.debug("Value(class): " + value.getClass());
623                     DBG.debug("Class: " + types[i]);
624                 }
625 
626                 if (value instanceof ArrayNode) {
627                     if (DBG.isDebugEnabled())
628                         DBG.debug("Converted object to List");
629                     value = ((ArrayNode)value).toJavaArray();
630                 } else 
631                 if (value instanceof PackedCharArrayNode) {
632                     if (DBG.isDebugEnabled())
633                         DBG.debug("Converted object to char[]");
634                     value = ((PackedCharArrayNode)value).internGetValues();
635                 } else
636                 if (value instanceof PackedByteArrayNode) {
637                     if (DBG.isDebugEnabled())
638                         DBG.debug("Converted object to byte[]");
639                     value = ((PackedByteArrayNode)value).internGetValues();
640                 }
641                 if (value != null) valClass = value.getClass();
642 
643                 if (DBG.isDebugEnabled()) DBG.debug("valClass: " + valClass);
644     
645                 if ((value == null || valClass == NullNode.class
646                     || valClass == UndefinedValueNode.class)
647                     &&
648                     Object.class.isAssignableFrom(types[i]))
649                 {
650                     score += 2;
651                 } else
652                 if (types[i] == valClass)
653                 {
654                     score += 2;
655                 } else
656                 if (types[i] == ByRefValue.class) {
657                     score += 1;
658                 } else
659                 if ((types[i] == java.util.Date.class) &&
660                     value instanceof AspDate) {
661                     score += 2;
662                 } else
663                 if ((types[i] == AspDate.class) &&
664                         value instanceof java.util.Date) {
665                     score += 2;
666                 } else
667                 if ((types[i] == Boolean.class) ||
668                     (types[i] == boolean.class)) {
669                     /* XXX The score=2 case may be handled above */
670                     if ((valClass == Boolean.class) ||
671                         (valClass == boolean.class)) score += 2;
672                     else
673                         score += 1;
674                 } else
675                 if (types[i] == String.class) {
676                     /* The exact case is handled above */
677                     score += 1;
678                 } else
679                 if ((types[i] == int.class) ||
680                     (types[i] == Integer.class) ||
681                     (types[i] == long.class) ||
682                     (types[i] == Long.class) ||
683                     (types[i] == double.class) ||
684                     (types[i] == Double.class) ||
685                     (types[i] == float.class) ||
686                     (types[i] == Float.class))
687                 {
688                     score += 1;
689                 } else
690                 if (types[i].isInstance(value))
691                 {
692                     score += 2;
693                 }
694                 if (DBG.isDebugEnabled())
695                     DBG.debug("Cumulative score: " + score);
696             }
697             return score;
698         }
699             
700         /**
701          * Internal method which obtains cached class information,
702          * class methods and fields.
703          * @param cls Class to obtain data of
704          * @return hashtable containin class data
705          */
706         static private Hashtable getClassData(Class cls)
707         {
708             Hashtable ht;
709             synchronized(classData)
710             {
711                 ht = (Hashtable)classData.get(cls);
712                 if (ht == null) {
713                     ht = new Hashtable();
714                     ht.put("allMethods", cls.getMethods());
715                     ht.put("allFields", cls.getFields());
716                     ht.put("methods", new Hashtable());
717                     ht.put("fields", new Hashtable());
718                     classData.put(cls, ht);
719                 }
720             }
721             return ht;
722         }
723 
724         /**
725          * Internal function which obtains the list of methods
726          * of a class which matches a given name (case insensitive).
727          * @param cls Class to obtain methods of
728          * @param ident Class name (case insensitive)
729          * @return vector of matching classes.
730          */
731         static synchronized Vector getMatchingMethods(Class cls, IdentNode ident)
732         {
733             Hashtable classData = getClassData(cls);
734             Hashtable cachedMethods =
735                 (Hashtable)classData.get("methods");
736             if (!cachedMethods.containsKey(ident))
737             {
738                 Vector matchingMethods = new Vector();
739 
740                 Method methods[] = (Method[])
741                     classData.get("allMethods");
742                 for (int i = 0; i < methods.length; i++)
743                 {
744                     if (methods[i].getName().equalsIgnoreCase(ident.ident))
745                     {
746                         matchingMethods.add(methods[i]);
747                     }
748                 }
749                 cachedMethods.put(ident, matchingMethods);
750                 return matchingMethods;
751             } else {
752                 return (Vector)cachedMethods.get(ident);
753             }
754         }
755 
756         /**
757          * Internal function which finds the list of fields which match
758          * a given name (case insensitive).
759          * @param cls Class to find fields of
760          * @param ident String name of field to find
761          * @return list of matching fields
762          */
763         static synchronized Vector getMatchingFields(Class cls, IdentNode ident)
764         {
765             Hashtable classData = getClassData(cls);
766             Hashtable cachedFields =
767                 (Hashtable)classData.get("fields");
768             if (!cachedFields.containsKey(ident))
769             {
770                 Vector matchingFields = new Vector();
771 
772                 Field fields[] = (Field[])
773                     classData.get("allFields");
774                 for (int i = 0; i < fields.length; i++)
775                 {
776                     if (fields[i].getName().equalsIgnoreCase(ident.ident))
777                     {
778                         matchingFields.add(fields[i]);
779                     }
780                 }
781                 cachedFields.put(ident, matchingFields);
782                 return matchingFields;
783             } else {
784                 return (Vector)cachedFields.get(ident);
785             }
786         }
787     }
788 }