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