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;
30
31import java.util.*;
32import java.io.Serializable;
33
34/**
35 * Implements a homogenous consistent linked list. All the objects in the linked
36 * list must derive from the same root class. This is a useful constraint to
37 * place on our code as this property is invariant.The list is created with the
38 * superclass which can be specified as either a class name or a Class.
39 *
40 * @version 1.2
41 *
42 * @author M. Ranganathan <br/>
43 *
44 *
45 *
46 */
47public abstract class GenericObjectList extends LinkedList<GenericObject> implements
48        Serializable, Cloneable{
49    // Useful constants.
50    protected static final String SEMICOLON = Separators.SEMICOLON;
51
52    protected static final String COLON = Separators.COLON;
53
54    protected static final String COMMA = Separators.COMMA;
55
56    protected static final String SLASH = Separators.SLASH;
57
58    protected static final String SP = Separators.SP;
59
60    protected static final String EQUALS = Separators.EQUALS;
61
62    protected static final String STAR = Separators.STAR;
63
64    protected static final String NEWLINE = Separators.NEWLINE;
65
66    protected static final String RETURN = Separators.RETURN;
67
68    protected static final String LESS_THAN = Separators.LESS_THAN;
69
70    protected static final String GREATER_THAN = Separators.GREATER_THAN;
71
72    protected static final String AT = Separators.AT;
73
74    protected static final String DOT = Separators.DOT;
75
76    protected static final String QUESTION = Separators.QUESTION;
77
78    protected static final String POUND = Separators.POUND;
79
80    protected static final String AND = Separators.AND;
81
82    protected static final String LPAREN = Separators.LPAREN;
83
84    protected static final String RPAREN = Separators.RPAREN;
85
86    protected static final String DOUBLE_QUOTE = Separators.DOUBLE_QUOTE;
87
88    protected static final String QUOTE = Separators.QUOTE;
89
90    protected static final String HT = Separators.HT;
91
92    protected static final String PERCENT = Separators.PERCENT;
93
94    protected int indentation;
95
96    protected String listName; // For debugging
97
98    private ListIterator<? extends GenericObject> myListIterator;
99
100    private String stringRep;
101
102    protected Class<?> myClass;
103
104    protected String separator;
105
106    protected String getIndentation() {
107        char[] chars = new char[indentation];
108        java.util.Arrays.fill(chars, ' ');
109        return new String(chars);
110    }
111
112    /**
113     * Return true if this supports reflection based cloning.
114     */
115    protected static boolean isCloneable(Object obj) {
116        return obj instanceof Cloneable;
117    }
118
119    public static boolean isMySubclass(Class<?> other) {
120        return GenericObjectList.class.isAssignableFrom(other);
121
122    }
123
124    /**
125     * Makes a deep clone of this list.
126     */
127    public Object clone() {
128        GenericObjectList retval = (GenericObjectList) super.clone();
129        for (ListIterator<GenericObject> iter = retval.listIterator(); iter.hasNext();) {
130            GenericObject obj = (GenericObject) ((GenericObject) iter.next())
131                    .clone();
132            iter.set(obj);
133        }
134        return retval;
135    }
136
137
138
139    public void setMyClass(Class cl) {
140        myClass = cl;
141    }
142
143    protected GenericObjectList() {
144        super();
145        listName = null;
146        stringRep = "";
147        separator = ";";
148    }
149
150    protected GenericObjectList(String lname) {
151        this();
152        listName = lname;
153    }
154
155    /**
156     * A Constructor which takes a list name and a class name (for assertion
157     * checking).
158     */
159
160    protected GenericObjectList(String lname, String classname) {
161        this(lname);
162        try {
163            myClass = Class.forName(classname);
164        } catch (ClassNotFoundException ex) {
165            InternalErrorHandler.handleException(ex);
166        }
167
168    }
169
170    /**
171     * A Constructor which takes a list name and a class (for assertion
172     * checking).
173     */
174
175    protected GenericObjectList(String lname, Class objclass) {
176        this(lname);
177        myClass = objclass;
178    }
179
180    /**
181     * Traverse the list given a list iterator
182     */
183    protected GenericObject next(ListIterator iterator) {
184        try {
185            return (GenericObject) iterator.next();
186        } catch (NoSuchElementException ex) {
187            return null;
188        }
189    }
190
191    /**
192     * This is the default list iterator.This will not handle nested list
193     * traversal.
194     */
195    protected GenericObject first() {
196        myListIterator = this.listIterator(0);
197        try {
198            return (GenericObject) myListIterator.next();
199        } catch (NoSuchElementException ex) {
200            return null;
201        }
202    }
203
204    /**
205     * Fetch the next object from the list based on the default list iterator
206     */
207    protected GenericObject next() {
208        if (myListIterator == null) {
209            myListIterator = this.listIterator(0);
210        }
211        try {
212            return (GenericObject) myListIterator.next();
213        } catch (NoSuchElementException ex) {
214            myListIterator = null;
215            return null;
216        }
217    }
218
219    /**
220     * Concatenate two compatible header lists, adding the argument to the tail
221     * end of this list.
222     *
223     * @param 
224     *            topFlag </var> set to true to add items to top of list
225     */
226    protected void concatenate(GenericObjectList objList) {
227        concatenate(objList, false);
228    }
229
230    /**
231     * Concatenate two compatible header lists, adding the argument either to
232     * the beginning or the tail end of this list. A type check is done before
233     * concatenation.
234     *
235     * @param 
236     *            topFlag </var> set to true to add items to top of list else
237     *            add them to the tail end of the list.
238     */
239    protected void concatenate(GenericObjectList objList, boolean topFlag) {
240        if (!topFlag) {
241            this.addAll(objList);
242        } else {
243            // add given items to the top end of the list.
244            this.addAll(0, objList);
245        }
246    }
247
248    /**
249     * string formatting function.
250     */
251
252    private void sprint(String s) {
253        if (s == null) {
254            stringRep += getIndentation();
255            stringRep += "<null>\n";
256            return;
257        }
258
259        if (s.compareTo("}") == 0 || s.compareTo("]") == 0) {
260            indentation--;
261        }
262        stringRep += getIndentation();
263        stringRep += s;
264        stringRep += "\n";
265        if (s.compareTo("{") == 0 || s.compareTo("[") == 0) {
266            indentation++;
267        }
268    }
269
270    /**
271     * Convert this list of headers to a formatted string.
272     */
273
274    public String debugDump() {
275        stringRep = "";
276        Object obj = this.first();
277        if (obj == null)
278            return "<null>";
279        sprint("listName:");
280        sprint(listName);
281        sprint("{");
282        while (obj != null) {
283            sprint("[");
284            sprint(((GenericObject) obj).debugDump(this.indentation));
285            obj = next();
286            sprint("]");
287        }
288        sprint("}");
289        return stringRep;
290    }
291
292    /**
293     * Convert this list of headers to a string (for printing) with an
294     * indentation given.
295     */
296
297    public String debugDump(int indent) {
298        int save = indentation;
299        indentation = indent;
300        String retval = this.debugDump();
301        indentation = save;
302        return retval;
303    }
304
305    public void addFirst(GenericObject objToAdd) {
306        if (myClass == null) {
307            myClass = objToAdd.getClass();
308        } else {
309            super.addFirst(objToAdd);
310        }
311    }
312
313    /**
314     * Do a merge of the GenericObjects contained in this list with the
315     * GenericObjects in the mergeList. Note that this does an inplace
316     * modification of the given list. This does an object by object merge of
317     * the given objects.
318     *
319     * @param mergeList
320     *            is the list of Generic objects that we want to do an object by
321     *            object merge with. Note that no new objects are added to this
322     *            list.
323     *
324     */
325
326    public void mergeObjects(GenericObjectList mergeList) {
327
328        if (mergeList == null)
329            return;
330        Iterator it1 = this.listIterator();
331        Iterator it2 = mergeList.listIterator();
332        while (it1.hasNext()) {
333            GenericObject outerObj = (GenericObject) it1.next();
334            while (it2.hasNext()) {
335                Object innerObj = it2.next();
336                outerObj.merge(innerObj);
337            }
338        }
339    }
340
341    /**
342     * Encode the list in semicolon separated form.
343     *
344     * @return an encoded string containing the objects in this list.
345     * @since v1.0
346     */
347    public String encode() {
348        if (this.isEmpty())
349            return "";
350        StringBuffer encoding = new StringBuffer();
351        ListIterator iterator = this.listIterator();
352        if (iterator.hasNext()) {
353            while (true) {
354                Object obj = iterator.next();
355                if (obj instanceof GenericObject) {
356                    GenericObject gobj = (GenericObject) obj;
357                    encoding.append(gobj.encode());
358                } else {
359                    encoding.append(obj.toString());
360                }
361                if (iterator.hasNext())
362                    encoding.append(separator);
363                else
364                    break;
365            }
366        }
367        return encoding.toString();
368    }
369
370    /**
371     * Alias for the encode function above.
372     */
373    public String toString() {
374        return this.encode();
375    }
376
377    /**
378     * Set the separator (for encoding the list)
379     *
380     * @since v1.0
381     * @param sep
382     *            is the new seperator (default is semicolon)
383     */
384    public void setSeparator(String sep) {
385        separator = sep;
386    }
387
388    /**
389     * Hash code. We never expect to put this in a hash table so return a constant.
390     */
391    public int hashCode() { return 42; }
392
393    /**
394     * Equality checking predicate.
395     *
396     * @param other
397     *            is the object to compare ourselves to.
398     * @return true if the objects are equal.
399     */
400    public boolean equals(Object other) {
401        if (other == null ) return false;
402        if (!this.getClass().equals(other.getClass()))
403            return false;
404        GenericObjectList that = (GenericObjectList) other;
405        if (this.size() != that.size())
406            return false;
407        ListIterator myIterator = this.listIterator();
408        while (myIterator.hasNext()) {
409            Object myobj = myIterator.next();
410            ListIterator hisIterator = that.listIterator();
411            try {
412                while (true) {
413                    Object hisobj = hisIterator.next();
414                    if (myobj.equals(hisobj))
415                        break;
416                }
417            } catch (NoSuchElementException ex) {
418                return false;
419            }
420        }
421        ListIterator hisIterator = that.listIterator();
422        while (hisIterator.hasNext()) {
423            Object hisobj = hisIterator.next();
424            myIterator = this.listIterator();
425            try {
426                while (true) {
427                    Object myobj = myIterator.next();
428                    if (hisobj.equals(myobj))
429                        break;
430                }
431            } catch (NoSuchElementException ex) {
432                return false;
433            }
434        }
435        return true;
436    }
437
438    /**
439     * Match with a template (return true if we have a superset of the given
440     * template. This can be used for partial match (template matching of SIP
441     * objects). Note -- this implementation is not unnecessarily efficient :-)
442     *
443     * @param other
444     *            template object to compare against.
445     */
446
447    public boolean match(Object other) {
448        if (!this.getClass().equals(other.getClass()))
449            return false;
450        GenericObjectList that = (GenericObjectList) other;
451        ListIterator hisIterator = that.listIterator();
452        outer: while (hisIterator.hasNext()) {
453            Object hisobj = hisIterator.next();
454            Object myobj = null;
455            ListIterator myIterator = this.listIterator();
456            while (myIterator.hasNext()) {
457                myobj = myIterator.next();
458                if (myobj instanceof GenericObject)
459                    System.out.println("Trying to match  = "
460                            + ((GenericObject) myobj).encode());
461                if (GenericObject.isMySubclass(myobj.getClass())
462                        && ((GenericObject) myobj).match(hisobj))
463                    break outer;
464                else if (GenericObjectList.isMySubclass(myobj.getClass())
465                        && ((GenericObjectList) myobj).match(hisobj))
466                    break outer;
467            }
468            System.out.println(((GenericObject) hisobj).encode());
469            return false;
470        }
471        return true;
472    }
473}
474