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 ******************************************************************************/
29package gov.nist.core;
30import java.lang.reflect.*;
31import java.io.Serializable;
32import 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
48public 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