/***************************************************************************** * 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.properties.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.ApplyStereotypeCommand; import org.eclipse.papyrus.uml.tools.commands.UnapplyStereotypeCommand; import org.eclipse.papyrus.uml.tools.databinding.AbstractStereotypeListener; import org.eclipse.uml2.uml.Element; import org.eclipse.uml2.uml.Stereotype; /** * An IObservableList for editing Stereotype Applications * * @author Camille Letavernier * */ @SuppressWarnings({ "unchecked", "rawtypes" }) public class StereotypeApplicationObservableList extends WritableList implements ICommitListener { private Element umlSource; private EditingDomain domain; private final List<Command> commands; private AbstractStereotypeListener listener; /** * * Constructor. * * @param umlSource * The UML Element being observed * @param domain * The Editing Domain on which the commands will be executed */ public StereotypeApplicationObservableList(Element umlSource, EditingDomain domain) { super(new LinkedList<Object>(umlSource.getAppliedStereotypes()), Stereotype.class); this.umlSource = umlSource; this.domain = domain; commands = new LinkedList<Command>(); listener = new AbstractStereotypeListener(umlSource) { @Override protected void handleUnappliedStereotype(final EObject newValue) { StereotypeApplicationObservableList.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) { StereotypeApplicationObservableList.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; } } }; } }); } }; } 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() }; for(Command cmd : commands) { compoundCommand.append(cmd); } domain.getCommandStack().execute(compoundCommand); refreshCacheList(); commands.clear(); } private void refreshCacheList() { wrappedList.clear(); wrappedList.addAll(umlSource.getAppliedStereotypes()); fireListChange(null); } @Override public void clear() { removeAll(new LinkedList<Object>(wrappedList)); } @Override public boolean add(Object o) { if(!(o instanceof Stereotype)) { return false; } Stereotype stereotype = (Stereotype)o; Command command = new ApplyStereotypeCommand(umlSource, stereotype, (TransactionalEditingDomain)domain); commands.add(command); return wrappedList.add(o); } @Override public boolean remove(Object o) { if(!(o instanceof Stereotype)) { return false; } final Stereotype stereotype = (Stereotype)o; Command command = new UnapplyStereotypeCommand(umlSource, stereotype, (TransactionalEditingDomain)domain); commands.add(command); return wrappedList.remove(o); } @Override public boolean addAll(Collection c) { //We only apply the stereotypes that are not applied yet (To avoid removing them when undo is called) c.removeAll(wrappedList); Command command = new ApplyStereotypeCommand(umlSource, c, (TransactionalEditingDomain)domain); commands.add(command); return wrappedList.addAll(c); } @Override public boolean removeAll(Collection c) { Command command = new UnapplyStereotypeCommand(umlSource, c, (TransactionalEditingDomain)domain); commands.add(command); return wrappedList.removeAll(c); } @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 @Override public void add(int index, Object value) { add(value); //The list is not ordered } @Override public boolean addAll(int index, Collection c) { return addAll(c); //The list is not ordered } @Override public Object set(int index, Object element) { throw new UnsupportedOperationException(); } @Override public Object move(int oldIndex, int newIndex) { throw new UnsupportedOperationException(); } @Override public Object remove(int index) { throw new UnsupportedOperationException(); } @Override public void dispose() { super.dispose(); listener.dispose(); } }