Fix version, move list management from Card/Contact/... into BaseClass
[jvcard.git] / src / be / nikiroo / jvcard / BaseClass.java
1 package be.nikiroo.jvcard;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Iterator;
6 import java.util.List;
7 import java.util.ListIterator;
8
9 /**
10 * This class is basically a List with a parent and a "dirty" state check. It
11 * sends all commands down to the initial list, but will mark itself and its
12 * children as dirty or not when needed.
13 *
14 * All child elements can identify their parent.
15 *
16 * The dirty state is bubbling up (when dirty = true) or down (when dirty =
17 * false) -- so, making changes to a child element will also mark its parent as
18 * "dirty", and marking an element as pristine will also affect all its child
19 * elements.
20 *
21 * @author niki
22 *
23 * @param <E>
24 * the type of the child elements
25 */
26 public abstract class BaseClass<E extends BaseClass<?>> implements List<E> {
27 protected boolean dirty;
28 protected BaseClass<?> parent;
29 private List<E> list;
30
31 /**
32 * Create a new {@link BaseClass} with the given list as its descendants.
33 *
34 * @param list
35 * the descendants of this object, or NULL if none
36 */
37 protected BaseClass(List<E> list) {
38 this.list = new ArrayList<E>();
39 list.addAll(list);
40 for (E child : this) {
41 _enter(child, true);
42 }
43 }
44
45 /**
46 * Check if this element has unsaved changes.
47 *
48 * @return TRUE if it has
49 */
50 public boolean isDirty() {
51 return dirty;
52 }
53
54 /**
55 * Delete this element from its parent if any.
56 *
57 * @return TRUE in case of success
58 */
59 public boolean delete() {
60 if (parent != null) {
61 return parent.remove(this);
62 }
63
64 return false;
65 }
66
67 /**
68 * Replace the elements contained in this with those in the given
69 * {@link List}.
70 *
71 * Note: the elements will be copied from the {@link List}, you cannot
72 * manage the {@link List} from outside
73 *
74 * @param list
75 * the list of new elements
76 */
77 public void replaceListContent(List<E> list) {
78 List<E> del = new ArrayList<E>();
79 List<E> add = new ArrayList<E>();
80
81 for (E oldChild : this) {
82 if (!list.contains(oldChild)) {
83 del.add(oldChild);
84 }
85 }
86 for (E newChild : list) {
87 if (!contains(newChild)) {
88 add.add(newChild);
89 }
90 }
91
92 removeAll(del);
93 addAll(add);
94 }
95
96 /**
97 * Notify that this element has unsaved changes.
98 */
99 void setDirty() {
100 dirty = true;
101 }
102
103 /**
104 * Notify this element <i>and all its descendants</i> that it is in pristine
105 * state (as opposed to dirty).
106 */
107 void setPristine() {
108 dirty = false;
109 for (E child : this) {
110 child.setPristine();
111 }
112 }
113
114 /**
115 * Set the parent of this element <i>and all its descendants</i>.
116 *
117 * @param parent
118 * the new parent
119 */
120 void setParent(BaseClass<?> parent) {
121 this.parent = parent;
122 for (E child : this) {
123 child.setParent(this);
124 }
125 }
126
127 /**
128 * Each element that leaves the parent will pass trough here.
129 *
130 * @param child
131 * the element to remove from this
132 */
133 private void _leave(E child) {
134 setDirty();
135 }
136
137 /**
138 * Each element that enters the parent will pass trough here.
139 *
140 * @param child
141 * the element to add to this
142 */
143 private void _enter(E child) {
144 _enter(child, false);
145 }
146
147 /**
148 * Each element that enters the parent will pass trough here.
149 *
150 * @param child
151 * the element to add to this
152 */
153 private void _enter(E child, boolean initialLoad) {
154 child.setParent(this);
155 if (!initialLoad)
156 child.setDirty();
157 }
158
159 @Override
160 public boolean add(E e) {
161 _enter(e, false);
162 return list.add(e);
163 }
164
165 @Override
166 @SuppressWarnings("unchecked")
167 public boolean remove(Object o) {
168 if (list.remove(o)) {
169 if (o instanceof BaseClass<?>) {
170 _leave((E) o); // expected warning
171 }
172 return true;
173 }
174
175 return false;
176 }
177
178 @Override
179 public boolean addAll(Collection<? extends E> c) {
180 for (E child : c) {
181 _enter(child);
182 }
183
184 return list.addAll(c);
185 }
186
187 @Override
188 public boolean addAll(int index, Collection<? extends E> c) {
189 for (E child : c) {
190 _enter(child);
191 }
192
193 return list.addAll(index, c);
194 }
195
196 @Override
197 public boolean removeAll(Collection<?> c) {
198 boolean changed = false;
199
200 for (Object o : c) {
201 if (remove(o))
202 changed = true;
203 }
204
205 return changed;
206 }
207
208 @Override
209 public boolean retainAll(Collection<?> c) {
210 ArrayList<Object> del = new ArrayList<Object>();
211 for (Object o : c) {
212 del.add(o);
213 }
214 return removeAll(del);
215 }
216
217 @Override
218 public void clear() {
219 for (E child : this) {
220 _leave(child);
221 }
222
223 list.clear();
224 }
225
226 @Override
227 public E set(int index, E element) {
228 E child = get(index);
229 if (child != null)
230 _leave(child);
231 _enter(element);
232
233 return list.set(index, element);
234 }
235
236 @Override
237 public void add(int index, E element) {
238 _enter(element);
239 list.add(index, element);
240 }
241
242 @Override
243 public E remove(int index) {
244 E child = get(index);
245 _leave(child);
246 return list.remove(index);
247 }
248
249 @Override
250 public Iterator<E> iterator() {
251 return listIterator(0);
252 }
253
254 @Override
255 public ListIterator<E> listIterator() {
256 return listIterator(0);
257 }
258
259 @Override
260 public ListIterator<E> listIterator(int index) {
261 final int i = index;
262 return new ListIterator<E>() {
263 ListIterator<E> base = list.listIterator(i);
264 E last;
265
266 @Override
267 public boolean hasNext() {
268 return base.hasNext();
269 }
270
271 @Override
272 public E next() {
273 last = base.next();
274 return last;
275 }
276
277 @Override
278 public boolean hasPrevious() {
279 return base.hasPrevious();
280 }
281
282 @Override
283 public E previous() {
284 last = base.previous();
285 return last;
286 }
287
288 @Override
289 public int nextIndex() {
290 return base.nextIndex();
291 }
292
293 @Override
294 public int previousIndex() {
295 return base.previousIndex();
296 }
297
298 @Override
299 public void remove() {
300 base.remove();
301 _leave(last);
302 }
303
304 @Override
305 public void set(E e) {
306 base.set(e);
307 _leave(last);
308 _enter(e);
309 }
310
311 @Override
312 public void add(E e) {
313 base.add(e);
314 _enter(e);
315 }
316 };
317 }
318
319 @Override
320 public Object[] toArray() {
321 return list.toArray();
322 }
323
324 @Override
325 public <T> T[] toArray(T[] a) {
326 return list.toArray(a);
327 }
328
329 @Override
330 public int size() {
331 return list.size();
332 }
333
334 @Override
335 public boolean isEmpty() {
336 return list.isEmpty();
337 }
338
339 @Override
340 public boolean contains(Object o) {
341 return list.contains(o);
342 }
343
344 @Override
345 public boolean containsAll(Collection<?> c) {
346 return list.containsAll(c);
347 }
348
349 @Override
350 public E get(int index) {
351 return list.get(index);
352 }
353
354 @Override
355 public int indexOf(Object o) {
356 return list.indexOf(o);
357 }
358
359 @Override
360 public int lastIndexOf(Object o) {
361 return list.lastIndexOf(o);
362 }
363
364 @Override
365 public List<E> subList(int fromIndex, int toIndex) {
366 return list.subList(fromIndex, toIndex);
367 }
368 }