content: /*
  * 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 gov2.nist.javax2.sip.header;
  
  import gov2.nist.core.GenericObject;
  import gov2.nist.core.Separators;
  import gov2.nist.javax2.sip.header.ims.PrivacyHeader;
  
  import javax2.sip.header.Header;
  
  import java.lang.reflect.Constructor;
  import java.util.*;
  
  /**
   *
   * This is the root class for all lists of SIP headers. It imbeds a
   * SIPObjectList object and extends SIPHeader Lists of ContactSIPObjects etc.
   * derive from this class. This supports homogeneous lists (all elements in the
   * list are of the same class). We use this for building type homogeneous lists
   * of SIPObjects that appear in SIPHeaders
   *
   * @version 1.2 $Revision: 1.15 $ $Date: 2005/10/09 18:47:53
   */
  public abstract class SIPHeaderList<HDR extends SIPHeader> extends SIPHeader implements java.util.List<HDR>, Header {
  
      private static boolean prettyEncode = false;
      /**
       * hlist field.
       */
      protected List<HDR> hlist;
  
      private Class<HDR> myClass;
  
      public String getName() {
          return this.headerName;
      }
  
  
      private SIPHeaderList() {
          hlist = new LinkedList<HDR>();
      }
  
      /**
       * Constructor
       *
       * @param objclass
       *            Class to set
       * @param hname
       *            String to set
       */
      protected SIPHeaderList(Class<HDR> objclass, String hname) {
          this();
          this.headerName = hname;
          this.myClass =  objclass;
      }
  
      /**
       * Concatenate the list of stuff that we are keeping around and also the
       * text corresponding to these structures (that we parsed).
       *
       * @param objectToAdd
       */
      public boolean add(HDR objectToAdd) {
          hlist.add((HDR)objectToAdd);
          return true;
      }
  
      /**
       * Concatenate the list of stuff that we are keeping around and also the
       * text corresponding to these structures (that we parsed).
       *
       * @param obj
       *            Genericobject to set
       */
      public void addFirst(HDR obj) {
          hlist.add(0,(HDR) obj);
      }
  
      /**
       * Add to this list.
       *
       * @param sipheader
       *            SIPHeader to add.
       * @param top
       *            is true if we want to add to the top of the list.
       */
      public void add(HDR sipheader, boolean top) {
          if (top)
              this.addFirst(sipheader);
          else
              this.add(sipheader);
      }
  
      /**
       * Concatenate two compatible lists. This appends or prepends the new list
       * to the end of this list.
       *
       * @param other
       *            SIPHeaderList to set
       * @param topFlag
       *            flag which indicates which end to concatenate
       *            the lists.
       * @throws IllegalArgumentException
       *             if the two lists are not compatible
       */
      public void concatenate(SIPHeaderList<HDR> other, boolean topFlag)
              throws IllegalArgumentException {
          if (!topFlag) {
              this.addAll(other);
          } else {
              // add given items to the top end of the list.
              this.addAll(0, other);
          }
  
      }
  
  
  
      /**
       * Encode a list of sip headers. Headers are returned in cannonical form.
       *
       * @return String encoded string representation of this list of headers.
       *         (Contains string append of each encoded header).
       */
      public String encode() {
          return encode(new StringBuffer()).toString();
      }
  
      public StringBuffer encode(StringBuffer buffer) {
          if (hlist.isEmpty()) {
              buffer.append(headerName).append(':').append(Separators.NEWLINE);
          }
          else {
              // The following headers do not have comma separated forms for
              // multiple headers. Thus, they must be encoded separately.
              if (this.headerName.equals(SIPHeaderNames.WWW_AUTHENTICATE)
                      || this.headerName.equals(SIPHeaderNames.PROXY_AUTHENTICATE)
                      || this.headerName.equals(SIPHeaderNames.AUTHORIZATION)
                      || this.headerName.equals(SIPHeaderNames.PROXY_AUTHORIZATION)
                      || (prettyEncode &&
                              (this.headerName.equals(SIPHeaderNames.VIA) || this.headerName.equals(SIPHeaderNames.ROUTE) || this.headerName.equals(SIPHeaderNames.RECORD_ROUTE))) // Less confusing to read
                      || this.getClass().equals( ExtensionHeaderList.class) ) {
                  ListIterator<HDR> li = hlist.listIterator();
                  while (li.hasNext()) {
                      HDR sipheader = (HDR) li.next();
                      sipheader.encode(buffer);
                  }
              } else {
                  // These can be concatenated together in an comma separated
                  // list.
                  buffer.append(headerName).append(Separators.COLON).append(Separators.SP);
                  this.encodeBody(buffer);
                  buffer.append(Separators.NEWLINE);
              }
          }
          return buffer;
      }
  
      /**
       * Return a list of encoded strings (one for each sipheader).
       *
       * @return LinkedList containing encoded strings in this header list. an
       *         empty list is returned if this header list contains no sip
       *         headers.
       */
  
      public List<String> getHeadersAsEncodedStrings() {
          List<String> retval = new LinkedList<String>();
  
          ListIterator<HDR> li = hlist.listIterator();
          while (li.hasNext()) {
              Header sipheader = li.next();
              retval.add(sipheader.toString());
  
          }
  
          return retval;
  
      }
  
      /**
       * Get the first element of this list.
       *
       * @return SIPHeader first element of the list.
       */
      public Header getFirst() {
          if (hlist == null || hlist.isEmpty())
              return null;
          else
              return  hlist.get(0);
      }
  
      /**
       * Get the last element of this list.
       *
       * @return SIPHeader last element of the list.
       */
      public Header getLast() {
          if (hlist == null || hlist.isEmpty())
              return null;
          return  hlist.get(hlist.size() - 1);
      }
  
      /**
       * Get the class for the headers of this list.
       *
       * @return Class of header supported by this list.
       */
      public Class<HDR> getMyClass() {
          return  this.myClass;
      }
  
      /**
       * Empty check
       *
       * @return boolean true if list is empty
       */
      public boolean isEmpty() {
          return hlist.isEmpty();
      }
  
      /**
       * Get an initialized iterator for my imbedded list
       *
       * @return the generated ListIterator
       */
      public ListIterator<HDR> listIterator() {
  
          return hlist.listIterator(0);
      }
  
      /**
       * Get the imbedded linked list.
       *
       * @return the imedded linked list of SIP headers.
       */
      public List<HDR> getHeaderList() {
          return this.hlist;
      }
  
      /**
       * Get the list iterator for a given position.
       *
       * @param position
       *            position for the list iterator to return
       * @return the generated list iterator
       */
      public ListIterator<HDR> listIterator(int position) {
          return hlist.listIterator(position);
      }
  
      /**
       * Remove the first element of this list.
       */
      public void removeFirst() {
          if (hlist.size() != 0)
              hlist.remove(0);
  
      }
  
      /**
       * Remove the last element of this list.
       */
      public void removeLast() {
          if (hlist.size() != 0)
              hlist.remove(hlist.size() - 1);
      }
  
      /**
       * Remove a sip header from this list of sip headers.
       *
       * @param obj
       *            SIPHeader to remove
       * @return boolean
       */
      public boolean remove(HDR obj) {
          if (hlist.size() == 0)
              return false;
          else
              return hlist.remove(obj);
      }
  
      /**
       * Set the root class for all objects inserted into my list (for assertion
       * check)
       *
       * @param cl
       *            class to set
       */
      protected void setMyClass(Class<HDR> cl) {
          this.myClass = cl;
      }
  
      /**
       * convert to a string representation (for printing).
       *
       * @param indentation
       *            int to set
       * @return String string representation of object (for printing).
       */
      public String debugDump(int indentation) {
          stringRepresentation = "";
          String indent = new Indentation(indentation).getIndentation();
  
          String className = this.getClass().getName();
          sprint(indent + className);
          sprint(indent + "{");
  
          for (Iterator<HDR> it = hlist.iterator(); it.hasNext();) {
              HDR sipHeader = (HDR) it.next();
              sprint(indent + sipHeader.debugDump());
          }
          sprint(indent + "}");
          return stringRepresentation;
      }
  
      /**
       * convert to a string representation
       *
       * @return String
       */
      public String debugDump() {
          return debugDump(0);
      }
  
      /**
       * Array conversion.
       *
       * @return SIPHeader []
       */
      public Object[] toArray() {
          return hlist.toArray();
  
      }
  
      /**
       * index of an element.
       *
       * @return index of the given element (-1) if element does not exist.
       */
      public int indexOf(GenericObject gobj) {
          return hlist.indexOf(gobj);
      }
  
      /**
       * insert at a location.
       *
       * @param index
       *            location where to add the sipHeader.
       * @param sipHeader
       *            SIPHeader structure to add.
       */
  
      public void add(int index, HDR  sipHeader)
              throws IndexOutOfBoundsException {
          hlist.add(index, sipHeader);
      }
  
      /**
       * Equality comparison operator.
       *
       * @param other
       *            the other object to compare with. true is returned iff the
       *            classes match and list of headers herein is equal to the list
       *            of headers in the target (order of the headers is not
       *            important).
       */
      @SuppressWarnings("unchecked")
      public boolean equals(Object other) {
  
          if (other == this)
              return true;
  
          if (other instanceof SIPHeaderList) {
              SIPHeaderList<SIPHeader> that = (SIPHeaderList<SIPHeader>) other;
              if (this.hlist == that.hlist)
                  return true;
              else if (this.hlist == null)
                  return that.hlist == null || that.hlist.size() == 0;
              return this.hlist.equals(that.hlist);
          }
          return false;
      }
  
      /**
       * Template match against a template. null field in template indicates wild
       * card match.
       */
      public boolean match(SIPHeaderList<?> template) {
          if (template == null)
              return true;
          if (!this.getClass().equals(template.getClass()))
              return false;
          SIPHeaderList<SIPHeader> that = (SIPHeaderList<SIPHeader>) template;
          if (this.hlist == that.hlist)
              return true;
          else if (this.hlist == null)
              return false;
          else {
  
              for (Iterator<SIPHeader> it = that.hlist.iterator(); it.hasNext();) {
                  SIPHeader sipHeader = (SIPHeader) it.next();
  
                  boolean found = false;
                  for (Iterator<HDR> it1 = this.hlist.iterator(); it1.hasNext()
                          && !found;) {
                      SIPHeader sipHeader1 = (SIPHeader) it1.next();
                      found = sipHeader1.match(sipHeader);
                  }
                  if (!found)
                      return false;
              }
              return true;
          }
      }
  
  
      /**
       * make a clone of this header list.
       *
       * @return clone of this Header.
       */
      public Object clone() {
          try {
              Class<?> clazz = this.getClass();
  
              Constructor<?> cons = clazz.getConstructor((Class[])null);
              SIPHeaderList<HDR> retval = (SIPHeaderList<HDR>) cons.newInstance((Object[])null);
              retval.headerName = this.headerName;
              retval.myClass  = this.myClass;
              return retval.clonehlist(this.hlist);
          } catch (Exception ex) {
              throw new RuntimeException("Could not clone!", ex);
          }
      }
  
      protected final SIPHeaderList<HDR> clonehlist(List<HDR> hlistToClone) {
          if (hlistToClone != null) {
              for (Iterator<HDR> it = (Iterator<HDR>) hlistToClone.iterator(); it.hasNext();) {
                  Header h = it.next();
                  this.hlist.add((HDR)h.clone());
              }
          }
          return this;
      }
  
      /**
       * Get the number of headers in the list.
       */
      public int size() {
          return hlist.size();
      }
  
      /**
       * Return true if this is a header list (overrides the base class method
       * which returns false).
       *
       * @return true
       */
      public boolean isHeaderList() {
          return true;
      }
  
      /**
       * Encode the body of this header (the stuff that follows headerName). A.K.A
       * headerValue. This will not give a reasonable result for WWW-Authenticate,
       * Authorization, Proxy-Authenticate and Proxy-Authorization and hence this
       * is protected.
       */
      protected String encodeBody() {
          return encodeBody(new StringBuffer()).toString();
      }
  
      protected StringBuffer encodeBody(StringBuffer buffer) {
          ListIterator<HDR> iterator = this.listIterator();
          while (true) {
              SIPHeader sipHeader = (SIPHeader) iterator.next();
              if ( sipHeader == this ) throw new RuntimeException ("Unexpected circularity in SipHeaderList");
              sipHeader.encodeBody(buffer);
              // if (body.equals("")) System.out.println("BODY == ");
              if (iterator.hasNext()) {
                  if (!this.headerName.equals(PrivacyHeader.NAME))
                      buffer.append(Separators.COMMA);
                  else
                      buffer.append(Separators.SEMICOLON);
                  continue;
              } else
                  break;
  
          }
          return buffer;
      }
  
      public boolean addAll(Collection<? extends HDR> collection) {
          return this.hlist.addAll(collection);
      }
  
      public boolean addAll(int index, Collection<? extends HDR> collection) {
          return this.hlist.addAll(index, collection);
  
      }
  
      public boolean containsAll(Collection<?> collection) {
          return this.hlist.containsAll(collection);
      }
  
  
  
  
      public void clear() {
          this.hlist.clear();
      }
  
      public boolean contains(Object header) {
          return this.hlist.contains(header);
      }
  
  
      /**
       * Get the object at the specified location.
       *
       * @param index --
       *            location from which to get the object.
       *
       */
      public HDR get(int index) {
          return  this.hlist.get(index);
      }
  
      /**
       * Return the index of a given object.
       *
       * @param obj --
       *            object whose index to compute.
       */
      public int indexOf(Object obj) {
          return this.hlist.indexOf(obj);
      }
  
      /**
       * Return the iterator to the imbedded list.
       *
       * @return iterator to the imbedded list.
       *
       */
  
      public java.util.Iterator<HDR> iterator() {
          return this.hlist.listIterator();
      }
  
      /**
       * Get the last index of the given object.
       *
       * @param obj --
       *            object whose index to find.
       */
      public int lastIndexOf(Object obj) {
  
          return this.hlist.lastIndexOf(obj);
      }
  
      /**
       * Remove the given object.
       *
       * @param obj --
       *            object to remove.
       *
       */
  
      public boolean remove(Object obj) {
  
          return this.hlist.remove(obj);
      }
  
      /**
       * Remove the object at a given index.
       *
       * @param index --
       *            index at which to remove the object
       */
  
      public HDR remove(int index) {
          return this.hlist.remove(index);
      }
  
      /**
       * Remove all the elements.
       * @see List#removeAll(java.util.Collection)
       */
      public boolean removeAll(java.util.Collection<?> collection) {
          return this.hlist.removeAll(collection);
      }
  
  
      /**
       * @see List#retainAll(java.util.Collection)
       * @param collection
       */
      public boolean retainAll(java.util.Collection<?> collection) {
          return this.hlist.retainAll(collection);
      }
  
      /**
       * Get a sublist of the list.
       *
       * @see List#subList(int, int)
       */
      public java.util.List<HDR> subList(int index1, int index2) {
          return this.hlist.subList(index1, index2);
  
      }
  
      /**
       * @see Object#hashCode()
       * @return -- the computed hashcode.
       */
      public int hashCode() {
  
          return this.headerName.hashCode();
      }
  
      /**
       * Set a SIPHeader at a particular position in the list.
       *
       * @see List#set(int, java.lang.Object)
       */
      public HDR set(int position, HDR sipHeader) {
  
          return hlist.set(position, sipHeader);
  
      }
  
  
      public static void setPrettyEncode(boolean flag) {
          prettyEncode = flag;
      }
  
  
      public <T> T[] toArray(T[] array) {
          return this.hlist.toArray(array);
      }
  
  
  }
  