/*******************************************************************************
* Copyright (c) 2008-2011 Chair for Applied Software Engineering,
* Technische Universitaet Muenchen.
* 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:
******************************************************************************/
package org.eclipse.emf.emfstore.server.core.subinterfaces;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.emfstore.common.model.ModelElementId;
import org.eclipse.emf.emfstore.server.EmfStoreController;
import org.eclipse.emf.emfstore.server.core.AbstractEmfstoreInterface;
import org.eclipse.emf.emfstore.server.core.AbstractSubEmfstoreInterface;
import org.eclipse.emf.emfstore.server.core.helper.HistoryCache;
import org.eclipse.emf.emfstore.server.exceptions.EmfStoreException;
import org.eclipse.emf.emfstore.server.exceptions.FatalEmfStoreException;
import org.eclipse.emf.emfstore.server.exceptions.InvalidInputException;
import org.eclipse.emf.emfstore.server.exceptions.StorageException;
import org.eclipse.emf.emfstore.server.model.ProjectId;
import org.eclipse.emf.emfstore.server.model.versioning.HistoryInfo;
import org.eclipse.emf.emfstore.server.model.versioning.HistoryQuery;
import org.eclipse.emf.emfstore.server.model.versioning.PrimaryVersionSpec;
import org.eclipse.emf.emfstore.server.model.versioning.TagVersionSpec;
import org.eclipse.emf.emfstore.server.model.versioning.Version;
import org.eclipse.emf.emfstore.server.model.versioning.VersionSpec;
import org.eclipse.emf.emfstore.server.model.versioning.VersioningFactory;
import org.eclipse.emf.emfstore.server.model.versioning.operations.AbstractOperation;
/**
* This subinterfaces implements all history related functionality for the
* EmfStoreImpl interface.
*
* @author wesendon
*/
public class HistorySubInterfaceImpl extends AbstractSubEmfstoreInterface {
/**
* Default constructor.
*
* @param parentInterface
* parent interface
* @throws FatalEmfStoreException
* in case of failure
*/
public HistorySubInterfaceImpl(AbstractEmfstoreInterface parentInterface)
throws FatalEmfStoreException {
super(parentInterface);
}
/**
* {@inheritDoc}
*/
public List<HistoryInfo> getHistoryInfo(ProjectId projectId,
HistoryQuery historyQuery) throws EmfStoreException {
synchronized (getMonitor()) {
// if modelelements are added to the query, only history infos which
// are related to these modelelements will
// be returned.
if (historyQuery.getModelElements().size() > 0) {
return getHistoryInfo(projectId,
historyQuery.getModelElements(),
historyQuery.isIncludeChangePackage());
} else {
List<HistoryInfo> result = getHistoryInfo(projectId,
historyQuery.getSource(), historyQuery.getTarget(),
historyQuery.isIncludeChangePackage());
if (historyQuery.getSource()
.compareTo(historyQuery.getTarget()) < 0) {
Collections.reverse(result);
}
return result;
}
}
}
/**
* {@inheritDoc}
*/
public void addTag(ProjectId projectId, PrimaryVersionSpec versionSpec,
TagVersionSpec tag) throws EmfStoreException {
synchronized (getMonitor()) {
Version version = getSubInterface(VersionSubInterfaceImpl.class)
.getVersion(projectId, versionSpec);
version.getTagSpecs().add(tag);
try {
save(version);
} catch (FatalEmfStoreException e) {
throw new StorageException(StorageException.NOSAVE);
}
}
}
/**
* {@inheritDoc}
*/
public void removeTag(ProjectId projectId, PrimaryVersionSpec versionSpec,
TagVersionSpec tag) throws EmfStoreException {
synchronized (getMonitor()) {
Version version = getSubInterface(VersionSubInterfaceImpl.class)
.getVersion(projectId, versionSpec);
Iterator<TagVersionSpec> iterator = version.getTagSpecs()
.iterator();
while (iterator.hasNext()) {
if (iterator.next().getName().equals(tag.getName())) {
iterator.remove();
}
}
try {
save(version);
} catch (FatalEmfStoreException e) {
throw new StorageException(StorageException.NOSAVE);
}
}
}
private List<HistoryInfo> getHistoryInfo(ProjectId projectId,
List<ModelElementId> moList, boolean includeChangePackage)
throws EmfStoreException {
HistoryCache historyCache = EmfStoreController.getInstance()
.getHistoryCache();
// TODO only the first modelelement is included in the request.
ModelElementId modelElementId = moList.get(0);
TreeSet<Version> elements = historyCache.getChangesForModelElement(
projectId, modelElementId);
ArrayList<Version> versions = new ArrayList<Version>(elements);
if (versions.size() == 0) {
return new ArrayList<HistoryInfo>();
}
// only the last 20 or less versions are considered
int historyCount = Math.min(versions.size(), 20);
List<HistoryInfo> historyInfos = getHistoryInfo(
versions.subList(0, historyCount), projectId,
includeChangePackage);
// filter operations to selected model element
for (HistoryInfo historyInfo : historyInfos) {
filterOperationsForSelectedME(modelElementId, historyInfo);
}
return historyInfos;
}
private void filterOperationsForSelectedME(ModelElementId modelElementId,
HistoryInfo historyInfo) {
if (historyInfo.getChangePackage() == null
|| historyInfo.getChangePackage().getOperations() == null) {
return;
}
Set<AbstractOperation> operationsToRemove = new HashSet<AbstractOperation>();
EList<AbstractOperation> operations = historyInfo.getChangePackage()
.getOperations();
for (AbstractOperation operation : operations) {
if (!operation.getAllInvolvedModelElements().contains(
modelElementId)) {
operationsToRemove.add(operation);
}
}
operations.removeAll(operationsToRemove);
}
private List<HistoryInfo> getHistoryInfo(ProjectId projectId,
PrimaryVersionSpec source, PrimaryVersionSpec target,
boolean includeChangePackage) throws EmfStoreException {
if (source == null || target == null) {
throw new InvalidInputException();
}
return getHistoryInfo(getSubInterface(VersionSubInterfaceImpl.class)
.getVersions(projectId, source, target), projectId,
includeChangePackage);
}
private List<HistoryInfo> getHistoryInfo(List<Version> versions,
ProjectId projectId, boolean includeChangePackage)
throws EmfStoreException {
List<HistoryInfo> result = new ArrayList<HistoryInfo>();
PrimaryVersionSpec headRevision = getSubInterface(
ProjectSubInterfaceImpl.class).getProject(projectId)
.getLastVersion().getPrimarySpec();
for (Version version : versions) {
HistoryInfo history = createHistoryInfo(headRevision, version,
includeChangePackage);
result.add(history);
}
return result;
}
/**
* Generates a history info from a version. If needed also adds the HEAD
* tag, which isn't persistent.
*
* @param headRevision
* head revision
* @param version
* version
* @param includeChangePackage
* @return history info
*/
private HistoryInfo createHistoryInfo(PrimaryVersionSpec headRevision,
Version version, boolean includeChangePackage) {
HistoryInfo history = VersioningFactory.eINSTANCE.createHistoryInfo();
if (includeChangePackage && version.getChanges() != null) {
history.setChangePackage(EcoreUtil.copy(version.getChanges()));
}
history.setLogMessage(EcoreUtil.copy(version.getLogMessage()));
history.setPrimerySpec(EcoreUtil.copy(version.getPrimarySpec()));
for (TagVersionSpec tagSpec : version.getTagSpecs()) {
history.getTagSpecs().add(EcoreUtil.copy(tagSpec));
}
// add HEAD tag to history info
if (version.getPrimarySpec().equals(headRevision)) {
TagVersionSpec spec = VersioningFactory.eINSTANCE
.createTagVersionSpec();
spec.setName(VersionSpec.HEAD);
history.getTagSpecs().add(spec);
}
return history;
}
}