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