• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
setMatcher(Match matchExpression)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      */
getMatcher()121     public Match getMatcher() {
122         return matchExpression;
123     }
124 
getClassFromName(String className)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 
isMySubclass(Class<?> other)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      */
makeClone(Object obj)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      */
clone()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      */
merge(Object mergeObject)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 
GenericObject()292     protected GenericObject() {
293         indentation = 0;
294         stringRepresentation = "";
295     }
296 
getIndentation()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 
sprint(String a)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 
sprint(Object o)329     protected void sprint(Object o) {
330         sprint(o.toString());
331     }
332 
333     /**
334      * Pretty printing accumulator function for ints
335      */
336 
sprint(int intField)337     protected void sprint(int intField) {
338         sprint(String.valueOf(intField));
339     }
340 
341     /**
342      * Pretty printing accumulator function for shorts
343      */
sprint(short shortField)344     protected void sprint(short shortField) {
345         sprint(String.valueOf(shortField));
346     }
347 
348     /**
349      * Pretty printing accumulator function for chars
350      */
351 
sprint(char charField)352     protected void sprint(char charField) {
353         sprint(String.valueOf(charField));
354 
355     }
356 
357     /**
358      * Pretty printing accumulator function for longs
359      */
360 
sprint(long longField)361     protected void sprint(long longField) {
362         sprint(String.valueOf(longField));
363     }
364 
365     /**
366      * Pretty printing accumulator function for booleans
367      */
368 
sprint(boolean booleanField)369     protected void sprint(boolean booleanField) {
370         sprint(String.valueOf(booleanField));
371     }
372 
373     /**
374      * Pretty printing accumulator function for doubles
375      */
376 
sprint(double doubleField)377     protected void sprint(double doubleField) {
378         sprint(String.valueOf(doubleField));
379     }
380 
381     /**
382      * Pretty printing accumulator function for floats
383      */
384 
sprint(float floatField)385     protected void sprint(float floatField) {
386         sprint(String.valueOf(floatField));
387     }
388 
389     /**
390      * Debug printing function.
391      */
392 
dbgPrint()393     protected void dbgPrint() {
394         Debug.println(debugDump());
395     }
396 
397     /**
398      * Debug printing function.
399      */
dbgPrint(String s)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      */
equals(Object that)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 
match(Object other)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 
debugDump()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      */
debugDump(int indent)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      */
encode()704     public abstract String encode();
705 
706     /**
707      * Put the encoded version of this object in the given StringBuffer.
708      */
encode(StringBuffer buffer)709     public StringBuffer encode(StringBuffer buffer) {
710         return buffer.append(encode());
711     }
712 }
713