blob: f9dd8ea348a33801a89cab41428b9da5e3209407 [file] [log] [blame]
/*******************************************************************************
* Copyright 2011 Chair for Applied Software Engineering,
* Technische Universitaet Muenchen.
* All rights reserved. This program and the accompanying materials
* are made available under the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
******************************************************************************/
package org.eclipse.emf.emfstore.client.model;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.emfstore.client.model.observers.CommitObserver;
import org.eclipse.emf.emfstore.client.model.observers.OperationObserver;
import org.eclipse.emf.emfstore.client.model.observers.ShareObserver;
import org.eclipse.emf.emfstore.common.model.ModelElementId;
import org.eclipse.emf.emfstore.server.model.versioning.ChangePackage;
import org.eclipse.emf.emfstore.server.model.versioning.PrimaryVersionSpec;
import org.eclipse.emf.emfstore.server.model.versioning.operations.AbstractOperation;
/**
* Caches all modified model elements.
*
* @author pfeifferc
*/
public class ModifiedModelElementsCache implements OperationObserver, CommitObserver, ShareObserver {
/**
* Contains the model elements that were changed, and a list of operations
* that need to be remembered for undone's.
*/
private Map<String, List<AbstractOperation>> modifiedModelElements;
/**
* Direct parents of model elements who were affected of changes.
*/
private Map<String, Integer> modifiedModelElementParents;
/**
* Maps child to parent elements to be able to retrieve hierarchies already
* deconstructed in the project space.
*/
private HashMap<String, String> childParentMapping;
/**
* The project space.
*/
private ProjectSpace projectSpace;
/**
* Constructor.
*
* @param projectSpace
* the project space
*/
public ModifiedModelElementsCache(ProjectSpace projectSpace) {
this.projectSpace = projectSpace;
modifiedModelElements = new HashMap<String, List<AbstractOperation>>();
modifiedModelElementParents = new HashMap<String, Integer>();
childParentMapping = new HashMap<String, String>();
}
/**
* Initialize the cache after restart.
*/
public void initializeCache() {
if (projectSpace != null && projectSpace.getOperations() != null) {
for (AbstractOperation abstractOperation : projectSpace.getOperations()) {
operationExecuted(abstractOperation);
}
}
}
/**
* If this model element has been modified.
*
* @param modelElementId
* model element id
* @return If this model element has been modified.
*/
public boolean isModelElementDirty(ModelElementId modelElementId) {
return modifiedModelElementParents.containsKey(modelElementId.getId())
|| modifiedModelElements.containsKey(modelElementId.getId());
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.client.model.observers.OperationObserver#operationExecuted(org.eclipse.emf.emfstore.server.model.versioning.operations.AbstractOperation)
*/
public void operationExecuted(AbstractOperation abstractOperation) {
// cache the model element and the operation, as well as the modified
// parents recursively
for (ModelElementId modelElementId : abstractOperation.getAllInvolvedModelElements()) {
if (modifiedModelElements.containsKey(modelElementId.getId())) {
modifiedModelElements.get(modelElementId.getId()).add(abstractOperation);
} else {
List<AbstractOperation> abstractOperations = new ArrayList<AbstractOperation>();
abstractOperations.add(abstractOperation);
modifiedModelElements.put(modelElementId.getId(), abstractOperations);
ModelElementId nextParentModelElementId = getNextParentModelElementId(modelElementId.getId());
if (nextParentModelElementId != null) {
childParentMapping.put(modelElementId.getId(), nextParentModelElementId.getId());
addOneToParent(nextParentModelElementId);
}
}
}
}
/**
* Removes one from the number of dirty children a parent has. Moves through
* the hierarchy recursively.
*
* @param parentModelElementId
*/
private void removeOneFromParent(String parentModelElementId) {
Integer number = modifiedModelElementParents.get(parentModelElementId);
if (number == null || number - 1 == 0) {
modifiedModelElementParents.remove(parentModelElementId);
if (!modifiedModelElements.containsKey(parentModelElementId)) {
ModelElementId nextParentModelElementId = getNextParentModelElementId(parentModelElementId);
if (nextParentModelElementId != null) {
removeOneFromParent(nextParentModelElementId.getId());
}
}
} else {
modifiedModelElementParents.put(parentModelElementId, number - 1);
}
}
private void addOneToParent(ModelElementId parentModelElementId) {
Integer number = modifiedModelElementParents.get(parentModelElementId.getId());
if (number == null || number < 1) {
number = 1;
if (!modifiedModelElements.containsKey(parentModelElementId.getId())) {
EObject nextParentModelElement = getModelElementForId(parentModelElementId.getId()).eContainer();
if (nextParentModelElement != null && nextParentModelElement != this.projectSpace.getProject()) {
addOneToParent(this.projectSpace.getProject().getModelElementId(nextParentModelElement));
}
}
} else {
number++;
}
modifiedModelElementParents.put(parentModelElementId.getId(), number);
}
/**
* @param childModelElementId
* the
* @return the model element id of the parent of the model element id passed
* as reference, or null if there is none
*/
private ModelElementId getNextParentModelElementId(String childModelElementId) {
EObject childModelElement = getModelElementForId(childModelElementId);
if (childModelElement == null) {
return null;
}
EObject nextParentModelElement = childModelElement.eContainer();
if (nextParentModelElement == null || nextParentModelElement == this.projectSpace.getProject()) {
return null;
} else {
return this.projectSpace.getProject().getModelElementId(nextParentModelElement);
}
}
/**
* @param modelElementId
* the
* @return the model element for the model element id passed as reference
*/
private EObject getModelElementForId(String modelElementIdString) {
ModelElementId modelElementId = org.eclipse.emf.emfstore.common.model.ModelFactory.eINSTANCE
.createModelElementId();
modelElementId.setId(modelElementIdString);
return projectSpace.getProject().getModelElement(modelElementId);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.client.model.observers.OperationObserver#operationUnDone(org.eclipse.emf.emfstore.server.model.versioning.operations.AbstractOperation)
*/
public void operationUnDone(AbstractOperation operation) {
// remove from cache
Set<ModelElementId> involvedMEs = operation.getAllInvolvedModelElements();
for (ModelElementId childModelElementId : involvedMEs) {
// update the model elements directly affected by the changes to the
// list of modified model elements
if (modifiedModelElements.containsKey(childModelElementId.getId())) {
modifiedModelElements.get(childModelElementId.getId()).remove(operation);
if (modifiedModelElements.get(childModelElementId.getId()).size() == 0) {
modifiedModelElements.remove(childModelElementId.getId());
if (childParentMapping.get(childModelElementId.getId()) != null) {
removeOneFromParent(childParentMapping.get(childModelElementId.getId()));
}
}
}
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.client.model.observers.CommitObserver#commitCompleted(org.eclipse.emf.emfstore.client.model.ProjectSpace,
* org.eclipse.emf.emfstore.server.model.versioning.PrimaryVersionSpec)
*/
public void commitCompleted(ProjectSpace projectSpace, PrimaryVersionSpec newRevision) {
// do the same as when project has been shared
shareDone(projectSpace);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.client.model.observers.CommitObserver#inspectChanges(org.eclipse.emf.emfstore.client.model.ProjectSpace,
* org.eclipse.emf.emfstore.server.model.versioning.ChangePackage)
*/
public boolean inspectChanges(ProjectSpace projectSpace, ChangePackage changePackage) {
return true;
}
/**
* {@inheritDoc}
*/
public void shareDone(ProjectSpace projectSpace) {
modifiedModelElementParents.clear();
modifiedModelElements.clear();
}
}