001 /*
002 * Copyright (c) 2005, 2007 IBM Corporation, Embarcadero Technologies, and others.
003 * All rights reserved. This program and the accompanying materials
004 * are made available under the terms of the Eclipse Public License v1.0
005 * which accompanies this distribution, and is available at
006 * http://www.eclipse.org/legal/epl-v10.html
007 *
008 * Contributors:
009 * IBM - initial API and implementation
010 * Kenn Hussey (Embarcadero Technologies) - 204200
011 *
012 * $Id: DerivedEObjectEList.java,v 1.14 2007/10/22 13:27:56 khussey Exp $
013 */
014 package org.eclipse.uml2.common.util;
015
016 import java.util.Collection;
017 import java.util.Iterator;
018 import java.util.List;
019 import java.util.ListIterator;
020 import java.util.NoSuchElementException;
021 import java.util.RandomAccess;
022
023 import org.eclipse.emf.common.notify.Notification;
024 import org.eclipse.emf.common.notify.impl.NotificationImpl;
025 import org.eclipse.emf.common.util.EList;
026 import org.eclipse.emf.common.util.UniqueEList;
027 import org.eclipse.emf.ecore.EObject;
028 import org.eclipse.emf.ecore.EStructuralFeature;
029 import org.eclipse.emf.ecore.InternalEObject;
030 import org.eclipse.emf.ecore.impl.ENotificationImpl;
031 import org.eclipse.emf.ecore.util.AbstractSequentialInternalEList;
032 import org.eclipse.emf.ecore.util.FeatureMap;
033 import org.eclipse.emf.ecore.util.FeatureMapUtil;
034 import org.eclipse.emf.ecore.util.InternalEList;
035
036 /**
037 * A list whose contents are derived (dynamically using a "smart" iterator) from
038 * the values of other features in a trivial way (e.g. by type). The default
039 * implementation does not support modification.
040 *
041 * @since 1.2
042 */
043 public class DerivedEObjectEList<E>
044 extends AbstractSequentialInternalEList<E>
045 implements EStructuralFeature.Setting, InternalEList.Unsettable<E> {
046
047 protected class DerivedListIterator
048 implements ListIterator<E> {
049
050 protected int index = 0;
051
052 protected int featureIndex = 0;
053
054 protected ListIterator<Object> valuesIterator = null;
055
056 protected EStructuralFeature preparedFeature = null;
057
058 protected EList<Object> preparedValues = new UniqueEList.FastCompare<Object>();
059
060 protected int prepared = 0;
061
062 protected boolean scanNext(EStructuralFeature nextFeature,
063 ListIterator<Object> nextValuesIterator) {
064 boolean isFeatureMap = FeatureMapUtil.isFeatureMap(nextFeature);
065
066 while (nextValuesIterator.hasNext()) {
067 Object nextValue = nextValuesIterator.next();
068
069 if (isFeatureMap) {
070 FeatureMap.Entry entry = (FeatureMap.Entry) nextValue;
071 nextFeature = entry.getEStructuralFeature();
072 nextValue = entry.getValue();
073 }
074
075 if ((isIncluded(nextFeature)
076 ? nextValue != null
077 : isIncluded(nextValue))
078 && ((index < preparedValues.size() && nextValue == preparedValues
079 .get(index)) || preparedValues.add(nextValue))) {
080
081 valuesIterator = nextValuesIterator;
082 preparedFeature = nextFeature;
083 return true;
084 }
085 }
086
087 return false;
088 }
089
090 protected boolean prepareNext() {
091
092 if (valuesIterator == null
093 || !scanNext(preparedFeature, valuesIterator)) {
094
095 while (featureIndex < sourceFeatureIDs.length) {
096 int sourceFeatureID = sourceFeatureIDs[featureIndex++];
097
098 if (owner.eIsSet(sourceFeatureID)) {
099 EStructuralFeature sourceFeature = getEStructuralFeature(sourceFeatureID);
100 Object value = owner.eGet(sourceFeatureID, resolve(),
101 true);
102
103 if (sourceFeature.isMany()
104 || FeatureMapUtil.isFeatureMap(sourceFeature)) {
105
106 @SuppressWarnings("unchecked")
107 InternalEList<Object> valuesList = (InternalEList<Object>) value;
108
109 if (scanNext(sourceFeature, resolve()
110 ? valuesList.listIterator()
111 : valuesList.basicListIterator())) {
112
113 prepared = 3;
114 return true;
115 }
116 } else if ((isIncluded(sourceFeature)
117 ? value != null
118 : isIncluded(value))
119 && ((index < preparedValues.size() && value == preparedValues
120 .get(index)) || preparedValues.add(value))) {
121
122 valuesIterator = null;
123 preparedFeature = sourceFeature;
124 prepared = 2;
125 return true;
126 }
127 }
128 }
129
130 prepared = 1;
131 return false;
132 } else {
133 prepared = 3;
134 return true;
135 }
136 }
137
138 public boolean hasNext() {
139
140 switch (prepared) {
141 case 3 :
142 case 2 :
143 return true;
144 case 1 :
145 return false;
146 case -3 :
147 valuesIterator.next();
148 default :
149 return prepareNext();
150 }
151 }
152
153 public E next() {
154
155 if (hasNext()) {
156 prepared = 0;
157 Object next = preparedValues.get(index++);
158 hasNext();
159 return derive(next);
160 } else {
161 throw new NoSuchElementException();
162 }
163 }
164
165 protected boolean scanPrevious(EStructuralFeature previousFeature,
166 ListIterator<Object> previousValuesIterator) {
167 boolean isFeatureMap = FeatureMapUtil.isFeatureMap(previousFeature);
168
169 while (previousValuesIterator.hasPrevious()) {
170 Object previousValue = previousValuesIterator.previous();
171
172 if (isFeatureMap) {
173 FeatureMap.Entry entry = (FeatureMap.Entry) previousValue;
174 previousFeature = entry.getEStructuralFeature();
175 previousValue = entry.getValue();
176 }
177
178 if (index > 0 && previousValue == preparedValues.get(index - 1)) {
179 valuesIterator = previousValuesIterator;
180 preparedFeature = previousFeature;
181 return true;
182 }
183 }
184
185 return false;
186 }
187
188 protected boolean preparePrevious() {
189
190 if (valuesIterator == null
191 || !scanPrevious(preparedFeature, valuesIterator)) {
192
193 while (featureIndex > 0) {
194 int sourceFeatureID = sourceFeatureIDs[--featureIndex];
195
196 if (owner.eIsSet(sourceFeatureID)) {
197 EStructuralFeature sourceFeature = getEStructuralFeature(sourceFeatureID);
198 Object value = owner.eGet(sourceFeatureID, resolve(),
199 true);
200
201 if (sourceFeature.isMany()
202 || FeatureMapUtil.isFeatureMap(sourceFeature)) {
203
204 @SuppressWarnings("unchecked")
205 InternalEList<Object> valuesList = (InternalEList<Object>) value;
206 int valuesListSize = valuesList.size();
207
208 if (scanPrevious(sourceFeature, resolve()
209 ? valuesList.listIterator(valuesListSize)
210 : valuesList.basicListIterator(valuesListSize))) {
211
212 prepared = -3;
213 return true;
214 }
215 } else if (index > 0
216 && value == preparedValues.get(index - 1)) {
217
218 valuesIterator = null;
219 preparedFeature = sourceFeature;
220 prepared = -2;
221 return true;
222 }
223 }
224 }
225
226 prepared = -1;
227 return false;
228 } else {
229 prepared = -3;
230 return true;
231 }
232 }
233
234 public boolean hasPrevious() {
235
236 switch (prepared) {
237 case -3 :
238 case -2 :
239 return true;
240 case -1 :
241 return false;
242 case 3 :
243 valuesIterator.previous();
244 default :
245 return preparePrevious();
246 }
247 }
248
249 public E previous() {
250
251 if (prepared < -1 || hasPrevious()) {
252 prepared = 0;
253 Object previous = preparedValues.remove(--index);
254 hasPrevious();
255 return derive(previous);
256 } else {
257 throw new NoSuchElementException();
258 }
259 }
260
261 public int nextIndex() {
262 return index;
263 }
264
265 public int previousIndex() {
266 return index - 1;
267 }
268
269 public void remove() {
270 throw new UnsupportedOperationException();
271 }
272
273 public void set(Object element) {
274 throw new UnsupportedOperationException();
275 }
276
277 public void add(Object element) {
278 throw new UnsupportedOperationException();
279 }
280
281 protected boolean resolve() {
282 return false;
283 }
284
285 }
286
287 protected class EmptyDerivedListIterator
288 extends DerivedListIterator {
289
290 @Override
291 public boolean hasNext() {
292 return false;
293 }
294
295 @Override
296 public boolean hasPrevious() {
297 return false;
298 }
299 }
300
301 protected class ResolvingDerivedListIterator
302 extends DerivedListIterator {
303
304 @Override
305 protected boolean resolve() {
306 return true;
307 }
308
309 }
310
311 protected final Class<?> dataClass;
312
313 protected final InternalEObject owner;
314
315 protected final int featureID;
316
317 protected final int[] sourceFeatureIDs;
318
319 public DerivedEObjectEList(Class<?> dataClass, InternalEObject owner,
320 int featureID, int[] sourceFeatureIDs) {
321 super();
322
323 this.dataClass = dataClass;
324 this.owner = owner;
325 this.featureID = featureID;
326 this.sourceFeatureIDs = sourceFeatureIDs;
327 }
328
329 public Object get(boolean resolve) {
330 return this;
331 }
332
333 public EObject getEObject() {
334 return owner;
335 }
336
337 public EStructuralFeature getEStructuralFeature() {
338 return getEStructuralFeature(featureID);
339 }
340
341 public EStructuralFeature getEStructuralFeature(int featureID) {
342 return owner.eClass().getEStructuralFeature(featureID);
343 }
344
345 public boolean isSet() {
346 return !isEmpty();
347 }
348
349 @SuppressWarnings("unchecked")
350 public void set(Object newValue) {
351 clear();
352 addAll((List<? extends E>) newValue);
353 }
354
355 public void unset() {
356 clear();
357 }
358
359 @Override
360 public ListIterator<E> listIterator(int index) {
361 return listIterator(index, true);
362 }
363
364 @Override
365 public int size() {
366
367 if (sourceFeatureIDs != null) {
368 EList<Object> values = new UniqueEList.FastCompare<Object>();
369
370 for (int i = 0; i < sourceFeatureIDs.length; i++) {
371 int sourceFeatureID = sourceFeatureIDs[i];
372
373 if (owner.eIsSet(sourceFeatureID)) {
374 EStructuralFeature sourceFeature = getEStructuralFeature(sourceFeatureID);
375 Object value = owner.eGet(sourceFeatureID, false, true);
376
377 if (FeatureMapUtil.isFeatureMap(sourceFeature)) {
378 FeatureMap featureMap = (FeatureMap) value;
379
380 for (int j = 0, size = featureMap.size(); j < size; j++) {
381 value = featureMap.getValue(j);
382
383 if (isIncluded(featureMap.getEStructuralFeature(j))
384 ? value != null
385 : isIncluded(value)) {
386
387 values.add(value);
388 }
389 }
390 } else if (isIncluded(sourceFeature)) {
391
392 if (sourceFeature.isMany()) {
393 InternalEList<?> valuesList = (InternalEList<?>) value;
394
395 if (valuesList instanceof RandomAccess) {
396
397 for (int j = 0, size = valuesList.size(); j < size; j++) {
398 values.add(valuesList.basicGet(j));
399 }
400 } else {
401
402 for (Iterator<?> v = valuesList.basicIterator(); v
403 .hasNext();) {
404
405 values.add(v.next());
406 }
407 }
408 } else if (value != null) {
409 values.add(value);
410 }
411 } else {
412
413 if (sourceFeature.isMany()) {
414 InternalEList<?> valuesList = (InternalEList<?>) value;
415
416 if (valuesList instanceof RandomAccess) {
417
418 for (int j = 0, size = valuesList.size(); j < size; j++) {
419 value = valuesList.basicGet(j);
420
421 if (isIncluded(value)) {
422 values.add(value);
423 }
424 }
425 } else {
426
427 for (Iterator<?> v = valuesList.basicIterator(); v
428 .hasNext();) {
429
430 value = v.next();
431
432 if (isIncluded(value)) {
433 values.add(value);
434 }
435 }
436 }
437 } else if (isIncluded(value)) {
438 values.add(value);
439 }
440 }
441 }
442 }
443
444 return values.size();
445 }
446
447 return 0;
448 }
449
450 @Override
451 public boolean isEmpty() {
452
453 if (sourceFeatureIDs != null) {
454
455 for (int i = 0; i < sourceFeatureIDs.length; i++) {
456 int sourceFeatureID = sourceFeatureIDs[i];
457
458 if (owner.eIsSet(sourceFeatureID)) {
459 EStructuralFeature sourceFeature = getEStructuralFeature(sourceFeatureID);
460 Object value = owner.eGet(sourceFeatureID, false, true);
461
462 if (FeatureMapUtil.isFeatureMap(sourceFeature)) {
463 FeatureMap featureMap = (FeatureMap) value;
464
465 for (int j = 0, size = featureMap.size(); j < size; j++) {
466
467 if (isIncluded(featureMap.getEStructuralFeature(j))
468 ? featureMap.getValue(j) != null
469 : isIncluded(featureMap.getValue(j))) {
470
471 return false;
472 }
473 }
474 } else if (isIncluded(sourceFeature)) {
475
476 if (sourceFeature.isMany()
477 ? ((List<?>) value).size() > 0
478 : value != null) {
479
480 return false;
481 }
482 } else {
483
484 if (sourceFeature.isMany()) {
485 InternalEList<?> valuesList = (InternalEList<?>) value;
486
487 if (valuesList instanceof RandomAccess) {
488
489 for (int j = 0, size = valuesList.size(); j < size; j++) {
490
491 if (isIncluded(valuesList.basicGet(j))) {
492 return false;
493 }
494 }
495 } else {
496
497 for (Iterator<?> v = valuesList.basicIterator(); v
498 .hasNext();) {
499
500 if (isIncluded(v.next())) {
501 return false;
502 }
503 }
504 }
505 } else if (isIncluded(value)) {
506 return false;
507 }
508 }
509 }
510 }
511 }
512
513 return true;
514 }
515
516 @Override
517 public boolean contains(Object object) {
518
519 if (sourceFeatureIDs != null) {
520
521 for (int i = 0; i < sourceFeatureIDs.length; i++) {
522 int sourceFeatureID = sourceFeatureIDs[i];
523
524 if (owner.eIsSet(sourceFeatureID)) {
525 EStructuralFeature sourceFeature = getEStructuralFeature(sourceFeatureID);
526 Object value = owner.eGet(sourceFeatureID, true, true);
527
528 if (FeatureMapUtil.isFeatureMap(sourceFeature)) {
529 FeatureMap featureMap = (FeatureMap) value;
530
531 for (int j = 0, size = featureMap.size(); j < size; j++) {
532 value = featureMap.getValue(j);
533
534 if (isIncluded(featureMap.getEStructuralFeature(j))
535 ? value == object
536 : isIncluded(value) && derive(value) == object) {
537
538 return true;
539 }
540 }
541 } else if (isIncluded(sourceFeature)) {
542
543 if (sourceFeature.isMany()
544 ? ((List<?>) value).contains(object)
545 : value == object) {
546
547 return true;
548 }
549 } else {
550
551 if (sourceFeature.isMany()) {
552 InternalEList<?> valuesList = (InternalEList<?>) value;
553
554 if (valuesList instanceof RandomAccess) {
555
556 for (int j = 0, size = valuesList.size(); j < size; j++) {
557 value = valuesList.basicGet(j);
558
559 if (isIncluded(value)
560 && derive(value) == object) {
561
562 return true;
563 }
564 }
565 } else {
566
567 for (Iterator<?> v = valuesList.basicIterator(); v
568 .hasNext();) {
569
570 value = v.next();
571
572 if (isIncluded(value)
573 && derive(value) == object) {
574
575 return true;
576 }
577 }
578 }
579 } else if (isIncluded(value) && derive(value) == object) {
580 return true;
581 }
582 }
583 }
584 }
585 }
586
587 return false;
588 }
589
590 @Override
591 public List<E> basicList() {
592 return new DerivedEObjectEList<E>(dataClass, owner, featureID,
593 sourceFeatureIDs) {
594
595 @Override
596 public ListIterator<E> listIterator(int index) {
597 return basicListIterator(index);
598 }
599 };
600 }
601
602 @Override
603 public ListIterator<E> basicListIterator(int index) {
604 return listIterator(index, false);
605 }
606
607 protected boolean isNotificationRequired() {
608 return false;
609 }
610
611 protected NotificationImpl createNotification(int eventType,
612 Object oldObject, Object newObject, int index, boolean wasSet) {
613 return new ENotificationImpl(owner, eventType, featureID, oldObject,
614 newObject, index, wasSet);
615 }
616
617 protected void dispatchNotification(Notification notification) {
618 owner.eNotify(notification);
619 }
620
621 @Override
622 public void add(int index, E object) {
623 addUnique(index, object);
624 }
625
626 @Override
627 public void addUnique(int index, E object) {
628
629 if (isNotificationRequired()) {
630 boolean oldIsSet = isSet();
631 super.add(index, validate(index, object));
632 NotificationImpl notification = createNotification(
633 Notification.ADD, null, object, index, oldIsSet);
634 dispatchNotification(notification);
635 } else {
636 super.add(index, validate(index, object));
637 }
638 }
639
640 @Override
641 public boolean addAll(int index, Collection<? extends E> objects) {
642 return addAllUnique(index, objects);
643 }
644
645 @Override
646 public boolean addAllUnique(int index, Collection<? extends E> objects) {
647 int size = objects.size();
648
649 if (size > 0) {
650
651 if (isNotificationRequired()) {
652 boolean oldIsSet = isSet();
653
654 if (doAddAllUnique(index, objects)) {
655 NotificationImpl notification = size == 1
656 ? createNotification(Notification.ADD, null, objects
657 .iterator().next(), index, oldIsSet)
658 : createNotification(Notification.ADD_MANY, null,
659 objects, index, oldIsSet);
660 dispatchNotification(notification);
661 return true;
662 }
663 } else {
664 return doAddAllUnique(index, objects);
665 }
666 }
667
668 return false;
669 }
670
671 protected boolean doAddAllUnique(int index, Collection<? extends E> objects) {
672 boolean modified = false;
673 ListIterator<E> listIterator = listIterator(index);
674
675 for (Iterator<? extends E> o = objects.iterator(); o.hasNext();) {
676 listIterator.add(validate(index, o.next()));
677 modified = true;
678 }
679
680 return modified;
681 }
682
683 @Override
684 public E remove(int index) {
685
686 if (isNotificationRequired()) {
687 boolean oldIsSet = isSet();
688 NotificationImpl notification = createNotification(
689 Notification.REMOVE, super.remove(index), null, index, oldIsSet);
690 dispatchNotification(notification);
691 @SuppressWarnings("unchecked")
692 E oldValue = (E) notification.getOldValue();
693 return oldValue;
694 } else {
695 return super.remove(index);
696 }
697 }
698
699 @Override
700 public E set(int index, E object) {
701 return setUnique(index, object);
702 }
703
704 @Override
705 public E setUnique(int index, E object) {
706
707 if (isNotificationRequired()) {
708 boolean oldIsSet = isSet();
709 Notification notification = createNotification(Notification.SET,
710 super.set(index, validate(index, object)), object, index,
711 oldIsSet);
712 dispatchNotification(notification);
713 @SuppressWarnings("unchecked")
714 E oldValue = (E) notification.getOldValue();
715 return oldValue;
716 } else {
717 return super.set(index, validate(index, object));
718 }
719 }
720
721 /**
722 * Indicates whether all elements from the specified source feature are
723 * included in this list.
724 *
725 * @param feature
726 * A source feature.
727 * @return <code>true</code> if the elements are included;
728 * <code>false</code> otherwise.
729 */
730 protected boolean isIncluded(EStructuralFeature feature) {
731 return dataClass
732 .isAssignableFrom(feature.getEType().getInstanceClass());
733 }
734
735 /**
736 * Indicates whether the specified element from a source feature is included
737 * in this list.
738 *
739 * @param object
740 * An element from a source feature.
741 * @return <code>true</code> if the element is included;
742 * <code>false</code> otherwise.
743 */
744 protected boolean isIncluded(Object object) {
745 return dataClass.isInstance(derive(object));
746 }
747
748 /**
749 * Derives a value for this list from the specified element in a source
750 * feature.
751 *
752 * @param object
753 * An element from a source feature.
754 * @return The "derived" value of the specified element.
755 */
756 @SuppressWarnings("unchecked")
757 protected E derive(Object object) {
758 return (E) object;
759 }
760
761 protected E validate(int index, E object) {
762
763 if (!dataClass.isInstance(object)) {
764 throw new IllegalArgumentException(String.valueOf(object));
765 }
766
767 return object;
768 }
769
770 protected ListIterator<E> newListIterator() {
771 return new DerivedListIterator();
772 }
773
774 protected ListIterator<E> newResolvingListIterator() {
775 return new ResolvingDerivedListIterator();
776 }
777
778 protected ListIterator<E> newEmptyListIterator() {
779 return new EmptyDerivedListIterator();
780 }
781
782 protected ListIterator<E> listIterator(int index, boolean resolve) {
783
784 if (sourceFeatureIDs == null || sourceFeatureIDs.length == 0) {
785
786 if (index != 0) {
787 throw new IndexOutOfBoundsException("index = " + index //$NON-NLS-1$
788 + ", size = 0"); //$NON-NLS-1$
789 }
790
791 return newEmptyListIterator();
792 }
793
794 ListIterator<E> listIterator = resolve
795 ? newResolvingListIterator()
796 : newListIterator();
797
798 for (int i = 0; i < index; i++) {
799 listIterator.next();
800 }
801
802 return listIterator;
803 }
804
805 }