Skip to content

Package: DerivedAttributeAdapter$NavigationAdapter

DerivedAttributeAdapter$NavigationAdapter

nameinstructionbranchcomplexitylinemethod
DerivedAttributeAdapter.NavigationAdapter(DerivedAttributeAdapter, InternalEObject, EReference[], EStructuralFeature)
M: 12 C: 80
87%
M: 3 C: 7
70%
M: 2 C: 4
67%
M: 2 C: 21
91%
M: 0 C: 1
100%
addNavigationAdapter(EStructuralFeature, EReference[], InternalEObject)
M: 0 C: 14
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
children()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
dispose()
M: 0 C: 22
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 4
100%
M: 0 C: 1
100%
notifyChanged(Notification)
M: 1 C: 118
99%
M: 1 C: 19
95%
M: 1 C: 12
92%
M: 1 C: 31
97%
M: 0 C: 1
100%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2011-2015 EclipseSource Muenchen GmbH and others.
3: *
4: * All rights reserved. This program and the accompanying materials
5: * are made available under the terms of the Eclipse Public License 2.0
6: * which accompanies this distribution, and is available at
7: * https://www.eclipse.org/legal/epl-2.0/
8: *
9: * SPDX-License-Identifier: EPL-2.0
10: *
11: * Contributors:
12: * Johannes Faltermeier - initial API and implementation
13: ******************************************************************************/
14: package org.eclipse.emf.ecp.common.spi;
15:
16: import java.util.Arrays;
17: import java.util.LinkedHashMap;
18: import java.util.LinkedHashSet;
19: import java.util.List;
20: import java.util.Map;
21: import java.util.Set;
22:
23: import org.eclipse.emf.common.notify.Notification;
24: import org.eclipse.emf.common.notify.impl.AdapterImpl;
25: import org.eclipse.emf.ecore.EObject;
26: import org.eclipse.emf.ecore.EReference;
27: import org.eclipse.emf.ecore.EStructuralFeature;
28: import org.eclipse.emf.ecore.InternalEObject;
29: import org.eclipse.emf.ecore.impl.ENotificationImpl;
30:
31: /**
32: * The DerivedAttributeAdapter can be added on EObjects which include derived {@link EStructuralFeature
33: * EStructuralFeatures}. This Adapter will make sure that correct SET/UNSET notifications are issued when a feature from
34: * which the derived feature is derived from changes.
35: *
36: * @author Johannes Faltermeier
37: * @since 1.8
38: *
39: */
40: public class DerivedAttributeAdapter extends AdapterImpl {
41:
42:         private final Set<EStructuralFeature> localFeatures = new LinkedHashSet<EStructuralFeature>();
43:
44:         private final InternalEObject source;
45:         private final EStructuralFeature derivedFeature;
46:
47:         private Object oldValue;
48:
49:         /**
50:          * @param source the owner of the derived feature
51:          * @param derivedFeature the derived feature
52:          */
53:         public DerivedAttributeAdapter(EObject source, EStructuralFeature derivedFeature) {
54:                 super();
55:                 this.source = (InternalEObject) source;
56:                 this.derivedFeature = derivedFeature;
57:                 oldValue = derivedFeature.getDefaultValue();
58:                 source.eAdapters().add(this);
59:         }
60:
61:         /**
62:          * Use this method to indicate that the given feature influences the derived value and is owned by the same
63:          * {@link EObject} as the derived feature.
64:          *
65:          * @param localFeature the local feature.
66:          */
67:         public void addLocalDependency(EStructuralFeature localFeature) {
68:                 localFeatures.add(localFeature);
69:         }
70:
71:         /**
72:          * Use this method to indicate the given given feature path influences the derived value.
73:          *
74:          * @param dependentFeature the last feature on the path
75:          * @param navigationFeatures the reference path. This must <b>NOT</b> be empty
76:          */
77:         public void addNavigatedDependency(EStructuralFeature dependentFeature, EReference... navigationFeatures) {
78:                 if (navigationFeatures.length < 1) {
79:                         throw new IllegalArgumentException("Use addLocalDependency instead."); //$NON-NLS-1$
80:                 }
81:                 new NavigationAdapter(source, navigationFeatures, dependentFeature);
82:         }
83:
84:         @Override
85:         public void notifyChanged(Notification notification) {
86:                 super.notifyChanged(notification);
87:
88:                 final Object feature = notification.getFeature();
89:
90:                 if (feature == null) {
91:                         return;
92:                 }
93:
94:                 if (localFeatures.contains(feature)) {
95:                         notifyDerivedAttributeChange();
96:                 }
97:         }
98:
99:         private void notifyDerivedAttributeChange() {
100:                 if (!source.eNotificationRequired()) {
101:                         return;
102:                 }
103:                 final boolean eIsSet = source.eIsSet(derivedFeature);
104:                 final Object newValue = source.eGet(derivedFeature, true, true);
105:                 final int eventType = eIsSet ? Notification.SET : Notification.UNSET;
106:                 source.eNotify(new ENotificationImpl(source, eventType, derivedFeature, oldValue, newValue));
107:                 oldValue = newValue;
108:         }
109:
110:         /**
111:          * Adapter which gets added on navigated features.
112:          *
113:          * @author Johannes Faltermeier
114:          *
115:          */
116:         class NavigationAdapter extends AdapterImpl {
117:
118:                 private boolean hasChildren;
119:
120:                 private EStructuralFeature ownFeature;
121:
122:                 private final Map<EObject, NavigationAdapter> children = new LinkedHashMap<EObject, DerivedAttributeAdapter.NavigationAdapter>();
123:
124:                 private EReference[] remainingPath;
125:
126:                 private EStructuralFeature dependentFeature;
127:
128:                 private final InternalEObject source;
129:
130:                 /**
131:                  * @param source the Object which will be the parent for the adapter
132:                  * @param navigationFeatures the remaining navigation path
133:                  * @param dependentFeature the final feature
134:                  */
135:                 NavigationAdapter(InternalEObject source, EReference[] navigationFeatures,
136:                         EStructuralFeature dependentFeature) {
137:                         this.source = source;
138:•                        if (navigationFeatures.length != 0) {
139:                                 hasChildren = true;
140:                                 ownFeature = navigationFeatures[0];
141:                                 remainingPath = Arrays.copyOfRange(navigationFeatures, 1, navigationFeatures.length);
142:                                 this.dependentFeature = dependentFeature;
143:•                                if (ownFeature.isMany()) {
144:                                         @SuppressWarnings("unchecked")
145:                                         final List<InternalEObject> list = (List<InternalEObject>) source.eGet(ownFeature);
146:•                                        for (final InternalEObject child : list) {
147:•                                                if (child != null) {
148:                                                         addNavigationAdapter(dependentFeature, remainingPath, child);
149:                                                 }
150:                                         }
151:                                 } else {
152:                                         final InternalEObject child = (InternalEObject) source.eGet(ownFeature);
153:•                                        if (child != null) {
154:                                                 addNavigationAdapter(dependentFeature, remainingPath, child);
155:                                         }
156:                                 }
157:                         } else {
158:                                 hasChildren = false;
159:                                 ownFeature = dependentFeature;
160:                         }
161:                         source.eAdapters().add(this);
162:                 }
163:
164:                 private void addNavigationAdapter(EStructuralFeature dependentFeature, EReference[] remainingPath,
165:                         InternalEObject child) {
166:                         children.put(child, new NavigationAdapter(child, remainingPath, dependentFeature));
167:                 }
168:
169:                 /**
170:                  * @return the children map (for testing purposes)
171:                  */
172:                 Map<EObject, NavigationAdapter> children() {
173:                         return children;
174:                 }
175:
176:                 @Override
177:                 public void notifyChanged(Notification notification) {
178:                         super.notifyChanged(notification);
179:
180:                         final Object feature = notification.getFeature();
181:
182:•                        if (feature == null) {
183:                                 return;
184:                         }
185:
186:•                        if (ownFeature != feature) {
187:                                 return;
188:                         }
189:
190:•                        if (hasChildren) {
191:•                                switch (notification.getEventType()) {
192:                                 case Notification.ADD:
193:                                         final InternalEObject added = (InternalEObject) notification.getNewValue();
194:                                         addNavigationAdapter(dependentFeature, remainingPath, added);
195:                                         break;
196:                                 case Notification.ADD_MANY:
197:                                         @SuppressWarnings("unchecked")
198:                                         final List<InternalEObject> allAdded = (List<InternalEObject>) notification.getNewValue();
199:•                                        for (final InternalEObject addedObject : allAdded) {
200:                                                 addNavigationAdapter(dependentFeature, remainingPath, addedObject);
201:                                         }
202:                                         break;
203:                                 case Notification.SET:
204:                                 case Notification.UNSET:
205:                                         final InternalEObject newValue = (InternalEObject) notification.getNewValue();
206:                                         final InternalEObject oldValue = (InternalEObject) notification.getOldValue();
207:•                                        if (oldValue != null) {
208:                                                 children.remove(oldValue).dispose();
209:                                         }
210:•                                        if (newValue != null) {
211:                                                 addNavigationAdapter(dependentFeature, remainingPath, newValue);
212:                                         }
213:                                         break;
214:                                 case Notification.REMOVE:
215:                                         final InternalEObject removed = (InternalEObject) notification.getOldValue();
216:                                         children.remove(removed).dispose();
217:                                         break;
218:                                 case Notification.REMOVE_MANY:
219:                                         @SuppressWarnings("unchecked")
220:                                         final List<InternalEObject> listRemoved = (List<InternalEObject>) notification.getOldValue();
221:•                                        for (final InternalEObject removedObj : listRemoved) {
222:                                                 children.remove(removedObj).dispose();
223:                                         }
224:                                         break;
225:                                 default:
226:                                         return; // No notification
227:                                 }
228:                         }
229:
230:                         notifyDerivedAttributeChange();
231:                 }
232:
233:                 private void dispose() {
234:•                        for (final NavigationAdapter navigationAdapter : children.values()) {
235:                                 navigationAdapter.dispose();
236:                         }
237:                         source.eAdapters().remove(this);
238:                 }
239:
240:         }
241: }