| /******************************************************************************* |
| * Copyright (c) 2018 Agence spatiale canadienne / Canadian Space Agency |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Pierre Allard, |
| * Regent L'Archeveque, |
| * Olivier L. Larouche - initial API and implementation |
| * SPDX-License-Identifier: EPL-1.0 |
| * |
| *******************************************************************************/ |
| package org.eclipse.apogy.common.emf.impl; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.apogy.common.emf.ApogyCommonEMFFactory; |
| import org.eclipse.apogy.common.emf.FeaturePathAdapterEntry; |
| import org.eclipse.emf.common.notify.Adapter; |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.common.notify.impl.AdapterImpl; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| |
| public abstract class FeaturePathAdapterCustomImpl extends FeaturePathAdapterImpl { |
| |
| private EObject root; |
| private List<? extends EStructuralFeature> featurePath; |
| /** |
| * List of {@link FeaturePathAdapterEntry}, this is used to know if |
| * notifyChanged needs to be called and to remove the adapters when no longer |
| * needed. |
| */ |
| private List<FeaturePathAdapterEntry> featurePathAdapterEntryList; |
| |
| public List<FeaturePathAdapterEntry> getFeaturePathAdapterEntryList() { |
| if (this.featurePathAdapterEntryList == null) { |
| this.featurePathAdapterEntryList = new ArrayList<FeaturePathAdapterEntry>(); |
| } |
| return this.featurePathAdapterEntryList; |
| } |
| |
| @Override |
| public List<? extends EStructuralFeature> getFeaturePath() { |
| if (this.featurePath == null) { |
| this.featurePath = getFeatureList(); |
| } |
| return this.featurePath; |
| } |
| |
| @Override |
| public void init(EObject root) { |
| this.root = root; |
| addAdapterOnFeaturePath(root, getFeaturePath()); |
| } |
| |
| |
| @Override |
| public void dispose() { |
| if (this.root != null) { |
| removeAdapterOnFeaturePath(this.root, getFeaturePath()); |
| this.root = null; |
| } |
| this.featurePath = null; |
| } |
| |
| @Override |
| abstract public List<? extends EStructuralFeature> getFeatureList(); |
| |
| private void notifyAdapterOnFeatureChanged(Notification msg) { |
| List<FeaturePathAdapterEntry> entries = new ArrayList<FeaturePathAdapterEntry>(); |
| entries.addAll(getFeaturePathAdapterEntryList()); |
| for (FeaturePathAdapterEntry entry : entries) { |
| if (entry.getNotifier() == msg.getNotifier() && entry.getFeature() == msg.getFeature()) { |
| List<EStructuralFeature> nextFeaturesList = new ArrayList<>(); |
| nextFeaturesList.addAll(getFeaturePath()); |
| /** |
| * Keeps only the StructuralFeatures up to the notification's feature. |
| */ |
| for (int i = 0; i <= getFeaturePath().indexOf(msg.getFeature()); i++) { |
| nextFeaturesList.remove(0); |
| } |
| |
| /** Remove the adapters if needed. */ |
| |
| List<FeaturePathAdapterEntry> entryList = new ArrayList<FeaturePathAdapterEntry>(); |
| entryList.addAll(getFeaturePathAdapterEntryList()); |
| for (FeaturePathAdapterEntry entry2 : entryList) { |
| if (entry2.getNotifier() == msg.getOldValue()) { |
| removeAdapterOnFeaturePath((EObject) msg.getOldValue(), nextFeaturesList); |
| break; |
| } |
| } |
| |
| /** Add the adapters if needed. */ |
| if (!nextFeaturesList.isEmpty() && msg.getNewValue() != null) { |
| addAdapterOnFeaturePath((EObject) msg.getNewValue(), nextFeaturesList); |
| } |
| |
| notifyChanged(msg); |
| break; |
| } |
| } |
| } |
| |
| @Override |
| abstract public void notifyChanged(Notification msg); |
| |
| private void addAdapterOnFeaturePath(EObject root, List<? extends EStructuralFeature> featurePath) { |
| /** Create the adapter */ |
| Adapter adapter = new AdapterImpl() { |
| @Override |
| public void notifyChanged(Notification msg) { |
| FeaturePathAdapterCustomImpl.this.notifyAdapterOnFeatureChanged(msg); |
| } |
| }; |
| |
| /** Add the adapter to the EObject and add the entry to the list. */ |
| root.eAdapters().add(adapter); |
| FeaturePathAdapterEntry entry = ApogyCommonEMFFactory.eINSTANCE.createFeaturePathAdapterEntry(); |
| entry.setNotifier(root); |
| entry.setAdapter(adapter); |
| entry.setFeature(featurePath.get(0)); |
| this.getFeaturePathAdapterEntryList().add(entry); |
| |
| /** |
| * If necessary add adapters on the next EObject in the feature path. |
| */ |
| if (featurePath.size() > 1) { |
| List<EStructuralFeature> nextFeaturesList = new ArrayList<>(); |
| nextFeaturesList.addAll(featurePath); |
| nextFeaturesList.remove(0); |
| |
| /** Get the next object */ |
| Object next = root.eGet(featurePath.get(0)); |
| |
| /** If the object is a list */ |
| if (next instanceof List) { |
| /** For each object of the list */ |
| for (Object object : (List<?>) next) { |
| if (object instanceof EObject) { |
| if (!nextFeaturesList.isEmpty() && nextFeaturesList.get(0).getEContainingClass() |
| .isSuperTypeOf(((EObject) object).eClass())) { |
| addAdapterOnFeaturePath((EObject) object, nextFeaturesList); |
| } |
| } |
| } |
| } |
| /** Otherwise, if the object is a EObject */ |
| else if (next instanceof EObject) { |
| if (!nextFeaturesList.isEmpty() |
| && nextFeaturesList.get(0).getEContainingClass().isSuperTypeOf(((EObject) next).eClass())) { |
| addAdapterOnFeaturePath((EObject) next, nextFeaturesList); |
| } |
| } |
| } |
| } |
| |
| private void removeAdapterOnFeaturePath(EObject root, List<? extends EStructuralFeature> featurePath) { |
| /** |
| * Remove the adapter of the EObject and remove the entry from the HashMap |
| */ |
| FeaturePathAdapterEntry entryToDelete = null; |
| for (FeaturePathAdapterEntry entry : getFeaturePathAdapterEntryList()) { |
| if (entry.getNotifier() == root) { |
| entryToDelete = entry; |
| root.eAdapters().remove(entry.getAdapter()); |
| break; |
| } |
| } |
| if (entryToDelete != null) { |
| getFeaturePathAdapterEntryList().remove(entryToDelete); |
| } |
| |
| /** |
| * If necessary remove adapters on the next EObject in the feature path |
| */ |
| if (featurePath.size() > 1) { |
| List<EStructuralFeature> nextFeaturePath = new ArrayList<>(); |
| nextFeaturePath.addAll(featurePath); |
| nextFeaturePath.remove(0); |
| |
| /** Get the next object */ |
| Object next = root.eGet(featurePath.get(0)); |
| |
| /** If the object is a list */ |
| if (next instanceof List) { |
| /** For each object of the list */ |
| for (Object object : (List<?>) next) { |
| if (object instanceof EObject) { |
| if (!nextFeaturePath.isEmpty()) { |
| removeAdapterOnFeaturePath((EObject) object, nextFeaturePath); |
| } |
| } |
| |
| } |
| } |
| /** Otherwise, if the object is a EObject */ |
| else if (next instanceof EObject) { |
| if (!nextFeaturePath.isEmpty()) { |
| removeAdapterOnFeaturePath((EObject) next, nextFeaturePath); |
| } |
| } |
| } |
| } |
| |
| } // FeaturePathAdapterImpl |