/** * <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: CopyCommand.java,v 1.8 2008/07/11 03:09:51 davidms Exp $ */ package net.enilink.komma.edit.command; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; 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.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 net.enilink.komma.core.IReference; /** * The copy command logically acts upon an owner object or collection or owner * objects and copies the tree structured implied by the MOF containment * hierarchy. The static create methods delegate command creation to * {@link IEditingDomain#createCommand EditingDomain.createCommand}. * * <p> * The copy implementation is, at each level, delegated to * {@link CreateCopyCommand} and {@link InitializeCopyCommand} which can be * overridden to control the copy's object creation and initialization * respectively. */ public class CopyCommand extends ExtendedCompositeCommand { /** * This helper class is used to keep track of copied objects and their * associated copies. */ public static class Helper extends HashMap<IResource, IResource> { private static final long serialVersionUID = 1L; protected boolean commitTransaction; protected int deferredInitializationCount; protected List<IResource> initializationList = new ArrayList<IResource>(); protected IModel targetModel; /** * @param targetModel */ public Helper(IModel targetModel) { this.targetModel = targetModel; } public int decrementDeferredInitializationCount() { return --deferredInitializationCount; } public boolean getCommitTransaction() { return commitTransaction; } public IModel getTargetModel() { return targetModel; } /** * Return the copy of the specified object if it has one. */ public IResource getCopy(IReference object) { return get(object); } public void incrementDeferredInitializationCount() { ++deferredInitializationCount; } public Iterator<IResource> initializationIterator() { return initializationList.iterator(); } @Override public IResource put(IResource key, IResource value) { initializationList.add(key); return super.put(key, value); } @Override public IResource remove(Object key) { initializationList.remove(key); return super.remove(key); } public void setCommitTransaction(boolean commitTransaction) { this.commitTransaction = commitTransaction; } } /** * This caches the description. */ protected static final String DESCRIPTION = KommaEditPlugin.INSTANCE .getString("_UI_CopyCommand_description"); /** * This caches the label. */ protected static final String LABEL = KommaEditPlugin.INSTANCE .getString("_UI_CopyCommand_label"); /** * This creates a command that copies the given collection of objects. If * the collection contains more than one object, then a compound command * will be created containing individual copy commands for each object. */ public static ICommand create(final IEditingDomain domain, final Collection<?> collection, IModel targetModel) { if (collection == null || collection.isEmpty()) { return UnexecutableCommand.INSTANCE; } Helper copyHelper = new Helper(targetModel); ExtendedCompositeCommand copyCommand = new ExtendedCompositeCommand( ExtendedCompositeCommand.MERGE_COMMAND_ALL); for (Object object : collection) { copyCommand.add(domain.createCommand(CopyCommand.class, new CommandParameter(object, null, copyHelper))); } return copyCommand.reduce(); } /** * This creates a command that copies the given object. */ public static ICommand create(IEditingDomain domain, Object owner, IModel targetModel) { return domain.createCommand(CopyCommand.class, new CommandParameter( owner, null, new Helper(targetModel))); } /** * This is a map of objects to their copies */ protected Helper copyHelper; /** * This keeps track of the domain in which this command is created. */ protected IEditingDomain domain; /** * This keeps track of the owner in the command parameter from the * constructor. */ protected IResource owner; /** * This creates and instance in the given domain and for the given owner */ public CopyCommand(IEditingDomain domain, IResource owner, Helper copyHelper) { super(LABEL, DESCRIPTION); this.resultIndex = 0; this.domain = domain; this.owner = owner; this.copyHelper = copyHelper; copyHelper.incrementDeferredInitializationCount(); } protected void addCreateCopyCommands( ExtendedCompositeCommand compoundCommand, IResource object) { // Create a command to create a copy of the object. ICommand createCopyCommand = CreateCopyCommand.create(domain, object, copyHelper); compoundCommand.add(createCopyCommand); if (createCopyCommand instanceof IChildrenToCopyProvider && createCopyCommand.canExecute()) { for (Object child : ((IChildrenToCopyProvider) createCopyCommand) .getChildrenToCopy()) { addCreateCopyCommands(compoundCommand, (IResource) child); } } else { // Create commands to create copies of the children. for (IResource child : object.getContents()) { addCreateCopyCommands(compoundCommand, child); } } } @Override public boolean canRedo() { return true; } @Override public boolean canUndo() { return true; } @Override protected CommandResult doExecuteWithResult( IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException { CommandResult result = super.doExecuteWithResult(progressMonitor, info); if (!result.getStatus().isOK()) { return result; } ExtendedCompositeCommand createCommand = new ExtendedCompositeCommand(0); boolean transactionWasActive = owner.getEntityManager() .getTransaction().isActive(); if (!transactionWasActive) { owner.getEntityManager().getTransaction().begin(); copyHelper.setCommitTransaction(true); } boolean rollback = false; try { addCreateCopyCommands(createCommand, owner); IStatus status = addAndExecute(createCommand, progressMonitor, info); if (!status.isOK()) { rollback = true; return CommandResult.newCommandResult(status, null); } // Create an initialize copy command for each of the created // objects. if (copyHelper.decrementDeferredInitializationCount() == 0) { ICommand initializeCommand = new ExtendedCompositeCommand() { @Override public boolean prepare() { for (Iterator<IResource> copiedObjects = copyHelper .initializationIterator(); copiedObjects .hasNext();) { IResource object = copiedObjects.next(); ICommand initializeCopyCommand = InitializeCopyCommand .create(domain, object, copyHelper); // Record it for execution. if (!this.appendIfCanExecute(initializeCopyCommand)) { return false; } copiedObjects.remove(); } return true; } }; status = addAndExecute(initializeCommand, progressMonitor, info); if (copyHelper.getCommitTransaction()) { owner.getEntityManager().getTransaction().commit(); } } } catch (Throwable e) { rollback = true; throw new ExecutionException("Error while copying element", e); } finally { if (rollback && !transactionWasActive) { owner.getEntityManager().getTransaction().rollback(); } } return CommandResult.newOKCommandResult(createCommand .getCommandResult().getReturnValue()); } @Override protected boolean prepare() { if (owner == null) { return false; } return true; } @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 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(" (domain: " + domain + ")"); result.append(" (owner: " + owner + ")"); return result.toString(); } }