/*******************************************************************************
* Copyright (c) 2012 Obeo.
* 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.docs.intent.modelingunit.update;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.mylyn.docs.intent.collab.common.logger.IIntentLogger.LogType;
import org.eclipse.mylyn.docs.intent.collab.common.logger.IntentLogger;
import org.eclipse.mylyn.docs.intent.collab.common.query.CompilationStatusQuery;
import org.eclipse.mylyn.docs.intent.collab.handlers.adapters.IntentCommand;
import org.eclipse.mylyn.docs.intent.collab.handlers.adapters.ReadOnlyException;
import org.eclipse.mylyn.docs.intent.collab.handlers.adapters.RepositoryAdapter;
import org.eclipse.mylyn.docs.intent.collab.handlers.adapters.SaveException;
import org.eclipse.mylyn.docs.intent.core.compiler.CompilationStatus;
import org.eclipse.mylyn.docs.intent.core.compiler.StructuralFeatureChangeStatus;
import org.eclipse.mylyn.docs.intent.core.document.IntentSection;
import org.eclipse.mylyn.docs.intent.core.document.UnitInstruction;
import org.eclipse.mylyn.docs.intent.core.document.descriptionunit.DescriptionBloc;
import org.eclipse.mylyn.docs.intent.core.document.descriptionunit.DescriptionUnit;
import org.eclipse.mylyn.docs.intent.core.document.descriptionunit.DescriptionUnitFactory;
import org.eclipse.mylyn.docs.intent.core.modelingunit.ContributionInstruction;
import org.eclipse.mylyn.docs.intent.core.modelingunit.InstanciationInstruction;
import org.eclipse.mylyn.docs.intent.core.modelingunit.ModelingUnit;
import org.eclipse.mylyn.docs.intent.core.modelingunit.ModelingUnitFactory;
import org.eclipse.mylyn.docs.intent.core.modelingunit.NewObjectValue;
import org.eclipse.mylyn.docs.intent.core.modelingunit.StructuralFeatureAffectation;
/**
* Utility which updates modeling units by merging elements into an existing model.
*
* @author <a href="mailto:william.piers@obeo.fr">William Piers</a>
*/
public class MergeUpdater extends AbstractModelingUnitUpdater {
/**
* The instanciations created during the process.
*/
private Map<EObject, InstanciationInstruction> newInstanciations;
/**
* Creates a modeling unit updater.
*
* @param repositoryAdapter
* the repository adapter
*/
public MergeUpdater(RepositoryAdapter repositoryAdapter) {
super(repositoryAdapter);
}
/**
* Creates instanciations for the given elements.
*
* @param section
* the section where to create a new modeling unit
* @param sibling
* the Intent element located right before the elements to create
* @param elements
* the elements to instanciate
*/
public void create(final IntentSection section, final EObject sibling, final List<EObject> elements) {
repositoryAdapter.execute(new IntentCommand() {
public void execute() {
ModelingUnit modelingUnit = ModelingUnitFactory.eINSTANCE.createModelingUnit();
EObject previousSibling = addModelingUnitInContainer(section, sibling, modelingUnit);
internalCreate(modelingUnit, previousSibling, elements);
try {
repositoryAdapter.save();
} catch (ReadOnlyException e) {
IntentLogger.getInstance().log(LogType.ERROR, e.getMessage());
} catch (SaveException e) {
IntentLogger.getInstance().log(LogType.ERROR, e.getMessage());
}
}
});
}
/**
* Creates instanciations for the given elements.
*
* @param modelingUnit
* the modeling unit where to create the instanciations
* @param sibling
* the Intent element located right before the elements to create
* @param elements
* the elements to instanciate
*/
public void create(final ModelingUnit modelingUnit, final EObject sibling, final List<EObject> elements) {
repositoryAdapter.execute(new IntentCommand() {
public void execute() {
internalCreate(modelingUnit, sibling, elements);
try {
repositoryAdapter.save();
} catch (ReadOnlyException e) {
IntentLogger.getInstance().log(LogType.ERROR, e.getMessage());
} catch (SaveException e) {
IntentLogger.getInstance().log(LogType.ERROR, e.getMessage());
}
}
});
}
/**
* Create a list of elements inside of the given modeling unit.
*
* @param modelingUnit
* the modeling unit
* @param sibling
* the Intent element located right before the elements to create
* @param elements
* the elements
*/
protected void internalCreate(final ModelingUnit modelingUnit, EObject sibling,
final List<EObject> elements) {
setNewObjects(getAllNewObjects(elements));
newInstanciations = new HashMap<EObject, InstanciationInstruction>();
for (EObject workingCopyObject : elements) {
Resource compiledResource = getMatchingCompiledResource(workingCopyObject);
includeMatch(compiledResource, workingCopyObject.eResource());
if (getExistingInstanciationFor(workingCopyObject) == null) {
InstanciationInstruction containerInstanciation = getExistingInstanciationFor(workingCopyObject
.eContainer());
if (containerInstanciation == null) {
// lookup in new instanciations
containerInstanciation = newInstanciations.get(workingCopyObject.eContainer());
}
if (containerInstanciation != null) {
ContributionInstruction contribution = generateContribution(containerInstanciation);
StructuralFeatureAffectation containment = generateSingleAffectation(
workingCopyObject.eContainingFeature(), workingCopyObject);
contribution.getContributions().add(containment);
modelingUnit.getInstructions().add(contribution);
newInstanciations.put(workingCopyObject,
((NewObjectValue)containment.getValues().get(0)).getValue());
} else {
InstanciationInstruction instanciation = generateInstanciation(workingCopyObject);
modelingUnit.getInstructions().add(instanciation);
newInstanciations.put(workingCopyObject, instanciation);
}
}
}
}
/**
* Gather all objects content.
*
* @param roots
* the root objects
* @return the objects and their content
*/
private List<EObject> getAllNewObjects(List<EObject> roots) {
List<EObject> res = new ArrayList<EObject>();
for (EObject root : roots) {
res.add(root);
res.addAll(getAllNewObjects(root.eContents()));
}
return res;
}
/**
* Finds a status associated to the given object.
*
* @param eObject
* the object
* @return a status associated to the given object
*/
private Resource getMatchingCompiledResource(EObject eObject) {
String uriFragment = EcoreUtil.getURI(eObject).toString();
CompilationStatusQuery query = new CompilationStatusQuery(repositoryAdapter);
for (CompilationStatus compilationStatus : query.getOrCreateCompilationStatusManager()
.getCompilationStatusList()) {
if (compilationStatus instanceof StructuralFeatureChangeStatus) {
StructuralFeatureChangeStatus status = (StructuralFeatureChangeStatus)compilationStatus;
if (uriFragment.equals(status.getWorkingCopyElementURIFragment())) {
return repositoryAdapter.getResource(status.getCompiledResourceURI());
}
}
}
return null;
}
/**
* Adds the given {@link ModelingUnit} to the given {@link IntentSection}, after the given sibling.
*
* @param section
* the section in which the {@link ModelingUnit} will be added
* @param sibling
* the slibing
* @param modelingUnit
* the {@link ModelingUnit} to add
* @return the sibling
*/
private EObject addModelingUnitInContainer(final IntentSection section, final EObject sibling,
ModelingUnit modelingUnit) {
// Splitting a description unit in several if dropping inside a description unit
EObject previousSibling = sibling;
DescriptionUnit rightUnit = null;
if (previousSibling instanceof DescriptionBloc) {
DescriptionUnit leftUnit = (DescriptionUnit)previousSibling.eContainer();
rightUnit = DescriptionUnitFactory.eINSTANCE.createDescriptionUnit();
Collection<UnitInstruction> descriptionInstructionsToMove = new LinkedHashSet<UnitInstruction>();
for (int i = leftUnit.getInstructions().indexOf(previousSibling) + 1; i < leftUnit
.getInstructions().size(); i++) {
descriptionInstructionsToMove.add(leftUnit.getInstructions().get(i));
}
leftUnit.getInstructions().removeAll(descriptionInstructionsToMove);
rightUnit.getInstructions().addAll(descriptionInstructionsToMove);
previousSibling = leftUnit;
}
int siblingIndex = section.getIntentContent().indexOf(previousSibling);
if (siblingIndex != -1) {
section.getIntentContent().add(siblingIndex + 1, modelingUnit);
if (rightUnit != null) {
section.getIntentContent().add(siblingIndex + 2, rightUnit);
}
} else {
section.getIntentContent().add(modelingUnit);
if (rightUnit != null) {
section.getIntentContent().add(1, rightUnit);
}
}
return previousSibling;
}
}