001    package org.rakeshv;
002    
003    import java.lang.reflect.Field;
004    import java.lang.reflect.Method;
005    import java.util.Collection;
006    import java.util.Iterator;
007    import java.util.Map;
008    import java.util.logging.Logger;
009    
010    /**
011     * A <code>sub-class</code> of <code>java.lang.Object</code> that is 
012     * meant to be used as the <code>base-class</code> for all classes.
013     * This class provides an over-ridden {@link #toString()} method that
014     * returns an XML representation of all the <code>fields</code> in a
015     * class that have Java Bean style accessor methods.
016     *
017     * <p>Copyright 2004 Rakesh Vidyadharan</p>
018     * @author Rakesh Vidyadharan 2004 October 3
019     * @version $Id: BaseObject.java,v 1.3 2004/11/06 20:40:55 rakesh Exp $
020     */
021    public class BaseObject extends Object
022    {
023      /**
024       * A logger used to log errors.
025       */
026      private static final Logger logger = 
027        Logger.getLogger( "org.rakeshv.BaseObject" );
028    
029      /**
030       * The end of line characters to use to delimit lines.
031       */
032      public static final String END_OF_LINE = 
033        System.getProperty( "line.separator" );
034    
035      /**
036       * Returns a string that presents an XML representation of the
037       * data encapsulated in this object.
038       *
039       * @return String - The XML representation of the object.
040       */
041      public String toString()
042      {
043        StringBuffer buffer = new StringBuffer( 1024 );
044    
045        buffer.append( "<" );
046        buffer.append( this.getClass().getName() );
047        buffer.append( ">" );
048        buffer.append( BaseObject.END_OF_LINE );
049    
050        try
051        {
052          Field[] fields = this.getClass().getDeclaredFields();
053          for ( int i = 0; i < fields.length; ++i )
054          {
055            printField( fields[i], buffer );
056          }
057        }
058        catch ( Throwable t )
059        {
060          logger.warning( "Error reflecting on object fields or methods. " +
061              t.toString() );
062        }
063    
064        buffer.append( "</" );
065        buffer.append( this.getClass().getName() );
066        buffer.append( ">" );
067        buffer.append( BaseObject.END_OF_LINE );
068    
069        return buffer.toString();
070      }
071    
072      /**
073       * Fetch the value of the specified <code>Field</code> using its
074       * <code>accessor</code> method (if it exists).  Write the XML
075       * representation of the data encapsulated in the field to the
076       * specified <code>StringBuffer</code>.
077       *
078       * @param field The field whose XML representation is to be derived.
079       * @param buffer The StringBuffer to which the field's XML
080       *   representation is to be written.
081       */
082      private void printField( Field field, StringBuffer buffer )
083      {
084        try
085        {
086          String fieldName = field.getName();
087          String methodName = "get" + 
088            fieldName.substring( 0, 1 ).toUpperCase() + 
089            fieldName.substring( 1 );
090          Method method = this.getClass().getDeclaredMethod( 
091              methodName, new Class[0] );
092          
093          buffer.append( "<" );
094          buffer.append( fieldName );
095          buffer.append( " type='" );
096          buffer.append( field.getType().getName() );
097          buffer.append( "'>" );
098    
099          Object result = method.invoke( this, null );
100          if ( result == null )
101          {
102            throw new CustomException( "Accessor method for field " +
103                fieldName + " returns void!" );
104          }
105    
106          try
107          {
108            Collection collection = (Collection) result;
109            printCollection( collection, buffer );
110          }
111          catch ( ClassCastException cex )
112          {
113            try
114            {
115              Map map = (Map) result;
116              printMap( map, buffer );
117            }
118            catch ( ClassCastException cex1 )
119            {
120              buffer.append( result );
121            }
122          }
123    
124          buffer.append( "</" );
125          buffer.append( field.getName() );
126          buffer.append( ">" );
127          buffer.append( BaseObject.END_OF_LINE );
128        }
129        catch ( Throwable t )
130        {
131          logger.warning( "Error fetching data for field " + 
132              field.getName() + "." );
133        }
134      }
135    
136      /**
137       * Append XML representations of each element stored in the 
138       * <code>Collection</code> to the specified <code>StringBuffer</code>.
139       * This method will be invoked recursively for elements that are
140       * themselves collections.
141       *
142       * @see #printMap( Map, StringBuffer )
143       */
144      protected void printCollection( Collection collection, 
145          StringBuffer buffer )
146      {
147        buffer.append( BaseObject.END_OF_LINE );
148        buffer.append( "<collection>" );
149        buffer.append( BaseObject.END_OF_LINE );
150    
151        for ( Iterator iterator = collection.iterator(); 
152            iterator.hasNext(); )
153        {
154          Object object = iterator.next();
155    
156          buffer.append( "<entry type='" );
157          buffer.append( object.getClass().getName() );
158          buffer.append( "'>" );
159          buffer.append( BaseObject.END_OF_LINE );
160    
161          if ( object instanceof java.util.Collection )
162          {
163            printCollection( (Collection) object, buffer );
164          }
165          else if ( object instanceof java.util.Map )
166          {
167            printMap( (Map) object, buffer );
168          }
169          else
170          {
171            buffer.append( object );
172            buffer.append( BaseObject.END_OF_LINE );
173          }
174    
175          buffer.append( "</entry>" );
176          buffer.append( BaseObject.END_OF_LINE );
177        }
178    
179        buffer.append( "</collection>" );
180        buffer.append( BaseObject.END_OF_LINE );
181      }
182    
183      /**
184       * Append an XML representation of the <code>key-value</code>
185       * mappings stored in the specified <code>Map</code> to the specified
186       * <code>StringBuffer</code>.  This method invokes itself recursively
187       * for <code>values</code> that are themselves maps.
188       *
189       * @param map The map whose XML representation is to be generated.
190       * @param buffer The StringBuffer to which the XML representation is
191       *   to be appended.
192       */
193      protected void printMap( Map map, StringBuffer buffer )
194      {
195        buffer.append( BaseObject.END_OF_LINE );
196        buffer.append( "<map>" );
197        buffer.append( BaseObject.END_OF_LINE );
198    
199        for ( Iterator iterator = map.keySet().iterator(); 
200            iterator.hasNext(); )
201        {
202          Object key = iterator.next();
203          Object value = map.get( key );
204    
205          buffer.append( "<entry>" );
206          buffer.append( BaseObject.END_OF_LINE );
207    
208          buffer.append( "<key type='" );
209          buffer.append( key.getClass().getName() );
210          buffer.append( "'>" );
211          buffer.append( key );
212          buffer.append( "</key>" );
213          buffer.append( BaseObject.END_OF_LINE );
214    
215          buffer.append( "<value type='" );
216          buffer.append( value.getClass().getName() );
217          buffer.append( "'>" );
218          buffer.append( BaseObject.END_OF_LINE );
219    
220          if ( value instanceof java.util.Collection )
221          {
222            printCollection( (Collection) value, buffer );
223          }
224          else if ( value instanceof java.util.Map )
225          {
226            printMap( (Map) value, buffer );
227          }
228          else
229          {
230            buffer.append( value );
231            buffer.append( BaseObject.END_OF_LINE );
232          }
233    
234          buffer.append( "</value>" );
235          buffer.append( BaseObject.END_OF_LINE );
236    
237          buffer.append( "</entry>" );
238          buffer.append( BaseObject.END_OF_LINE );
239        }
240    
241        buffer.append( "</map>" );
242        buffer.append( BaseObject.END_OF_LINE );
243      }
244    }