blob: 6d4e8e34bb3fa8261757fdc07ff9f32177ab8e2a [file] [log] [blame]
//------------------------------------------------------------------------------
// Copyright (c) 2005, 2006 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 Corporation - initial implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.library.ui.util;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.edit.command.RemoveCommand;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.epf.common.ui.util.MsgBox;
import org.eclipse.epf.common.utils.StrUtil;
import org.eclipse.epf.library.LibraryResources;
import org.eclipse.epf.library.LibraryService;
import org.eclipse.epf.library.LibraryServiceUtil;
import org.eclipse.epf.library.edit.LibraryEditResources;
import org.eclipse.epf.library.edit.command.IResourceAwareCommand;
import org.eclipse.epf.library.edit.command.NestedCommandExcecutor;
import org.eclipse.epf.library.edit.util.ExtensionManager;
import org.eclipse.epf.library.edit.util.IOppositeFeatureLoader;
import org.eclipse.epf.library.edit.util.Messenger;
import org.eclipse.epf.library.edit.util.TngUtil;
import org.eclipse.epf.library.edit.validation.IValidator;
import org.eclipse.epf.library.edit.validation.IValidatorFactory;
import org.eclipse.epf.library.ui.LibraryUIPlugin;
import org.eclipse.epf.library.util.ContentResourceScanner;
import org.eclipse.epf.library.util.LibraryUtil;
import org.eclipse.epf.library.util.ResourceHelper;
import org.eclipse.epf.persistence.util.PersistenceUtil;
import org.eclipse.epf.services.ILibraryPersister;
import org.eclipse.epf.services.Services;
import org.eclipse.epf.uma.Activity;
import org.eclipse.epf.uma.BreakdownElementDescription;
import org.eclipse.epf.uma.ContentDescription;
import org.eclipse.epf.uma.MethodElement;
import org.eclipse.epf.uma.NamedElement;
import org.eclipse.epf.uma.UmaFactory;
import org.eclipse.epf.uma.UmaPackage;
import org.eclipse.epf.uma.util.AssociationHelper;
import org.eclipse.epf.uma.util.ContentDescriptionFactory;
import org.eclipse.epf.uma.util.UmaUtil;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
/**
* @author Phong Nguyen Le
* @since 1.0
*/
public class TypeConverter {
private static final Comparator activityFeatureComparator = new Comparator() {
public int compare(Object arg0, Object arg1) {
if(arg0 == UmaPackage.Literals.BREAKDOWN_ELEMENT__SUPER_ACTIVITIES) {
return -1;
}
return 0;
}
};
/**
* Converts the specified object to a object of new type specified by <code>newType</code> while still
* preserving as many attributes and relationships as possible
*
* @param object
* @param newType
* @param oldFeatureToNewFeatureMap map of feature of <code>object</code> to feature of <code>newType</code>
* @return new object of type <code>newType</code>
*/
public static final EObject convert(EObject object, EClass newType,
Map oldFeatureToNewFeatureMap, Comparator featureComparator, Set modifiedResources)
{
TypeConversionCommand cmd = createTypeConversionCommand(object, newType, oldFeatureToNewFeatureMap, featureComparator);
Collection<Resource> resources = cmd.getModifiedResources();
if(!resources.isEmpty()) {
Resource[] resourceArray = new Resource[resources.size()];
resources.toArray(resourceArray);
IStatus status = Services.getAccessController().checkModify(resourceArray, ExtensionManager.getDefaultUserInteractionHandler().getUIContext());
if(!status.isOK()) {
Messenger.INSTANCE.showError(LibraryResources.convertActivityError_title, null, status);
return null;
}
}
try {
cmd.execute();
modifiedResources.addAll(cmd.getModifiedResources());
return (EObject) cmd.getResult().iterator().next();
}
finally {
cmd.dispose();
}
}
private static class ResourceCopyTask {
ContentResourceScanner scanner;
MethodElement owner;
String content;
String contentPath;
/**
* @param scanner
* @param owner
* @param content
* @param contentPath
*/
public ResourceCopyTask(ContentResourceScanner scanner, MethodElement owner, String content, String contentPath) {
super();
this.scanner = scanner;
this.owner = owner;
this.content = content;
this.contentPath = contentPath;
}
}
public static class TypeConversionCommand extends CompoundCommand implements IResourceAwareCommand {
private Collection<?> result;
private EObject object;
private HashSet<Resource> modifiedResources;
private boolean gotModifiedResources;
private List<ResourceCopyTask> resourceCopyTasks;
private Resource resource;
private Collection illegalReferencers;
private NestedCommandExcecutor nestedCommandExcecutor;
/**
* @param object
*/
public TypeConversionCommand(EObject object) {
this.object = object;
modifiedResources = new HashSet<Resource>();
resource = object.eResource();
if(resource != null) {
modifiedResources.add(resource);
}
resourceCopyTasks = new ArrayList<ResourceCopyTask>();
nestedCommandExcecutor = new NestedCommandExcecutor(this);
}
/* (non-Javadoc)
* @see org.eclipse.emf.common.command.CompoundCommand#dispose()
*/
public void dispose() {
if(illegalReferencers != null) {
illegalReferencers.clear();
}
if(modifiedResources != null) {
modifiedResources.clear();
}
if(resourceCopyTasks != null) {
resourceCopyTasks.clear();
}
if(nestedCommandExcecutor != null) {
nestedCommandExcecutor.dispose();
}
super.dispose();
}
/**
* Gets referencers that become illegal after the type conversion
*
* @return
*/
public Collection getIllegalReferencers() {
if(illegalReferencers == null) {
getModifiedResources();
}
return illegalReferencers;
}
public void copyResources() {
if(!resourceCopyTasks.isEmpty()) {
for (ResourceCopyTask t : resourceCopyTasks) {
File newResourcePath = new File(ResourceHelper.getAbsoluteElementResourcePath(t.owner));
t.scanner.setTargetRootPath(newResourcePath);
if(t.content != null) {
t.scanner.resolveResources(t.owner, t.content, t.contentPath);
}
else {
for (EAttribute attribute : t.owner.eClass().getEAllAttributes()) {
Object value = t.owner.eGet(attribute);
if(value instanceof String) {
t.scanner.resolveResources(t.owner, (String) value, t.contentPath);
}
}
}
}
}
}
/* (non-Javadoc)
* @see org.eclipse.epf.library.edit.command.IResourceAwareCommand#getModifiedResources()
*/
public Collection<Resource> getModifiedResources() {
if(!gotModifiedResources) {
illegalReferencers = new HashSet();
for (Iterator iter = commandList.iterator(); iter.hasNext();) {
Command cmd = (Command) iter.next();
if(cmd instanceof RemoveCommand) {
RemoveCommand removeCommand = (RemoveCommand) cmd;
EObject owner = removeCommand.getOwner();
Resource ownerResource = owner.eResource();
if(ownerResource != null && ownerResource != resource) {
modifiedResources.add(ownerResource);
}
if(ownerResource != null && owner != object && !UmaUtil.isContainedBy(owner, object)) {
illegalReferencers.add(owner);
}
}
else if(cmd instanceof SetCommand) {
SetCommand setCommand = (SetCommand) cmd;
EObject owner = setCommand.getOwner();
Resource ownerResource = owner.eResource();
if(ownerResource != null && ownerResource != resource && setCommand.getValue() instanceof EObject) {
EObject value = (EObject) setCommand.getValue();
if(value != object || !UmaUtil.isContainedBy(value, object)) {
modifiedResources.add(ownerResource);
}
}
if(ownerResource != null && owner != object && !UmaUtil.isContainedBy(owner, object)
&& setCommand.getValue() == setCommand.getFeature().getDefaultValue()) {
illegalReferencers.add(owner);
}
}
}
for (Object obj: illegalReferencers) {
if (obj instanceof MethodElement) {
Resource res = ((MethodElement) obj).eResource();
if (res != null) {
modifiedResources.add(res);
}
}
}
gotModifiedResources = true;
}
modifiedResources.addAll(nestedCommandExcecutor.getModifiedResources());
return modifiedResources;
}
private void setResult(Collection<?> result) {
this.result = result;
}
/* (non-Javadoc)
* @see org.eclipse.emf.common.command.CompoundCommand#getResult()
*/
public Collection<?> getResult() {
return result;
}
@Override
public void execute() {
super.execute();
nestedCommandExcecutor.executeNestedCommands();
}
@Override
public void undo() {
nestedCommandExcecutor.undoNestedCommands();
super.undo();
}
private List<Command> commandList() {
return commandList;
}
}
/**
*
* @param object EObject
* @param newType EClass
* @param oldFeatureToNewFeatureMap Map
* @param featureComparator Comparator
* @return TypeConversionCommand
*/
public static final TypeConversionCommand createTypeConversionCommand(EObject object, EClass newType,
Map oldFeatureToNewFeatureMap, Comparator featureComparator) {
return createTypeConversionCommand(object, newType, oldFeatureToNewFeatureMap, featureComparator, false, false);
}
/**
*
* @param object EObject
* @param newType EClass
* @param oldFeatureToNewFeatureMap Map
* @param featureComparator Comparator
* @param removeIncomingReferences boolean
* @param removeVariability boolean
* @return TypeConversionCommand
*/
public static final TypeConversionCommand createTypeConversionCommand(EObject object, EClass newType,
Map oldFeatureToNewFeatureMap, Comparator featureComparator,
boolean removeIncomingReferences, boolean removeVariability) {
TypeConversionCommand cmd = new TypeConversionCommand(object);
EObject newObject = prepareConvert(object, newType, oldFeatureToNewFeatureMap, featureComparator, removeIncomingReferences, removeVariability, cmd);
// replace object with newObject in the object's container
//
EObject container = object.eContainer();
if(container != null) {
EReference ref = object.eContainmentFeature();
if(ref.isMany()) {
List list = (List) container.eGet(ref);
int index = list.indexOf(object);
cmd.commandList().add(0, new SetCommand(null, container, ref, newObject, index));
}
else {
cmd.commandList().add(0, new SetCommand(null, container, ref, newObject));
}
}
cmd.setResult(Collections.singletonList(newObject));
return cmd;
}
private static ContentResourceScanner createScanner(EObject object) {
if(object instanceof ContentDescription && !(object instanceof BreakdownElementDescription)) {
File oldResourcePath = new File(ResourceHelper.getFolderAbsolutePath((MethodElement) object));
String rootContentpath = ResourceHelper.getElementPath((MethodElement)object);
ContentResourceScanner scanner = new ContentResourceScanner(oldResourcePath, null, rootContentpath, null);
return scanner;
}
return null;
}
/**
* Prepares a compound command to do a type conversion
*
* @param object
* @param newType
* @param oldFeatureToNewFeatureMap
* @param featureComparator used to sort the feature list of object to convert before copying/moving
* feature values to new object.
* @param removeIncomingReferences if true will remove all incoming references
* @param compoundCommand
* @return EObject
*/
public static final EObject prepareConvert(EObject object, EClass newType, Map oldFeatureToNewFeatureMap,
Comparator featureComparator, boolean removeIncomingReferences, boolean removeVariability,
TypeConversionCommand compoundCommand) {
return prepareConvert(object, newType, oldFeatureToNewFeatureMap,
featureComparator, removeIncomingReferences, removeVariability,
compoundCommand, 0);
}
private static final EObject prepareConvert(EObject object, EClass newType, Map oldFeatureToNewFeatureMap,
Comparator featureComparator, boolean removeIncomingReferences, boolean removeVariability,
TypeConversionCommand compoundCommand, int level) {
EObject newObject = UmaFactory.eINSTANCE.create(newType);
if(object instanceof MethodElement) {
// Handle incoming references.
// If removeIncommingReferences is false, redirect all incoming references of object to newObject
// If removeIncommingReferences is true, remove all of them.
//
MethodElement me = (MethodElement) object;
Collection oppositeFeatures = me.getOppositeFeatures();
if(!oppositeFeatures.isEmpty()) {
// load opposite features
//
Collection collection = Collections.singletonList(object);
for (Iterator iter = ExtensionManager.getOppositeFeatureLoaders().iterator(); iter
.hasNext();) {
IOppositeFeatureLoader loader = (IOppositeFeatureLoader) iter.next();
loader.loadOppositeFeatures(collection);
}
if (level == 0) {
LibraryUtil.loadAllSkipContents(LibraryService.getInstance().getCurrentMethodLibrary());
}
Map referencerToFeatureListMap = AssociationHelper.getReferenceMap(me);
for (Iterator iter = referencerToFeatureListMap.entrySet().iterator(); iter
.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
EObject referencer = (EObject) entry.getKey();
for (Iterator iterator = ((Collection)entry.getValue()).iterator(); iterator
.hasNext();) {
EReference feature = (EReference) iterator.next();
if(!removeIncomingReferences && feature.getEType().isInstance(newObject)) {
// new type is compatible, reference can be preserved
//
if(feature.isMany()) {
List list = (List) referencer.eGet(feature);
int index = list.indexOf(object);
compoundCommand.append(new SetCommand(null, referencer, feature, newObject, index));
}
else {
compoundCommand.append(new SetCommand(null, referencer, feature, newObject));
}
}
else {
// new type is not compatible, reference must be removed
//
if(feature.isMany()) {
compoundCommand.append(new RemoveCommand(null, referencer, feature, object));
}
else {
compoundCommand.append(new SetCommand(null, referencer, feature, feature.getDefaultValue()));
}
}
}
}
}
}
// copy all the features
//
List newObjectFeatures = newType.getEAllStructuralFeatures();
List objectFeatures = new ArrayList(object.eClass().getEAllStructuralFeatures());
if(removeVariability) {
objectFeatures.remove(UmaPackage.Literals.VARIABILITY_ELEMENT__VARIABILITY_BASED_ON_ELEMENT);
objectFeatures.remove(UmaPackage.Literals.VARIABILITY_ELEMENT__VARIABILITY_TYPE);
}
if(featureComparator != null) {
Collections.sort(objectFeatures, featureComparator);
}
ContentResourceScanner scanner = createScanner(object);
for (Iterator iter = objectFeatures.iterator(); iter.hasNext();) {
EStructuralFeature feature = (EStructuralFeature) iter.next();
if(!newObjectFeatures.contains(feature)) {
if(oldFeatureToNewFeatureMap != null) {
Object f = oldFeatureToNewFeatureMap.get(feature);
if(f != null && newObjectFeatures.contains(f)) {
feature = (EStructuralFeature) f;
}
else {
feature = null;
}
}
else {
feature = null;
}
}
if(feature != null && feature.isChangeable()) {
boolean featureHandled = false;
if(feature instanceof EReference) {
EReference ref = (EReference) feature;
if(ref == UmaPackage.Literals.DESCRIBABLE_ELEMENT__PRESENTATION) {
if(ContentDescriptionFactory.hasPresentation((MethodElement) object)) {
ContentDescription content = (ContentDescription) object.eGet(ref);
EClass contentDescType = ContentDescriptionFactory.getContentDescriptionType(newType);
if(contentDescType != null && contentDescType != content.eClass()) {
EObject newContent = prepareConvert(content, contentDescType, null, null, removeIncomingReferences, removeVariability, compoundCommand, level + 1);
// remove old content
//
EObject container = content.eContainer();
if(container != null) {
compoundCommand.append(new SetCommand(null, object, ref, null));
Resource resource = content.eResource();
if(resource != null) {
compoundCommand.modifiedResources.add(content.eResource());
}
}
content = (ContentDescription) newContent;
}
else {
compoundCommand.resourceCopyTasks.add(new ResourceCopyTask(createScanner(content), content, null, "")); //$NON-NLS-1$
}
compoundCommand.append(new SetCommand(null, newObject, ref, content));
}
featureHandled = true;
}
else if(!ref.isContainment() && ref.getEOpposite() != null) {
// Reference is bi-directional. If opposite reference is a list, replace object
// with new object at the same position in the list.
//
EReference oppositeRef = ref.getEOpposite();
if(oppositeRef.isMany()) {
Object value = object.eGet(ref);
if(value != null) {
if(ref.isMany()) {
for (Iterator iterator = ((Collection)value)
.iterator(); iterator.hasNext();) {
EObject otherEnd = (EObject) iterator.next();
List list = (List) otherEnd.eGet(oppositeRef);
int index = list.indexOf(object);
compoundCommand.append(new SetCommand(null, otherEnd, oppositeRef, newObject, index));
}
}
else {
EObject otherEnd = (EObject) value;
List list = (List) otherEnd.eGet(oppositeRef);
int index = list.indexOf(object);
compoundCommand.append(new SetCommand(null, otherEnd, oppositeRef, newObject, index)); }
}
featureHandled = true;
}
}
}
if(!featureHandled) {
Object value = object.eGet(feature);
if (feature == UmaPackage.Literals.NAMED_ELEMENT__NAME) {
// validate name
String newName = checkName(object, newType);
if (newName != null) {
value = newName;
}
}
if(scanner != null && value instanceof String && object instanceof MethodElement) {
String str = (String) value;
if(!StrUtil.isBlank(str)) {
compoundCommand.resourceCopyTasks.add(new ResourceCopyTask(scanner, (MethodElement)object, str, "")); //$NON-NLS-1$
}
}
compoundCommand.append(new SetCommand(null, newObject, feature, value));
}
}
}
return newObject;
}
/**
* Converts an activity to new type.
*
* @param oldActivity Activity
* @param newType EClass
* @return Activity
*/
public static Activity convertActivity(Activity oldActivity, EClass newType) {
// activity is already this type
//
if (newType == oldActivity.eClass()) {
return null;
}
HashSet<Resource> modifiedResources = new HashSet<Resource>();
Activity newActivity = (Activity) TypeConverter.convert(oldActivity, newType, null, activityFeatureComparator, modifiedResources);
// make oldActivity a proxy so reference to it can be resolved to newActivity
//
URI uri = PersistenceUtil.getProxyURI(newActivity);
((InternalEObject)oldActivity).eSetProxyURI(uri);
// save modified resources
//
if (!modifiedResources.isEmpty()) {
ILibraryPersister.FailSafeMethodLibraryPersister persister = LibraryServiceUtil.getCurrentPersister().getFailSafePersister();
try {
for (Resource res : modifiedResources) {
persister.save(res);
}
persister.commit();
} catch (Exception e) {
LibraryUIPlugin
.getDefault()
.getMsgDialog()
.displayError(
LibraryResources.convertActivityError_title,
NLS
.bind(
LibraryResources.saveConvertedActivityError_msg,
newActivity.getName()),
LibraryResources.error_reason, e);
persister.rollback();
}
}
return newActivity;
}
/**
*
* @param typeID int
* @return EClass
*/
public static EClass getActivityType(int typeID) {
switch (typeID) {
case UmaPackage.ACTIVITY:
return UmaPackage.Literals.ACTIVITY;
case UmaPackage.ITERATION:
return UmaPackage.Literals.ITERATION;
case UmaPackage.PHASE:
return UmaPackage.Literals.PHASE;
default:
return null;
}
}
/**
* @param oldGuidance
* @param newGuidance
* @return
*/
private static String checkName(EObject object, EClass newType) {
// TODO Auto-generated method stub
if (object instanceof NamedElement) {
NamedElement e = (NamedElement)object;
final IValidator validator = IValidatorFactory.INSTANCE.createNameValidator(e.eContainer(), e, newType);
String msg = validator.isValid(e.getName());
if(msg != null) {
String featureTxt = TngUtil.getFeatureText(UmaPackage.eINSTANCE
.getNamedElement_Name());
String title = LibraryEditResources.resolveNameConflictDialog_title;
String dlgMsg = NLS.bind(
LibraryEditResources.resolveNameConflictDialog_text,
StrUtil.toLower(featureTxt), e.getName());
String currentValue = (String) e.eGet(UmaPackage.eINSTANCE
.getNamedElement_Name());
IInputValidator inputValidator = new IInputValidator() {
public String isValid(String newText) {
return validator.isValid(newText);
}
};
InputDialog inputDlg = new InputDialog(
MsgBox.getDefaultShell(), title, dlgMsg, currentValue,
inputValidator);
if (inputDlg.open() == Window.CANCEL) {
throw new OperationCanceledException();
}
return inputDlg.getValue();
}
}
return null;
}
}