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.javax.sip.header;
30import gov.nist.core.GenericObject;
31import gov.nist.core.GenericObjectList;
32import gov.nist.core.InternalErrorHandler;
33
34import java.lang.reflect.Field;
35import java.lang.reflect.Modifier;
36
37/**
38 * Root class for all singleton objects in this package:
39 * specializes the gov.nist.sip.header.GenericObject class for SIPHeader
40 * related objects.
41 *
42 * @version 1.2 $Revision: 1.10 $ $Date: 2009/07/17 18:57:38 $
43 *
44 * @author M. Ranganathan   <br/>
45 *
46 *
47 *
48 */
49
50public abstract class SIPObject extends GenericObject {
51
52    /** default Constructor
53     */
54    protected SIPObject() {
55        super();
56    }
57
58
59
60    /** Debug function
61     */
62    public void dbgPrint() {
63        super.dbgPrint();
64    }
65
66    /** Encode the header into a String.
67     * @return String
68     */
69    public abstract String encode();
70
71    /** Encode the header into the given StringBuffer.
72     * Default implemation calls encode().
73     */
74    public StringBuffer encode(StringBuffer buffer) {
75        return buffer.append(encode());
76    }
77
78    /**
79     * An introspection based equality predicate for SIPObjects.
80     *@param other the other object to test against.
81     */
82    public boolean equals(Object other) {
83        if (!this.getClass().equals(other.getClass()))
84            return false;
85        SIPObject that = (SIPObject) other;
86        Class myclass = this.getClass();
87        Class hisclass = other.getClass();
88        while (true) {
89            Field[] fields = myclass.getDeclaredFields();
90            if (!hisclass.equals(myclass))
91                return false;
92            Field[] hisfields = hisclass.getDeclaredFields();
93            for (int i = 0; i < fields.length; i++) {
94                Field f = fields[i];
95                Field g = hisfields[i];
96                // Only print protected and public members.
97                int modifier = f.getModifiers();
98                if ((modifier & Modifier.PRIVATE) == Modifier.PRIVATE)
99                    continue;
100                Class fieldType = f.getType();
101                String fieldName = f.getName();
102                if (fieldName.compareTo("stringRepresentation") == 0) {
103                    continue;
104                }
105                if (fieldName.compareTo("indentation") == 0) {
106                    continue;
107                }
108                try {
109                    // Primitive fields are printed with type: value
110                    if (fieldType.isPrimitive()) {
111                        String fname = fieldType.toString();
112                        if (fname.compareTo("int") == 0) {
113                            if (f.getInt(this) != g.getInt(that))
114                                return false;
115                        } else if (fname.compareTo("short") == 0) {
116                            if (f.getShort(this) != g.getShort(that))
117                                return false;
118                        } else if (fname.compareTo("char") == 0) {
119                            if (f.getChar(this) != g.getChar(that))
120                                return false;
121                        } else if (fname.compareTo("long") == 0) {
122                            if (f.getLong(this) != g.getLong(that))
123                                return false;
124                        } else if (fname.compareTo("boolean") == 0) {
125                            if (f.getBoolean(this) != g.getBoolean(that))
126                                return false;
127                        } else if (fname.compareTo("double") == 0) {
128                            if (f.getDouble(this) != g.getDouble(that))
129                                return false;
130                        } else if (fname.compareTo("float") == 0) {
131                            if (f.getFloat(this) != g.getFloat(that))
132                                return false;
133                        }
134                    } else if (g.get(that) == f.get(this))
135                        continue;
136                    else if (f.get(this) == null && g.get(that) != null)
137                        return false;
138                    else if (g.get(that) == null && f.get(this) != null)
139                        return false;
140                    else if (!f.get(this).equals(g.get(that)))
141                        return false;
142                } catch (IllegalAccessException ex1) {
143                    System.out.println("accessed field " + fieldName);
144                    System.out.println("modifier  " + modifier);
145                    System.out.println("modifier.private  " + Modifier.PRIVATE);
146                    InternalErrorHandler.handleException(ex1);
147                }
148            }
149            if (myclass.equals(SIPObject.class))
150                break;
151            else {
152                myclass = myclass.getSuperclass();
153                hisclass = hisclass.getSuperclass();
154            }
155        }
156        return true;
157    }
158
159    /** An introspection based predicate matching using a template
160     * object. Allows for partial match of two protocl Objects.
161     * You can set a generalized matcher (using regular expressions
162     * for example) by implementing the Match interface and registering
163     * it with the template.
164     *@param other the match pattern to test against. The match object
165     * has to be of the same type (class). Primitive types
166     * and non-sip fields that are non null are matched for equality.
167     * Null in any field  matches anything. Some book-keeping fields
168     * are ignored when making the comparison.
169     *
170     */
171    public boolean match(Object other) {
172        if (other == null) {
173            return true;
174        }
175
176        if (!this.getClass().equals(other.getClass()))
177            return false;
178        GenericObject that = (GenericObject) other;
179        Class myclass = this.getClass();
180        Class hisclass = other.getClass();
181        while (true) {
182            Field[] fields = myclass.getDeclaredFields();
183            Field[] hisfields = hisclass.getDeclaredFields();
184            for (int i = 0; i < fields.length; i++) {
185                Field f = fields[i];
186                Field g = hisfields[i];
187                // Only print protected and public members.
188                int modifier = f.getModifiers();
189                if ((modifier & Modifier.PRIVATE) == Modifier.PRIVATE)
190                    continue;
191                Class fieldType = f.getType();
192                String fieldName = f.getName();
193                if (fieldName.compareTo("stringRepresentation") == 0) {
194                    continue;
195                }
196                if (fieldName.compareTo("indentation") == 0) {
197                    continue;
198                }
199                try {
200                    if (fieldType.isPrimitive()) {
201                        String fname = fieldType.toString();
202                        if (fname.compareTo("int") == 0) {
203                            if (f.getInt(this) != g.getInt(that))
204                                return false;
205                        } else if (fname.compareTo("short") == 0) {
206                            if (f.getShort(this) != g.getShort(that))
207                                return false;
208                        } else if (fname.compareTo("char") == 0) {
209                            if (f.getChar(this) != g.getChar(that))
210                                return false;
211                        } else if (fname.compareTo("long") == 0) {
212                            if (f.getLong(this) != g.getLong(that))
213                                return false;
214                        } else if (fname.compareTo("boolean") == 0) {
215                            if (f.getBoolean(this) != g.getBoolean(that))
216                                return false;
217                        } else if (fname.compareTo("double") == 0) {
218                            if (f.getDouble(this) != g.getDouble(that))
219                                return false;
220                        } else if (fname.compareTo("float") == 0) {
221                            if (f.getFloat(this) != g.getFloat(that))
222                                return false;
223                        } else {
224                            InternalErrorHandler.handleException(
225                                "unknown type");
226                        }
227                    } else {
228                        Object myObj = f.get(this);
229                        Object hisObj = g.get(that);
230                        if (hisObj != null && myObj == null)
231                            return false;
232                        else if (hisObj == null && myObj != null)
233                            continue;
234                        else if (hisObj == null && myObj == null)
235                            continue;
236                        else if (
237                            hisObj instanceof java.lang.String
238                                && myObj instanceof java.lang.String) {
239                            if ((((String) hisObj).trim()).equals(""))
240                                continue;
241                            if (((String) myObj)
242                                .compareToIgnoreCase((String) hisObj)
243                                != 0)
244                                return false;
245                        } else if (
246                            hisObj != null
247                                && GenericObject.isMySubclass(myObj.getClass())
248                                && GenericObject.isMySubclass(hisObj.getClass())
249                                && myObj.getClass().equals(hisObj.getClass())
250                                && ((GenericObject) hisObj).getMatcher()
251                                    != null) {
252                            String myObjEncoded =
253                                ((GenericObject) myObj).encode();
254                            boolean retval =
255                                ((GenericObject) hisObj).getMatcher().match(
256                                    myObjEncoded);
257                            if (!retval)
258                                return false;
259                        } else if (
260                            GenericObject.isMySubclass(myObj.getClass())
261                                && !((GenericObject) myObj).match(hisObj))
262                            return false;
263                        else if (
264                            GenericObjectList.isMySubclass(myObj.getClass())
265                                && !((GenericObjectList) myObj).match(hisObj))
266                            return false;
267
268                    }
269                } catch (IllegalAccessException ex1) {
270                    InternalErrorHandler.handleException(ex1);
271                }
272            }
273            if (myclass.equals(SIPObject.class))
274                break;
275            else {
276                myclass = myclass.getSuperclass();
277                hisclass = hisclass.getSuperclass();
278            }
279        }
280        return true;
281    }
282
283    /**
284     * An introspection based string formatting method. We need this because
285     * in this package (although it is an exact duplicate of the one in
286     * the superclass) because it needs to access the protected members
287     * of the other objects in this class.
288     * @return String
289     */
290    public String debugDump() {
291        stringRepresentation = "";
292        Class myclass = getClass();
293        sprint(myclass.getName());
294        sprint("{");
295        Field[] fields = myclass.getDeclaredFields();
296        for (int i = 0; i < fields.length; i++) {
297            Field f = fields[i];
298            // Only print protected and public members.
299            int modifier = f.getModifiers();
300            if ((modifier & Modifier.PRIVATE) == Modifier.PRIVATE)
301                continue;
302            Class fieldType = f.getType();
303            String fieldName = f.getName();
304            if (fieldName.compareTo("stringRepresentation") == 0) {
305                // avoid nasty recursions...
306                continue;
307            }
308            if (fieldName.compareTo("indentation") == 0) {
309                // formatting stuff - not relevant here.
310                continue;
311            }
312            sprint(fieldName + ":");
313            try {
314                // Primitive fields are printed with type: value
315                if (fieldType.isPrimitive()) {
316                    String fname = fieldType.toString();
317                    sprint(fname + ":");
318                    if (fname.compareTo("int") == 0) {
319                        int intfield = f.getInt(this);
320                        sprint(intfield);
321                    } else if (fname.compareTo("short") == 0) {
322                        short shortField = f.getShort(this);
323                        sprint(shortField);
324                    } else if (fname.compareTo("char") == 0) {
325                        char charField = f.getChar(this);
326                        sprint(charField);
327                    } else if (fname.compareTo("long") == 0) {
328                        long longField = f.getLong(this);
329                        sprint(longField);
330                    } else if (fname.compareTo("boolean") == 0) {
331                        boolean booleanField = f.getBoolean(this);
332                        sprint(booleanField);
333                    } else if (fname.compareTo("double") == 0) {
334                        double doubleField = f.getDouble(this);
335                        sprint(doubleField);
336                    } else if (fname.compareTo("float") == 0) {
337                        float floatField = f.getFloat(this);
338                        sprint(floatField);
339                    }
340                } else if (GenericObject.class.isAssignableFrom(fieldType)) {
341                    if (f.get(this) != null) {
342                        sprint(
343                            ((GenericObject) f.get(this)).debugDump(
344                                indentation + 1));
345                    } else {
346                        sprint("<null>");
347                    }
348
349                } else if (
350                    GenericObjectList.class.isAssignableFrom(fieldType)) {
351                    if (f.get(this) != null) {
352                        sprint(
353                            ((GenericObjectList) f.get(this)).debugDump(
354                                indentation + 1));
355                    } else {
356                        sprint("<null>");
357                    }
358
359                } else {
360                    // Dont do recursion on things that are not
361                    // of our header type...
362                    if (f.get(this) != null) {
363                        sprint(f.get(this).getClass().getName() + ":");
364                    } else {
365                        sprint(fieldType.getName() + ":");
366                    }
367
368                    sprint("{");
369                    if (f.get(this) != null) {
370                        sprint(f.get(this).toString());
371                    } else {
372                        sprint("<null>");
373                    }
374                    sprint("}");
375                }
376            } catch (IllegalAccessException ex1) {
377                continue; // we are accessing a private field...
378            }
379        }
380        sprint("}");
381        return stringRepresentation;
382    }
383
384    /**
385     * Formatter with a given starting indentation (for nested structs).
386     * @param indent int to set
387     * @return String
388     */
389    public String debugDump(int indent) {
390        int save = indentation;
391        indentation = indent;
392        String retval = this.debugDump();
393        indentation = save;
394        return retval;
395    }
396
397
398    public String toString() {
399        return this.encode();
400    }
401
402}
403