/*
* grEMF
*
* Copyright (C) 2006-2012 Institute for Software Technology
* University of Koblenz-Landau, Germany
* ist@uni-koblenz.de
*
* For bug reports, documentation and further information, visit
*
* https://github.com/jgralab/gremf
*
* 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; either version 3 of the License, or (at your
* option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses>.
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this Program, or any covered work, by linking or combining
* it with Eclipse (or a modified version of that program or an Eclipse
* plugin), containing parts covered by the terms of the Eclipse Public
* License (EPL), the licensors of this Program grant you additional
* permission to convey the resulting work. Corresponding Source for a
* non-source form of such a combination shall include the source code for
* the parts of JGraLab used as well as that of the covered work.
*/
package de.uni_koblenz.gremf.notification;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.pcollections.ArrayPSet;
import de.uni_koblenz.gremf.schema.impl.GrEMFAttributeImpl;
import de.uni_koblenz.jgralab.AttributedElement;
import de.uni_koblenz.jgralab.eca.Action;
import de.uni_koblenz.jgralab.eca.events.ChangeAttributeEvent;
import de.uni_koblenz.jgralab.eca.events.Event;
import de.uni_koblenz.jgralab.schema.AttributedElementClass;
public class AttributeNotifyAction<AEC extends AttributedElementClass<AEC, ?>>
implements Action<AEC> {
@Override
public void doAction(Event<AEC> event) {
if (event instanceof ChangeAttributeEvent) {
ChangeAttributeEvent<AEC> ev = (ChangeAttributeEvent<AEC>) event;
AttributedElement<AEC, ?> ae = event.getElement();
if (!((InternalEObject) ae).eNotificationRequired()) {
return;
}
GrEMFAttributeImpl attribute = (GrEMFAttributeImpl) ae
.getAttributedElementClass().getAttribute(
ev.getAttributeName());
if ((ev.getOldValue() == null) && (ev.getNewValue() == null)) {
return; // no changes
}
if ((ev.getOldValue() != null)
&& ev.getOldValue().equals(ev.getNewValue())) {
// if value doesn't change, return
return;
}
// LIST
else if (ev.getNewValue() instanceof List) {
this.notifyListChange(ev, ae, attribute);
}
// ARRAYPSET
else if (ev.getNewValue() instanceof ArrayPSet) {
this.notifySetChange(ev, ae, attribute);
}
// SINGLE VALUE
else {
this.notifySingleValueChange(ev, ae, attribute);
}
} else {
throw new IllegalArgumentException();
}
}
private void notifySingleValueChange(ChangeAttributeEvent<AEC> ev,
AttributedElement<AEC, ?> ae, GrEMFAttributeImpl attribute) {
Notification not = new ENotificationImpl((InternalEObject) ae,
Notification.SET, attribute, ev.getOldValue(), ev.getNewValue());
((EObject) ae).eNotify(not);
}
private void notifySetChange(ChangeAttributeEvent<AEC> ev,
AttributedElement<AEC, ?> ae, GrEMFAttributeImpl attribute) {
ArrayPSet<?> oldCol = (ArrayPSet<?>) ev.getOldValue();
ArrayPSet<?> newCol = (ArrayPSet<?>) ev.getNewValue();
// check what has changed, cause EMF only regards the real
// changes
// CLEAR
if ((newCol == null) || newCol.isEmpty()) {
if (oldCol.size() > 1) {
Notification not1 = new ENotificationImpl((InternalEObject) ae,
Notification.REMOVE_MANY, attribute, oldCol, null, 0);
((EObject) ae).eNotify(not1);
} else {
Notification not1 = new ENotificationImpl((InternalEObject) ae,
Notification.REMOVE, attribute, oldCol.get(0), null, 0);
((EObject) ae).eNotify(not1);
}
return;
}
int i = this.compareSets(oldCol, newCol);
// ADD
if (i == this.ADD) {
Notification not1 = new ENotificationImpl((InternalEObject) ae,
Notification.ADD, attribute, null,
((List<?>) ev.getNewValue()).get(((List<?>) ev
.getNewValue()).size() - 1));
((EObject) ae).eNotify(not1);
}
// REMOVE
else if (i == this.REMOVE) {
Object removed = null;
for (Object o : oldCol) {
if (!newCol.contains(o)) {
removed = o;
break;
}
}
Notification not1 = new ENotificationImpl((InternalEObject) ae,
Notification.REMOVE, attribute, removed, null);
((EObject) ae).eNotify(not1);
}
// SET
else {
// -- REMOVE old elements
Notification not1 = new ENotificationImpl((InternalEObject) ae,
Notification.REMOVE_MANY, attribute, ev.getOldValue(), null);
((EObject) ae).eNotify(not1);
// -- ADD MANY elements
Notification not2 = new ENotificationImpl((InternalEObject) ae,
Notification.ADD_MANY, attribute, null, newCol, 0);
((EObject) ae).eNotify(not2);
}
}
private void notifyListChange(ChangeAttributeEvent<AEC> ev,
AttributedElement<AEC, ?> ae, GrEMFAttributeImpl attribute) {
List<?> oldCol = (List<?>) ev.getOldValue();
List<?> newCol = (List<?>) ev.getNewValue();
// check what has changed, cause EMF only regards the real
// changes
int i = this.compareLists(oldCol, newCol);
// ADD one Element - simply add one element
if (i == this.ADD) {
Notification not1 = new ENotificationImpl((InternalEObject) ae,
Notification.ADD, attribute, null,
((List<?>) ev.getNewValue()).get(((List<?>) ev
.getNewValue()).size() - 1));
((EObject) ae).eNotify(not1);
} else if (i == this.REMOVE) {
Notification not1 = new ENotificationImpl((InternalEObject) ae,
Notification.REMOVE, attribute,
((List<?>) ev.getOldValue()).get(this.REMOVE), null,
this.REMOVE);
((EObject) ae).eNotify(not1);
}
// REMOVE + ADD MANY elements cause set was called
else {
@SuppressWarnings("unchecked")
Collection<Object> oldList = (Collection<Object>) ev.getOldValue();
if (oldList == null) {
oldList = new ArrayList<Object>();
}
// REMOVE old elements
Notification not1 = new ENotificationImpl((InternalEObject) ae,
Notification.REMOVE_MANY, attribute, oldList, null);
((EObject) ae).eNotify(not1);
// ADD MANY elements
if (newCol.size() == 1) {
Notification not = new ENotificationImpl((InternalEObject) ae,
Notification.ADD, attribute, null, newCol.get(0), 0);
((EObject) ae).eNotify(not);
} else {
Notification not2 = new ENotificationImpl((InternalEObject) ae,
Notification.ADD_MANY, attribute, null, newCol, 0);
((EObject) ae).eNotify(not2);
}
}
}
private int REMOVE = 0;
private int ADD = -1;
private int SET = -2;
private int compareSets(ArrayPSet<?> oldSet, ArrayPSet<?> newSet) {
if ((newSet == null) || (oldSet == null)) {
return this.SET;
}
if ((oldSet.size() + 1) == newSet.size()) { // possible ADD
if (newSet.containsAll(oldSet)) {
return this.ADD;
} else {
return this.SET;
}
} else if (oldSet.size() == (newSet.size() + 1)) { // possible REMOVE
if (oldSet.containsAll(newSet)) {
return this.REMOVE;
} else {
return this.SET;
}
} else {
return this.SET;
}
}
private int compareLists(List<?> oldList, List<?> newList) {
if ((newList == null) || (oldList == null)) {
return this.SET;
}
if ((oldList.size() + 1) == newList.size()) { // possible ADD
for (int i = 0; i < oldList.size(); i++) {
if (!oldList.get(i).equals(newList.get(i))) {
return this.SET;
}
}
return this.ADD;
} else if (oldList.size() == (newList.size() + 1)) { // possible REMOVE
int j = 0;
int k = -5;
boolean isSet = false;
for (int i = 0; i < newList.size(); i++) {
if ((newList.get(i) != oldList.get(j))) {
if (i == j) {
j++;
k = i;
if (newList.get(i) != oldList.get(j)) {
isSet = true;
break;
}
} else {
isSet = true;
break;
}
}
j++;
}
if (isSet) {
return this.SET;
}
if (k == -5) {
this.REMOVE = newList.size();
return this.REMOVE;
} else {
this.REMOVE = k;
return this.REMOVE;
}
} else {
return this.SET;
}
}
}