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 }