View Javadoc

1   /*
2    * Grapht, an open source dependency injector.
3    * Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt)
4    * Copyright 2010-2014 Regents of the University of Minnesota
5    *
6    * This program is free software; you can redistribute it and/or modify
7    * it under the terms of the GNU Lesser General Public License as
8    * published by the Free Software Foundation; either version 2.1 of the
9    * License, or (at your option) any later version.
10   *
11   * This program is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13   * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14   * details.
15   *
16   * You should have received a copy of the GNU General Public License along with
17   * this program; if not, write to the Free Software Foundation, Inc., 51
18   * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19   */
20  package org.grouplens.grapht.util;
21  
22  import com.google.common.collect.Iterators;
23  
24  import javax.annotation.Nonnull;
25  import java.io.Serializable;
26  import java.util.AbstractList;
27  import java.util.Iterator;
28  import java.util.NoSuchElementException;
29  
30  /**
31   * Base class for implementing chains, immutable reverse singly-linked lists.
32   *
33   * @since 0.7.0
34   * @author <a href="http://www.grouplens.org">GroupLens Research</a>
35   */
36  public abstract class AbstractChain<E extends Serializable> extends AbstractList<E> implements Serializable {
37      private static final long serialVersionUID = 1L;
38  
39      protected final AbstractChain<E> previous;
40      protected final E tailValue;
41      protected final int length;
42  
43      /**
44       * Construct a new chain node.
45       * @param prev The previous node, or {@code null} for a singleton chain.
46       * @param tv The value to go at the end of the chain.
47       */
48      protected AbstractChain(AbstractChain<E> prev, E tv) {
49          previous = prev;
50          tailValue = tv;
51          if (prev == null) {
52              length = 1;
53          } else {
54              length = prev.length + 1;
55          }
56      }
57  
58      public E getTailValue() {
59          return tailValue;
60      }
61  
62      @Override
63      public int size() {
64          return length;
65      }
66  
67      @Override
68      public E get(int i) {
69          com.google.common.base.Preconditions.checkElementIndex(i, length);
70          if (i == length - 1) {
71              return tailValue;
72          } else {
73              assert previous != null;
74              return previous.get(i);
75          }
76      }
77  
78      @Nonnull
79      @Override
80      public Iterator<E> iterator() {
81          Iterator<E> current = Iterators.singletonIterator(tailValue);
82          if (previous == null) {
83              return current;
84          } else {
85              return Iterators.concat(previous.iterator(), current);
86          }
87      }
88  
89      /**
90       * Iterate over this chain's elements in reverse order.
91       * @return An iterator over the chain's elements in reverse order (current first).
92       */
93      public Iterator<E> reverseIterator() {
94          return new Iterator<E>() {
95              AbstractChain<E> cur = AbstractChain.this;
96              @Override
97              public boolean hasNext() {
98                  return cur != null;
99              }
100 
101             @Override
102             public E next() {
103                 if (cur == null) {
104                     throw new NoSuchElementException();
105                 }
106                 E d = cur.tailValue;
107                 cur = cur.previous;
108                 return d;
109             }
110 
111             @Override
112             public void remove() {
113                 throw new UnsupportedOperationException();
114             }
115         };
116     }
117 
118     public Iterable<E> reverse() {
119         return new Iterable<E>() {
120             @Override
121             public Iterator<E> iterator() {
122                 return reverseIterator();
123             }
124         };
125     }
126 
127     @Override
128     public int hashCode() {
129         // override hashCode so that lint tools don't complain
130         return super.hashCode();
131     }
132 
133     @Override
134     public boolean equals(Object o) {
135         if (o == this) {
136             return true;
137         } else if (o instanceof AbstractChain) {
138             // optimize comparing two chains
139             return Iterators.elementsEqual(reverseIterator(),
140                                            ((AbstractChain) o).reverseIterator());
141         } else {
142             return super.equals(o);
143         }
144     }
145 }