View Javadoc
1   package org.apache.commons.jcs.utils.struct;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  
25  /**
26   * This is a generic thread safe double linked list. It's very simple and all the operations are so
27   * quick that course grained synchronization is more than acceptable.
28   */
29  @SuppressWarnings({ "unchecked", "rawtypes" }) // Don't know how to resolve this with generics
30  public class DoubleLinkedList<T extends DoubleLinkedListNode>
31  {
32      /** record size to avoid having to iterate */
33      private int size = 0;
34  
35      /** The logger */
36      private static final Log log = LogFactory.getLog( DoubleLinkedList.class );
37  
38      /** LRU double linked list head node */
39      private T first;
40  
41      /** LRU double linked list tail node */
42      private T last;
43  
44      /**
45       * Default constructor.
46       */
47      public DoubleLinkedList()
48      {
49          super();
50      }
51  
52      /**
53       * Adds a new node to the end of the link list.
54       * <p>
55       * @param me The feature to be added to the Last
56       */
57      public synchronized void addLast(T me)
58      {
59          if ( first == null )
60          {
61              // empty list.
62              first = me;
63          }
64          else
65          {
66              last.next = me;
67              me.prev = last;
68          }
69          last = me;
70          size++;
71      }
72  
73      /**
74       * Adds a new node to the start of the link list.
75       * <p>
76       * @param me The feature to be added to the First
77       */
78      public synchronized void addFirst(T me)
79      {
80          if ( last == null )
81          {
82              // empty list.
83              last = me;
84          }
85          else
86          {
87              first.prev = me;
88              me.next = first;
89          }
90          first = me;
91          size++;
92      }
93  
94      /**
95       * Returns the last node from the link list, if there are any nodes.
96       * <p>
97       * @return The last node.
98       */
99      public synchronized T getLast()
100     {
101         if ( log.isDebugEnabled() )
102         {
103             log.debug( "returning last node" );
104         }
105         return last;
106     }
107 
108     /**
109      * Removes the specified node from the link list.
110      * <p>
111      * @return DoubleLinkedListNode, the first node.
112      */
113     public synchronized T getFirst()
114     {
115         if ( log.isDebugEnabled() )
116         {
117             log.debug( "returning first node" );
118         }
119         return first;
120     }
121 
122     /**
123      * Moves an existing node to the start of the link list.
124      * <p>
125      * @param ln The node to set as the head.
126      */
127     public synchronized void makeFirst(T ln)
128     {
129         if ( ln.prev == null )
130         {
131             // already the first node. or not a node
132             return;
133         }
134         // splice: remove it from the list
135         ln.prev.next = ln.next;
136 
137         if ( ln.next == null )
138         {
139             // last but not the first.
140             last = (T) ln.prev;
141             last.next = null;
142         }
143         else
144         {
145             // neither the last nor the first.
146             ln.next.prev = ln.prev;
147         }
148         first.prev = ln;
149         ln.next = first;
150         ln.prev = null;
151         first = ln;
152     }
153 
154     /**
155      * Moves an existing node to the end of the link list.
156      * <p>
157      * @param ln The node to set as the head.
158      */
159     public synchronized void makeLast(T ln)
160     {
161         if ( ln.next == null )
162         {
163             // already the last node. or not a node
164             return;
165         }
166         // splice: remove it from the list
167         if ( ln.prev != null )
168         {
169             ln.prev.next = ln.next;
170         }
171         else
172         {
173             // first
174             first = last;
175         }
176 
177         if ( last != null )
178         {
179             last.next = ln;
180         }
181         ln.prev = last;
182         ln.next = null;
183         last = ln;
184     }
185 
186     /**
187      * Remove all of the elements from the linked list implementation.
188      */
189     public synchronized void removeAll()
190     {
191         for (T me = first; me != null; )
192         {
193             if ( me.prev != null )
194             {
195                 me.prev = null;
196             }
197             T next = (T) me.next;
198             me = next;
199         }
200         first = last = null;
201         // make sure this will work, could be add while this is happening.
202         size = 0;
203     }
204 
205     /**
206      * Removes the specified node from the link list.
207      * <p>
208      * @param me Description of the Parameter
209      * @return true if an element was removed.
210      */
211     public synchronized boolean remove(T me)
212     {
213         if ( log.isDebugEnabled() )
214         {
215             log.debug( "removing node" );
216         }
217 
218         if ( me.next == null )
219         {
220             if ( me.prev == null )
221             {
222                 // Make sure it really is the only node before setting head and
223                 // tail to null. It is possible that we will be passed a node
224                 // which has already been removed from the list, in which case
225                 // we should ignore it
226 
227                 if ( me == first && me == last )
228                 {
229                     first = last = null;
230                 }
231             }
232             else
233             {
234                 // last but not the first.
235                 last = (T) me.prev;
236                 last.next = null;
237                 me.prev = null;
238             }
239         }
240         else if ( me.prev == null )
241         {
242             // first but not the last.
243             first = (T) me.next;
244             first.prev = null;
245             me.next = null;
246         }
247         else
248         {
249             // neither the first nor the last.
250             me.prev.next = me.next;
251             me.next.prev = me.prev;
252             me.prev = me.next = null;
253         }
254         size--;
255 
256         return true;
257     }
258 
259     /**
260      * Removes the specified node from the link list.
261      * <p>
262      * @return The last node if there was one to remove.
263      */
264     public synchronized T removeLast()
265     {
266         if ( log.isDebugEnabled() )
267         {
268             log.debug( "removing last node" );
269         }
270         T temp = last;
271         if ( last != null )
272         {
273             remove( last );
274         }
275         return temp;
276     }
277 
278     /**
279      * Returns the size of the list.
280      * <p>
281      * @return int
282      */
283     public synchronized int size()
284     {
285         return size;
286     }
287 
288     // ///////////////////////////////////////////////////////////////////
289     /**
290      * Dump the cache entries from first to list for debugging.
291      */
292     public synchronized void debugDumpEntries()
293     {
294         if ( log.isDebugEnabled() )
295         {
296             log.debug( "dumping Entries" );
297             for (T me = first; me != null; me = (T) me.next)
298             {
299                 log.debug( "dump Entries> payload= '" + me.getPayload() + "'" );
300             }
301         }
302     }
303 }