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; 30 31import gov.nist.core.GenericObject; 32import gov.nist.core.Separators; 33import gov.nist.javax.sip.header.ims.PrivacyHeader; 34 35import javax.sip.header.Header; 36import java.lang.reflect.Constructor; 37import java.util.*; 38 39/** 40 * 41 * This is the root class for all lists of SIP headers. It imbeds a 42 * SIPObjectList object and extends SIPHeader Lists of ContactSIPObjects etc. 43 * derive from this class. This supports homogeneous lists (all elements in the 44 * list are of the same class). We use this for building type homogeneous lists 45 * of SIPObjects that appear in SIPHeaders 46 * 47 * @version 1.2 $Revision: 1.15 $ $Date: 2005/10/09 18:47:53 48 */ 49public abstract class SIPHeaderList<HDR extends SIPHeader> extends SIPHeader implements java.util.List<HDR>, Header { 50 51 private static boolean prettyEncode = false; 52 /** 53 * hlist field. 54 */ 55 protected List<HDR> hlist; 56 57 private Class<HDR> myClass; 58 59 public String getName() { 60 return this.headerName; 61 } 62 63 64 private SIPHeaderList() { 65 hlist = new LinkedList<HDR>(); 66 } 67 68 /** 69 * Constructor 70 * 71 * @param objclass 72 * Class to set 73 * @param hname 74 * String to set 75 */ 76 protected SIPHeaderList(Class<HDR> objclass, String hname) { 77 this(); 78 this.headerName = hname; 79 this.myClass = objclass; 80 } 81 82 /** 83 * Concatenate the list of stuff that we are keeping around and also the 84 * text corresponding to these structures (that we parsed). 85 * 86 * @param objectToAdd 87 */ 88 public boolean add(HDR objectToAdd) { 89 hlist.add((HDR)objectToAdd); 90 return true; 91 } 92 93 /** 94 * Concatenate the list of stuff that we are keeping around and also the 95 * text corresponding to these structures (that we parsed). 96 * 97 * @param obj 98 * Genericobject to set 99 */ 100 public void addFirst(HDR obj) { 101 hlist.add(0,(HDR) obj); 102 } 103 104 /** 105 * Add to this list. 106 * 107 * @param sipheader 108 * SIPHeader to add. 109 * @param top 110 * is true if we want to add to the top of the list. 111 */ 112 public void add(HDR sipheader, boolean top) { 113 if (top) 114 this.addFirst(sipheader); 115 else 116 this.add(sipheader); 117 } 118 119 /** 120 * Concatenate two compatible lists. This appends or prepends the new list 121 * to the end of this list. 122 * 123 * @param other 124 * SIPHeaderList to set 125 * @param topFlag 126 * flag which indicates which end to concatenate 127 * the lists. 128 * @throws IllegalArgumentException 129 * if the two lists are not compatible 130 */ 131 public void concatenate(SIPHeaderList<HDR> other, boolean topFlag) 132 throws IllegalArgumentException { 133 if (!topFlag) { 134 this.addAll(other); 135 } else { 136 // add given items to the top end of the list. 137 this.addAll(0, other); 138 } 139 140 } 141 142 143 144 /** 145 * Encode a list of sip headers. Headers are returned in cannonical form. 146 * 147 * @return String encoded string representation of this list of headers. 148 * (Contains string append of each encoded header). 149 */ 150 public String encode() { 151 return encode(new StringBuffer()).toString(); 152 } 153 154 public StringBuffer encode(StringBuffer buffer) { 155 if (hlist.isEmpty()) { 156 buffer.append(headerName).append(':').append(Separators.NEWLINE); 157 } 158 else { 159 // The following headers do not have comma separated forms for 160 // multiple headers. Thus, they must be encoded separately. 161 if (this.headerName.equals(SIPHeaderNames.WWW_AUTHENTICATE) 162 || this.headerName.equals(SIPHeaderNames.PROXY_AUTHENTICATE) 163 || this.headerName.equals(SIPHeaderNames.AUTHORIZATION) 164 || this.headerName.equals(SIPHeaderNames.PROXY_AUTHORIZATION) 165 || (prettyEncode && 166 (this.headerName.equals(SIPHeaderNames.VIA) || this.headerName.equals(SIPHeaderNames.ROUTE) || this.headerName.equals(SIPHeaderNames.RECORD_ROUTE))) // Less confusing to read 167 || this.getClass().equals( ExtensionHeaderList.class) ) { 168 ListIterator<HDR> li = hlist.listIterator(); 169 while (li.hasNext()) { 170 HDR sipheader = (HDR) li.next(); 171 sipheader.encode(buffer); 172 } 173 } else { 174 // These can be concatenated together in an comma separated 175 // list. 176 buffer.append(headerName).append(Separators.COLON).append(Separators.SP); 177 this.encodeBody(buffer); 178 buffer.append(Separators.NEWLINE); 179 } 180 } 181 return buffer; 182 } 183 184 /** 185 * Return a list of encoded strings (one for each sipheader). 186 * 187 * @return LinkedList containing encoded strings in this header list. an 188 * empty list is returned if this header list contains no sip 189 * headers. 190 */ 191 192 public List<String> getHeadersAsEncodedStrings() { 193 List<String> retval = new LinkedList<String>(); 194 195 ListIterator<HDR> li = hlist.listIterator(); 196 while (li.hasNext()) { 197 Header sipheader = li.next(); 198 retval.add(sipheader.toString()); 199 200 } 201 202 return retval; 203 204 } 205 206 /** 207 * Get the first element of this list. 208 * 209 * @return SIPHeader first element of the list. 210 */ 211 public Header getFirst() { 212 if (hlist == null || hlist.isEmpty()) 213 return null; 214 else 215 return hlist.get(0); 216 } 217 218 /** 219 * Get the last element of this list. 220 * 221 * @return SIPHeader last element of the list. 222 */ 223 public Header getLast() { 224 if (hlist == null || hlist.isEmpty()) 225 return null; 226 return hlist.get(hlist.size() - 1); 227 } 228 229 /** 230 * Get the class for the headers of this list. 231 * 232 * @return Class of header supported by this list. 233 */ 234 public Class<HDR> getMyClass() { 235 return this.myClass; 236 } 237 238 /** 239 * Empty check 240 * 241 * @return boolean true if list is empty 242 */ 243 public boolean isEmpty() { 244 return hlist.isEmpty(); 245 } 246 247 /** 248 * Get an initialized iterator for my imbedded list 249 * 250 * @return the generated ListIterator 251 */ 252 public ListIterator<HDR> listIterator() { 253 254 return hlist.listIterator(0); 255 } 256 257 /** 258 * Get the imbedded linked list. 259 * 260 * @return the imedded linked list of SIP headers. 261 */ 262 public List<HDR> getHeaderList() { 263 return this.hlist; 264 } 265 266 /** 267 * Get the list iterator for a given position. 268 * 269 * @param position 270 * position for the list iterator to return 271 * @return the generated list iterator 272 */ 273 public ListIterator<HDR> listIterator(int position) { 274 return hlist.listIterator(position); 275 } 276 277 /** 278 * Remove the first element of this list. 279 */ 280 public void removeFirst() { 281 if (hlist.size() != 0) 282 hlist.remove(0); 283 284 } 285 286 /** 287 * Remove the last element of this list. 288 */ 289 public void removeLast() { 290 if (hlist.size() != 0) 291 hlist.remove(hlist.size() - 1); 292 } 293 294 /** 295 * Remove a sip header from this list of sip headers. 296 * 297 * @param obj 298 * SIPHeader to remove 299 * @return boolean 300 */ 301 public boolean remove(HDR obj) { 302 if (hlist.size() == 0) 303 return false; 304 else 305 return hlist.remove(obj); 306 } 307 308 /** 309 * Set the root class for all objects inserted into my list (for assertion 310 * check) 311 * 312 * @param cl 313 * class to set 314 */ 315 protected void setMyClass(Class<HDR> cl) { 316 this.myClass = cl; 317 } 318 319 /** 320 * convert to a string representation (for printing). 321 * 322 * @param indentation 323 * int to set 324 * @return String string representation of object (for printing). 325 */ 326 public String debugDump(int indentation) { 327 stringRepresentation = ""; 328 String indent = new Indentation(indentation).getIndentation(); 329 330 String className = this.getClass().getName(); 331 sprint(indent + className); 332 sprint(indent + "{"); 333 334 for (Iterator<HDR> it = hlist.iterator(); it.hasNext();) { 335 HDR sipHeader = (HDR) it.next(); 336 sprint(indent + sipHeader.debugDump()); 337 } 338 sprint(indent + "}"); 339 return stringRepresentation; 340 } 341 342 /** 343 * convert to a string representation 344 * 345 * @return String 346 */ 347 public String debugDump() { 348 return debugDump(0); 349 } 350 351 /** 352 * Array conversion. 353 * 354 * @return SIPHeader [] 355 */ 356 public Object[] toArray() { 357 return hlist.toArray(); 358 359 } 360 361 /** 362 * index of an element. 363 * 364 * @return index of the given element (-1) if element does not exist. 365 */ 366 public int indexOf(GenericObject gobj) { 367 return hlist.indexOf(gobj); 368 } 369 370 /** 371 * insert at a location. 372 * 373 * @param index 374 * location where to add the sipHeader. 375 * @param sipHeader 376 * SIPHeader structure to add. 377 */ 378 379 public void add(int index, HDR sipHeader) 380 throws IndexOutOfBoundsException { 381 hlist.add(index, sipHeader); 382 } 383 384 /** 385 * Equality comparison operator. 386 * 387 * @param other 388 * the other object to compare with. true is returned iff the 389 * classes match and list of headers herein is equal to the list 390 * of headers in the target (order of the headers is not 391 * important). 392 */ 393 @SuppressWarnings("unchecked") 394 public boolean equals(Object other) { 395 396 if (other == this) 397 return true; 398 399 if (other instanceof SIPHeaderList) { 400 SIPHeaderList<SIPHeader> that = (SIPHeaderList<SIPHeader>) other; 401 if (this.hlist == that.hlist) 402 return true; 403 else if (this.hlist == null) 404 return that.hlist == null || that.hlist.size() == 0; 405 return this.hlist.equals(that.hlist); 406 } 407 return false; 408 } 409 410 /** 411 * Template match against a template. null field in template indicates wild 412 * card match. 413 */ 414 public boolean match(SIPHeaderList<?> template) { 415 if (template == null) 416 return true; 417 if (!this.getClass().equals(template.getClass())) 418 return false; 419 SIPHeaderList<SIPHeader> that = (SIPHeaderList<SIPHeader>) template; 420 if (this.hlist == that.hlist) 421 return true; 422 else if (this.hlist == null) 423 return false; 424 else { 425 426 for (Iterator<SIPHeader> it = that.hlist.iterator(); it.hasNext();) { 427 SIPHeader sipHeader = (SIPHeader) it.next(); 428 429 boolean found = false; 430 for (Iterator<HDR> it1 = this.hlist.iterator(); it1.hasNext() 431 && !found;) { 432 SIPHeader sipHeader1 = (SIPHeader) it1.next(); 433 found = sipHeader1.match(sipHeader); 434 } 435 if (!found) 436 return false; 437 } 438 return true; 439 } 440 } 441 442 443 /** 444 * make a clone of this header list. 445 * 446 * @return clone of this Header. 447 */ 448 public Object clone() { 449 try { 450 Class<?> clazz = this.getClass(); 451 452 Constructor<?> cons = clazz.getConstructor((Class[])null); 453 SIPHeaderList<HDR> retval = (SIPHeaderList<HDR>) cons.newInstance((Object[])null); 454 retval.headerName = this.headerName; 455 retval.myClass = this.myClass; 456 return retval.clonehlist(this.hlist); 457 } catch (Exception ex) { 458 throw new RuntimeException("Could not clone!", ex); 459 } 460 } 461 462 protected final SIPHeaderList<HDR> clonehlist(List<HDR> hlistToClone) { 463 if (hlistToClone != null) { 464 for (Iterator<HDR> it = (Iterator<HDR>) hlistToClone.iterator(); it.hasNext();) { 465 Header h = it.next(); 466 this.hlist.add((HDR)h.clone()); 467 } 468 } 469 return this; 470 } 471 472 /** 473 * Get the number of headers in the list. 474 */ 475 public int size() { 476 return hlist.size(); 477 } 478 479 /** 480 * Return true if this is a header list (overrides the base class method 481 * which returns false). 482 * 483 * @return true 484 */ 485 public boolean isHeaderList() { 486 return true; 487 } 488 489 /** 490 * Encode the body of this header (the stuff that follows headerName). A.K.A 491 * headerValue. This will not give a reasonable result for WWW-Authenticate, 492 * Authorization, Proxy-Authenticate and Proxy-Authorization and hence this 493 * is protected. 494 */ 495 protected String encodeBody() { 496 return encodeBody(new StringBuffer()).toString(); 497 } 498 499 protected StringBuffer encodeBody(StringBuffer buffer) { 500 ListIterator<HDR> iterator = this.listIterator(); 501 while (true) { 502 SIPHeader sipHeader = (SIPHeader) iterator.next(); 503 if ( sipHeader == this ) throw new RuntimeException ("Unexpected circularity in SipHeaderList"); 504 sipHeader.encodeBody(buffer); 505 // if (body.equals("")) System.out.println("BODY == "); 506 if (iterator.hasNext()) { 507 if (!this.headerName.equals(PrivacyHeader.NAME)) 508 buffer.append(Separators.COMMA); 509 else 510 buffer.append(Separators.SEMICOLON); 511 continue; 512 } else 513 break; 514 515 } 516 return buffer; 517 } 518 519 public boolean addAll(Collection<? extends HDR> collection) { 520 return this.hlist.addAll(collection); 521 } 522 523 public boolean addAll(int index, Collection<? extends HDR> collection) { 524 return this.hlist.addAll(index, collection); 525 526 } 527 528 public boolean containsAll(Collection<?> collection) { 529 return this.hlist.containsAll(collection); 530 } 531 532 533 534 535 public void clear() { 536 this.hlist.clear(); 537 } 538 539 public boolean contains(Object header) { 540 return this.hlist.contains(header); 541 } 542 543 544 /** 545 * Get the object at the specified location. 546 * 547 * @param index -- 548 * location from which to get the object. 549 * 550 */ 551 public HDR get(int index) { 552 return this.hlist.get(index); 553 } 554 555 /** 556 * Return the index of a given object. 557 * 558 * @param obj -- 559 * object whose index to compute. 560 */ 561 public int indexOf(Object obj) { 562 return this.hlist.indexOf(obj); 563 } 564 565 /** 566 * Return the iterator to the imbedded list. 567 * 568 * @return iterator to the imbedded list. 569 * 570 */ 571 572 public java.util.Iterator<HDR> iterator() { 573 return this.hlist.listIterator(); 574 } 575 576 /** 577 * Get the last index of the given object. 578 * 579 * @param obj -- 580 * object whose index to find. 581 */ 582 public int lastIndexOf(Object obj) { 583 584 return this.hlist.lastIndexOf(obj); 585 } 586 587 /** 588 * Remove the given object. 589 * 590 * @param obj -- 591 * object to remove. 592 * 593 */ 594 595 public boolean remove(Object obj) { 596 597 return this.hlist.remove(obj); 598 } 599 600 /** 601 * Remove the object at a given index. 602 * 603 * @param index -- 604 * index at which to remove the object 605 */ 606 607 public HDR remove(int index) { 608 return this.hlist.remove(index); 609 } 610 611 /** 612 * Remove all the elements. 613 * @see List#removeAll(java.util.Collection) 614 */ 615 public boolean removeAll(java.util.Collection<?> collection) { 616 return this.hlist.removeAll(collection); 617 } 618 619 620 /** 621 * @see List#retainAll(java.util.Collection) 622 * @param collection 623 */ 624 public boolean retainAll(java.util.Collection<?> collection) { 625 return this.hlist.retainAll(collection); 626 } 627 628 /** 629 * Get a sublist of the list. 630 * 631 * @see List#subList(int, int) 632 */ 633 public java.util.List<HDR> subList(int index1, int index2) { 634 return this.hlist.subList(index1, index2); 635 636 } 637 638 /** 639 * @see Object#hashCode() 640 * @return -- the computed hashcode. 641 */ 642 public int hashCode() { 643 644 return this.headerName.hashCode(); 645 } 646 647 /** 648 * Set a SIPHeader at a particular position in the list. 649 * 650 * @see List#set(int, java.lang.Object) 651 */ 652 public HDR set(int position, HDR sipHeader) { 653 654 return hlist.set(position, sipHeader); 655 656 } 657 658 659 public static void setPrettyEncode(boolean flag) { 660 prettyEncode = flag; 661 } 662 663 664 public <T> T[] toArray(T[] array) { 665 return this.hlist.toArray(array); 666 } 667 668 669} 670