/** * <copyright> * Copyright (c) 2010-2014 Henshin developers. 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 * </copyright> */ package org.eclipse.emf.henshin.interpreter.impl; import java.util.ArrayList; import java.util.List; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.henshin.interpreter.Change; import org.eclipse.emf.henshin.interpreter.EGraph; import org.eclipse.emf.henshin.interpreter.util.InterpreterUtil; /** * Default implementation of {@link Change} and its sub-interfaces. * @author Christian Krause */ public abstract class ChangeImpl implements Change { /** * Flag indicating whether warnings should be printed. */ public static boolean PRINT_WARNINGS = true; /** * {@link EGraph} to be changed. */ protected final EGraph graph; /** * Default constructor. * @param graph {@link EGraph} to be changed. */ public ChangeImpl(EGraph graph) { this.graph = graph; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change#getEGraph() */ @Override public EGraph getEGraph() { return graph; } /** * Default implementation of {@link ObjectChange}. * @author Christian Krause */ public static final class ObjectChangeImpl extends ChangeImpl implements ObjectChange { private final EObject object; private boolean create; public ObjectChangeImpl(EGraph graph, EObject object, boolean create) { super(graph); this.object = object; this.create = create; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change#applyAndReverse() */ @Override public void applyAndReverse() { if (create) { graph.add(object); } else { graph.remove(object); } create = !create; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change.ObjectChange#getObject() */ @Override public EObject getObject() { return object; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change.ObjectChange#isCreate() */ @Override public boolean isCreate() { return create; } } /** * Default implementation of {@link AttributeChange}. * @author Christian Krause */ public static final class AttributeChangeImpl extends ChangeImpl implements AttributeChange { private final EObject object; private EAttribute attribute; private Object oldValue; private Object newValue; private boolean initialized; public AttributeChangeImpl(EGraph graph, EObject object, EAttribute attribute, Object newValue) { super(graph); this.object = object; this.attribute = attribute; this.newValue = newValue; this.initialized = false; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change#applyAndReverse() */ @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public void applyAndReverse() { // Need to initialize? if (!initialized) { oldValue = object.eGet(attribute); if ((oldValue==null && newValue==null) || (oldValue!=null && oldValue.equals(newValue))) { attribute = null; } } // Nothing to do? if (attribute==null) { return; } if (attribute.isMany()) { List values = (List) object.eGet(attribute); values.clear(); if (newValue instanceof List) { values.addAll((List) newValue); } else { values.add(newValue); } } else { object.eSet(attribute, newValue); } java.lang.Object dummy = oldValue; oldValue = newValue; newValue = dummy; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change.AttributeChange#getObject() */ @Override public EObject getObject() { return object; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change.AttributeChange#getAttribute() */ @Override public EAttribute getAttribute() { return attribute; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change.AttributeChange#getOldValue() */ @Override public Object getOldValue() { return oldValue; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change.AttributeChange#getNewValue() */ @Override public Object getNewValue() { return newValue; } } /** * Default implementation of {@link ReferenceChange}. * @author Christian Krause */ public static final class ReferenceChangeImpl extends ChangeImpl implements ReferenceChange { private final EObject source; private EReference reference; private int index; private EObject target, oldTarget; private boolean create; private boolean initialized; public ReferenceChangeImpl(EGraph graph, EObject source, EObject target, EReference reference, boolean create) { super(graph); this.source = source; this.target = target; this.create = create; this.reference = reference; this.initialized = false; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change#applyAndReverse() */ @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public void applyAndReverse() { // Need to initialize ? if (!initialized) { if (reference.isMany()) { List values = (List) source.eGet(reference); index = values.indexOf(target); if ((create && index>=0) || (!create && index<0)) { reference = null; // nothing to do } if (create && index<0) { index = values.size(); // append the new element at the end } // TODO: add a warning for containment side effects } else { oldTarget = (EObject) source.eGet(reference); // reference is redirected to the same target if (((create && target==oldTarget) || (!create && target!=oldTarget))) { if (PRINT_WARNINGS){ System.out.println("WARNING (Hidden step): recreating '" + reference.getName() + "'-edge from " + InterpreterUtil.objectToString(source) + " to " + InterpreterUtil.objectToString(oldTarget)); } reference = null; // nothing to do } // reference is redirected to a new target if (create && oldTarget!=null && reference!=null && PRINT_WARNINGS) { System.out.println("WARNING (Side effect): deleting '" + reference.getName() + "'-edge from " + InterpreterUtil.objectToString(source) + " to " + InterpreterUtil.objectToString(oldTarget)); } // reference is containment and new child (target) already has a parent if (create && reference!=null && reference.isContainment() && target.eContainer()!=null && PRINT_WARNINGS) { System.out.println("WARNING (Side effect): deleting '" + reference.getName() + "'-edge from " + InterpreterUtil.objectToString(target.eContainer()) + " to " + InterpreterUtil.objectToString(target)); } if (!create) { target = null; // we want to remove it } } initialized = true; } // Nothing to do? if (reference==null) { return; } // Otherwise do the change: if (reference.isMany()) { List values = (List) source.eGet(reference); if (create) { values.add(index, target); } else { values.remove(index); } create = !create; } else { source.eSet(reference, target); // set the new target EObject dummy = target; // switch target and old target target = oldTarget; oldTarget = dummy; } } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change.ReferenceChange#getSource() */ @Override public EObject getSource() { return source; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change.ReferenceChange#getTarget() */ @Override public EObject getTarget() { return target; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change.ReferenceChange#getReference() */ @Override public EReference getReference() { return reference; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change.ReferenceChange#isCreate() */ @Override public boolean isCreate() { return create; } } /** * Default implementation of {@link IndexChange}. * @author Christian Krause */ public static final class IndexChangeImpl extends ChangeImpl implements IndexChange { private final EObject source; private final EObject target; private final EReference reference; private int oldIndex; private int newIndex; private boolean initialized; public IndexChangeImpl(EGraph graph, EObject source, EObject target, EReference reference, int newIndex) { super(graph); this.source = source; this.target = target; this.reference = reference; this.newIndex = newIndex; this.initialized = false; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change#applyAndReverse() */ @SuppressWarnings({ "rawtypes" }) @Override public void applyAndReverse() { // Need to initialize ? if (!initialized) { if (reference.isMany()) { List values = (List) source.eGet(reference); oldIndex = values.indexOf(target); } else { if (target==source.eGet(reference)) { oldIndex = 0; } else { oldIndex = -1; } } if (oldIndex<0) { throw new RuntimeException("Error changing edge index for reference " + reference + " in object " + source + ": target " + target + " not found"); } initialized = true; } // Try to move the element to the new position: if (reference.isMany()) { EList values = (EList) source.eGet(reference); values.move(newIndex, oldIndex); } else if (newIndex!=0) { throw new RuntimeException("Cannot move element to position " + newIndex + " in the non-multi reference" + reference); } // Revert the change: int dummy = oldIndex; oldIndex = newIndex; newIndex = dummy; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change.IndexChange#getSource() */ @Override public EObject getSource() { return source; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change.IndexChange#getTarget() */ @Override public EObject getTarget() { return target; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change.IndexChange#getReference() */ @Override public EReference getReference() { return reference; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change.IndexChange#getOldIndex() */ @Override public int getOldIndex() { return oldIndex; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change.IndexChange#getNewIndex() */ @Override public int getNewIndex() { return newIndex; } } /** * Default implementation of {@link CompoundChange}. * @author Christian Krause */ public static final class CompoundChangeImpl extends ChangeImpl implements CompoundChange { // The list of changes: private final List<Change> changes; // Whether to apply them in reverse order: private boolean reverse; /** * Default constructor. * @param graph EGraph to be changed. */ public CompoundChangeImpl(EGraph graph) { super(graph); changes = new ArrayList<Change>(); reverse = false; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change#applyAndReverse() */ @Override public void applyAndReverse() { int size = changes.size(); if (reverse) { for (int i=size-1; i>=0; i--) { changes.get(i).applyAndReverse(); } } else { for (int i=0; i<size; i++) { changes.get(i).applyAndReverse(); } } reverse = !reverse; } /* * (non-Javadoc) * @see org.eclipse.emf.henshin.interpreter.Change.ComplexChange#getChanges() */ @Override public List<Change> getChanges() { return changes; } } }