001    /*
002     * Copyright (c) 2006, 2007 IBM Corporation 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     *
011     * $Id: SubsetSupersetEObjectEList.java,v 1.6 2007/04/04 03:15:12 khussey Exp $
012     */
013    package org.eclipse.uml2.common.util;
014    
015    import java.util.Collection;
016    import java.util.Iterator;
017    
018    import org.eclipse.emf.common.notify.Notification;
019    import org.eclipse.emf.common.notify.NotificationChain;
020    import org.eclipse.emf.common.util.EList;
021    import org.eclipse.emf.ecore.EStructuralFeature;
022    import org.eclipse.emf.ecore.InternalEObject;
023    import org.eclipse.emf.ecore.resource.Resource;
024    import org.eclipse.emf.ecore.util.EObjectEList;
025    
026    /**
027     * A list that enforces subset/superset consraints. Specifically, when an
028     * element is added to a subset, it is also added to the associated superset(s),
029     * if not already present; when an element is removed from a superset, it is
030     * also removed from the associated subset(s), if present.
031     * 
032     * @since 1.2
033     */
034    public class SubsetSupersetEObjectEList<E>
035                    extends EObjectEList<E> {
036    
037            private static final long serialVersionUID = 1L;
038    
039            public static class Unsettable<E>
040                            extends SubsetSupersetEObjectEList<E> {
041    
042                    private static final long serialVersionUID = 1L;
043    
044                    protected boolean isSet;
045    
046                    public Unsettable(Class<?> dataClass, InternalEObject owner,
047                                    int featureID, int[] supersetFeatureIDs, int[] subsetFeatureIDs) {
048                            super(dataClass, owner, featureID, supersetFeatureIDs,
049                                    subsetFeatureIDs);
050                    }
051    
052                    @Override
053                    protected void didChange() {
054                            isSet = true;
055                    }
056    
057                    @Override
058                    public boolean isSet() {
059                            return isSet;
060                    }
061    
062                    @Override
063                    public void unset() {
064                            super.unset();
065    
066                            if (isNotificationRequired()) {
067                                    boolean oldIsSet = isSet;
068                                    isSet = false;
069    
070                                    owner.eNotify(createNotification(Notification.UNSET, oldIsSet,
071                                            false));
072                            } else {
073                                    isSet = false;
074                            }
075                    }
076            }
077    
078            /**
079             * An array of superset feature identifiers.
080             */
081            protected final int[] supersetFeatureIDs;
082    
083            /**
084             * An array of subset feature identifiers.
085             */
086            protected final int[] subsetFeatureIDs;
087    
088            public SubsetSupersetEObjectEList(Class<?> dataClass,
089                            InternalEObject owner, int featureID, int[] supersetFeatureIDs,
090                            int[] subsetFeatureIDs) {
091                    super(dataClass, owner, featureID);
092    
093                    this.supersetFeatureIDs = supersetFeatureIDs;
094                    this.subsetFeatureIDs = subsetFeatureIDs;
095            }
096    
097            /**
098             * Indicates whether subset constraints should be enforced.
099             * 
100             * @return <code>true</code> if subset constraints should be enforced;
101             *         <code>false</code> otherwise.
102             */
103            protected boolean enforceSubsetConstraints() {
104                    Resource.Internal eInternalResource = owner.eInternalResource();
105                    return eInternalResource == null || !eInternalResource.isLoading();
106            }
107    
108            /**
109             * Adds the specified element to the superset(s).
110             * 
111             * @param object
112             *            The element to be added.
113             */
114            protected void supersetAdd(Object object) {
115    
116                    if (supersetFeatureIDs != null && enforceSubsetConstraints()) {
117    
118                            for (int i = 0; i < supersetFeatureIDs.length; i++) {
119                                    EStructuralFeature supersetEStructuralFeature = owner.eClass()
120                                            .getEStructuralFeature(supersetFeatureIDs[i]);
121    
122                                    if (supersetEStructuralFeature.isMany()) {
123                                            @SuppressWarnings("unchecked")
124                                            EList<Object> supersetEList = (EList<Object>) owner
125                                                    .eGet(supersetEStructuralFeature);
126    
127                                            if (!supersetEList.contains(object)) {
128                                                    supersetEList.add(object);
129                                            }
130                                    }
131                            }
132                    }
133            }
134    
135            /**
136             * Indicates whether superset constraints should be enforced.
137             * 
138             * @return <code>true</code> if superset constraints should be enforced;
139             *         <code>false</code> otherwise.
140             */
141            protected boolean enforceSupersetConstraints() {
142                    return true;
143            }
144    
145            /**
146             * Removes the specified element from the subset(s).
147             * 
148             * @param object
149             *            The element to be removed.
150             */
151            protected void subsetRemove(Object object) {
152    
153                    if (subsetFeatureIDs != null && enforceSupersetConstraints()) {
154    
155                            for (int i = 0; i < subsetFeatureIDs.length; i++) {
156                                    EStructuralFeature subsetEStructuralFeature = owner.eClass()
157                                            .getEStructuralFeature(subsetFeatureIDs[i]);
158    
159                                    if (subsetEStructuralFeature.isMany()) {
160                                            @SuppressWarnings("unchecked")
161                                            EList<Object> list = ((EList<Object>) owner
162                                                    .eGet(subsetEStructuralFeature));
163                                            list.remove(object);
164                                    } else if (object.equals(owner.eGet(subsetEStructuralFeature))) {
165                                            owner.eSet(subsetEStructuralFeature, null);
166                                    }
167                            }
168                    }
169            }
170    
171            @Override
172            public NotificationChain basicAdd(E object, NotificationChain notifications) {
173                    notifications = super.basicAdd(object, notifications);
174    
175                    supersetAdd(object);
176    
177                    return notifications;
178            }
179    
180            @Override
181            public NotificationChain basicSet(int index, E object,
182                            NotificationChain notifications) {
183                    Object oldObject = data[index];
184    
185                    notifications = super.basicSet(index, object, notifications);
186    
187                    supersetAdd(object);
188    
189                    if (oldObject != object) {
190                            subsetRemove(oldObject);
191                    }
192    
193                    return notifications;
194            }
195    
196            @Override
197            public void add(int index, E object) {
198                    super.add(index, object);
199    
200                    supersetAdd(object);
201            }
202    
203            @Override
204            public boolean add(E object) {
205                    boolean result = super.add(object);
206    
207                    supersetAdd(object);
208    
209                    return result;
210            }
211    
212            @Override
213            public boolean addAll(Collection<? extends E> collection) {
214                    boolean result = super.addAll(collection);
215    
216                    for (Iterator<? extends E> elements = collection.iterator(); elements
217                            .hasNext();) {
218    
219                            supersetAdd(elements.next());
220                    }
221    
222                    return result;
223            }
224    
225            @Override
226            public boolean addAll(int index, Collection<? extends E> collection) {
227                    boolean result = super.addAll(index, collection);
228    
229                    for (Iterator<? extends E> elements = collection.iterator(); elements
230                            .hasNext();) {
231    
232                            supersetAdd(elements.next());
233                    }
234    
235                    return result;
236            }
237    
238            @Override
239            public E set(int index, E object) {
240                    E result = super.set(index, object);
241    
242                    supersetAdd(object);
243    
244                    if (result != object) {
245                            subsetRemove(result);
246                    }
247    
248                    return result;
249            }
250    
251            @Override
252            protected void didRemove(int index, E oldObject) {
253                    super.didRemove(index, oldObject);
254    
255                    subsetRemove(oldObject);
256            }
257    
258    }