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    }