/*****************************************************************************
* Copyright (c) 2011 CEA LIST.
*
* 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:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
*****************************************************************************/
package org.eclipse.papyrus.uml.tools.databinding;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.core.databinding.observable.list.ListDiff;
import org.eclipse.core.databinding.observable.list.ListDiffEntry;
import org.eclipse.core.databinding.observable.list.WritableList;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.papyrus.infra.widgets.editors.AbstractEditor;
import org.eclipse.papyrus.infra.widgets.editors.ICommitListener;
import org.eclipse.papyrus.uml.tools.commands.ApplyProfileCommand;
import org.eclipse.papyrus.uml.tools.commands.UnapplyProfileCommand;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Profile;
/**
*
* An IObservableList for Profile application
*
* @author Camille Letavernier
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public class ProfileApplicationObservableList extends WritableList implements ICommitListener {
private Package umlSource;
private EditingDomain domain;
private final List<Command> commands;
private AbstractStereotypeListener listener;
/**
*
* Constructor.
*
* @param umlSource
* The Package on which the profiles are applied or unapplied
* @param domain
* The editing domain on which the commands are executed
*/
public ProfileApplicationObservableList(Package umlSource, EditingDomain domain) {
super(new LinkedList<Object>(umlSource.getAllAppliedProfiles()), Profile.class);
this.umlSource = umlSource;
this.domain = domain;
commands = new LinkedList<Command>();
listener = new AbstractStereotypeListener(umlSource) {
@Override
protected void handleUnappliedStereotype(final EObject newValue) {
ProfileApplicationObservableList.this.fireListChange(new ListDiff() {
@Override
public ListDiffEntry[] getDifferences() {
return new ListDiffEntry[]{ new ListDiffEntry() {
@Override
public int getPosition() {
return 0;
}
@Override
public boolean isAddition() {
return false;
}
@Override
public Object getElement() {
return newValue;
}
} };
}
});
}
@Override
protected void handleAppliedStereotype(final EObject newValue) {
ProfileApplicationObservableList.this.fireListChange(new ListDiff() {
@Override
public ListDiffEntry[] getDifferences() {
return new ListDiffEntry[]{ new ListDiffEntry() {
@Override
public int getPosition() {
return 0;
}
@Override
public boolean isAddition() {
return true;
}
@Override
public Object getElement() {
return newValue;
}
} };
}
});
}
};
}
@Override
public synchronized void dispose() {
super.dispose();
this.listener.dispose();
}
public void commit(AbstractEditor editor) {
if(commands.isEmpty()) {
return;
}
CompoundCommand compoundCommand = new CompoundCommand() {
@Override
public void execute() {
super.execute();
refreshCacheList();
}
@Override
public void undo() {
super.undo();
refreshCacheList();
}
@Override
public void redo() {
super.redo();
refreshCacheList();
}
/**
* We have a sequential execution : the method canExecute() in
* the command n+1 depends on the result of the command n. We can't
* check every command's canExecute() method here, so we only
* check the first one.
*/
@Override
public boolean canExecute() {
return commandList.isEmpty() ? false : commandList.get(0).canExecute();
}
//TODO : edit the execute() method to call the remaining canExecute() checks
//during the execution
//(n).canExecute()
//(n).execute()
//(n+1).canExecute()
//(n+1).execute()
//Problem : this is the StrictCompoundCommand's behavior. However, in the
//StrictCompoundCommand implementation, the execute() is called outside of
//the current CommandStack, which is forbidden
};
for(Command cmd : commands) {
compoundCommand.append(cmd);
}
domain.getCommandStack().execute(compoundCommand);
refreshCacheList();
commands.clear();
}
private void refreshCacheList() {
wrappedList.clear();
wrappedList.addAll(umlSource.getAllAppliedProfiles());
fireListChange(null);
}
/**
* {@inheritDoc}
*/
@Override
public void clear() {
removeAll(new LinkedList<Object>(wrappedList));
}
/**
* {@inheritDoc}
*/
@Override
public boolean add(Object o) {
if(!(o instanceof Profile)) {
return false;
}
Profile profile = (Profile)o;
Command command = new ApplyProfileCommand(umlSource, profile, (TransactionalEditingDomain)domain);
commands.add(command);
return wrappedList.add(o);
}
/**
* {@inheritDoc}
*/
@Override
public boolean remove(Object o) {
if(!(o instanceof Profile)) {
return false;
}
final Profile profile = (Profile)o;
Command command = new UnapplyProfileCommand(umlSource, profile, (TransactionalEditingDomain)domain);
commands.add(command);
return wrappedList.remove(o);
}
/**
* {@inheritDoc}
*/
@Override
public boolean addAll(Collection c) {
//We only apply the profiles that are not applied yet (To avoid removing them when undo is called)
c.removeAll(wrappedList);
Command command = new ApplyProfileCommand(umlSource, c, (TransactionalEditingDomain)domain);
commands.add(command);
return wrappedList.addAll(c);
}
/**
* {@inheritDoc}
*/
@Override
public boolean removeAll(Collection c) {
Command command = new UnapplyProfileCommand(umlSource, c, (TransactionalEditingDomain)domain);
commands.add(command);
return wrappedList.removeAll(c);
}
/**
* {@inheritDoc}
*/
@Override
public boolean retainAll(Collection c) {
List<Object> objectsToRemove = new LinkedList<Object>();
for(Object object : c) {
if(!contains(object)) {
objectsToRemove.add(object);
}
}
return removeAll(objectsToRemove);
}
//Unsupported operations. Some of them have a "proxy" implementation
/**
* {@inheritDoc}
*/
@Override
public void add(int index, Object value) {
add(value); //The list is not ordered
}
/**
* {@inheritDoc}
*/
@Override
public boolean addAll(int index, Collection c) {
return addAll(c); //The list is not ordered
}
/**
* {@inheritDoc}
*/
@Override
public Object set(int index, Object element) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
public Object move(int oldIndex, int newIndex) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
public Object remove(int index) {
throw new UnsupportedOperationException();
}
}