/**
* <copyright>
*
* Copyright (c) 2002, 2009 IBM Corporation and others.
* 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:
* IBM - Initial API and implementation
*
* </copyright>
*
* $Id: ReplaceCommand.java,v 1.8 2008/05/07 19:08:46 emerks Exp $
*/
package net.enilink.komma.edit.command;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import net.enilink.vocab.rdf.List;
import net.enilink.komma.common.command.CommandResult;
import net.enilink.komma.common.command.ICommand;
import net.enilink.komma.edit.KommaEditPlugin;
import net.enilink.komma.edit.domain.AdapterFactoryEditingDomain;
import net.enilink.komma.edit.domain.IEditingDomain;
import net.enilink.komma.em.concepts.IProperty;
import net.enilink.komma.em.concepts.IResource;
import net.enilink.komma.model.IModel;
import net.enilink.komma.model.IObject;
import net.enilink.komma.core.IReference;
/**
* The replace command logically acts upon an owner object that has a
* collection-type feature in which an object can be replaced by a collection of
* other objects. The static create methods delegate command creation to
* {@link IEditingDomain#createCommand EditingDomain.createCommand}, which may
* or may not result in the actual creation of an instance of this class.
*
* <p>
* The implementation of this class is low-level and EMF specific; it allows an
* object from a many-valued feature of an owner to be replaced by a collection
* of other objects. i.e., it is equivalent of the call
*
* <pre>
* int index = ((EList) ((EObject) owner).eGet((EStructuralFeature) feature))
* .indexOf(value);
* ((EList) ((EObject) owner).eGet((EStructuralFeature) feature)).remove(value);
* ((EList) ((EObject) owner).eGet((EStructuralFeature) feature)).addAll(index,
* (Collection) collection);
* </pre>
*
* <p>
* It can also be used as an equivalent to the call
*
* <pre>
* int index = ((EList) extent).indexOf(value);
* ((EList) extent).remove(value);
* ((EList) extent).addAll(index, (Collection) collection);
* </pre>
*
* which is how root objects are replaced in the contents of a resource. Like
* all the low level commands in this package, the replace command is undoable.
*
* <p>
* A replace command is an {@link IOverrideableCommand}.
*/
public class ReplaceCommand extends AbstractOverrideableCommand {
/**
* This creates a command to replace an object with a collection of
* replacements.
*/
public static ICommand create(IEditingDomain domain, Object value,
Collection<?> collection) {
return create(domain, null, null, value, collection);
}
/**
* This creates a command to replace a particular value in the specified
* feature of the owner with a collection replacements objects.
*/
public static ICommand create(IEditingDomain domain, Object owner,
Object feature, Object value, Collection<?> collection) {
return domain.createCommand(ReplaceCommand.class, new CommandParameter(
owner, feature, value, collection));
}
/**
* This caches the label.
*/
protected static final String LABEL = KommaEditPlugin.INSTANCE
.getString("_UI_ReplaceCommand_label");
/**
* This caches the description.
*/
protected static final String DESCRIPTION = KommaEditPlugin.INSTANCE
.getString("_UI_ReplaceCommand_description");
/**
* This is the owner object upon which the command will act. It could be
* null, in the case that we are dealing with an
* {@link net.enilink.komma.rmf.common.util.EList}.
*/
protected IResource owner;
/**
* This is the feature of the owner object upon the command will act. It
* could be null, in the case that we are dealing with an
* {@link net.enilink.komma.rmf.common.util.EList}.
*/
protected IReference property;
/**
* This is the list from which the command will replace.
*/
protected Collection<Object> ownerList;
/**
* This is value that is being replaced.
*/
protected Object value;
/**
* This is the collection of replacements.
*/
protected Collection<?> collection;
/**
* The is the value returned by {@link ICommand#getAffectedObjects}. The
* affected objects are different after an execute than after an undo, so we
* record it.
*/
protected Collection<?> affectedObjects;
/**
* This constructs a primitive command to replace a particular value in the
* specified feature of the owner with the specified replacement.
*/
public ReplaceCommand(IEditingDomain domain, IResource owner,
IReference feature, Object value, Object replacement) {
this(domain, owner, feature, value, Collections.singleton(replacement));
}
/**
* This constructs a primitive command to replace a particular value in the
* specified feature of the owner with the specified collection of
* replacements.
*/
public ReplaceCommand(IEditingDomain domain, IResource owner,
IReference feature, Object value, Collection<?> collection) {
super(domain, LABEL, DESCRIPTION);
// Initialize all the fields from the command parameter.
//
this.owner = owner;
this.property = feature;
this.value = value;
this.collection = collection;
ownerList = getOwnerList(this.owner, feature);
}
/**
* This constructs a primitive command to replace a particular value in the
* specified extent with the given replacement.
*/
public ReplaceCommand(IEditingDomain domain, Collection<?> list,
Object value, Object replacement) {
this(domain, list, value, Collections.singleton(replacement));
}
/**
* This constructs a primitive command to replace a particular value in the
* specified extent with the given collection of replacements.
*/
public ReplaceCommand(IEditingDomain domain, Collection<?> list,
Object value, Collection<?> collection) {
super(domain, LABEL, DESCRIPTION);
// Initialize all the fields from the command parameter.
//
this.value = value;
this.collection = collection;
@SuppressWarnings("unchecked")
Collection<Object> untypedList = (Collection<Object>) list;
ownerList = untypedList;
}
/**
* This returns the owner object upon which the command will act. It could
* be null, in the case that we are dealing with an
* {@link net.enilink.komma.rmf.common.util.EList}.
*/
public IResource getOwner() {
return owner;
}
/**
* This returns the feature of the owner object upon the command will act.
* It could be null, in the case that we are dealing with an
* {@link net.enilink.komma.rmf.common.util.EList}.
*/
public IReference getProperty() {
return property;
}
/**
* This returns the list in which the command will replace.
*/
public Collection<Object> getOwnerList() {
return ownerList;
}
/**
* This returns the value being replaced.
*/
public Object getValue() {
return value;
}
/**
* This returns the collection of replacement objects.
*/
public Collection<?> getCollection() {
return collection;
}
@Override
protected boolean prepare() {
// This can't execute if there is no owner list
// or the owner list doesn't contain the value being replaced or
// there are not replacements.
//
if (ownerList == null || !ownerList.contains(value)
|| collection == null || collection.isEmpty()) {
return false;
} else if (owner != null && getDomain().isReadOnly(owner)) {
return false;
} else if (property == null) {
// An extent allows anything to be added.
//
return true;
} else {
if (owner != null && property != null) {
IProperty property = (IProperty) owner.getEntityManager().find(
this.property);
// Make sure each object conforms to the range of the property.
for (Object replacement : collection) {
if (!property.isRangeCompatible(replacement)) {
return false;
}
}
}
return true;
}
}
@Override
protected CommandResult doExecuteWithResult(
IProgressMonitor progressMonitor, IAdaptable info)
throws ExecutionException {
// Record the position of the value in the owner list.
int index = -1;
if (ownerList instanceof List<?>) {
index = ((List<?>) ownerList).indexOf(value);
}
// Simply remove the object from the owner list.
//
ownerList.remove(value);
// Insert the collection at the right place.
if (index != -1 && ownerList instanceof List<?>) {
((List<Object>) ownerList).addAll(index, collection);
} else {
ownerList.addAll(collection);
}
// We'd like the collection of replacements selected after this replace
// completes.
//
affectedObjects = collection;
return CommandResult.newOKCommandResult(collection);
}
@Override
public Collection<?> doGetAffectedObjects() {
return affectedObjects;
}
@Override
public Collection<?> doGetAffectedResources(Object type) {
if (IModel.class.equals(type)
&& (owner != null || ownerList != null || collection != null)) {
Collection<Object> affected = new HashSet<Object>(
super.doGetAffectedResources(type));
if (value instanceof IObject) {
affected.add(((IObject) value).getModel());
}
if (owner instanceof IObject) {
affected.add(((IObject) owner).getModel());
}
if (ownerList != null) {
for (Object element : ownerList) {
Object object = AdapterFactoryEditingDomain.unwrap(element);
if (object instanceof IObject) {
affected.add(((IObject) object).getModel());
}
}
}
return affected;
}
return super.doGetAffectedResources(type);
}
/**
* This gives an abbreviated name using this object's own class' name,
* without package qualification, followed by a space separated list of
* <tt>field:value</tt> pairs.
*/
@Override
public String toString() {
StringBuffer result = new StringBuffer(super.toString());
result.append(" (owner: " + owner + ")");
result.append(" (property: " + property + ")");
result.append(" (ownerList: " + ownerList + ")");
result.append(" (value: " + value + ")");
result.append(" (collection: " + collection + ")");
result.append(" (affectedObjects:" + affectedObjects + ")");
return result.toString();
}
}