blob: 989f7d54b3fbc55adea5e7136d12620da7b55d21 [file] [log] [blame]
/*******************************************************************************
* 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:
* Edgar Mueller - initial API and implementation
******************************************************************************/
package org.eclipse.emf.emfstore.internal.client.ui.views.scm;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.emf.emfstore.internal.client.ui.views.changes.ChangePackageVisualizationHelper;
import org.eclipse.emf.emfstore.internal.common.model.ModelElementIdToEObjectMapping;
import org.eclipse.emf.emfstore.internal.server.model.versioning.AbstractChangePackage;
import org.eclipse.emf.emfstore.internal.server.model.versioning.ChangePackage;
import org.eclipse.emf.emfstore.internal.server.model.versioning.FileBasedChangePackage;
import org.eclipse.emf.emfstore.internal.server.model.versioning.HistoryInfo;
import org.eclipse.emf.emfstore.internal.server.model.versioning.LogMessage;
import org.eclipse.emf.emfstore.internal.server.model.versioning.OperationProxy;
import org.eclipse.emf.emfstore.internal.server.model.versioning.VersioningFactory;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.AbstractOperation;
import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.CompositeOperation;
import org.eclipse.emf.emfstore.server.ESCloseableIterable;
/**
* Content provider for the SCM views.
*
* @author emueller
*/
public class SCMContentProvider extends AdapterFactoryContentProvider {
private boolean showRootNodes = true;
private boolean reverseNodes = true;
private final Map<ChangePackage, VirtualNode<AbstractOperation>> changePackageToFilteredMapping;
private final Map<ChangePackage, List<Object>> changePackageToNonFilteredMapping;
private ModelElementIdToEObjectMapping idToEObjectMapping;
/**
* Default constructor.
*
*/
public SCMContentProvider() {
super(new ComposedAdapterFactory(
ComposedAdapterFactory.Descriptor.Registry.INSTANCE));
changePackageToFilteredMapping = new LinkedHashMap<ChangePackage, VirtualNode<AbstractOperation>>();
changePackageToNonFilteredMapping = new LinkedHashMap<ChangePackage, List<Object>>();
}
/**
* @param idToEObjectMapping
* a mapping from IDs to EObjects that is necessary to resolve
* deleted EObjects
*/
public SCMContentProvider(
ModelElementIdToEObjectMapping idToEObjectMapping) {
this();
this.idToEObjectMapping = idToEObjectMapping;
}
/**
* Sets the flag to reverse the order of the nodes. Default value is true -
* i.e. the more recent operations are on top.
*
* @param reverseNodes
* the new value
*/
public void setReverseNodes(boolean reverseNodes) {
this.reverseNodes = reverseNodes;
}
/**
* Returns if the nodes should be reversed.
*
* @return true if the nodes should be reversed in order
*/
public boolean isReverseNodes() {
return reverseNodes;
}
@SuppressWarnings("unchecked")
@Override
public Object[] getElements(Object object) {
if (object instanceof List<?> && showRootNodes) {
final List<?> list = (List<?>) object;
final List<Object> result = new ArrayList<Object>(list.size());
result.addAll(list);
return ((List<?>) result).toArray();
} else if (object instanceof List<?>) {
// valid inputs are a list of HistoryInfos,
// a list of operations as well as a list
// of ChangePackages
final List<?> list = (List<?>) object;
if (list.size() == 0) {
return list.toArray();
}
final List<Object> result = new ArrayList<Object>(list.size());
if (isListOf(list, HistoryInfo.class)) {
for (final HistoryInfo info : (List<HistoryInfo>) list) {
if (info.getChangePackage() != null) {
result.addAll(getReversedOperations(info
.getChangePackage()));
}
}
} else if (isListOf(list, AbstractOperation.class)) {
final FilteredOperationsResult filteredOpsResult = new FilterOperations(
idToEObjectMapping).filter(list.toArray());
result.addAll(filteredOpsResult.getNonFiltered());
if (filteredOpsResult.getFilteredOperations().size() > 0) {
final VirtualNode<AbstractOperation> node = new VirtualNode<AbstractOperation>(
filteredOpsResult.getFilteredOperations());
result.add(node);
}
} else {
for (final ChangePackage changePackage : (List<ChangePackage>) list) {
result.addAll(getReversedOperations(changePackage));
}
}
return result.toArray();
} else if (object instanceof EObject) {
return new Object[] { object };
}
return super.getElements(object);
}
private List<OperationProxy> getReversedOperations(AbstractChangePackage changePackage) {
final ESCloseableIterable<AbstractOperation> operations = changePackage.operations();
final List<OperationProxy> operationProxies = new ArrayList<OperationProxy>();
final ChangePackageVisualizationHelper changePackageVisualizationHelper = new ChangePackageVisualizationHelper(
idToEObjectMapping);
try {
final Iterable<AbstractOperation> operationIterable = operations.iterable();
for (final AbstractOperation abstractOperation : operationIterable) {
final OperationProxy operationProxy = VersioningFactory.eINSTANCE.createOperationProxy();
operationProxy.setLabel(changePackageVisualizationHelper.getDescription(abstractOperation));
operationProxies.add(0, operationProxy);
}
} finally {
operations.close();
}
return operationProxies;
}
private boolean isListOf(List<?> list, Class<? extends EObject> clazz) {
final Object firstElement = list.get(0);
return clazz.isInstance(firstElement);
}
private void filter(ChangePackage changePackage, Object[] input,
Class<? extends EObject> clazz) {
// check whether we already filtered this change package
if (changePackageHasBeenFiltered(changePackage)) {
return;
}
final FilteredOperationsResult result = new FilterOperations(
idToEObjectMapping, clazz).filter(input);
final VirtualNode<AbstractOperation> node = new VirtualNode<AbstractOperation>(
result.getFilteredOperations());
changePackageToNonFilteredMapping.put(changePackage,
result.getNonFiltered());
changePackageToFilteredMapping.put(changePackage, node);
}
private boolean changePackageHasBeenFiltered(AbstractChangePackage changePackage) {
return changePackageToNonFilteredMapping.containsKey(changePackage);
}
@Override
public boolean hasChildren(Object object) {
if (object instanceof FileBasedChangePackage) {
return true;
}
return getChildren(object).length > 0;
}
@Override
public Object[] getChildren(Object object) {
if (object instanceof OperationProxy) {
final OperationProxy proxy = OperationProxy.class.cast(object);
final FileBasedChangePackage changePackage = (FileBasedChangePackage) proxy.eContainer();
final AbstractOperation abstractOperation = changePackage.get(proxy.getIndex());
return getChildren(abstractOperation);
} else if (object instanceof HistoryInfo) {
final HistoryInfo historyInfo = (HistoryInfo) object;
return getChildren(historyInfo.getChangePackage());
} else if (object instanceof FileBasedChangePackage) {
final FileBasedChangePackage changePackage = (FileBasedChangePackage) object;
createOperationProxies(changePackage);
return changePackage.getOperationProxies().toArray();
} else if (object instanceof ChangePackage) {
final List<Object> result = new ArrayList<Object>();
final ChangePackage changePackage = (ChangePackage) object;
filter(changePackage, super.getChildren(object), LogMessage.class);
result.addAll(changePackageToNonFilteredMapping.get(changePackage));
final VirtualNode<AbstractOperation> node = changePackageToFilteredMapping
.get(changePackage);
if (node.getContent().size() > 0) {
result.add(node);
}
return result.toArray();
} else if (object instanceof VirtualNode<?>) {
return ((VirtualNode<?>) object).getContent().toArray();
} else if (object instanceof CompositeOperation) {
return ((CompositeOperation) object).getSubOperations().toArray();
}
return super.getChildren(object);
}
private void createOperationProxies(final FileBasedChangePackage changePackage) {
final ESCloseableIterable<AbstractOperation> operations = changePackage.operations();
int opIndex = 0;
final List<OperationProxy> updatedProxies = new ArrayList<OperationProxy>();
try {
for (final Iterator<AbstractOperation> iterator = operations.iterable().iterator(); iterator.hasNext();) {
final AbstractOperation operation = iterator.next();
final OperationProxy newProxy = createProxy(operation);
newProxy.setIndex(opIndex);
updatedProxies.add(newProxy);
opIndex += 1;
}
} finally {
operations.close();
}
changePackage.getOperationProxies().clear();
changePackage.getOperationProxies().addAll(updatedProxies);
}
private static OperationProxy createProxy(AbstractOperation operation) {
final OperationProxy newProxy = VersioningFactory.eINSTANCE.createOperationProxy();
if (CompositeOperation.class.isInstance(operation)) {
final CompositeOperation compositeOperation = (CompositeOperation) operation;
final List<AbstractOperation> leafOperations = compositeOperation.getSubOperations();
for (final AbstractOperation op : leafOperations) {
newProxy.getProxies().add(createProxy(op));
}
}
return newProxy;
}
/**
* Whether to show root nodes.
*
* @return true, if root nodes are shown, false otherwise
*/
public boolean isShowRootNodes() {
return showRootNodes;
}
/**
* Determines whether root nodes are shown.
*
* @param showRootNodes
* if true, root nodes will be shown
*/
public void setShowRootNodes(boolean showRootNodes) {
this.showRootNodes = showRootNodes;
}
}