Home | History | Annotate | Download | only in core
      1 /*
      2 * Conditions Of Use
      3 *
      4 * This software was developed by employees of the National Institute of
      5 * Standards and Technology (NIST), an agency of the Federal Government.
      6 * Pursuant to title 15 Untied States Code Section 105, works of NIST
      7 * employees are not subject to copyright protection in the United States
      8 * and are considered to be in the public domain.  As a result, a formal
      9 * license is not needed to use the software.
     10 *
     11 * This software is provided by NIST as a service and is expressly
     12 * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
     13 * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
     14 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
     15 * AND DATA ACCURACY.  NIST does not warrant or make any representations
     16 * regarding the use of the software or the results thereof, including but
     17 * not limited to the correctness, accuracy, reliability or usefulness of
     18 * the software.
     19 *
     20 * Permission to use this software is contingent upon your acceptance
     21 * of the terms of this agreement
     22 *
     23 * .
     24 *
     25 */
     26 /******************************************************************************
     27  * Product of NIST/ITL Advanced Networking Technologies Division (ANTD).      *
     28  ******************************************************************************/
     29 package gov.nist.core;
     30 import java.lang.reflect.*;
     31 import java.io.Serializable;
     32 import java.util.*;
     33 
     34 /**
     35 * The base class from which all the other classes in the
     36 * sipheader, sdpfields and sipmessage packages are extended.
     37 * Provides a few utility funcitons such as indentation and
     38 * pretty printing that all other classes benifit from.
     39 *
     40 *@version 1.2
     41 *
     42 *@author M. Ranganathan   <br/>
     43 *
     44 *
     45 *
     46 */
     47 
     48 public abstract class GenericObject implements Serializable, Cloneable {
     49     // Useful constants.
     50     protected static final String SEMICOLON = Separators.SEMICOLON;
     51     protected static final String COLON = Separators.COLON;
     52     protected static final String COMMA = Separators.COMMA;
     53     protected static final String SLASH = Separators.SLASH;
     54     protected static final String SP = Separators.SP;
     55     protected static final String EQUALS = Separators.EQUALS;
     56     protected static final String STAR = Separators.STAR;
     57     protected static final String NEWLINE = Separators.NEWLINE;
     58     protected static final String RETURN = Separators.RETURN;
     59     protected static final String LESS_THAN = Separators.LESS_THAN;
     60     protected static final String GREATER_THAN = Separators.GREATER_THAN;
     61     protected static final String AT = Separators.AT;
     62     protected static final String DOT = Separators.DOT;
     63     protected static final String QUESTION = Separators.QUESTION;
     64     protected static final String POUND = Separators.POUND;
     65     protected static final String AND = Separators.AND;
     66     protected static final String LPAREN = Separators.LPAREN;
     67     protected static final String RPAREN = Separators.RPAREN;
     68     protected static final String DOUBLE_QUOTE = Separators.DOUBLE_QUOTE;
     69     protected static final String QUOTE = Separators.QUOTE;
     70     protected static final String HT = Separators.HT;
     71     protected static final String PERCENT = Separators.PERCENT;
     72 
     73     protected static final Set<Class<?>> immutableClasses = new HashSet<Class<?>> (10);
     74     static final String[] immutableClassNames ={
     75         "String", "Character",
     76         "Boolean", "Byte", "Short", "Integer", "Long",
     77         "Float", "Double"
     78         };
     79 
     80     protected int indentation;
     81     protected String stringRepresentation;
     82     protected Match matchExpression; // Pattern matcher.
     83 
     84     static {
     85         try {
     86             for (int i = 0; i < immutableClassNames.length; i++)
     87                 immutableClasses.add(Class.forName("java.lang." + immutableClassNames [i]));
     88         } catch (ClassNotFoundException e) {
     89             throw new RuntimeException ("Internal error", e);
     90         }
     91     }
     92 
     93     /** Set the  pattern matcher. To match on the
     94      * field of a sip message, set the match expression in the match template
     95      * and invoke the match function. This useful because
     96      * SIP headers and parameters may appear in different orders and are not
     97      * necessarily in canonical form. This makes it hard to write a pattern
     98      * matcher that relies on regular expressions alone.
     99      * Thus we rely on the following  strategy i.e. To do pattern matching on
    100      * an incoming message, first parse it, and then construct a match template,
    101      * filling in the fields that you want to
    102      * match. The rules for matching are: A null object matches wild card -
    103      * that is a match template of null matches any parsed SIP object.
    104      * To match with any subfield, set the match template on a template object
    105      * of the same type and invoke the match interface.
    106      * Regular expressions matching implements the gov.nist.sip.Match interface
    107      * that can be done using the Jakarta regexp package for example.
    108      * package included herein. This can be used to implement the Match interface
    109      * <a href=http://www.apache.org> See the APACHE website for documents </a>
    110      *
    111      */
    112     public void setMatcher(Match matchExpression) {
    113         if (matchExpression == null)
    114             throw new IllegalArgumentException("null arg!");
    115         this.matchExpression = matchExpression;
    116     }
    117 
    118     /** Return the match expression.
    119      *@return the match expression that has previously been set.
    120      */
    121     public Match getMatcher() {
    122         return matchExpression;
    123     }
    124 
    125     public static Class<?> getClassFromName(String className) {
    126         try {
    127             return Class.forName(className);
    128         } catch (Exception ex) {
    129             InternalErrorHandler.handleException(ex);
    130             return null;
    131         }
    132     }
    133 
    134     public static boolean isMySubclass(Class<?> other) {
    135 
    136             return GenericObject.class.isAssignableFrom(other);
    137 
    138     }
    139 
    140     /** Clones the given object.
    141      *  If the object is a wrapped type, an array, a GenericObject
    142      *  or a GenericObjectList, it is cast to the appropriate type
    143      *  and the clone() method is invoked. Else if the object implements
    144      *  Cloneable, reflection is used to discover and invoke
    145      *  clone() method. Otherwise, the original object is returned.
    146      */
    147     public static Object makeClone(Object obj) {
    148         if (obj == null)
    149             throw new NullPointerException("null obj!");
    150         Class<?> c = obj.getClass();
    151         Object clone_obj = obj;
    152         if (immutableClasses.contains (c))
    153             return obj;
    154         else if (c.isArray ()) {
    155             Class<?> ec = c.getComponentType();
    156             if (! ec.isPrimitive())
    157                 clone_obj = ((Object []) obj).clone();
    158             else {
    159                 if (ec == Character.TYPE)
    160                     clone_obj = ((char []) obj).clone();
    161                 else if (ec == Boolean.TYPE)
    162                     clone_obj = ((boolean []) obj).clone();
    163                 if (ec == Byte.TYPE)
    164                     clone_obj = ((byte []) obj).clone();
    165                 else if (ec == Short.TYPE)
    166                     clone_obj = ((short []) obj).clone();
    167                 else if (ec == Integer.TYPE)
    168                     clone_obj = ((int []) obj).clone();
    169                 else if (ec == Long.TYPE)
    170                     clone_obj = ((long []) obj).clone();
    171                 else if (ec == Float.TYPE)
    172                     clone_obj = ((float []) obj).clone();
    173                 else if (ec == Double.TYPE)
    174                     clone_obj = ((double []) obj).clone();
    175             }
    176         } else if (GenericObject.class.isAssignableFrom (c))
    177             clone_obj = ((GenericObject) obj).clone();
    178         else if (GenericObjectList.class.isAssignableFrom (c))
    179             clone_obj = ((GenericObjectList) obj).clone();
    180         else if (Cloneable.class.isAssignableFrom (c)) {
    181             // If a clone method exists for the object, then
    182             // invoke it
    183             try {
    184                 Method meth = c.getMethod("clone", (Class[]) null);
    185                 clone_obj = meth.invoke(obj,(Object[]) null);
    186             } catch (SecurityException ex) {
    187             } catch (IllegalArgumentException ex) {
    188                 InternalErrorHandler.handleException(ex);
    189             } catch (IllegalAccessException ex) {
    190             } catch (InvocationTargetException ex) {
    191             } catch (NoSuchMethodException ex) {
    192             }
    193         }
    194         return clone_obj;
    195     }
    196 
    197     /** Clones this object.
    198      */
    199     public Object clone() {
    200         try {
    201             return super.clone();
    202         } catch (CloneNotSupportedException e) {
    203             throw new RuntimeException("Internal error");
    204         }
    205     }
    206     /**
    207      * Recursively override the fields of this object with the fields
    208      * of a new object. This is useful when you want to genrate a template
    209      * and override the fields of an incoming SIPMessage with another
    210      * SIP message that you have already generated.
    211      *
    212      * @param mergeObject is the replacement object.  The override
    213      * obect must be of the same class as this object.
    214      * Set any fields that you do not want to override as null in the
    215      * mergeOject object.
    216      */
    217     public void merge(Object mergeObject) {
    218         // Base case.
    219         if (mergeObject == null)
    220             return;
    221 
    222         if (!mergeObject.getClass().equals(this.getClass()))
    223             throw new IllegalArgumentException("Bad override object");
    224 
    225         Class<?> myclass = this.getClass();
    226         while (true) {
    227             Field[] fields = myclass.getDeclaredFields();
    228             for (int i = 0; i < fields.length; i++) {
    229                 Field f = fields[i];
    230                 int modifier = f.getModifiers();
    231                 if (Modifier.isPrivate(modifier)) {
    232                     continue;
    233                 } else if (Modifier.isStatic(modifier)) {
    234                     continue;
    235                 } else if (Modifier.isInterface(modifier)) {
    236                     continue;
    237                 }
    238                 Class<?> fieldType = f.getType();
    239                 String fname = fieldType.toString();
    240                 try {
    241                     // Primitive fields are printed with type: value
    242                     if (fieldType.isPrimitive()) {
    243                         if (fname.compareTo("int") == 0) {
    244                             int intfield = f.getInt(mergeObject);
    245                             f.setInt(this, intfield);
    246                         } else if (fname.compareTo("short") == 0) {
    247                             short shortField = f.getShort(mergeObject);
    248                             f.setShort(this, shortField);
    249                         } else if (fname.compareTo("char") == 0) {
    250                             char charField = f.getChar(mergeObject);
    251                             f.setChar(this, charField);
    252                         } else if (fname.compareTo("long") == 0) {
    253                             long longField = f.getLong(mergeObject);
    254                             f.setLong(this, longField);
    255                         } else if (fname.compareTo("boolean") == 0) {
    256                             boolean booleanField = f.getBoolean(mergeObject);
    257                             f.setBoolean(this, booleanField);
    258                         } else if (fname.compareTo("double") == 0) {
    259                             double doubleField = f.getDouble(mergeObject);
    260                             f.setDouble(this, doubleField);
    261                         } else if (fname.compareTo("float") == 0) {
    262                             float floatField = f.getFloat(mergeObject);
    263                             f.setFloat(this, floatField);
    264                         }
    265                     } else {
    266                         Object obj = f.get(this);
    267                         Object mobj = f.get(mergeObject);
    268                         if (mobj == null)
    269                             continue;
    270                         if (obj == null) {
    271                             f.set(this, mobj);
    272                             continue;
    273                         }
    274                         if (obj instanceof GenericObject) {
    275                             GenericObject gobj = (GenericObject) obj;
    276                             gobj.merge(mobj);
    277                         } else {
    278                             f.set(this, mobj);
    279                         }
    280                     }
    281                 } catch (IllegalAccessException ex1) {
    282                     ex1.printStackTrace();
    283                     continue; // we are accessing a private field...
    284                 }
    285             }
    286             myclass = myclass.getSuperclass();
    287             if (myclass.equals(GenericObject.class))
    288                 break;
    289         }
    290     }
    291 
    292     protected GenericObject() {
    293         indentation = 0;
    294         stringRepresentation = "";
    295     }
    296 
    297     protected String getIndentation() {
    298     char [] chars = new char [indentation];
    299     java.util.Arrays.fill (chars, ' ');
    300     return new String (chars);
    301     }
    302 
    303     /**
    304      * Add a new string to the accumulated string representation.
    305      */
    306 
    307     protected void sprint(String a) {
    308         if (a == null) {
    309             stringRepresentation += getIndentation();
    310             stringRepresentation += "<null>\n";
    311             return;
    312         }
    313         if (a.compareTo("}") == 0 || a.compareTo("]") == 0) {
    314             indentation--;
    315         }
    316         stringRepresentation += getIndentation();
    317         stringRepresentation += a;
    318         stringRepresentation += "\n";
    319         if (a.compareTo("{") == 0 || a.compareTo("[") == 0) {
    320             indentation++;
    321         }
    322 
    323     }
    324 
    325     /**
    326      * Pretty printing function accumulator for objects.
    327      */
    328 
    329     protected void sprint(Object o) {
    330         sprint(o.toString());
    331     }
    332 
    333     /**
    334      * Pretty printing accumulator function for ints
    335      */
    336 
    337     protected void sprint(int intField) {
    338         sprint(String.valueOf(intField));
    339     }
    340 
    341     /**
    342      * Pretty printing accumulator function for shorts
    343      */
    344     protected void sprint(short shortField) {
    345         sprint(String.valueOf(shortField));
    346     }
    347 
    348     /**
    349      * Pretty printing accumulator function for chars
    350      */
    351 
    352     protected void sprint(char charField) {
    353         sprint(String.valueOf(charField));
    354 
    355     }
    356 
    357     /**
    358      * Pretty printing accumulator function for longs
    359      */
    360 
    361     protected void sprint(long longField) {
    362         sprint(String.valueOf(longField));
    363     }
    364 
    365     /**
    366      * Pretty printing accumulator function for booleans
    367      */
    368 
    369     protected void sprint(boolean booleanField) {
    370         sprint(String.valueOf(booleanField));
    371     }
    372 
    373     /**
    374      * Pretty printing accumulator function for doubles
    375      */
    376 
    377     protected void sprint(double doubleField) {
    378         sprint(String.valueOf(doubleField));
    379     }
    380 
    381     /**
    382      * Pretty printing accumulator function for floats
    383      */
    384 
    385     protected void sprint(float floatField) {
    386         sprint(String.valueOf(floatField));
    387     }
    388 
    389     /**
    390      * Debug printing function.
    391      */
    392 
    393     protected void dbgPrint() {
    394         Debug.println(debugDump());
    395     }
    396 
    397     /**
    398      * Debug printing function.
    399      */
    400     protected void dbgPrint(String s) {
    401         Debug.println(s);
    402     }
    403 
    404     /**
    405      * An introspection based equality predicate for GenericObjects.
    406      *@param that is the other object to test against.
    407      *@return true if the objects are euqal and false otherwise
    408      */
    409     public boolean equals(Object that) {
    410         if ( that == null ) return false;
    411         if (!this.getClass().equals(that.getClass()))
    412             return false;
    413         Class<?> myclass = this.getClass();
    414         Class<?> hisclass = that.getClass();
    415         while (true) {
    416             Field[] fields = myclass.getDeclaredFields();
    417             Field[] hisfields = hisclass.getDeclaredFields();
    418             for (int i = 0; i < fields.length; i++) {
    419                 Field f = fields[i];
    420                 Field g = hisfields[i];
    421                 // Only print protected and public members.
    422                 int modifier = f.getModifiers();
    423                 if ((modifier & Modifier.PRIVATE) == Modifier.PRIVATE)
    424                     continue;
    425                 Class<?> fieldType = f.getType();
    426                 String fieldName = f.getName();
    427                 if (fieldName.compareTo("stringRepresentation") == 0) {
    428                     continue;
    429                 }
    430                 if (fieldName.compareTo("indentation") == 0) {
    431                     continue;
    432                 }
    433                 try {
    434                     // Primitive fields are printed with type: value
    435                     if (fieldType.isPrimitive()) {
    436                         String fname = fieldType.toString();
    437                         if (fname.compareTo("int") == 0) {
    438                             if (f.getInt(this) != g.getInt(that))
    439                                 return false;
    440                         } else if (fname.compareTo("short") == 0) {
    441                             if (f.getShort(this) != g.getShort(that))
    442                                 return false;
    443                         } else if (fname.compareTo("char") == 0) {
    444                             if (f.getChar(this) != g.getChar(that))
    445                                 return false;
    446                         } else if (fname.compareTo("long") == 0) {
    447                             if (f.getLong(this) != g.getLong(that))
    448                                 return false;
    449                         } else if (fname.compareTo("boolean") == 0) {
    450                             if (f.getBoolean(this) != g.getBoolean(that))
    451                                 return false;
    452                         } else if (fname.compareTo("double") == 0) {
    453                             if (f.getDouble(this) != g.getDouble(that))
    454                                 return false;
    455                         } else if (fname.compareTo("float") == 0) {
    456                             if (f.getFloat(this) != g.getFloat(that))
    457                                 return false;
    458                         }
    459                     } else if (g.get(that) == f.get(this))
    460                         return true;
    461                     else if (f.get(this) == null)
    462                         return false;
    463                     else if (g.get(that) == null)
    464                         return false;
    465                     else if (g.get(that) == null && f.get(this) != null)
    466                         return false;
    467                     else if (!f.get(this).equals(g.get(that)))
    468                         return false;
    469                 } catch (IllegalAccessException ex1) {
    470                     InternalErrorHandler.handleException(ex1);
    471                 }
    472             }
    473             if (myclass.equals(GenericObject.class))
    474                 break;
    475             else {
    476                 myclass = myclass.getSuperclass();
    477                 hisclass = hisclass.getSuperclass();
    478             }
    479 
    480         }
    481         return true;
    482     }
    483 
    484     /** An introspection based predicate matching using a template
    485      * object. Allows for partial match of two protocl Objects.
    486      *@param other the match pattern to test against. The match object
    487      * has to be of the same type (class). Primitive types
    488      * and non-sip fields that are non null are matched for equality.
    489      * Null in any field  matches anything. Some book-keeping fields
    490      * are ignored when making the comparison.
    491      */
    492 
    493     public boolean match(Object other) {
    494         if (other == null)
    495             return true;
    496         if (!this.getClass().equals(other.getClass()))
    497             return false;
    498         GenericObject that = (GenericObject) other;
    499         Class<?> myclass = this.getClass();
    500         Field[] fields = myclass.getDeclaredFields();
    501         Class<?> hisclass = other.getClass();
    502         Field[] hisfields = hisclass.getDeclaredFields();
    503         for (int i = 0; i < fields.length; i++) {
    504             Field f = fields[i];
    505             Field g = hisfields[i];
    506             // Only print protected and public members.
    507             int modifier = f.getModifiers();
    508             if ((modifier & Modifier.PRIVATE) == Modifier.PRIVATE)
    509                 continue;
    510             Class<?> fieldType = f.getType();
    511             String fieldName = f.getName();
    512             if (fieldName.compareTo("stringRepresentation") == 0) {
    513                 continue;
    514             }
    515             if (fieldName.compareTo("indentation") == 0) {
    516                 continue;
    517             }
    518             try {
    519                 // Primitive fields are printed with type: value
    520                 if (fieldType.isPrimitive()) {
    521                     String fname = fieldType.toString();
    522                     if (fname.compareTo("int") == 0) {
    523                         if (f.getInt(this) != g.getInt(that))
    524                             return false;
    525                     } else if (fname.compareTo("short") == 0) {
    526                         if (f.getShort(this) != g.getShort(that))
    527                             return false;
    528                     } else if (fname.compareTo("char") == 0) {
    529                         if (f.getChar(this) != g.getChar(that))
    530                             return false;
    531                     } else if (fname.compareTo("long") == 0) {
    532                         if (f.getLong(this) != g.getLong(that))
    533                             return false;
    534                     } else if (fname.compareTo("boolean") == 0) {
    535                         if (f.getBoolean(this) != g.getBoolean(that))
    536                             return false;
    537                     } else if (fname.compareTo("double") == 0) {
    538                         if (f.getDouble(this) != g.getDouble(that))
    539                             return false;
    540                     } else if (fname.compareTo("float") == 0) {
    541                         if (f.getFloat(this) != g.getFloat(that))
    542                             return false;
    543                     }
    544                 } else {
    545                     Object myObj = f.get(this);
    546                     Object hisObj = g.get(that);
    547                     if (hisObj != null && myObj == null)
    548                         return false;
    549                     else if (hisObj == null && myObj != null)
    550                         continue;
    551                     else if (hisObj == null && myObj == null)
    552                         continue;
    553                     else if (
    554                         hisObj instanceof java.lang.String
    555                             && myObj instanceof java.lang.String) {
    556                         if ((((String) hisObj).trim()).equals(""))
    557                             continue;
    558                         if (((String) myObj)
    559                             .compareToIgnoreCase((String) hisObj)
    560                             != 0)
    561                             return false;
    562                     } else if (
    563                         GenericObject.isMySubclass(myObj.getClass())
    564                             && !((GenericObject) myObj).match(hisObj))
    565                         return false;
    566                     else if (
    567                         GenericObjectList.isMySubclass(myObj.getClass())
    568                             && !((GenericObjectList) myObj).match(hisObj))
    569                         return false;
    570 
    571                 }
    572             } catch (IllegalAccessException ex1) {
    573                 InternalErrorHandler.handleException(ex1);
    574             }
    575         }
    576         return true;
    577     }
    578 
    579     /**
    580      * Generic print formatting function:
    581      * Does depth-first descent of the structure and
    582      * recursively prints all non-private objects pointed to
    583      * by this object.
    584      * <bf>
    585      * Warning - the following generic string routine will
    586      * bomb (go into infinite loop) if there are any circularly linked
    587      * structures so if you have these, they had better be private!
    588      * </bf>
    589      * We dont have to worry about such things for our structures
    590      *(we never use circular linked structures).
    591      */
    592 
    593     public String debugDump() {
    594         stringRepresentation = "";
    595         Class<?> myclass = getClass();
    596         sprint(myclass.getName());
    597         sprint("{");
    598         Field[] fields = myclass.getDeclaredFields();
    599         for (int i = 0; i < fields.length; i++) {
    600             Field f = fields[i];
    601             // Only print protected and public members.
    602             int modifier = f.getModifiers();
    603             if ((modifier & Modifier.PRIVATE) == Modifier.PRIVATE)
    604                 continue;
    605             Class<?> fieldType = f.getType();
    606             String fieldName = f.getName();
    607             if (fieldName.compareTo("stringRepresentation") == 0) {
    608                 // avoid nasty recursions...
    609                 continue;
    610             }
    611             if (fieldName.compareTo("indentation") == 0) {
    612                 // formatting stuff - not relevant here.
    613                 continue;
    614             }
    615             sprint(fieldName + ":");
    616             try {
    617                 // Primitive fields are printed with type: value
    618                 if (fieldType.isPrimitive()) {
    619                     String fname = fieldType.toString();
    620                     sprint(fname + ":");
    621                     if (fname.compareTo("int") == 0) {
    622                         int intfield = f.getInt(this);
    623                         sprint(intfield);
    624                     } else if (fname.compareTo("short") == 0) {
    625                         short shortField = f.getShort(this);
    626                         sprint(shortField);
    627                     } else if (fname.compareTo("char") == 0) {
    628                         char charField = f.getChar(this);
    629                         sprint(charField);
    630                     } else if (fname.compareTo("long") == 0) {
    631                         long longField = f.getLong(this);
    632                         sprint(longField);
    633                     } else if (fname.compareTo("boolean") == 0) {
    634                         boolean booleanField = f.getBoolean(this);
    635                         sprint(booleanField);
    636                     } else if (fname.compareTo("double") == 0) {
    637                         double doubleField = f.getDouble(this);
    638                         sprint(doubleField);
    639                     } else if (fname.compareTo("float") == 0) {
    640                         float floatField = f.getFloat(this);
    641                         sprint(floatField);
    642                     }
    643                 } else if (GenericObject.class.isAssignableFrom(fieldType)) {
    644                     if (f.get(this) != null) {
    645                         sprint(
    646                             ((GenericObject) f.get(this)).debugDump(
    647                                 indentation + 1));
    648                     } else {
    649                         sprint("<null>");
    650                     }
    651 
    652                 } else if (
    653                     GenericObjectList.class.isAssignableFrom(fieldType)) {
    654                     if (f.get(this) != null) {
    655                         sprint(
    656                             ((GenericObjectList) f.get(this)).debugDump(
    657                                 indentation + 1));
    658                     } else {
    659                         sprint("<null>");
    660                     }
    661 
    662                 } else {
    663                     // Dont do recursion on things that are not
    664                     // of our header type...
    665                     if (f.get(this) != null) {
    666                         sprint(f.get(this).getClass().getName() + ":");
    667                     } else {
    668                         sprint(fieldType.getName() + ":");
    669                     }
    670 
    671                     sprint("{");
    672                     if (f.get(this) != null) {
    673                         sprint(f.get(this).toString());
    674                     } else {
    675                         sprint("<null>");
    676                     }
    677                     sprint("}");
    678                 }
    679             } catch (IllegalAccessException ex1) {
    680                 continue; // we are accessing a private field...
    681             } catch (Exception ex) {
    682                 InternalErrorHandler.handleException(ex);
    683             }
    684         }
    685         sprint("}");
    686         return stringRepresentation;
    687     }
    688 
    689     /**
    690      * Formatter with a given starting indentation.
    691      */
    692     public String debugDump(int indent) {
    693         indentation = indent;
    694         String retval = this.debugDump();
    695         indentation = 0;
    696         return retval;
    697     }
    698 
    699 
    700     /**
    701      *  Get the string encoded version of this object
    702      * @since v1.0
    703      */
    704     public abstract String encode();
    705 
    706     /**
    707      * Put the encoded version of this object in the given StringBuffer.
    708      */
    709     public StringBuffer encode(StringBuffer buffer) {
    710         return buffer.append(encode());
    711     }
    712 }
    713