/* license-start * * Copyright (C) 2008 - 2013 Crispico, <http://www.crispico.com/>. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details, at <http://www.gnu.org/licenses/>. * * Contributors: * Crispico - Initial API and implementation * * license-end */ package org.flowerplatform.editor.model.change_processor; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature.Setting; import org.eclipse.emf.ecore.change.ChangeDescription; import org.eclipse.emf.ecore.change.ChangeKind; import org.eclipse.emf.ecore.change.FeatureChange; import org.eclipse.emf.ecore.change.ListChange; import org.eclipse.emf.ecore.util.ECrossReferenceAdapter; import org.flowerplatform.editor.model.EditorModelPlugin; import org.flowerplatform.emf_model.notation.NotationElement; import org.flowerplatform.emf_model.notation.NotationPackage; import org.flowerplatform.emf_model.notation.View; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DiagramUpdaterChangeProcessor implements IChangeProcessor { private static final Logger logger = LoggerFactory.getLogger(DiagramUpdaterChangeProcessor.class); // TODO CS/CS3 vizib public de test public Map<String, List<IDiagrammableElementFeatureChangesProcessor>> diagrammableElementFeatureChangeProcessors = new HashMap<String, List<IDiagrammableElementFeatureChangesProcessor>>(); public void addDiagrammableElementFeatureChangeProcessor(String viewType, IDiagrammableElementFeatureChangesProcessor processor) { List<IDiagrammableElementFeatureChangesProcessor> processors = diagrammableElementFeatureChangeProcessors.get(viewType); if (processors == null) { diagrammableElementFeatureChangeProcessors.put(viewType, new ArrayList<IDiagrammableElementFeatureChangesProcessor>()); } diagrammableElementFeatureChangeProcessors.get(viewType).add(processor); } public List<IDiagrammableElementFeatureChangesProcessor> getDiagrammableElementFeatureChangesProcessors(String viewType) { return diagrammableElementFeatureChangeProcessors.get(viewType); } protected boolean isDiagramOpen(NotationElement diagramChildObject) { return true; // while (diagramChildObject != null) { // if (diagramChildObject instanceof Diagram) { // return ((Diagram) diagramChildObject).isOpen(); // } else { // if (diagramChildObject.eContainer() instanceof ChangeDescription) { // // TODO CS/CS3: idBeforeRemoval am putea face si un mecanism de retinut diagrama, sau id-ul diagramei. Astfel, voi putea sa zic cu precizie daca apartine unei diagra deschise, si pentru // // un view sters // return true; // } // // TODO CS/CS3: idBeforeRemoval am comentat pentru ca pentru cazul stergerii unui view cu sub-view-uri, da exceptie. Propunere: // // sa modificam acest feature sa nu arunce notificare. Altfel, ar trebui ca aici fie sa nu mai fac verificare, fie inainte sa mai fac // // o parcurgere a listei de feature, sa ma sigur ca nu este idBeforeRemoval //// if (!(diagramChildObject.eContainer() instanceof NotationElement)) { //// throw new IllegalArgumentException("Finding diagram for NotationElement; illegal containment: " + diagramChildObject.eContainer() + " for NotationElement: " + diagramChildObject); //// } // diagramChildObject = (NotationElement) diagramChildObject.eContainer(); // } // } //// throw new IllegalArgumentException("Could not find diagram for NotationElement: " + diagramChildObject); // return false; } protected void processAddedOrRemovedObjectRecursive(EObject object, DiagramUpdaterChangeProcessorContext diagramUpdaterChangeDescriptionProcessingContext, Map<String, Object> context, boolean added, StringBuffer debugStringBuffer) { for (EObject child : object.eContents()) { processAddedOrRemovedObjectRecursive(child, diagramUpdaterChangeDescriptionProcessingContext, context, added, debugStringBuffer); } if (debugStringBuffer != null) { debugStringBuffer.append(object); debugStringBuffer.append(','); } if (added) { diagramUpdaterChangeDescriptionProcessingContext.getObjectsToUpdate().add(object); if (object instanceof View) { View view = (View) object; List<IDiagrammableElementFeatureChangesProcessor> processors = EditorModelPlugin.getInstance().getDiagramUpdaterChangeProcessor().getDiagrammableElementFeatureChangesProcessors(view.getViewType()); if (processors != null) { for (IDiagrammableElementFeatureChangesProcessor processor : processors) { processor.processFeatureChanges(view.getDiagrammableElement(), null, view, context); } } } } else { // removed diagramUpdaterChangeDescriptionProcessingContext.getObjectIdsToDispose().add(((NotationElement) object).getIdBeforeRemoval()); } } protected void addNewlyAddedObject(EObject object, DiagramUpdaterChangeProcessorContext diagramUpdaterChangeDescriptionProcessingContext, Map<String, Object> context) { StringBuffer debugStringBuffer = null; if (logger.isDebugEnabled()) { debugStringBuffer = new StringBuffer(); } // we use this recursive method (instead of a TreeIterator/object.eAllContens()), because we prefer to have the // children before the parents. This may be convenient during deserialization on Flex: when we deserialize an object, // we can use its child references right away. At the moment of writing, this was not a MUST, but this may be helpful // in the future. processAddedOrRemovedObjectRecursive(object, diagramUpdaterChangeDescriptionProcessingContext, context, true, debugStringBuffer); if (logger.isDebugEnabled()) { logger.debug("Added newly added objects = [{}]", debugStringBuffer); } } protected void addRemovedObject(EObject object, DiagramUpdaterChangeProcessorContext diagramUpdaterChangeDescriptionProcessingContext, Map<String, Object> context) { StringBuffer debugStringBuffer = null; if (logger.isDebugEnabled()) { debugStringBuffer = new StringBuffer(); } processAddedOrRemovedObjectRecursive(object, diagramUpdaterChangeDescriptionProcessingContext, context, false, debugStringBuffer); if (logger.isDebugEnabled()) { logger.debug("Added objects id to dispose for objects = [{}]", debugStringBuffer); } } /** * @author Cristina Constantinescu */ protected void processFeatureChangesForNotationElement(Map.Entry<EObject, EList<FeatureChange>> entry, Map<String, Object> context) { if (!(entry.getKey() instanceof NotationElement)) { return; } NotationElement notationElement = (NotationElement) entry.getKey(); if (!isDiagramOpen(notationElement)) { return; } DiagramUpdaterChangeProcessorContext diagramUpdaterChangeDescriptionProcessingContext = DiagramUpdaterChangeProcessorContext.getDiagramUpdaterChangeDescriptionProcessingContext(context, true); boolean diagrammableElementWasSetToNull = false; for (FeatureChange featureChange : entry.getValue()) { // if (FlowerDiagramNotationPackage.eINSTANCE.getDiagramNotationElement_IdBeforeRemoval().equals(featureChange.getFeature())) { // // TODO CS/CS3 idBeforeRemoval: de hotarat; deocamdata pot spune ca daca modific aici, sigur pot sa ignor si celelalte // return; // } if (featureChange.getFeature().equals(NotationPackage.eINSTANCE.getView_DiagrammableElement()) && notationElement.eGet(featureChange.getFeature()) == null) { diagrammableElementWasSetToNull = true; } if (featureChange.getFeature() instanceof EReference && ((EReference) featureChange.getFeature()).isContainment()) { if (!featureChange.getFeature().isMany()) { // TODO CS/CS3 LOW de prelucrat si cazul in care avem containment pe feature care nu e many throw new UnsupportedOperationException("Containment feature that is not many is not yet supported; feature = " + featureChange.getFeatureName()); } else { // containment & many // if (featureChange.getListChanges().isEmpty()) { // special case: the list was empty before adding => we don't have listChanges; this happens because // it's not actually an ADD operation; it's a SET operation, from an empty list. I think the same scenario // happens if the user does a SET instead of an ADD (on a non-empty list) @SuppressWarnings("unchecked") List<EObject> oldList = (List<EObject>) featureChange.getValue(); @SuppressWarnings("unchecked") List<EObject> currentList = (List<EObject>) notationElement.eGet(featureChange.getFeature()); for (EObject mostProbablyNewlyAddedElement : currentList) { if (oldList == null || !oldList.contains(mostProbablyNewlyAddedElement)) { // the .contains(...) is for the case where the current object was both in the old + new list; // I named the variable "probably..." because this case is unlikely to exist, but not impossible addNewlyAddedObject(mostProbablyNewlyAddedElement, diagramUpdaterChangeDescriptionProcessingContext, context); // TODO CS/CS3 LOW De testat pentru cazul setList(null), sau setList(ceva): cred ca tot aici picam, si tr sa raportam niste obiecte suplimentare ca sterse; // de asemenea de testat si ca cazul de mai sus, pt. add in care avem o lista noua care contine si elemente care erau in lista veche; eu l-am copiat, insa // nu l-am vazut cu ochii mei. } } // } else { // normal case: the list was not empty before adding for (ListChange listChange : featureChange.getListChanges()) { // TODO pentru cazul in care avem mai multe listChanges datorate la mai multe add-uri in lista, nu gasim corect elementele noi => de vazut // cum interpretam corect cazul acesta. if (listChange.getKind() == ChangeKind.REMOVE_LITERAL) { // // an element was added; the recording only indicates the index, so we need to look it up // // by ourselves // @SuppressWarnings("unchecked") // EObject newlyAddedObject = ((List<EObject>) notationElement.eGet(featureChange.getFeature())).get(listChange.getIndex()); // addNewlyAddedObject(newlyAddedObject, diagramUpdaterChangeDescriptionProcessingContext, context); } else if (listChange.getKind() == ChangeKind.ADD_LITERAL) { // an element was removed for (EObject removedObject : listChange.getReferenceValues()) { addRemovedObject(removedObject, diagramUpdaterChangeDescriptionProcessingContext, context); } } } // } } } } if (logger.isDebugEnabled()) { logger.debug("Adding object to update = {}", notationElement); } if (diagrammableElementWasSetToNull) { return; } if (!diagramUpdaterChangeDescriptionProcessingContext.getObjectsToUpdate().contains(notationElement)) { if (notationElement instanceof View) { View view = (View) notationElement; List<IDiagrammableElementFeatureChangesProcessor> processors = EditorModelPlugin.getInstance().getDiagramUpdaterChangeProcessor().getDiagrammableElementFeatureChangesProcessors(view.getViewType()); if (processors != null) { for (IDiagrammableElementFeatureChangesProcessor processor : processors) { // view.getDiagrammableElement() is null for diagram, so the if was removed // be aware: there were cases when processFeatureChanges had crashed if null was sent // as view.getDiagrammableElement() processor.processFeatureChanges(view.getDiagrammableElement(), null, view, context); } } } } else { diagramUpdaterChangeDescriptionProcessingContext.getObjectsToUpdate().remove(notationElement); } diagramUpdaterChangeDescriptionProcessingContext.getObjectsToUpdate().add(notationElement); } protected void processFeatureChangesForDiagrammableElement(Map.Entry<EObject, EList<FeatureChange>> entry, Map<String, Object> context) { ECrossReferenceAdapter adapter = ECrossReferenceAdapter.getCrossReferenceAdapter(entry.getKey()); EObject diagrammableElement = entry.getKey(); for (Setting setting : adapter.getNonNavigableInverseReferences(diagrammableElement)) { if (NotationPackage.eINSTANCE.getView_DiagrammableElement().equals(setting.getEStructuralFeature())) { View view = (View) setting.getEObject(); List<IDiagrammableElementFeatureChangesProcessor> processors = getDiagrammableElementFeatureChangesProcessors(view.getViewType()); if (processors != null) { if (logger.isDebugEnabled()) { logger.debug("Delegating to IDiagrammableElementFeatureChangesProcessor for view = {}, object = {}", view, diagrammableElement); } for (IDiagrammableElementFeatureChangesProcessor processor : processors) { processor.processFeatureChanges(diagrammableElement, entry.getValue(), view, context); } } } } // if (isDiagramOpen(view)) { } private String printFeatureChanges(List<FeatureChange> list) { StringBuffer sb = new StringBuffer("["); for (FeatureChange fc : list) { sb.append("("); sb.append(fc.getFeatureName()); sb.append("),"); } sb.append("]"); return sb.toString(); } @Override public void processChangeDescription(ChangeDescription changeDescription, Map<String, Object> context) { for (Map.Entry<EObject, EList<FeatureChange>> entry : changeDescription.getObjectChanges().entrySet()) { if (logger.isDebugEnabled()) { logger.debug("Processing change description for object = {}, feature changes = {}", entry.getKey(), printFeatureChanges(entry.getValue())); } processFeatureChangesForNotationElement(entry, context); processFeatureChangesForDiagrammableElement(entry, context); } } }