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 }