/*******************************************************************************
* Copyright (c) 2013, 2015 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.emf.compare.rcp.ui.internal.structuremergeviewer.groups.provider;
import static com.google.common.collect.Iterators.any;
import static com.google.common.collect.Iterators.filter;
import static com.google.common.collect.Iterators.transform;
import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasState;
import com.google.common.base.Predicate;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.collect.UnmodifiableIterator;
import java.util.Collection;
import java.util.Iterator;
import org.eclipse.core.runtime.Assert;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.compare.Comparison;
import org.eclipse.emf.compare.Conflict;
import org.eclipse.emf.compare.ConflictKind;
import org.eclipse.emf.compare.Diff;
import org.eclipse.emf.compare.DifferenceState;
import org.eclipse.emf.compare.Match;
import org.eclipse.emf.compare.internal.EMFCompareEditMessages;
import org.eclipse.emf.compare.provider.ExtendedAdapterFactoryItemDelegator;
import org.eclipse.emf.compare.provider.IItemStyledLabelProvider;
import org.eclipse.emf.compare.provider.utils.ComposedStyledString;
import org.eclipse.emf.compare.provider.utils.IStyledString.IComposedStyledString;
import org.eclipse.emf.compare.provider.utils.IStyledString.Style;
import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.filters.StructureMergeViewerFilter;
import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.groups.IDifferenceGroup;
import org.eclipse.emf.compare.rcp.ui.structuremergeviewer.groups.IDifferenceGroupProvider;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.edit.provider.IItemColorProvider;
import org.eclipse.emf.edit.provider.IItemFontProvider;
import org.eclipse.emf.edit.tree.TreeNode;
import org.eclipse.emf.edit.tree.provider.TreeNodeItemProvider;
/**
* A specific implementation of {@link TreeNodeItemProvider}.
*
* @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
* @since 4.0
*/
public class TreeNodeItemProviderSpec extends TreeNodeItemProvider implements IItemStyledLabelProvider, IItemColorProvider, IItemFontProvider {
/** A map of IDifferenceGroupProvider, GroupItemProviderAdapter. */
private Multimap<IDifferenceGroupProvider, GroupItemProviderAdapter> groupItemProviderAdapters;
/** An instance of {@code StructureMergeViewerFilter}. */
private StructureMergeViewerFilter structureMergeViewerFilter;
/**
* This constructs an instance from a factory.
*
* @param adapterFactory
* the given factory
* @param structureMergeViewerFilter
* the given structure merge viewer filter
*/
public TreeNodeItemProviderSpec(AdapterFactory adapterFactory,
StructureMergeViewerFilter structureMergeViewerFilter) {
super(adapterFactory);
this.structureMergeViewerFilter = structureMergeViewerFilter;
itemDelegator = new ExtendedAdapterFactoryItemDelegator(getRootAdapterFactory());
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.edit.tree.provider.TreeNodeItemProvider#getParent(java.lang.Object)
*/
@Override
public Object getParent(Object object) {
Object parent = null;
TreeNode treeNode = (TreeNode)object;
TreeNode superParent = (TreeNode)super.getParent(object);
if (superParent == null) {
GroupItemProviderAdapter groupItemProvider = (GroupItemProviderAdapter)EcoreUtil
.getExistingAdapter(treeNode, GroupItemProviderAdapter.class);
return groupItemProvider;
} else if (object instanceof GroupItemProviderAdapter) {
parent = ((GroupItemProviderAdapter)object).getParent(null);
} else {
parent = superParent;
}
return parent;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.edit.provider.ItemProviderAdapter#getChildren(java.lang.Object)
*/
@Override
public Collection<?> getChildren(Object object) {
TreeNode treeNode = (TreeNode)object;
EObject data = treeNode.getData();
if (data instanceof Comparison) {
IDifferenceGroupProvider groupProvider = (IDifferenceGroupProvider)EcoreUtil
.getExistingAdapter(treeNode, IDifferenceGroupProvider.class);
Comparison comparison = (Comparison)data;
Collection<? extends IDifferenceGroup> groups = groupProvider.getGroups(comparison);
if (groups.isEmpty()) {
return ImmutableList.of();
} else if (groups.size() == 1) {
// do not display group if there is only one.
return groups.iterator().next().getChildren();
} else {
if (groupItemProviderAdapters == null) {
groupItemProviderAdapters = ArrayListMultimap.create();
initMapping(groups, groupProvider, treeNode);
return groupItemProviderAdapters.get(groupProvider);
} else {
Collection<GroupItemProviderAdapter> adapters = groupItemProviderAdapters
.get(groupProvider);
if (adapters.isEmpty()) {
initMapping(groups, groupProvider, treeNode);
}
return adapters;
}
}
} else {
return super.getChildren(object);
}
}
/**
* Init the mapping.
*
* @param groups
* the list of IDifferenceGroup to map with {@link GroupItemProviderAdapter}s.
* @param groupProvider
* the IDifferenceGroupProvider used to create a {@link GroupItemProviderAdapter}.
* @param treeNode
* the TreeNode used to create a {@link GroupItemProviderAdapter}.
*/
protected void initMapping(Collection<? extends IDifferenceGroup> groups,
IDifferenceGroupProvider groupProvider, TreeNode treeNode) {
for (IDifferenceGroup differenceGroup : groups) {
groupItemProviderAdapters.put(groupProvider,
new GroupItemProviderAdapter(adapterFactory, treeNode, differenceGroup));
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.compare.provider.IItemStyledLabelProvider#getStyledText(java.lang.Object)
*/
@Override
public IComposedStyledString getStyledText(Object object) {
TreeNode treeNode = (TreeNode)object;
EObject data = treeNode.getData();
ComposedStyledString styledString = new ComposedStyledString();
final IComposedStyledString treeNodeText;
if (data instanceof Match) {
Iterator<EObject> eAllContents = transform(treeNode.eAllContents(),
IDifferenceGroup.TREE_NODE_DATA);
Iterator<Diff> allDifferences = filter(eAllContents, Diff.class);
if (any(allDifferences, hasState(DifferenceState.UNRESOLVED))) {
styledString.append("> ", Style.DECORATIONS_STYLER); //$NON-NLS-1$
}
treeNodeText = ((ExtendedAdapterFactoryItemDelegator)itemDelegator)
.getStyledText(treeNode.getData());
} else if (data instanceof Conflict) {
treeNodeText = getTreeNodeText(treeNode, (Conflict)data);
} else {
treeNodeText = ((ExtendedAdapterFactoryItemDelegator)itemDelegator)
.getStyledText(treeNode.getData());
}
return styledString.append(treeNodeText);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.edit.provider.ItemProviderAdapter#getBackground(java.lang.Object)
*/
@Override
public Object getBackground(Object object) {
TreeNode treeNode = (TreeNode)object;
return itemDelegator.getBackground(treeNode.getData());
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.edit.provider.ItemProviderAdapter#getForeground(java.lang.Object)
*/
@Override
public Object getForeground(Object object) {
TreeNode treeNode = (TreeNode)object;
return itemDelegator.getForeground(treeNode.getData());
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.edit.provider.ItemProviderAdapter#getBackground(java.lang.Object, int)
*/
@Override
public Object getBackground(Object object, int columnIndex) {
TreeNode treeNode = (TreeNode)object;
return itemDelegator.getBackground(treeNode.getData(), columnIndex);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.edit.provider.ItemProviderAdapter#getForeground(java.lang.Object, int)
*/
@Override
public Object getForeground(Object object, int columnIndex) {
TreeNode treeNode = (TreeNode)object;
return itemDelegator.getForeground(treeNode.getData(), columnIndex);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.edit.provider.ItemProviderAdapter#getFont(java.lang.Object)
*/
@Override
public Object getFont(Object object) {
TreeNode treeNode = (TreeNode)object;
return itemDelegator.getFont(treeNode.getData());
}
@Override
public void dispose() {
super.dispose();
if (groupItemProviderAdapters != null) {
groupItemProviderAdapters.clear();
}
}
/**
* Due to filters (especially UML & Diagram Refined elements filters), refining & refined elements are
* store in the same conflicts. But refining & refined elements will never be displayed together. It leads
* to have a number of unresolved diffs that is confusing because it counts refining & refined unresolved
* diffs together. This method makes sure to only count visible unresolved nodes.
*
* @param conflict
* the conflict for which we want to get the text to display.
* @return the styled string.
*/
private IComposedStyledString getTreeNodeText(TreeNode treeNode, Conflict conflict) {
final ComposedStyledString ret = new ComposedStyledString();
Assert.isNotNull(structureMergeViewerFilter);
final Predicate<? super EObject> unfilteredNode = structureMergeViewerFilter.getAggregatedPredicate();
final UnmodifiableIterator<EObject> visibleNodes = Iterators.filter(treeNode.eAllContents(),
unfilteredNode);
final Iterator<EObject> eAllContents = transform(visibleNodes, IDifferenceGroup.TREE_NODE_DATA);
final Iterator<Diff> allDifferences = filter(eAllContents, Diff.class);
final Collection<Diff> d = Sets.newHashSet(allDifferences);
final int unresolvedDiffCount = Iterables
.size(Iterables.filter(d, hasState(DifferenceState.UNRESOLVED)));
if (unresolvedDiffCount > 0) {
ret.append("> ", Style.DECORATIONS_STYLER); //$NON-NLS-1$
}
if (conflict.getKind() == ConflictKind.PSEUDO) {
ret.append(EMFCompareEditMessages.getString("pseudoconflict")); //$NON-NLS-1$
} else {
ret.append(EMFCompareEditMessages.getString("conflict")); //$NON-NLS-1$
}
if (unresolvedDiffCount > 0) {
ret.append(
" [" //$NON-NLS-1$
+ EMFCompareEditMessages.getString("unresolved", //$NON-NLS-1$
Integer.valueOf(unresolvedDiffCount), Integer.valueOf(d.size()))
+ "]", //$NON-NLS-1$
Style.DECORATIONS_STYLER);
} else {
ret.append(" [" + EMFCompareEditMessages.getString("resolved") + "]", Style.DECORATIONS_STYLER); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
return ret;
}
}