uidesigner/com.nokia.sdt.emf.dm/src/com/nokia/sdt/emf/dm/impl/PasteInstancesFromClipboardCommand.java
/*
* Copyright (c) 2005 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package com.nokia.sdt.emf.dm.impl;
import com.nokia.sdt.component.IComponent;
import com.nokia.sdt.datamodel.IDesignerDataModel;
import com.nokia.sdt.datamodel.util.ModelUtils;
import com.nokia.sdt.datamodel.util.NamePropertySupport;
import com.nokia.sdt.datamodel.util.NamePropertySupport.INameFilter;
import com.nokia.sdt.editor.IComponentEditor;
import com.nokia.sdt.emf.dm.*;
import com.nokia.sdt.emf.dm.impl.NodeCopier.IFilter;
import com.nokia.cpp.internal.api.utils.core.Check;
import org.eclipse.emf.common.command.AbstractCommand;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.edit.EMFEditPlugin;
import org.eclipse.emf.edit.domain.EditingDomain;
import java.util.*;
/**
*
*
*/
public class PasteInstancesFromClipboardCommand extends AbstractCommand implements Command {
protected static final String LABEL =
EMFEditPlugin.INSTANCE.getString("_UI_PasteFromClipboardCommand_label");
protected static final String DESCRIPTION =
EMFEditPlugin.INSTANCE.getString("_UI_PasteFromClipboardCommand_description");
private String ownerName;
private IDesignerDataModel ownerModel;
private EditingDomain editingDomain;
private Collection clipboard;
private List<INode> affectedObjects;
private List<String> pastedNames;
private List<NodeCopier> copiedNodesForRedo;
class NameReplaceVisitor implements INodeVisitor {
private INode root;
private IDesignerDataModel model;
/**
* When visiting each node, check it for clashes with the new model
* If clashes, assign the node a new name that does not clash with the new model
* or any of the children of the current node (which themselves may already have a name)
* @param root the root INode of the new model
*/
public NameReplaceVisitor(INode root) {
this.root = root;
model = root.getDesignerData().getDesignerDataModel();
}
private IComponent getComponent(INode node) {
String componentId = node.getComponentId();
return model.getComponentSet().lookupComponent(componentId);
}
private void assignNewName(final INode node, IComponent component) {
// This filter is passed to the name generator to allow it to check
// not only the target model, but also the children of the root of the current node
final INode root = node.getRootContainer();
INameFilter filter = new INameFilter() {
public boolean test(final String name) {
INodeVisitor visitor = new INodeVisitor() {
public Object visit(INode node) {
if (name.equals(node.getName()))
return node;
return null;
}
};
return root.visitPreorder(visitor) == null;
}
};
String generatedName =
NamePropertySupport.generateNameForModel(model, component, null, true, filter);
StringValue sv = new StringValue(StringValue.LITERAL, generatedName);
node.getProperties().set(INode.NAME_PROPERTY, sv, true);
}
public Object visit(INode node) {
if (hasClashingName(root, node)) {
assignNewName(node, getComponent(node));
}
return null;
}
}
class NameClashDetector implements INodeVisitor {
private INode root;
public NameClashDetector(INode root) {
this.root = root;
}
public Object visit(INode node) {
if (hasClashingName(root, node)) {
return node;
}
return null;
}
}
public PasteInstancesFromClipboardCommand(EditingDomain editingDomain, EObject owner) {
super(LABEL, DESCRIPTION);
Check.checkArg(owner instanceof INode);
Check.checkArg(editingDomain);
this.ownerName = ((INode) owner).getName();
this.ownerModel = ((INode) owner).getDesignerData().getDesignerDataModel();
this.editingDomain = editingDomain;
}
/* (non-Javadoc)
* @see org.eclipse.emf.common.command.AbstractCommand#prepare()
*/
protected boolean prepare() {
affectedObjects = new ArrayList<INode>();
pastedNames = new ArrayList<String>();
copiedNodesForRedo = new ArrayList<NodeCopier>();
clipboard = editingDomain.getClipboard();
return (clipboard != null) && !clipboard.isEmpty();
}
private boolean hasNameClashes(INode root, INode node) {
return node.visitPreorder(new NameClashDetector(root)) != null;
}
private void replaceClashingNames(INode root, INode node) {
node.visitPreorder(new NameReplaceVisitor(root));
}
/**
* @param root the INode whose tree we check for clashing names
* @param node the INode whose name is checked for clashes
* @return true if INode.getName() exists in root's tree
*/
protected boolean hasClashingName(INode root, INode node) {
String name = node.getName();
return (name != null) && (root.findByNameProperty(name) != null);
}
/* (non-Javadoc)
* @see org.eclipse.emf.common.command.Command#execute()
*/
public void execute() {
if (!canExecute())
return;
affectedObjects.clear();
pastedNames.clear();
copiedNodesForRedo.clear();
INode owner = getOwner();
INode root = owner.getRootContainer();
// For each node on the clipboard,
// if it, or any of its children's names clashes with the owner's model,
// make a copy of the node and replace any clashing names
// Copy the resulting node (or the original, if no clashes) to the owner
for (Iterator iter = clipboard.iterator(); iter.hasNext();) {
INode node = (INode) iter.next();
NodeCopier localCopier = new NodeCopier(node, new IFilter() {
public boolean test(INode node) {
IComponentEditor componentEditor = ModelUtils.getComponentEditor(node);
return (componentEditor == null) || !componentEditor.isTemporaryObject();
}
});
if (hasNameClashes(root, node)) {
node = localCopier.getCopy();
replaceClashingNames(root, node);
}
INode copy = NodeCopier.copyNode(node, owner, IDesignerDataModel.AT_END, false);
if (copy != null) {
ModelUtils.getPropertyInformation(copy).refresh();
affectedObjects.add(copy);
pastedNames.add(copy.getName());
copiedNodesForRedo.add(localCopier);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.emf.common.command.AbstractCommand#undo()
*/
public void undo() {
INode owner = getOwner();
// don't use removeAll, because notification is indices, instead of objects
EList children = owner.getChildren();
for (String name : pastedNames) {
INode child = owner.findByNameProperty(name);
if (child != null)
children.remove(child);
}
}
/* (non-Javadoc)
* @see org.eclipse.emf.common.command.Command#redo()
*/
public void redo() {
affectedObjects.clear();
pastedNames.clear();
INode owner = getOwner();
for (NodeCopier copier : copiedNodesForRedo) {
INode copy = NodeCopier.copyNode(copier.getCopy(), owner, IDesignerDataModel.AT_END, false);
if (copy != null) {
ModelUtils.getPropertyInformation(copy).refresh();
affectedObjects.add(copy);
pastedNames.add(copy.getName());
}
}
}
private INode getOwner() {
return (INode) ownerModel.findByNameProperty(ownerName);
}
/* (non-Javadoc)
* @see org.eclipse.emf.common.command.AbstractCommand#getAffectedObjects()
*/
public Collection getAffectedObjects() {
return affectedObjects;
}
}