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