KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > util > IndexPropertyList


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.util;
21
22 import java.util.AbstractList JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Collections JavaDoc;
25 import java.util.Comparator JavaDoc;
26 import java.util.List JavaDoc;
27
28 import org.apache.cayenne.CayenneRuntimeException;
29 import org.apache.cayenne.ValueHolder;
30 import org.apache.cayenne.reflect.PropertyUtils;
31
32 /**
33  * A List implementation that would maintain its internal ordering based on some object
34  * numeric "index" property. When objects are added to the list at a certain index, an
35  * "index" property is modified to reflect list order, when objects are removed, their
36  * index property is set to the negative number.
37  * <p>
38  * For performance reasons this implementation does not guarantee that there is no gaps in
39  * the integer ordering sequence (i.e. generally
40  * <code>object.getIndexProperty() != list.indexOf(object)</code>). However it
41  * guarantees the right ordering based on index property.
42  *
43  * @since 1.2
44  * @author Andrus Adamchik
45  */

46 public class IndexPropertyList extends AbstractList JavaDoc implements ValueHolder {
47
48     /**
49      * A default gap maintained between elements index property values. Gaps bigger than 1
50      * ensure faster and less intrusive additions and removals.
51      */

52     static final int DEFAULT_GAP = 3;
53
54     /**
55      * A list used for the actual objects storage.
56      */

57     protected List JavaDoc list;
58     protected String JavaDoc indexProperty;
59
60     boolean dirty;
61     Comparator JavaDoc comparator;
62
63     /**
64      * Creates an empty NumericPropertyOrderedList.
65      */

66     public IndexPropertyList(String JavaDoc indexProperty) {
67         this(indexProperty, new ArrayList JavaDoc(), false);
68     }
69
70     /**
71      * Creates a NumericPropertyOrderedList that decorates another list. If the list is
72      * not known to be properly sorted, caller must set <em>sortNeeded</em> to true.
73      * This will result in sorting the original list on first access attempt.
74      */

75     public IndexPropertyList(String JavaDoc indexProperty, List JavaDoc objects, boolean sortNeeded) {
76
77         if (indexProperty == null) {
78             throw new IllegalArgumentException JavaDoc("Null sortProperty");
79         }
80
81         if (objects == null) {
82             throw new IllegalArgumentException JavaDoc("Null objects list.");
83         }
84
85         this.indexProperty = indexProperty;
86         this.list = objects;
87
88         // be lazy - don't sort here, as (a) it may never be needed and (b) a list can be
89
// a Cayenne fault, so resolving it too early is undesirable
90
this.dirty = sortNeeded;
91     }
92
93     ValueHolder getWrappedValueHolder() {
94         return (list instanceof ValueHolder) ? (ValueHolder) list : null;
95     }
96
97     public boolean isFault() {
98         ValueHolder h = getWrappedValueHolder();
99         return (h != null) ? h.isFault() : false;
100     }
101
102     public Object JavaDoc setValueDirectly(Object JavaDoc value) throws CayenneRuntimeException {
103         ValueHolder h = getWrappedValueHolder();
104         return h != null ? h.setValueDirectly(value) : null;
105     }
106
107     public Object JavaDoc setValue(Object JavaDoc value) throws CayenneRuntimeException {
108         ValueHolder h = getWrappedValueHolder();
109         return h != null ? h.setValue(value) : null;
110     }
111
112     public Object JavaDoc getValue() throws CayenneRuntimeException {
113         ValueHolder h = getWrappedValueHolder();
114         return h != null ? h.getValue() : null;
115     }
116     
117     public Object JavaDoc getValueDirectly() throws CayenneRuntimeException {
118         ValueHolder h = getWrappedValueHolder();
119         return h != null ? h.getValueDirectly() : null;
120     }
121
122     public void invalidate() {
123         ValueHolder h = getWrappedValueHolder();
124         if (h != null) {
125             h.invalidate();
126         }
127     }
128
129     /**
130      * Changes list state to "dirty" forcing reordering on next access.
131      */

132     public void touch() {
133         this.dirty = true;
134     }
135
136     public Object JavaDoc get(int index) {
137         if (dirty) {
138             sort();
139         }
140
141         return list.get(index);
142     }
143
144     public int size() {
145         if (dirty) {
146             sort();
147         }
148
149         return list.size();
150     }
151
152     public Object JavaDoc set(int index, Object JavaDoc element) {
153         if (dirty) {
154             sort();
155         }
156
157         Object JavaDoc removed = list.set(index, element);
158
159         if (element != null) {
160             int indexValue = (removed != null)
161                     ? getIndexValue(removed)
162                     : calculateIndexValue(index);
163
164             setIndexValue(element, indexValue);
165             shift(index + 1, indexValue);
166         }
167
168         if (removed != null && removed != element) {
169             setIndexValue(removed, -1);
170         }
171
172         return removed;
173     }
174
175     public void add(int index, Object JavaDoc element) {
176         if (dirty) {
177             sort();
178         }
179
180         list.add(index, element);
181
182         if (element != null) {
183             int indexValue = calculateIndexValue(index);
184
185             setIndexValue(element, indexValue);
186             shift(index + 1, indexValue);
187         }
188     }
189
190     public Object JavaDoc remove(int index) {
191         if (dirty) {
192             sort();
193         }
194
195         Object JavaDoc removed = list.remove(index);
196
197         if (removed != null) {
198             setIndexValue(removed, -1);
199         }
200
201         return removed;
202     }
203
204     // ============================================
205
// ***** Methods to maintain ordering ******
206
// ============================================
207

208     /**
209      * Calculates an index value at the specified list index. Note that using this value
210      * may require a shift of the objects following this index.
211      */

212     protected int calculateIndexValue(int listIndex) {
213         if (list.isEmpty()) {
214             throw new ArrayIndexOutOfBoundsException JavaDoc(listIndex);
215         }
216
217         if (list.size() == 1 && listIndex == 0) {
218             return 0;
219         }
220
221         // handle lists with teo or more elements...
222

223         // last element
224
if (listIndex == list.size() - 1) {
225             return getIndexValue(get(listIndex - 1)) + DEFAULT_GAP;
226         }
227
228         int from = (listIndex == 0) ? -1 : getIndexValue(get(listIndex - 1));
229         int to = getIndexValue(get(listIndex + 1));
230         return (to - from > 1) ? (to - from) / 2 + from : from + DEFAULT_GAP;
231     }
232
233     protected int getIndexValue(Object JavaDoc object) {
234         Number JavaDoc n = (Number JavaDoc) PropertyUtils.getProperty(object, indexProperty);
235         if (n == null) {
236             throw new CayenneRuntimeException("Null index property '"
237                     + indexProperty
238                     + "' for object "
239                     + object);
240         }
241
242         return n.intValue();
243     }
244
245     protected void setIndexValue(Object JavaDoc object, int index) {
246         PropertyUtils.setProperty(object, indexProperty, new Integer JavaDoc(index));
247     }
248
249     protected void shift(int startIndex, int afterIndexValue) {
250         int size = size();
251         for (int i = startIndex; i < size; i++) {
252             Object JavaDoc object = get(i);
253
254             int indexValue = getIndexValue(object);
255             if (indexValue > afterIndexValue) {
256                 break;
257             }
258
259             int newValue = calculateIndexValue(i);
260             setIndexValue(object, newValue);
261             afterIndexValue = newValue;
262         }
263     }
264
265     /**
266      * Sorts internal list.
267      */

268     protected void sort() {
269         if (!dirty) {
270             return;
271         }
272
273         // do not directly sort Cayenne lists, sort the underlying list instead to avoid a
274
// bunch of additions/removals
275
Collections.sort(unwrapList(), getComparator());
276         dirty = false;
277     }
278
279     List JavaDoc unwrapList() {
280         if (list instanceof PersistentObjectList) {
281             return ((PersistentObjectList) list).resolvedObjectList();
282         }
283         else {
284             return list;
285         }
286     }
287
288     /**
289      * Returns a property Comaparator, creating it on demand.
290      */

291     Comparator JavaDoc getComparator() {
292         if (comparator == null) {
293             comparator = new Comparator JavaDoc() {
294
295                 public int compare(Object JavaDoc o1, Object JavaDoc o2) {
296                     if ((o1 == null && o2 == null) || o1 == o2) {
297                         return 0;
298                     }
299                     else if (o1 == null && o2 != null) {
300                         return -1;
301                     }
302                     else if (o1 != null && o2 == null) {
303                         return 1;
304                     }
305
306                     Comparable JavaDoc p1 = (Comparable JavaDoc) PropertyUtils.getProperty(
307                             o1,
308                             indexProperty);
309                     Comparable JavaDoc p2 = (Comparable JavaDoc) PropertyUtils.getProperty(
310                             o2,
311                             indexProperty);
312                     return (p1 == null) ? -1 : p1.compareTo(p2);
313                 }
314             };
315         }
316         return comparator;
317     }
318 }
319
Popular Tags