| /* |
| * Conditions Of Use |
| * |
| * This software was developed by employees of the National Institute of |
| * Standards and Technology (NIST), an agency of the Federal Government. |
| * Pursuant to title 15 Untied States Code Section 105, works of NIST |
| * employees are not subject to copyright protection in the United States |
| * and are considered to be in the public domain. As a result, a formal |
| * license is not needed to use the software. |
| * |
| * This software is provided by NIST as a service and is expressly |
| * provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED |
| * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT |
| * AND DATA ACCURACY. NIST does not warrant or make any representations |
| * regarding the use of the software or the results thereof, including but |
| * not limited to the correctness, accuracy, reliability or usefulness of |
| * the software. |
| * |
| * Permission to use this software is contingent upon your acceptance |
| * of the terms of this agreement |
| * |
| * . |
| * |
| */ |
| /****************************************************************************** |
| * Product of NIST/ITL Advanced Networking Technologies Division (ANTD). * |
| ******************************************************************************/ |
| package gov.nist.core; |
| import java.lang.reflect.*; |
| import java.io.Serializable; |
| import java.util.*; |
| |
| /** |
| * The base class from which all the other classes in the |
| * sipheader, sdpfields and sipmessage packages are extended. |
| * Provides a few utility funcitons such as indentation and |
| * pretty printing that all other classes benifit from. |
| * |
| *@version 1.2 |
| * |
| *@author M. Ranganathan <br/> |
| * |
| * |
| * |
| */ |
| |
| public abstract class GenericObject implements Serializable, Cloneable { |
| // Useful constants. |
| protected static final String SEMICOLON = Separators.SEMICOLON; |
| protected static final String COLON = Separators.COLON; |
| protected static final String COMMA = Separators.COMMA; |
| protected static final String SLASH = Separators.SLASH; |
| protected static final String SP = Separators.SP; |
| protected static final String EQUALS = Separators.EQUALS; |
| protected static final String STAR = Separators.STAR; |
| protected static final String NEWLINE = Separators.NEWLINE; |
| protected static final String RETURN = Separators.RETURN; |
| protected static final String LESS_THAN = Separators.LESS_THAN; |
| protected static final String GREATER_THAN = Separators.GREATER_THAN; |
| protected static final String AT = Separators.AT; |
| protected static final String DOT = Separators.DOT; |
| protected static final String QUESTION = Separators.QUESTION; |
| protected static final String POUND = Separators.POUND; |
| protected static final String AND = Separators.AND; |
| protected static final String LPAREN = Separators.LPAREN; |
| protected static final String RPAREN = Separators.RPAREN; |
| protected static final String DOUBLE_QUOTE = Separators.DOUBLE_QUOTE; |
| protected static final String QUOTE = Separators.QUOTE; |
| protected static final String HT = Separators.HT; |
| protected static final String PERCENT = Separators.PERCENT; |
| |
| protected static final Set<Class<?>> immutableClasses = new HashSet<Class<?>> (10); |
| static final String[] immutableClassNames ={ |
| "String", "Character", |
| "Boolean", "Byte", "Short", "Integer", "Long", |
| "Float", "Double" |
| }; |
| |
| protected int indentation; |
| protected String stringRepresentation; |
| protected Match matchExpression; // Pattern matcher. |
| |
| static { |
| try { |
| for (int i = 0; i < immutableClassNames.length; i++) |
| immutableClasses.add(Class.forName("java.lang." + immutableClassNames [i])); |
| } catch (ClassNotFoundException e) { |
| throw new RuntimeException ("Internal error", e); |
| } |
| } |
| |
| /** Set the pattern matcher. To match on the |
| * field of a sip message, set the match expression in the match template |
| * and invoke the match function. This useful because |
| * SIP headers and parameters may appear in different orders and are not |
| * necessarily in canonical form. This makes it hard to write a pattern |
| * matcher that relies on regular expressions alone. |
| * Thus we rely on the following strategy i.e. To do pattern matching on |
| * an incoming message, first parse it, and then construct a match template, |
| * filling in the fields that you want to |
| * match. The rules for matching are: A null object matches wild card - |
| * that is a match template of null matches any parsed SIP object. |
| * To match with any subfield, set the match template on a template object |
| * of the same type and invoke the match interface. |
| * Regular expressions matching implements the gov.nist.sip.Match interface |
| * that can be done using the Jakarta regexp package for example. |
| * package included herein. This can be used to implement the Match interface |
| * <a href=http://www.apache.org> See the APACHE website for documents </a> |
| * |
| */ |
| public void setMatcher(Match matchExpression) { |
| if (matchExpression == null) |
| throw new IllegalArgumentException("null arg!"); |
| this.matchExpression = matchExpression; |
| } |
| |
| /** Return the match expression. |
| *@return the match expression that has previously been set. |
| */ |
| public Match getMatcher() { |
| return matchExpression; |
| } |
| |
| public static Class<?> getClassFromName(String className) { |
| try { |
| return Class.forName(className); |
| } catch (Exception ex) { |
| InternalErrorHandler.handleException(ex); |
| return null; |
| } |
| } |
| |
| public static boolean isMySubclass(Class<?> other) { |
| |
| return GenericObject.class.isAssignableFrom(other); |
| |
| } |
| |
| /** Clones the given object. |
| * If the object is a wrapped type, an array, a GenericObject |
| * or a GenericObjectList, it is cast to the appropriate type |
| * and the clone() method is invoked. Else if the object implements |
| * Cloneable, reflection is used to discover and invoke |
| * clone() method. Otherwise, the original object is returned. |
| */ |
| public static Object makeClone(Object obj) { |
| if (obj == null) |
| throw new NullPointerException("null obj!"); |
| Class<?> c = obj.getClass(); |
| Object clone_obj = obj; |
| if (immutableClasses.contains (c)) |
| return obj; |
| else if (c.isArray ()) { |
| Class<?> ec = c.getComponentType(); |
| if (! ec.isPrimitive()) |
| clone_obj = ((Object []) obj).clone(); |
| else { |
| if (ec == Character.TYPE) |
| clone_obj = ((char []) obj).clone(); |
| else if (ec == Boolean.TYPE) |
| clone_obj = ((boolean []) obj).clone(); |
| if (ec == Byte.TYPE) |
| clone_obj = ((byte []) obj).clone(); |
| else if (ec == Short.TYPE) |
| clone_obj = ((short []) obj).clone(); |
| else if (ec == Integer.TYPE) |
| clone_obj = ((int []) obj).clone(); |
| else if (ec == Long.TYPE) |
| clone_obj = ((long []) obj).clone(); |
| else if (ec == Float.TYPE) |
| clone_obj = ((float []) obj).clone(); |
| else if (ec == Double.TYPE) |
| clone_obj = ((double []) obj).clone(); |
| } |
| } else if (GenericObject.class.isAssignableFrom (c)) |
| clone_obj = ((GenericObject) obj).clone(); |
| else if (GenericObjectList.class.isAssignableFrom (c)) |
| clone_obj = ((GenericObjectList) obj).clone(); |
| else if (Cloneable.class.isAssignableFrom (c)) { |
| // If a clone method exists for the object, then |
| // invoke it |
| try { |
| Method meth = c.getMethod("clone", (Class[]) null); |
| clone_obj = meth.invoke(obj,(Object[]) null); |
| } catch (SecurityException ex) { |
| } catch (IllegalArgumentException ex) { |
| InternalErrorHandler.handleException(ex); |
| } catch (IllegalAccessException ex) { |
| } catch (InvocationTargetException ex) { |
| } catch (NoSuchMethodException ex) { |
| } |
| } |
| return clone_obj; |
| } |
| |
| /** Clones this object. |
| */ |
| public Object clone() { |
| try { |
| return super.clone(); |
| } catch (CloneNotSupportedException e) { |
| throw new RuntimeException("Internal error"); |
| } |
| } |
| /** |
| * Recursively override the fields of this object with the fields |
| * of a new object. This is useful when you want to genrate a template |
| * and override the fields of an incoming SIPMessage with another |
| * SIP message that you have already generated. |
| * |
| * @param mergeObject is the replacement object. The override |
| * obect must be of the same class as this object. |
| * Set any fields that you do not want to override as null in the |
| * mergeOject object. |
| */ |
| public void merge(Object mergeObject) { |
| // Base case. |
| if (mergeObject == null) |
| return; |
| |
| if (!mergeObject.getClass().equals(this.getClass())) |
| throw new IllegalArgumentException("Bad override object"); |
| |
| Class<?> myclass = this.getClass(); |
| while (true) { |
| Field[] fields = myclass.getDeclaredFields(); |
| for (int i = 0; i < fields.length; i++) { |
| Field f = fields[i]; |
| int modifier = f.getModifiers(); |
| if (Modifier.isPrivate(modifier)) { |
| continue; |
| } else if (Modifier.isStatic(modifier)) { |
| continue; |
| } else if (Modifier.isInterface(modifier)) { |
| continue; |
| } |
| Class<?> fieldType = f.getType(); |
| String fname = fieldType.toString(); |
| try { |
| // Primitive fields are printed with type: value |
| if (fieldType.isPrimitive()) { |
| if (fname.compareTo("int") == 0) { |
| int intfield = f.getInt(mergeObject); |
| f.setInt(this, intfield); |
| } else if (fname.compareTo("short") == 0) { |
| short shortField = f.getShort(mergeObject); |
| f.setShort(this, shortField); |
| } else if (fname.compareTo("char") == 0) { |
| char charField = f.getChar(mergeObject); |
| f.setChar(this, charField); |
| } else if (fname.compareTo("long") == 0) { |
| long longField = f.getLong(mergeObject); |
| f.setLong(this, longField); |
| } else if (fname.compareTo("boolean") == 0) { |
| boolean booleanField = f.getBoolean(mergeObject); |
| f.setBoolean(this, booleanField); |
| } else if (fname.compareTo("double") == 0) { |
| double doubleField = f.getDouble(mergeObject); |
| f.setDouble(this, doubleField); |
| } else if (fname.compareTo("float") == 0) { |
| float floatField = f.getFloat(mergeObject); |
| f.setFloat(this, floatField); |
| } |
| } else { |
| Object obj = f.get(this); |
| Object mobj = f.get(mergeObject); |
| if (mobj == null) |
| continue; |
| if (obj == null) { |
| f.set(this, mobj); |
| continue; |
| } |
| if (obj instanceof GenericObject) { |
| GenericObject gobj = (GenericObject) obj; |
| gobj.merge(mobj); |
| } else { |
| f.set(this, mobj); |
| } |
| } |
| } catch (IllegalAccessException ex1) { |
| ex1.printStackTrace(); |
| continue; // we are accessing a private field... |
| } |
| } |
| myclass = myclass.getSuperclass(); |
| if (myclass.equals(GenericObject.class)) |
| break; |
| } |
| } |
| |
| protected GenericObject() { |
| indentation = 0; |
| stringRepresentation = ""; |
| } |
| |
| protected String getIndentation() { |
| char [] chars = new char [indentation]; |
| java.util.Arrays.fill (chars, ' '); |
| return new String (chars); |
| } |
| |
| /** |
| * Add a new string to the accumulated string representation. |
| */ |
| |
| protected void sprint(String a) { |
| if (a == null) { |
| stringRepresentation += getIndentation(); |
| stringRepresentation += "<null>\n"; |
| return; |
| } |
| if (a.compareTo("}") == 0 || a.compareTo("]") == 0) { |
| indentation--; |
| } |
| stringRepresentation += getIndentation(); |
| stringRepresentation += a; |
| stringRepresentation += "\n"; |
| if (a.compareTo("{") == 0 || a.compareTo("[") == 0) { |
| indentation++; |
| } |
| |
| } |
| |
| /** |
| * Pretty printing function accumulator for objects. |
| */ |
| |
| protected void sprint(Object o) { |
| sprint(o.toString()); |
| } |
| |
| /** |
| * Pretty printing accumulator function for ints |
| */ |
| |
| protected void sprint(int intField) { |
| sprint(String.valueOf(intField)); |
| } |
| |
| /** |
| * Pretty printing accumulator function for shorts |
| */ |
| protected void sprint(short shortField) { |
| sprint(String.valueOf(shortField)); |
| } |
| |
| /** |
| * Pretty printing accumulator function for chars |
| */ |
| |
| protected void sprint(char charField) { |
| sprint(String.valueOf(charField)); |
| |
| } |
| |
| /** |
| * Pretty printing accumulator function for longs |
| */ |
| |
| protected void sprint(long longField) { |
| sprint(String.valueOf(longField)); |
| } |
| |
| /** |
| * Pretty printing accumulator function for booleans |
| */ |
| |
| protected void sprint(boolean booleanField) { |
| sprint(String.valueOf(booleanField)); |
| } |
| |
| /** |
| * Pretty printing accumulator function for doubles |
| */ |
| |
| protected void sprint(double doubleField) { |
| sprint(String.valueOf(doubleField)); |
| } |
| |
| /** |
| * Pretty printing accumulator function for floats |
| */ |
| |
| protected void sprint(float floatField) { |
| sprint(String.valueOf(floatField)); |
| } |
| |
| /** |
| * Debug printing function. |
| */ |
| |
| protected void dbgPrint() { |
| Debug.println(debugDump()); |
| } |
| |
| /** |
| * Debug printing function. |
| */ |
| protected void dbgPrint(String s) { |
| Debug.println(s); |
| } |
| |
| /** |
| * An introspection based equality predicate for GenericObjects. |
| *@param that is the other object to test against. |
| *@return true if the objects are euqal and false otherwise |
| */ |
| public boolean equals(Object that) { |
| if ( that == null ) return false; |
| if (!this.getClass().equals(that.getClass())) |
| return false; |
| Class<?> myclass = this.getClass(); |
| Class<?> hisclass = that.getClass(); |
| while (true) { |
| Field[] fields = myclass.getDeclaredFields(); |
| Field[] hisfields = hisclass.getDeclaredFields(); |
| for (int i = 0; i < fields.length; i++) { |
| Field f = fields[i]; |
| Field g = hisfields[i]; |
| // Only print protected and public members. |
| int modifier = f.getModifiers(); |
| if ((modifier & Modifier.PRIVATE) == Modifier.PRIVATE) |
| continue; |
| Class<?> fieldType = f.getType(); |
| String fieldName = f.getName(); |
| if (fieldName.compareTo("stringRepresentation") == 0) { |
| continue; |
| } |
| if (fieldName.compareTo("indentation") == 0) { |
| continue; |
| } |
| try { |
| // Primitive fields are printed with type: value |
| if (fieldType.isPrimitive()) { |
| String fname = fieldType.toString(); |
| if (fname.compareTo("int") == 0) { |
| if (f.getInt(this) != g.getInt(that)) |
| return false; |
| } else if (fname.compareTo("short") == 0) { |
| if (f.getShort(this) != g.getShort(that)) |
| return false; |
| } else if (fname.compareTo("char") == 0) { |
| if (f.getChar(this) != g.getChar(that)) |
| return false; |
| } else if (fname.compareTo("long") == 0) { |
| if (f.getLong(this) != g.getLong(that)) |
| return false; |
| } else if (fname.compareTo("boolean") == 0) { |
| if (f.getBoolean(this) != g.getBoolean(that)) |
| return false; |
| } else if (fname.compareTo("double") == 0) { |
| if (f.getDouble(this) != g.getDouble(that)) |
| return false; |
| } else if (fname.compareTo("float") == 0) { |
| if (f.getFloat(this) != g.getFloat(that)) |
| return false; |
| } |
| } else if (g.get(that) == f.get(this)) |
| return true; |
| else if (f.get(this) == null) |
| return false; |
| else if (g.get(that) == null) |
| return false; |
| else if (g.get(that) == null && f.get(this) != null) |
| return false; |
| else if (!f.get(this).equals(g.get(that))) |
| return false; |
| } catch (IllegalAccessException ex1) { |
| InternalErrorHandler.handleException(ex1); |
| } |
| } |
| if (myclass.equals(GenericObject.class)) |
| break; |
| else { |
| myclass = myclass.getSuperclass(); |
| hisclass = hisclass.getSuperclass(); |
| } |
| |
| } |
| return true; |
| } |
| |
| /** An introspection based predicate matching using a template |
| * object. Allows for partial match of two protocl Objects. |
| *@param other the match pattern to test against. The match object |
| * has to be of the same type (class). Primitive types |
| * and non-sip fields that are non null are matched for equality. |
| * Null in any field matches anything. Some book-keeping fields |
| * are ignored when making the comparison. |
| */ |
| |
| public boolean match(Object other) { |
| if (other == null) |
| return true; |
| if (!this.getClass().equals(other.getClass())) |
| return false; |
| GenericObject that = (GenericObject) other; |
| Class<?> myclass = this.getClass(); |
| Field[] fields = myclass.getDeclaredFields(); |
| Class<?> hisclass = other.getClass(); |
| Field[] hisfields = hisclass.getDeclaredFields(); |
| for (int i = 0; i < fields.length; i++) { |
| Field f = fields[i]; |
| Field g = hisfields[i]; |
| // Only print protected and public members. |
| int modifier = f.getModifiers(); |
| if ((modifier & Modifier.PRIVATE) == Modifier.PRIVATE) |
| continue; |
| Class<?> fieldType = f.getType(); |
| String fieldName = f.getName(); |
| if (fieldName.compareTo("stringRepresentation") == 0) { |
| continue; |
| } |
| if (fieldName.compareTo("indentation") == 0) { |
| continue; |
| } |
| try { |
| // Primitive fields are printed with type: value |
| if (fieldType.isPrimitive()) { |
| String fname = fieldType.toString(); |
| if (fname.compareTo("int") == 0) { |
| if (f.getInt(this) != g.getInt(that)) |
| return false; |
| } else if (fname.compareTo("short") == 0) { |
| if (f.getShort(this) != g.getShort(that)) |
| return false; |
| } else if (fname.compareTo("char") == 0) { |
| if (f.getChar(this) != g.getChar(that)) |
| return false; |
| } else if (fname.compareTo("long") == 0) { |
| if (f.getLong(this) != g.getLong(that)) |
| return false; |
| } else if (fname.compareTo("boolean") == 0) { |
| if (f.getBoolean(this) != g.getBoolean(that)) |
| return false; |
| } else if (fname.compareTo("double") == 0) { |
| if (f.getDouble(this) != g.getDouble(that)) |
| return false; |
| } else if (fname.compareTo("float") == 0) { |
| if (f.getFloat(this) != g.getFloat(that)) |
| return false; |
| } |
| } else { |
| Object myObj = f.get(this); |
| Object hisObj = g.get(that); |
| if (hisObj != null && myObj == null) |
| return false; |
| else if (hisObj == null && myObj != null) |
| continue; |
| else if (hisObj == null && myObj == null) |
| continue; |
| else if ( |
| hisObj instanceof java.lang.String |
| && myObj instanceof java.lang.String) { |
| if ((((String) hisObj).trim()).equals("")) |
| continue; |
| if (((String) myObj) |
| .compareToIgnoreCase((String) hisObj) |
| != 0) |
| return false; |
| } else if ( |
| GenericObject.isMySubclass(myObj.getClass()) |
| && !((GenericObject) myObj).match(hisObj)) |
| return false; |
| else if ( |
| GenericObjectList.isMySubclass(myObj.getClass()) |
| && !((GenericObjectList) myObj).match(hisObj)) |
| return false; |
| |
| } |
| } catch (IllegalAccessException ex1) { |
| InternalErrorHandler.handleException(ex1); |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Generic print formatting function: |
| * Does depth-first descent of the structure and |
| * recursively prints all non-private objects pointed to |
| * by this object. |
| * <bf> |
| * Warning - the following generic string routine will |
| * bomb (go into infinite loop) if there are any circularly linked |
| * structures so if you have these, they had better be private! |
| * </bf> |
| * We dont have to worry about such things for our structures |
| *(we never use circular linked structures). |
| */ |
| |
| public String debugDump() { |
| stringRepresentation = ""; |
| Class<?> myclass = getClass(); |
| sprint(myclass.getName()); |
| sprint("{"); |
| Field[] fields = myclass.getDeclaredFields(); |
| for (int i = 0; i < fields.length; i++) { |
| Field f = fields[i]; |
| // Only print protected and public members. |
| int modifier = f.getModifiers(); |
| if ((modifier & Modifier.PRIVATE) == Modifier.PRIVATE) |
| continue; |
| Class<?> fieldType = f.getType(); |
| String fieldName = f.getName(); |
| if (fieldName.compareTo("stringRepresentation") == 0) { |
| // avoid nasty recursions... |
| continue; |
| } |
| if (fieldName.compareTo("indentation") == 0) { |
| // formatting stuff - not relevant here. |
| continue; |
| } |
| sprint(fieldName + ":"); |
| try { |
| // Primitive fields are printed with type: value |
| if (fieldType.isPrimitive()) { |
| String fname = fieldType.toString(); |
| sprint(fname + ":"); |
| if (fname.compareTo("int") == 0) { |
| int intfield = f.getInt(this); |
| sprint(intfield); |
| } else if (fname.compareTo("short") == 0) { |
| short shortField = f.getShort(this); |
| sprint(shortField); |
| } else if (fname.compareTo("char") == 0) { |
| char charField = f.getChar(this); |
| sprint(charField); |
| } else if (fname.compareTo("long") == 0) { |
| long longField = f.getLong(this); |
| sprint(longField); |
| } else if (fname.compareTo("boolean") == 0) { |
| boolean booleanField = f.getBoolean(this); |
| sprint(booleanField); |
| } else if (fname.compareTo("double") == 0) { |
| double doubleField = f.getDouble(this); |
| sprint(doubleField); |
| } else if (fname.compareTo("float") == 0) { |
| float floatField = f.getFloat(this); |
| sprint(floatField); |
| } |
| } else if (GenericObject.class.isAssignableFrom(fieldType)) { |
| if (f.get(this) != null) { |
| sprint( |
| ((GenericObject) f.get(this)).debugDump( |
| indentation + 1)); |
| } else { |
| sprint("<null>"); |
| } |
| |
| } else if ( |
| GenericObjectList.class.isAssignableFrom(fieldType)) { |
| if (f.get(this) != null) { |
| sprint( |
| ((GenericObjectList) f.get(this)).debugDump( |
| indentation + 1)); |
| } else { |
| sprint("<null>"); |
| } |
| |
| } else { |
| // Dont do recursion on things that are not |
| // of our header type... |
| if (f.get(this) != null) { |
| sprint(f.get(this).getClass().getName() + ":"); |
| } else { |
| sprint(fieldType.getName() + ":"); |
| } |
| |
| sprint("{"); |
| if (f.get(this) != null) { |
| sprint(f.get(this).toString()); |
| } else { |
| sprint("<null>"); |
| } |
| sprint("}"); |
| } |
| } catch (IllegalAccessException ex1) { |
| continue; // we are accessing a private field... |
| } catch (Exception ex) { |
| InternalErrorHandler.handleException(ex); |
| } |
| } |
| sprint("}"); |
| return stringRepresentation; |
| } |
| |
| /** |
| * Formatter with a given starting indentation. |
| */ |
| public String debugDump(int indent) { |
| indentation = indent; |
| String retval = this.debugDump(); |
| indentation = 0; |
| return retval; |
| } |
| |
| |
| /** |
| * Get the string encoded version of this object |
| * @since v1.0 |
| */ |
| public abstract String encode(); |
| |
| /** |
| * Put the encoded version of this object in the given StringBuffer. |
| */ |
| public StringBuffer encode(StringBuffer buffer) { |
| return buffer.append(encode()); |
| } |
| } |