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