1 // Copyright (c) 2001, 2003  Per M.A. Bothner and Brainfood Inc.
2 // This is free software;  for terms and warranty disclaimer see ./COPYING.
3 
4 package gnu.lists;
5 
6 import java.io.*;
7 
8 /** A "pair" object, as used in Lisp-like languages.
9  * When used to implement a list, the 'car' field contains an
10  * element's value, and the 'cdr' field points to either the next Pair
11  * or LList.Empty (which represents the end of the list).  (The names
12  * "car" and "cdr" [pronounced "coulder"] are historical; better names
13  * might be "value" and "next".)  While a Pair is normally usued to
14  * implement a linked list, sometimes the 'cdr' field points to some
15  * other non-list object; this is traditionally callled a "dotted list".
16  */
17 
18 public class Pair extends LList implements Externalizable
19 {
20   protected Object car;
21   protected Object cdr;
22 
23     /** A special pair used to indicate incomplete input. */
24     public static final Pair incompleteListMarker
25         = new ImmutablePair("<incomplete>", LList.Empty);
26 
Pair(Object carval, Object cdrval)27   public Pair (Object carval, Object cdrval)
28   {
29     car = carval;
30     cdr = cdrval;
31   }
32 
Pair()33   public Pair ()
34   {
35   }
36 
getCar()37   public Object getCar () { return car; }
getCdr()38   public Object getCdr () { return cdr; }
setCar(Object car)39   public void setCar(Object car) { this.car = car; }
setCdr(Object cdr)40   public void setCdr(Object cdr) { this.cdr = cdr; }
41 
42   /** May go away soon. */
setCarBackdoor(Object car)43   public void setCarBackdoor (Object car) { this.car = car; }
setCdrBackdoor(Object cdr)44   public void setCdrBackdoor (Object cdr) { this.cdr = cdr; }
45 
size()46   public int size()
47   {
48     int n = listLength(this, true);
49     if (n >= 0)
50       return n;
51     if (n == -1)
52       return Integer.MAX_VALUE;
53     throw new RuntimeException("not a true list");
54   }
55 
isEmpty()56   public boolean isEmpty()
57   {
58     return false;
59   }
60 
61   // A generalization of LList.list_length
62   // FIXME is this needed, given listLength?
length()63   public int length ()
64   {
65     // Based on list-length implementation in
66     // Guy L Steele jr: "Common Lisp:  The Language", 2nd edition, page 414
67     int n = 0;
68     Object slow = this;
69     Object fast = this;
70     for (;;)
71       {
72 	if (fast == Empty)
73 	  return n;
74 	if (! (fast instanceof Pair))
75 	  {
76 	    if (fast instanceof Sequence)
77 	      {
78 		int j = ((Sequence) fast).size();
79 		return j >= 0 ? n + j : j;
80 	      }
81 	    return -2;
82 	  }
83 	Pair fast_pair = (Pair) fast;
84         Object fast_pair_cdr = fast_pair.getCdr();
85 	if (fast_pair_cdr == Empty)
86 	  return n+1;
87 	if (fast == slow && n > 0)
88 	  return -1;
89 	if (! (fast_pair_cdr instanceof Pair))
90 	  {
91 	    n++;
92 	    fast = fast_pair_cdr;
93 	    continue;
94 	  }
95 	if (!(slow instanceof Pair))
96 	  return -2;
97 	slow = ((Pair)slow).getCdr();
98 	fast = ((Pair)fast_pair_cdr).getCdr();
99 	n += 2;
100       }
101   }
102 
hasNext(int ipos)103   public boolean hasNext (int ipos)
104   {
105     if (ipos <= 0)
106       return ipos == 0;
107     return PositionManager.getPositionObject(ipos).hasNext();
108   }
109 
nextPos(int ipos)110   public int nextPos (int ipos)
111   {
112     if (ipos <= 0)
113       {
114 	if (ipos < 0)
115 	  return 0;
116 	return PositionManager.manager
117 	  .register(new LListPosition(this, 1, true));
118       }
119     LListPosition it = (LListPosition) PositionManager.getPositionObject(ipos);
120     return it.gotoNext() ? ipos : 0;
121   }
122 
getPosNext(int ipos)123   public Object getPosNext (int ipos)
124   {
125     if (ipos <= 0)
126         return ipos == 0 ? getCar() : eofValue;
127     return PositionManager.getPositionObject(ipos).getNext();
128   }
129 
getPosPrevious(int ipos)130   public Object getPosPrevious (int ipos)
131   {
132     if (ipos <= 0)
133       return ipos == 0 ? eofValue : lastPair().getCar();
134     return PositionManager.getPositionObject(ipos).getPrevious();
135   }
136 
lastPair()137   public Pair lastPair()
138   {
139     Pair pair = this;
140     for (;;)
141       {
142         Object next = pair.getCdr();
143 	if (next instanceof Pair)
144 	  pair = (Pair) next;
145 	else
146 	  return pair;
147       }
148   }
149 
150   /*
151   public void print(java.io.PrintWriter ps)
152   {
153     ps.print("(");
154     printNoParen(this, ps);
155     ps.print(")");
156   }
157   */
158 
equals(Pair pair1, Pair pair2)159   static public boolean equals (Pair pair1, Pair pair2)
160   {
161     if (pair1 == pair2)
162       return true;
163     if (pair1 == null || pair2 == null)
164       return false;
165     for (;;)
166       {
167         Object x1 = pair1.getCar();
168 	Object x2 = pair2.getCar();
169 	if (x1 != x2 && (x1 == null || ! x1.equals(x2)))
170 	  return false;
171 	x1 = pair1.getCdr();
172 	x2 = pair2.getCdr();
173 	if (x1 == x2)
174 	  return true;
175 	if (x1 == null || x2 == null)
176 	  return false;
177 	if (! (x1 instanceof Pair) || !(x2 instanceof Pair))
178 	  return x1.equals(x2);
179 	pair1 = (Pair) x1;
180 	pair2 = (Pair) x2;
181 
182       }
183   }
184 
185   /* #ifdef JAVA2 */
compareTo(Pair pair1, Pair pair2)186   static public int compareTo (Pair pair1, Pair pair2)
187   {
188     if (pair1 == pair2)
189       return 0;
190     if (pair1 == null )
191       return -1;
192     if (pair2 == null)
193       return 1;
194     for (;;)
195       {
196         Object x1 = pair1.getCar();
197         Object x2 = pair2.getCar();
198         int d = ((Comparable) x1).compareTo((Comparable) x2);
199         if (d != 0)
200           return d;
201         x1 = pair1.cdr;
202         x2 = pair2.cdr;
203         if (x1 == x2)
204           return 0;
205         if (x1 == null)
206           return -1;
207         if (x2 == null)
208           return 1;
209         if (! (x1 instanceof Pair) || !(x2 instanceof Pair))
210           return ((Comparable) x1).compareTo((Comparable) x2);
211         pair1 = (Pair) x1;
212         pair2 = (Pair) x2;
213       }
214   }
215 
compareTo(Object obj)216   public int compareTo(Object obj)
217   {
218     if (obj == Empty)
219       return 1;
220     else
221       return compareTo(this, (Pair) obj);
222   }
223   /* #endif */
224 
get(int index)225   public Object get (int index)
226   {
227     Pair pair = this;
228     int i = index;
229     while (i > 0)
230       {
231 	i--;
232         Object pair_cdr = pair.getCdr();
233 	if (pair_cdr instanceof Pair)
234 	  pair = (Pair)pair_cdr;
235 	else if (pair_cdr instanceof Sequence)
236 	  return ((Sequence)pair_cdr).get(i);
237 	else
238 	  break;
239       }
240     if (i == 0)
241       return pair.getCar();
242     else
243       throw new IndexOutOfBoundsException ();
244   }
245 
equals(Object obj)246   public boolean equals (Object obj)
247   {
248     if ((obj != null) && (obj instanceof Pair))
249       return equals (this, (Pair) obj);
250     else
251       return false;
252   }
253 
make(Object car, Object cdr)254   public static Pair make (Object car, Object cdr)
255   {
256     return new Pair (car, cdr);
257   }
258 
toArray()259   public Object[] toArray()
260   {
261     int len = size(); // size()
262     Object[] arr = new Object[len];
263     int i = 0;
264     Sequence rest = this;
265     for ( ;  i < len && rest instanceof Pair;  i++)
266     {
267       Pair pair = (Pair) rest;
268       arr[i] = pair.getCar();
269       rest = (Sequence) pair.getCdr();
270     }
271     int prefix = i;
272     for ( ;  i < len;  i++)
273     {
274       arr[i] = rest.get(i - prefix);
275     }
276     return arr;
277   }
278 
toArray(Object[] arr)279   public Object[] toArray(Object[] arr)
280   {
281     int alen = arr.length;
282     int len = length();
283     if (len > alen)
284     {
285       // FIXME Collection spec requires arr to keep same run-time type
286       arr = new Object[len];
287       alen = len;
288     }
289     int i = 0;
290     Sequence rest = this;
291     for ( ;  i < len && rest instanceof Pair;  i++)
292     {
293       Pair pair = (Pair) rest;
294       arr[i] = pair.getCar();
295       rest = (Sequence) pair.getCdr();
296     }
297     int prefix = i;
298     for ( ;  i < len;  i++)
299     {
300       arr[i] = rest.get(i - prefix);
301     }
302     if (len < alen)
303       arr[len] = null;
304     return arr;
305   }
306 
307   /**
308    * @serialData Write the car followed by the cdr.
309    */
writeExternal(ObjectOutput out)310   public void writeExternal(ObjectOutput out) throws IOException
311   {
312     out.writeObject(car);
313     out.writeObject(cdr);
314   }
315 
readExternal(ObjectInput in)316   public void readExternal(ObjectInput in)
317     throws IOException, ClassNotFoundException
318   {
319     car = in.readObject();
320     cdr = in.readObject();
321   }
322 
323   /** Needed to override readResolve in LList. */
readResolve()324   public Object readResolve() throws ObjectStreamException
325   {
326     return this;
327   }
328 
329 };
330