/**
* <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: CreateChildCommand.java,v 1.10 2008/05/07 19:08:46 emerks Exp $
*/
package net.enilink.komma.edit.command;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import net.enilink.komma.common.command.CommandResult;
import net.enilink.komma.common.command.ExtendedCompositeCommand;
import net.enilink.komma.common.command.ICommand;
import net.enilink.komma.common.command.UnexecutableCommand;
import net.enilink.komma.core.IReference;
import net.enilink.komma.edit.KommaEditPlugin;
import net.enilink.komma.edit.domain.IEditingDomain;
import net.enilink.komma.em.concepts.IResource;
import net.enilink.komma.model.IModel;
import net.enilink.komma.model.IObject;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
/**
* This command wraps an {@link AddCommand} or {@link SetCommand} to provide the
* higher-level operation of "creating" an appropriate child object and adding
* it to a owner object. In fact, all of the possible child objects are created
* by the owner's item provider before this command is created, and specified in
* the <code>newChildDescriptor</code> argument to {@link #create create()} --
* they must be, so that the user can specify which he actually wishes to
* create. As a result, this command essentially just creates and executes the
* appropriate lower-level command, and delegates matters of appearance (text,
* icon, result) to the appropriate item provider, so that it may be handled
* correctly for the given model.
*
* <p>
* Note that this command makes no assumptions about the relationship between
* the <code>owner</code>, to which the new child will be added, and the
* selected object. This allows the command to be reused for sibling creation.
* As a result, the <code>selection</code> be explicitly specified, so that it
* can be restored when the command is undone.
*/
public class CreateChildCommand extends ExtendedCompositeCommand implements
ICommandActionDelegate {
/**
* This is the helper interface to which <code>CreateChildCommand</code>
* functionality is delegated.
*/
public static interface IHelper {
Object createChild(Object owner, Object property,
Object childDescription, IAdaptable info);
/**
* This returns the description of the action of creating the specified
* <code>child</code> under the specified <code>feature</code> of the
* <code>owner</code>. The <code>selection</code> is given as context,
* from which the <code>Helper</code> can determine whether the object
* is being added as a child or a sibling, if it wishes.
*/
String getCreateChildDescription(Object owner, Object property,
Object childDescription, Collection<?> selection);
/**
* This returns the icon for the action of creating the specified
* <code>child</code> under the specified <code>feature</code> of the
* <code>owner</code>. The <code>selection</code> is given as context,
* from which the <code>Helper</code> can determine whether the object
* is being added as a child or a sibling, if it wishes.
*/
Object getCreateChildImage(Object owner, Object property,
Object childDescription, Collection<?> selection);
/**
* For a given child description, this returns the complete collection
* of objects to be presented as the command's result.
*/
Collection<?> getCreateChildResult(Object child);
/**
* This returns the text for the action of creating the specified
* <code>child</code> under the specified <code>feature</code> of the
* <code>owner</code>. The <code>selection</code> is given as context,
* from which the <code>Helper</code> can determine whether the object
* is being added as a child or a sibling, if it wishes.
*/
String getCreateChildText(Object owner, Object property,
Object childDescription, Collection<?> selection);
/**
* This returns the tool tip text for the action of creating the
* specified <code>child</code> under the specified <code>feature</code>
* of the <code>owner</code>. The <code>selection</code> is given as
* context, from which the <code>Helper</code> can determine whether the
* object is being added as a child or a sibling, if it wishes.
*/
String getCreateChildToolTipText(Object owner, Object property,
Object childDescription, Collection<?> selection);
}
/**
* This returns a command created by the editing domain to add the child
* described by <code>newChildDescriptor</code> to the given
* <code>object</code>.
*/
public static ICommand create(IEditingDomain domain, Object owner,
Object newChildDescriptor, Collection<?> selection) {
return domain.createCommand(CreateChildCommand.class,
new CommandParameter(owner, null, newChildDescriptor,
new ArrayList<Object>(selection)));
}
protected ICommand addChildCommand;
/**
* This is the value to be returned by {@link #getAffectedObjects}. The
* affected objects are different after an execute or redo from after an
* undo, so we record them.
*/
protected Collection<?> affectedObjects;
protected Object child;
/**
* This is the description for the child object to be added.
*/
protected Object childDescription;
protected boolean doAdd = false;
protected IEditingDomain domain;
/**
* This is the helper object, usually the item provider for {@link #owner},
* to which parts of this command's functionality are delegated.
*/
protected CreateChildCommand.IHelper helper;
/**
* This is the index for the new object's position under the feature.
*/
protected int index;
/**
* This is the object to which the child will be added.
*/
protected IResource owner;
/**
* This is the property of the owner to which the child will be added.
*/
protected IReference property;
/**
* This is the collection of objects that were selected when this command
* was created. After an undo, these are considered the affected objects.
*/
protected Collection<?> selection;
/**
* This constructor initializes an instance that adds the specified
* <code>child</code> object to the <code>feature</code> of the
* <code>owner</code> object. If any of <code>owner</code>,
* <code>feature</code>, or <code>child</code> are <code>null</code>,
* {@link #createCommand} will return {@link UnexecutableCommand#INSTANCE}
* and, hence,
* {@link net.enilink.komma.common.command.AbstractCommand#canExecute} will
* return <code>false</code>. If non-null, <code>selection</code> is the
* collection of selected objects. An internal default {@link IHelper
* Helper} will provide generic implmentations for the delegated command
* methods.
*/
public CreateChildCommand(IEditingDomain domain, IResource owner,
IReference property, Object childDescription,
Collection<?> selection) {
this(domain, owner, property, childDescription,
CommandParameter.NO_INDEX, selection, null);
}
/**
* This constructor initializes an instance, as above, but the command
* delegates functionality to the specified {@link IHelper Helper}. If
* <code>helper</code> is <code>null</code>, the internal default helper is
* used.
*/
public CreateChildCommand(IEditingDomain domain, IResource owner,
IReference property, Object childDescription,
Collection<?> selection, CreateChildCommand.IHelper helper) {
this(domain, owner, property, childDescription,
CommandParameter.NO_INDEX, selection, helper);
}
/**
* This constructor initializes an instance that adds the specified
* <code>child</code> object to the <code>owner</code> object, at the
* specified <code>index</code> of its <code>feature</code> feature, if it
* is multi-valued. If any of <code>owner</code>, <code>feature</code>, or
* <code>child</code> are <code>null</code>, {@link #createCommand} will
* return {@link UnexecutableCommand#INSTANCE} and, hence,
* {@link net.enilink.komma.common.command.AbstractCommand#canExecute} will
* return <code>false</code>. If non-null, <code>selection</code> is the
* collection of selected objects. The internal default helper is used by
* the command. If <code>index</code> is {@link CommandParameter#NO_INDEX},
* this behaves just like the first constructor form.
*/
public CreateChildCommand(IEditingDomain domain, IResource owner,
IReference property, Object childDescription, int index,
Collection<?> selection) {
this(domain, owner, property, childDescription, index, selection, null);
}
/**
* This constructor initializes an instance, as above, but the command
* delegates functionality to the specified {@link IHelper Helper}. If
* <code>helper</code> is <code>null</code>, the internal default helper is
* used.
*/
public CreateChildCommand(IEditingDomain domain, IResource owner,
IReference property, Object childDescription, int index,
Collection<?> selection, CreateChildCommand.IHelper helper) {
this.domain = domain;
this.owner = owner;
this.property = property;
this.childDescription = childDescription;
this.index = index;
this.selection = selection == null ? Collections.EMPTY_LIST : selection;
this.helper = helper;
String text = this.helper.getCreateChildText(owner, property,
childDescription, selection);
setLabel(KommaEditPlugin.INSTANCE.getString(
"_UI_CreateChildCommand_label", new Object[] { text }));
setDescription(KommaEditPlugin.INSTANCE
.getString("_UI_CreateChildCommand_description"));
}
@Override
public boolean canRedo() {
return true;
}
@Override
public boolean canUndo() {
return true;
}
/**
* This executes the wrapped command and sets the affected objects to the
* collection returned by <code>helper.getCreateChildResult()</code>.
*/
@Override
protected CommandResult doExecuteWithResult(
IProgressMonitor progressMonitor, IAdaptable info)
throws ExecutionException {
child = helper.createChild(owner, property, childDescription, info);
if (child != null) {
addAndExecute(
doAdd ? AddCommand.create(domain, owner, property, child,
index) : SetCommand.create(domain, owner, property,
child), progressMonitor, info);
}
affectedObjects = helper.getCreateChildResult(child);
Collection<?> result = affectedObjects;
return CommandResult
.newOKCommandResult(result == null ? Collections.EMPTY_LIST
: result);
}
/**
* This redoes the wrapped command and sets the affected objects to the
* collection returned by <code>helper.getCreateChildResult()</code>.
*/
@Override
protected CommandResult doRedoWithResult(IProgressMonitor progressMonitor,
IAdaptable info) throws ExecutionException {
CommandResult result = super.doRedoWithResult(progressMonitor, info);
affectedObjects = helper.getCreateChildResult(child);
return result;
}
/**
* This undoes the wrapped command and sets the affected objects to the
* original selection.
*/
@Override
protected CommandResult doUndoWithResult(IProgressMonitor progressMonitor,
IAdaptable info) throws ExecutionException {
CommandResult result = super.doUndoWithResult(progressMonitor, info);
affectedObjects = selection;
return result;
}
/**
* This returns the affected objects.
*/
public Collection<?> getAffectedObjects() {
return affectedObjects == null ? Collections.EMPTY_LIST
: affectedObjects;
}
@Override
public Collection<Object> getAffectedResources(Object type) {
if (IModel.class.equals(type) && owner instanceof IObject) {
Collection<Object> affected = new HashSet<Object>(
super.getAffectedResources(type));
affected.add(((IObject) owner).getModel());
return affected;
}
return super.getAffectedResources(type);
}
/**
* This returns the description by delegating to
* <code>helper.getCreateChildDescription()</code>.
*/
@Override
public String getDescription() {
return helper.getCreateChildDescription(owner, property,
childDescription, selection);
}
/**
* This returns the icon by delegating to
* <code>helper.getCreateChildImage()</code>.
*/
public Object getImage() {
return helper.getCreateChildImage(owner, property, childDescription,
selection);
}
/**
* This returns the label by delegating to
* <code>helper.getCreateChildText()</code>.
*/
public String getText() {
return helper.getCreateChildText(owner, property, childDescription,
selection);
}
/**
* This returns the tool tip text by delegating to
* <code>helper.getCreateChildToolTipText()</code>.
*/
public String getToolTipText() {
return helper.getCreateChildToolTipText(owner, property,
childDescription, selection);
}
@Override
public boolean prepare() {
if (owner == null || property == null || childDescription == null) {
return false;
}
int maxCardinality = owner.getApplicableCardinality(property)
.getSecond();
if (maxCardinality != 1) {
if (maxCardinality != Integer.MAX_VALUE
&& owner.getCardinality(property) >= maxCardinality) {
// cannot execute command since maximal cardinality is already
// reached
return false;
}
doAdd = true;
return true;
} else if (owner.getCardinality(property) == 0) {
return true;
}
return false;
}
/**
* This gives an abbreviated name using this object's own class name,
* without package qualification, followed by a space-separated list of
* <code>field:value</code> pairs.
*/
@Override
public String toString() {
StringBuffer result = new StringBuffer(super.toString());
result.append(" (domain: " + domain + ")");
result.append(" (owner: " + owner + ")");
result.append(" (property: " + property + ")");
result.append(" (child: " + childDescription + ")");
result.append(" (index: " + index + ")");
result.append(" (helper: " + helper + ")");
result.append(" (affectedObjects: " + affectedObjects + ")");
result.append(" (selection: " + selection + ")");
return result.toString();
}
}