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

Quick Search    Search Deep

Source code: org/vrspace/server/VRObject.java


1   package org.vrspace.server;
2   
3   import java.util.*;
4   import java.lang.reflect.*;
5   import java.net.*;
6   import org.vrspace.util.*;
7   
8   /** Generic VRObject class.<br>
9    ** Defines event forwarding, does not check nor set actual field value. <br>
10   ** Subclasses should override setValue() method to implement different
11   ** behavior, i.e. store object to database and/or forward events.<br>
12   **<br>
13   ** NOTES:<br>
14   **   - set_* (see setField()) method may access both Client and Dispatcher
15   **     via Request<br>
16   **   - before invoking set_*, request may forwarded to all the Observers,
17   **     depending on sendEvent()/setValue() implementation<br>
18   **   - along with request, other Clients (Observers) get reference to
19   **     originating Client - TAKE CARE of public variables !!!<br>
20   **   - this way, clients may send requests (Client.update()) to each other
21   **     without Dispatcher interference - usefull for chat, smart
22   **     server-side classes etc.<br>
23   **   - it's up to each client to check the request origin and perform other
24   **     actions<br>
25   **   - if set_ method is invoked before forwarding, it may cancel forwarding
26   **     by calling clearChanged(), depending on sendEvent()/setValue() implementation <br>
27   **   - in general, sendEvent() should invoke setChanged() or generate
28   **     VRObjectException, setValue() should invoke notifyObservers( Request ),
29   **     and somewhere between those field is set and clearChanged() is
30   **     eventualy called<br>
31   **
32   */
33  public class VRObject extends Observable implements Cloneable {
34    //public String name;
35    public long db_id;
36    private int pos;
37    private Request request;
38    private String myClassName;
39    private Object lock;
40    private static Hashtable lockedClasses;
41    boolean isNew = false;
42    private static long instances = 0;
43  
44    public static final HashMap primitives = new HashMap();
45    public static final HashMap primitiveMap = new HashMap();
46    
47    static {
48      primitives.put( "boolean", new Boolean( false ) );
49      primitives.put( "char", new Character( ' ' ) );
50      primitives.put( "byte", new Byte( "0" ) );
51      primitives.put( "short", new Short( "0" ) );
52      primitives.put( "int", new Integer( 0 ) );
53      primitives.put( "long", new Long( 0 ) );
54      primitives.put( "float", new Float( 0 ) );
55      primitives.put( "double", new Double( 0 ) );
56      //primitives.put( "void", new Void() );
57      
58      primitiveMap.put( Boolean.class, "boolean" );
59      primitiveMap.put( Character.class, "char" );
60      primitiveMap.put( Byte.class, "byte" );
61      primitiveMap.put( Short.class, "short" );
62      primitiveMap.put( Integer.class, "int" );
63      primitiveMap.put( Long.class, "long" );
64      primitiveMap.put( Float.class, "float" );
65      primitiveMap.put( Double.class, "double" );
66    }
67    
68    public VRObject() {
69      instances++;
70    }
71  
72    /** new object? */
73    public boolean isNew() {
74      return isNew;
75    }
76    /** send OK on successfull execution? **/
77    public boolean sendResponse() {
78      return false;
79    }
80  
81    /** Returns objects unique id */
82    public long getId() {
83      return db_id;
84    }
85    /** Returns objects unique id */
86    public ID getID() {
87      return new ID( getClass(), db_id );
88    }
89  
90    /** Uses toString() formated String to set variables
91     */
92    public int fromString( String s )
93               throws VRObjectException,
94                      NoSuchFieldException,
95                      ClassNotFoundException,
96                      IllegalAccessException,
97                      InstantiationException,
98                      NoSuchMethodException,
99                      InvocationTargetException
100   {
101     StringTokenizer st = new StringTokenizer( s, " ", true );
102     pos = 0;
103     String tmp;
104     if ( ! nextToken(st).equals( "(" ) ){
105       // wrong class
106       throw new VRObjectException( "Invalid syntax" );
107     }
108     if ( ! (tmp = nextToken(st)).equals( getClass().getName() ) ){
109       // wrong class
110       throw new VRObjectException( "Invalid class: " + tmp);
111     }
112     db_id = new Long( nextToken(st) ).longValue();
113 
114     while ( st.hasMoreTokens() ) {
115       String var = nextToken(st);
116       if ( var.equals( ")" ) ) {
117         break;
118       }
119       Field f = getClass().getField( var );
120       String val = nextToken( st );
121       if ( val.equals( "(" )) {
122         int index = pos;
123         String className = nextToken( st );
124         VRObject obj = (VRObject) Class.forName( className ).newInstance();
125         index += obj.fromString( "( " + s.substring( index )) - 1;
126         f.set( this, obj );
127 
128         // TODO: count pos and skip tokens without tokenization !!!
129         while( pos < index - 2 ) {
130           nextToken( st );
131         }
132       } else {
133         if ( val.equals( "null" ) ) {
134           val = null;
135         }
136         //f.set( this, val );
137         setField( var, val );
138       }
139     }
140     return pos;
141   }
142 
143   private String nextToken( StringTokenizer st ) {
144     // SUCKS !!!
145     // if string contains ' \" ', error
146     // if string contains ' \"anything', ok
147     String s = st.nextToken();
148     while ( s.equals( " " ) ) {
149       s = st.nextToken();
150       pos++;
151     }
152     pos += s.length();
153     if ( s.charAt(0) == '"' ) {
154       while ( true ) {
155         if ( s.charAt( s.length()-1 ) == '"'
156              && s.length() > 1
157              && s.charAt( s.length() - 2 ) != '\\'
158             )
159         {
160           break;
161         }
162         s += st.nextToken();
163         pos+=s.length();
164       }
165       s = s.substring( 1, s.length() -1 );
166     }
167     return s;
168   }
169 
170   /** Converts VRObject to String.
171    **
172    ** BUG: quotes inside string
173    */
174   public String toString() {
175     String ret = "( " + getClass().getName() + " " + db_id;
176 //    String ret = db_id + "";
177     Field [] fields = getClass().getFields();
178     for ( int i = 0; i < fields.length; i++ ) {
179       int mods = fields[i].getModifiers();
180       if ( Modifier.isFinal( mods ) || Modifier.isStatic( mods ) || fields[i].getName().equals( "db_id" ) ) {
181       //if ( fields[i].getName().equals( "db_id" ) ) {
182         continue;
183       }
184       ret += " " + fields[i].getName();
185       try {
186         if ( fields[i].getType().equals( String.class ) ) {
187           String val = (String) fields[i].get(this);
188           if ( val == null ) {
189             ret += " null";
190           } else {
191             ret += " \"" + fields[i].get(this) + "\"";
192           }
193         } else if ( fields[i].getType().equals( Vector.class )) {
194           // Vector is much like an array
195           Vector vec = (Vector) fields[i].get( this );
196           Class c = vec.elementAt(0).getClass();
197           Object tmp=Array.newInstance( c, vec.size() );
198           tmp = vec.toArray( (Object[]) tmp );
199           //if ( val != null ) {
200             ret += " " + arrayToString((Object[]) tmp);
201           //}
202         } else if ( Set.class.isAssignableFrom( fields[i].getType() ) ) {
203           // Set is much like a Vector
204           Object[] val = ((Set) fields[i].get( this )).toArray();
205           if ( val != null ) {
206             ret += " " + arrayToString(val);
207           }
208         } else {
209           ret += " " + fields[i].get( this );
210         }
211       } catch ( IllegalAccessException e ) {
212       }
213 //      ret += " /" + fields[i].getName();
214     }
215     ret += " )";
216     return ret;
217   }
218 
219   /**
220   Converts array <b>val</b> to String.
221   */
222   //public static String arrayToString( Object[] val ) {
223   public static String arrayToString( Object array ) {
224     if ( ! array.getClass().isArray() ) {
225       throw new IllegalArgumentException( "Argument is not an array!" );
226     }
227     StringBuffer ret=new StringBuffer();
228     String name = array.getClass().getName();
229     int len = Array.getLength( array );
230     // we ignore empty arrays - CHECKME
231     if ( len > 0 ) {
232       if ( name.length() > 2 ) {
233         // ! primitive !!!!! stupid workaround for java stupidity...
234         name = name.substring(2,name.length()-1);
235       } else {
236         // array with primitives
237         Object obj = Array.get( array, 0 );
238         name = (String) primitiveMap.get( obj.getClass() );
239       }
240       try {
241         ret.append( "[" );
242         ret.append( name );
243         for (int i=0; i<len; i++ ) {
244           //ret.append( "("+val[i]+")" );
245           ret.append( "("+Array.get(array,i)+")" );
246         }
247         ret.append( "]" );
248       } catch ( NullPointerException e ) {
249         ret.append( "[()]" );
250       }
251     //} else {
252       //ret.append( "[()]" );
253     }
254     return ret.toString();
255   }
256   
257   /**
258   Converts String in arrayToString() format back to array.
259   */
260   public static Object stringToArray( Class cl, String val ) {
261     Object ret=null;
262     StringTokenizer st=new StringTokenizer(val,"[()]" );
263     try {
264       String token = st.nextToken();
265       Vector vec = new Vector();
266       // skip "["
267       String className=token;
268       Class c = null;
269       try {
270         Object primitive = (Object) primitives.get( className );
271         if ( primitive == null ) {
272           // array with non-primitive values
273           c = Class.forName(className);
274         } else {
275           c = primitive.getClass();
276         }
277         //Class[] params = { new String().getClass() };
278         Class[] params = { String.class };
279         Constructor constructor=c.getConstructor( params );
280         while (st.hasMoreElements()) {
281           token = st.nextToken();
282           Object[] args = { token };
283           vec.add(constructor.newInstance( args ));
284         }
285         if ( primitive != null ) {
286           ret=Array.newInstance( (Class) primitive.getClass().getField("TYPE").get(null), vec.size() );
287         } else {
288           ret=Array.newInstance( c, vec.size() );
289         }
290         //generates exception for null arrays:
291         //System.arraycopy( vec.toArray(), 0, ret, 0, vec.size()-1 );
292         for (int i=0; i<vec.size(); i++ ){
293           //System.out.println(((Object[])ret)[i]);
294           //System.out.println(vec.elementAt(i).getClass().getName());
295           Array.set(ret,i,vec.elementAt(i));
296         }
297       } catch (Exception e) {
298         Logger.logError("Error constructing array", e);
299       }
300     } catch ( NoSuchElementException e ) {
301       // no class name?
302       Logger.logError( "Error processing "+val, e );
303     }
304     return ret;
305   }
306 
307   /** Returns string representation of objects, used by fromText() */
308   public static String toText( Object[] obj ) throws IllegalAccessException
309   {
310     StringBuffer ret = new StringBuffer();
311     for ( int i = 0; i < obj.length; i++ ) {
312       ret.append(((VRObject) obj[i]).toText());
313     }
314     return ret.toString();
315   }
316   /** Returns string representation of objects, used by fromText() */
317   public String toText() throws IllegalAccessException
318   {
319     StringBuffer ret = new StringBuffer();
320     // Store all public fields?
321     //Field [] fields = obj.getClass().getDeclaredFields();
322     Field [] fields = getClass().getFields();
323     for ( int i = 0; i < fields.length; i++ ) {
324       int mods = fields[i].getModifiers();
325       if ( ! Modifier.isFinal( mods ) && ! Modifier.isStatic( mods ) && ! fields[i].getName().equals( "db_id" ) ) {
326         ret.append( fieldToText( fields[i] ) );
327       }
328     }
329     return ret.toString();
330   }
331 
332   String fieldToText( Field field ) throws IllegalAccessException {
333     String ret = "";
334     long id = getId();
335     String c = getClassName();
336     if ( field.getType().isArray() ) {
337       Object val = field.get( this );
338       // we ignore empty arrays - CHECKME
339       if ( val != null && Array.getLength( val ) > 0 ) {
340         ret = c + " " + id + " " + field.getName() + " " + arrayToString(val) + ";\n";
341       }
342     } else if ( VRObject.class.isAssignableFrom( field.getType() ) ){
343       // TODO - VRObjects: show only class and id?
344       Object val = field.get( this );
345       if ( val != null ) {
346         ret = c + " " + id + " " + field.getName() + " " + val + ";\n";
347       }
348     } else if ( field.getType().equals( Vector.class )) {
349       // Vector is much like an array
350       Object[] val = ((Vector) field.get( this )).toArray();
351       if ( val != null ) {
352         ret = c + " " + id + " " + field.getName() + " " + arrayToString(val) + ";\n";
353       }
354       Logger.logDebug( "Vector: "+ret );
355     } else if ( Set.class.isAssignableFrom( field.getType() ) ) {
356       // Set is much like a Vector
357       Object[] val = ((Set) field.get( this )).toArray();
358       if ( val != null ) {
359         ret = c + " " + id + " " + field.getName() + " " + arrayToString(val) + ";\n";
360       }
361     } else {
362       Object val = field.get( this );
363       if ( val != null && ! ( field.getType().equals( String.class ) && ((String)val).length() == 0 )) {
364         ret = c + " " + id + " " + field.getName() + " " + val + ";\n";
365       }
366     }
367     return ret;
368   }
369   
370   /**
371   As toText(), but does not return fields beginning with <b>filter</b>.
372   Used for sending events over the network.
373   */
374   public String toText( String filter ) throws IllegalAccessException {
375     StringBuffer ret = new StringBuffer();
376     // Store all public fields?
377     //Field [] fields = obj.getClass().getDeclaredFields();
378     Field [] fields = getClass().getFields();
379     for ( int i = 0; i < fields.length; i++ ) {
380       int mods = fields[i].getModifiers();
381       if ( ! Modifier.isFinal( mods ) && ! Modifier.isStatic( mods ) &&
382            ! fields[i].getName().equals( "db_id" ) &&
383            ! fields[i].getName().substring( 0, filter.length() ).equals( filter )
384          )
385       {
386         ret.append( fieldToText( fields[i] ) );
387       }
388     }
389     return ret.toString();
390   }
391 
392   /**
393   Returns class name without package name.
394   */
395   public String getClassName() {
396     if ( myClassName == null ) {
397       /*
398       myClassName = getClass().getName();
399       Package pkg = getClass().getPackage();
400       if ( pkg != null ) {
401         myClassName = myClassName.substring( pkg.getName().length()+1 );
402       }
403       */
404       myClassName = Util.getClassName( this );
405     }
406     return myClassName;
407   }
408   
409   /** Returns array of objects defined by string in toText() format */
410   public static VRObject[] fromText( String definition )
411                          throws NoSuchMethodException,
412                                 NoSuchFieldException,
413                                 IllegalAccessException,
414                                 InstantiationException,
415                                 ClassNotFoundException,
416                                 InvocationTargetException,
417                                 VRObjectException
418   {
419     StringTokenizer lines = new StringTokenizer ( definition, ";"+System.getProperty( "line.separator" ) );
420     HashMap ret = new HashMap();
421     VRObject obj = null;
422     while ( lines.hasMoreTokens() ) {
423       String line = lines.nextToken();
424       StringTokenizer st = new StringTokenizer( line );
425 
426       String c = st.nextToken();
427       String cid = st.nextToken();
428       long id = new Long(cid).longValue();
429       String var = st.nextToken();
430       String val = line.substring( c.length()+cid.length()+var.length()+3 );
431       //System.out.println( "VRObject.fromText(): "+c+"["+id+"]."+var+"="+val );
432 //      if ( ret.containsKey( new Long(id) ) ) {
433       if ( ret.containsKey( c+" "+new Long(id) ) ) {
434         if ( obj.db_id != id ) {
435           Logger.logError("Wrong id: db "+obj.db_id+" "+id);
436           obj = (VRObject) ret.get( new Long(id) );
437         }
438       } else {
439         obj = (VRObject) newInstance( c );
440         obj.db_id = id;
441         ret.put( c + " " + new Long(id), obj );
442       }
443       try {
444         obj.setField( var, val );
445       } catch ( Throwable t ) {
446         // ignore field errors
447         Logger.logError("Error in setField for "+obj.getID(), t);
448       }
449     }
450     return (VRObject[]) ret.values().toArray(new VRObject[0]);
451   }
452 
453   /**
454   Creates new instance of <b>className</b>
455   */
456   public static VRObject newInstance( String className ) {
457     Object obj=null;
458     //Logger.logDebug( "new "+className );
459     try {
460       obj = Class.forName( "org.vrspace.server."+className ).newInstance();
461     } catch (Throwable e) {
462       //Logger.logWarning("Class "+className+" not found - trying org.vrspace.server."+className);
463       try {
464         obj = Class.forName( "org.vrspace.server.object."+className ).newInstance();
465       } catch (Throwable t1) {
466         //Logger.logWarning("Class "+className+" not found - trying org.vrspace.server.object."+className);
467         try {
468           obj = Class.forName( className ).newInstance();
469         } catch (Throwable t2) {
470           Logger.logError( "Can't instantiate "+className, t2 );
471         }
472       }
473     }
474     //Logger.logDebug( "new "+className+" instanceof "+obj.getClass().getName() );
475     return (VRObject)obj;
476   }
477   
478   /** setFields() on all variables specified in <names>
479    ** parse args and set each
480    */
481   public void setFields( String[] names, String args )
482               throws VRObjectException
483   {
484     StringTokenizer st = new StringTokenizer( args, " ", true);
485     pos = 0;
486     String[] values = new String [ names.length ];
487     try {
488       for ( int i = 0; i < names.length-1; i++ ) {
489         setField( names[i], nextToken(st) );
490       }
491       setField( names[ names.length ], args.substring( pos ) );
492     } catch ( Exception e ) {
493       Logger.logError(getID()+": error in setField", e);
494       throw new VRObjectException( e.getMessage() );
495     }
496   }
497 
498   /**
499   Takes field values from passed object, by field name. Non-exisisting
500   fields ignored.
501   */
502   public void setFields( VRObject o ) {
503     Field[] fields = o.getClass().getFields();
504     for ( int i = 0; i < fields.length; i++ ) {
505       try {
506         setField( fields[i].getName(), o.getField(fields[i].getName()).toString() );
507       } catch ( Exception e ) {
508       }
509     }
510   }
511   
512   /**
513   Sets field <b>name</b> to <b>value</b>
514   */
515   public void setField( String name, String value ) throws VRObjectException {
516     setField( name, value, null );
517   }
518   
519   /**
520   Sets field <b>r.getEventName()</b> to <b>r.getEventValue()</b>
521   @see Request
522   */
523   public void setField( Request r ) throws VRObjectException {
524     if ( (lock != null && ! lock.equals(r.getClient() )) ||
525          ( lockedClasses != null && lockedClasses.get(getClass()).equals( r.object ) )
526        )
527     {
528       throw new VRObjectException ( "Object locked!" );
529     }
530     setField( r.getEventName(), r.getEventValue(), r );
531   }
532   
533   /**
534   Equals to <b>obj.name = value</b>. 
535   If this raises exception, tries
536   <b>obj</b>.set_<b>name</b>( Request <b>r</b>, String <b>value</b> )
537   */
538   public void setField( String name, String value, Request r )
539               throws VRObjectException
540   {
541   //    System.out.println( "VRObject.setField(): "+obj.getClass().getName()+" "+name+" "+value );
542     Field field = null;
543     VRObject obj = this;
544     try {
545       // get public field
546       field = obj.getClass().getField(name);
547     } catch ( NoSuchFieldException e ) {
548       // forget non-public field
549       //try {
550         // get private/protected field
551         //field = obj.getClass().getDeclaredField(name);
552       //} catch ( NoSuchFieldException e1 ) {
553         // not field, try the method
554         try {
555           //Class[] classes = { r.getClass(), new String().getClass() };
556           Class[] classes = { Request.class, String.class };
557           Method method = obj.getClass().getMethod("set_" + name, classes );
558           Object[] o = { r, value };
559           method.invoke( obj, o );
560         } catch (InvocationTargetException e3) {
561           Logger.logError( "Unable to access field "+name+" in class "+obj.getClass().getName(), e3.getTargetException() );
562           throw new VRObjectException( "Unable to access field "+name+" in class "+obj.getClass().getName()+" - "+e3.getTargetException().toString());
563         } catch (Exception e2) {
564           throw new VRObjectException( "No field "+name+" in class "+obj.getClass().getName()+" "+e2.toString());
565         }
566         // field is null!!
567       //}
568     } catch (Throwable e) {
569       Logger.logError(getID()+": error attempting to set field value",e);
570     }
571     if ( field != null ) {
572       int modifiers = field.getModifiers();
573       Class type = field.getType();
574       try {
575         if ( type.isPrimitive() ) {
576           // Primitive
577           String typeName = field.getType().getName();
578           if ( typeName.equals( "long" ) ) {
579             field.setLong( obj, Long.valueOf( value ).longValue() );
580           } else if ( typeName.equals( "int" ) ) {
581             field.setInt( obj, Integer.valueOf( value ).intValue() );
582           } else if ( typeName.equals( "float" ) ) {
583             field.setFloat( obj, Float.valueOf( value ).floatValue() );
584           } else if ( typeName.equals( "double" ) ) {
585             field.setDouble( obj, Double.valueOf( value ).doubleValue() );
586           } else if ( typeName.equals( "short" ) ) {
587             field.setShort( obj, Short.valueOf( value ).shortValue() );
588           } else if ( typeName.equals( "char" ) ) {
589             field.setChar( obj, value.charAt(0) );
590           } else if ( typeName.equals( "byte" ) ) {
591             field.setByte( obj, new Integer(value).byteValue() );
592           } else if ( typeName.equals( "boolean" ) ) {
593             field.setBoolean( obj, new Boolean(value).booleanValue() );
594           } else {
595             Logger.logError( "Field has uknown typeName:"+typeName+" "+name+" = "+value );
596           }
597         } else if ( value == null || value.equals( "null" ) ) {
598           field.set( obj, null );
599         //} else if ( type.isInstance( this )) {
600         } else if ( VRObject.class.isAssignableFrom( type ) ) {
601           // VRObject in toString() format
602           VRObject arg = (VRObject) type.newInstance();
603           // TODO: Should this be only class/id or fromString()?
604           arg.fromString( value );
605           field.set( obj, arg );
606         } else if ( type.isArray() ){
607           // How to store arrays?
608           try {
609             field.set( obj, stringToArray( type, value ) );
610           } catch ( Exception e ) {
611             Logger.logError( "Field "+type.getName()+" "+name+" = "+value, e );
612           }
613         } else if ( type.equals( Vector.class ) ) {
614           // Vector is a wrapped-around array
615           Vector tmp = new Vector();
616           Object a = stringToArray( Object.class, value ); //?
617           for ( int i = 0; i < Array.getLength(a); i++ ) {
618             tmp.add( Array.get(a, i) );
619           }
620           field.set( obj, tmp );
621         } else if ( type.equals( Set.class )) {
622           // Set can be represented by an array
623           Set tmp = (Set) type.newInstance();
624           Object a = stringToArray( Object.class, value ); // wrong! change class
625           for ( int i = 0; i < Array.getLength(a); i++ ) {
626             tmp.add( Array.get(a, i) );
627           }
628           field.set( obj, tmp );
629         } else {
630           try {
631             // try to get a constructor with String argument
632             Class[] params = { String.class };
633             Constructor constructor=type.getConstructor( params );
634             Object[] args = { value };
635             field.set( obj, constructor.newInstance( args ));
636           } catch ( NoSuchMethodException nomE ) {
637             // last try
638             try {
639               field.set( obj, value );
640             } catch ( Exception e ) {
641               Logger.logError( "Field "+field.getName()+" ("+type.getName()+") = "+value+" ("+value.getClass()+")", e );
642             }
643           }
644         }
645       } catch (Throwable e) {
646         Logger.logError( "Field "+type.getName()+" "+name+" = "+value, e );
647       }
648     }
649   }
650   /** Forwards request <b>r</b> to other clients, Observers added by Dispatcher.
651    ** Throws RequestException if request target is another object.
652    ** Calls setValue() method which actualy forwards the event.
653    **
654    ** NOTE: All the Observers (Clients!) have access to VRObject -
655    **       TAKE CARE of public variables (reading) and public
656    **       methodes (generating events)
657    */
658   public void sendEvent( Request r ) throws RequestException
659   {
660     if ( ! r.getClassName().equals( getClassName () ) ) {
661       RequestException e = new RequestException( r, "Object of " + getClass().getName() + " class got event sent to " + r.getClassName() + " class" );
662       r.exception = e;
663       throw (e);
664     }
665     if ( r.getId() != db_id ) {
666       RequestException e = new RequestException( r, "VRObject " + db_id + " got event sent to " + r.getId() );
667       r.exception = e;
668       throw( e );
669     }
670     setChanged();
671     setValue( r );
672   }
673 
674   /** This implementation does nothing */
675   protected void setValue( Request r ) {
676   }
677   
678   /**
679   Returns VRObjects that are member variables of this object.
680   */
681   public VRObject[] getMemebers() {
682     Field [] fields = getClass().getFields();
683     Vector members = new Vector();
684     for ( int i = 0; i < fields.length; i++ ) {
685       if ( fields[i].getType().isInstance( this ) ) {
686         try {
687           members.add(fields[i].get(this));
688         } catch (Exception e) {
689           // this cannot happen
690         }
691       }
692     }
693     return (VRObject[]) members.toArray( new VRObject[members.size()] );
694   }
695   /**
696   Returns names of all public member varaibles. Use getField( String ) to retreive value.
697   @see #getField
698   */
699   public String[] getFields() {
700     Vector ret = new Vector();
701     Field [] fields = getClass().getFields();
702     for ( int i = 0; i < fields.length; i++ ) {
703       try {
704         ret.add(fields[i].getName());
705       } catch (Exception e) {
706         // this cannot happen
707       }
708     }
709     return (String[]) ret.toArray( new String[ret.size()] );
710   }
711   /**
712   Returns true if this object contains public field with this name
713   */
714   public boolean hasField(String name) {
715     boolean ret = false;
716     Field[] fields = getClass().getFields();
717     for ( int i = 0; !ret && i < fields.length; i++ ) {
718       ret = name.equals( fields[i].getName() );
719     }
720     return ret;
721   }
722   /**
723   Returns true if this object contains public method with this name
724   */
725   public boolean hasMethod( String name ) {
726     boolean ret = false;
727     Method[] methods = getClass().getMethods();
728     for ( int i = 0; !ret && i < methods.length; i++ ) {
729       ret = name.equals( methods[i].getName() );
730     }
731     return ret;
732   }
733   /**
734   Returns true if there's either field or set_ method
735   */
736   public boolean canWrite( String name ) {
737     return hasField( name ) || hasMethod( "set_"+name );
738   }
739   /**
740   Returns true if there's either field or get_ method
741   */
742   public boolean canRead( String name ) {
743     return hasField( name ) || hasMethod( "get_"+name );
744   }
745   /**
746   */
747   
748   /**
749   Returns a field value. If there's no member with this name, tries to execute "get_"+name method. 
750   @throws VRObjectException if neither field nor method were found
751   */
752   public Object getField( String name ) throws VRObjectException {
753     Field field = null;
754     Object ret = null;
755     try {
756       // get public field
757       field = getClass().getField(name);
758       ret = field.get( this );
759     } catch ( NoSuchFieldException e ) {
760       // not a field, try the method
761       try {
762         //Class[] classes = { r.getClass(), new String().getClass() };
763         Class[] classes = {};
764         Method method = getClass().getMethod("get_" + name, classes );
765         Object[] o = { null };
766         ret = method.invoke( this, o );
767       } catch (InvocationTargetException e3) {
768         Logger.logError( getID()+": error executing method get_"+name, e3.getTargetException() );
769         throw new VRObjectException( "Unable to access field "+name+" in class "+getClass().getName()+" - "+e3.getTargetException().toString());
770       } catch (Exception e2) {
771         throw new VRObjectException( "No field "+name+" in class "+getClass().getName()+" "+e2.toString());
772       }
773     } catch ( Throwable t ) {
774       throw new VRObjectException( "No field "+name+" in class "+getClass().getName()+" "+t.toString());
775     }
776     return ret;
777   }
778   /**
779   VRObject.equals( VRObject ) if class and db_id are the same
780   */
781   public boolean equals( VRObject obj ) {
782     return (this.db_id == obj.db_id && this.getClass().getName().equals(obj.getClass().getName()));
783   }
784   /**
785   Lock the object, <b>o</b> is new owner. Usage: transactions.
786   This should be package protected.
787   */
788   boolean lock( VRObject o ) {
789     if ( lock == null ) {
790       lock = o;
791       return true;
792     }
793     return (lock.equals(o));
794   }
795   /**
796   unlock
797   */
798   boolean unlock( VRObject o ) {
799     if ( o.equals( lock ) ) {
800       lock = null;
801       return true;
802     }
803     return false;
804   }
805   /**
806   Lock the class, <b>o</b> is new owner. Usage: transactions.
807   This should be package protected.
808   */
809   boolean lockClass( VRObject o ) {
810     if ( lockedClasses == null ) {
811       lockedClasses = new Hashtable();
812     }
813     if ( !lockedClasses.contains(getClass()) ) {
814       lockedClasses.put( getClass(), o );
815       return true;
816     }
817     return (lockedClasses.get(getClass()).equals( o ));
818   }
819   /**
820   unlock
821   */
822   boolean unlockClass( VRObject o ) {
823     if ( lockedClasses != null && lockedClasses.get(getClass()).equals( o )) {
824       lockedClasses.remove( getClass() );
825       if ( lockedClasses.size() == 0 ) {
826         lockedClasses = null;
827       }
828       return true;
829     }
830     return false;
831   }
832   /**
833   */
834   public Object clone() {
835     VRObject ret = null;
836     try { 
837       ret = (VRObject) super.clone();
838     } catch ( CloneNotSupportedException e ) {
839       Logger.logError(getID()+": can't clone me", e);
840     }
841     return ret;
842   }
843   /**
844   */
845   /*
846   public void finalize() throws Throwable {
847     super.finalize();
848     instances--;
849     Logger.logDebug( "Finalized: "+getClassName()+" "+db_id+" - "+instances+" instances remaining" );
850     if ( instances == 0 ) {
851       Logger.logDebug( "ClassLoader "+getClass().getClassLoader().getClass().getName()+" unloading "+getClassName() );
852     }
853   }
854   */
855 }