blob: e05b1e66264ed9bde4ab38b741fc45697fd3a5cd [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 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.util;
import static com.google.common.collect.Iterables.filter;
import static java.util.Collections.emptyList;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.Map.Entry;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.compare.Comparison;
import org.eclipse.emf.compare.Match;
import org.eclipse.emf.compare.graph.IGraphView;
import org.eclipse.emf.compare.match.impl.NotLoadedFragmentMatch;
import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin;
import org.eclipse.emf.compare.rcp.ui.internal.mergeviewer.item.impl.MergeViewerItem;
import org.eclipse.emf.compare.rcp.ui.mergeviewer.IMergeViewer.MergeViewerSide;
import org.eclipse.emf.compare.rcp.ui.mergeviewer.item.IMergeViewerItem;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.edit.tree.TreeNode;
/**
* This class will be used to provide various utilities aimed at NotLoadedFragment manipulation.
*
* @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a>
*/
public class ResourceUIUtil {
/** ID of the graph of EMF resources used by EMFCompare to compute the logical model. */
public static final String RESOURCES_GRAPH_ID = "org.eclipse.emf.compare.resources.graph"; //$NON-NLS-1$
/**
* Function that retrieve the data of the given TreeNode.
*/
private static Function<EObject, EObject> TREE_NODE_DATA = new Function<EObject, EObject>() {
public EObject apply(EObject node) {
final EObject data;
if (node instanceof TreeNode) {
data = ((TreeNode)node).getData();
} else {
data = node;
}
return data;
}
};
/**
* Get the graph of resources' URI for the models involved in the current comparison.
*
* @return the graph if it exists, <code>null</code> otherwise.
*/
public static IGraphView<URI> getResourcesURIGraph() {
return EMFCompareRCPPlugin.getDefault().getGraphView(RESOURCES_GRAPH_ID);
}
/**
* Check if the given URI correspond to the root resource of a model. In this case a root resource is a
* piece of a whole model that is not a fragment of the model.
*
* @param uri
* the given URI.
* @return <code>true</code> if the given URI is root resource of a model, <code>false</code> otherwise.
*/
public static boolean isRootResource(URI uri) {
return !isFragment(uri);
}
/**
* Check if the given URI correspond to a fragment of model. In this case a fragment is a piece of a whole
* model that is not the root resource of the model.
*
* @param uri
* the given URI.
* @return <code>true</code> if the given URI is a fragment of a model, <code>false</code> otherwise.
*/
public static boolean isFragment(URI uri) {
final IGraphView<URI> graph = getResourcesURIGraph();
if (uri != null && graph != null) {
URI parentData = graph.getParentData(uri);
if (parentData != null) {
return true;
}
}
return false;
}
/**
* Check if the given match is a root match of its comparison model and is a fragment. In this case a
* fragment is a piece of a whole model that is not the root resource of the model.
*
* @param rootMatch
* the given match.
* @param side
* the side for which we want to know if it is a fragment or not.
* @return <code>true</code> if the given match is a root match of its comparison model and is a fragment,
* <code>false</code> otherwise.
*/
public static boolean isFragment(Match rootMatch, MergeViewerSide side) {
if (rootMatch != null && rootMatch.eContainer() instanceof Comparison) {
URI uri = getDataURI(rootMatch, side);
return isFragment(uri);
}
return false;
}
/**
* Check if the given URI corresponds to a fragment of model that is at the first level of the model, in
* other words a fragment that is directly under a root resource. In this case a fragment is a piece of a
* whole model that is not the root resource of the model.
* <p>
* If the given fragment (represented by the given URI) has several parents, this method will return
* <code>false</code>.
* </p>
*
* @param uri
* the given URI.
* @return <code>true</code> if the given URI is a fragment of a model, <code>false</code> otherwise.
*/
public static boolean isFirstLevelFragment(URI uri) {
final IGraphView<URI> graph = getResourcesURIGraph();
if (uri != null && graph != null) {
URI parentData = graph.getParentData(uri);
if (parentData != null) {
URI parent = parentData.trimFragment();
return !isFragment(parent);
}
}
return false;
}
/**
* Get the root resource of the whole model that contains the given fragment (represented by its uri).
* <p>
* If at some point of the process a fragment has several parents, this method will return
* <code>null</code>.
* </p>
*
* @param uri
* the given URI.
* @return the root resource of the whole model that contains the given fragment if found,
* <code>null</code> otherwise.
*/
public static URI getRootResourceURI(URI uri) {
final URI uriRoot;
final IGraphView<URI> graph = getResourcesURIGraph();
if (uri != null && graph != null) {
URI parentData = graph.getParentData(uri);
if (parentData == null) {
uriRoot = uri;
} else {
URI parent = parentData.trimFragment();
uriRoot = getRootResourceURI(parent);
}
} else {
uriRoot = null;
}
return uriRoot;
}
/**
* Get the first loaded parent resource URI of the given resource (represented by its URI) contained in
* the given ResourceSet.
* <p>
* If at some point of the process the current resource (represented by its URI) has several parents, this
* method will return <code>null</code>.
*
* @param rs
* the ResourceSet in which the first loaded parent must be found.
* @param uri
* the URI of the resource for which we want to get its first loaded parent.
* @return the URI of the first loaded resource parent if found, <code>null</code> otherwise.
*/
public static URI getParentResourceURI(ResourceSet rs, URI uri) {
final URI parentURI;
final Entry<URI, Resource> entry = getResourceParent(rs, uri);
if (entry != null) {
final Resource resource = entry.getValue();
if (resource != null) {
parentURI = entry.getKey().trimFragment();
} else {
parentURI = null;
}
} else {
parentURI = null;
}
return parentURI;
}
/**
* Get the parent of the given resource (represented by its URI) contained in the given ResourceSet.
* <p>
* If the given resource (represented by its URI) has several parents, this method will return
* <code>null</code>.
* </p>
*
* @param rs
* the ResourceSet in which the parent must be found.
* @param uri
* the URI of the resource for which we want to get its parent.
* @return the parent of the given resource (represented by its URI) if found, <code>null</code>
* otherwise.
*/
public static Resource getParent(ResourceSet rs, URI uri) {
final Resource resource;
final IGraphView<URI> graph = getResourcesURIGraph();
if (uri != null && graph != null) {
URI parentData = graph.getParentData(uri);
if (parentData != null) {
resource = rs.getResource(parentData.trimFragment(), false);
} else {
resource = null;
}
} else {
resource = null;
}
return resource;
}
/**
* Get the first loaded EObject parent of the given resource (represented by its URI) contained in the
* given ResourceSet.
* <p>
* If at some point of the process the current resource (represented by its URI) has several parents, this
* method will return <code>null</code>.
*
* @param rs
* the ResourceSet in which the first loaded parent must be found.
* @param uri
* the URI of the resource for which we want to get its first loaded parent.
* @return the first loaded EObject parent of the given resource (represented by its URI) if found,
* <code>null</code> otherwise.
*/
public static EObject getEObjectParent(ResourceSet rs, URI uri) {
final EObject eObject;
final Entry<URI, Resource> entry = getResourceParent(rs, uri);
if (entry != null) {
final Resource resource = entry.getValue();
if (resource != null) {
eObject = resource.getEObject(entry.getKey().fragment());
} else {
eObject = null;
}
} else {
eObject = null;
}
return eObject;
}
/**
* Get the first loaded parent of the given resource (represented by its URI) contained in the given
* ResourceSet.
* <p>
* If at some point of the process the current resource (represented by its URI) has several parents, this
* method will return <code>null</code>.
*
* @param rs
* the ResourceSet in which the first loaded parent must be found.
* @param uri
* the URI of the resource for which we want to get its first loaded parent.
* @return an entry composed with the first loaded EObject parent of the given resource (represented by
* its URI) and the Resource associated if found, <code>null</code> otherwise.
*/
private static Entry<URI, Resource> getResourceParent(ResourceSet rs, URI uri) {
Entry<URI, Resource> entry = null;
final IGraphView<URI> graph = getResourcesURIGraph();
if (uri != null && graph != null) {
URI parentData = graph.getParentData(uri);
if (parentData != null) {
URI parent = parentData.trimFragment();
Resource resourceParent = rs.getResource(parent, false);
if (resourceParent != null) {
entry = Maps.immutableEntry(parentData, resourceParent);
} else {
entry = getResourceParent(rs, parent);
}
}
}
return entry;
}
/**
* Search from the given list of TreeNodes (and recursively on its children), the one that is associated
* to the given Match.
*
* @param nodes
* the given list of TreeNodes.
* @param match
* the given Match.
* @return the TreeNode that is associated to the given Match.
*/
public static TreeNode getTreeNode(Collection<TreeNode> nodes, Match match) {
for (TreeNode treeNode : nodes) {
EObject data = TREE_NODE_DATA.apply(treeNode);
if (data.equals(match)) {
return treeNode;
}
}
for (TreeNode treeNode : nodes) {
TreeNode treeNode2 = getTreeNode(treeNode.getChildren(), match);
if (treeNode2 != null) {
return treeNode2;
}
}
return null;
}
/**
* Get from the given list of TreeNodes, the one that has its data's resource's URI (TreeNode -> Match ->
* EObject -> Resource -> URI) corresponding to the given URI.
*
* @param nodes
* the given list of TreeNodes.
* @param uri
* the given URI.
* @return the TreeNode that has its data's resource's URI corresponding to the given URI, or null if no
* one match.
*/
public static TreeNode getTreeNodeFromURI(Collection<TreeNode> nodes, URI uri) {
for (TreeNode treeNode : nodes) {
EObject data = TREE_NODE_DATA.apply(treeNode);
URI dataURI = getDataURI((Match)data);
if (uri.equals(dataURI)) {
return treeNode;
}
}
return null;
}
/**
* Get the Resource's URI of the data associated to the given Match
*
* @param match
* the given Match.
* @return the Resource's URI of the data associated to the given Match.
*/
public static URI getDataURI(Match match) {
final URI uri;
final Resource resource;
if (match.getLeft() != null) {
resource = match.getLeft().eResource();
} else if (match.getRight() != null) {
resource = match.getRight().eResource();
} else if (match.getOrigin() != null) {
resource = match.getOrigin().eResource();
} else {
resource = null;
}
if (resource != null) {
uri = resource.getURI();
} else {
uri = null;
}
return uri;
}
/**
* Get the Resource's URIs of the data associated to the given list of Matches.
*
* @param matches
* the given list of Matches.
* @param side
* the given side of the comparison.
* @return the Resource's URIs of the data associated to the given list of Matches.
*/
public static Collection<URI> getDataURIs(Collection<Match> matches, MergeViewerSide side) {
final Collection<URI> uris = Lists.newArrayList();
for (Match match : matches) {
URI dataURI = getDataURI(match, side);
if (dataURI != null) {
uris.add(dataURI);
}
}
return uris;
}
/**
* Get the Resource's URI of the data associated to the given Match, and for the given side of the
* comparison. .
*
* @param match
* the given Match.
* @param side
* the given side of the comparison.
* @return the Resource's URI of the data associated to the given Match.
*/
public static URI getDataURI(Match match, MergeViewerSide side) {
final URI uri;
final Resource resource;
if (MergeViewerSide.LEFT == side && match.getLeft() != null) {
resource = match.getLeft().eResource();
} else if (MergeViewerSide.RIGHT == side && match.getRight() != null) {
resource = match.getRight().eResource();
} else if (MergeViewerSide.ANCESTOR == side && match.getOrigin() != null) {
resource = match.getOrigin().eResource();
} else {
resource = null;
}
if (resource != null) {
uri = resource.getURI();
} else {
uri = null;
}
return uri;
}
/**
* Get the Resource's ResourceSet of the data associated to the given Match.
*
* @param match
* the given Match.
* @return the Resource's ResourceSet of the data associated to the given Match.
*/
public static ResourceSet getDataResourceSet(Match match) {
final ResourceSet rs;
final Resource resource;
if (match.getLeft() != null) {
resource = match.getLeft().eResource();
} else if (match.getRight() != null) {
resource = match.getRight().eResource();
} else if (match.getOrigin() != null) {
resource = match.getOrigin().eResource();
} else {
resource = null;
}
if (resource != null) {
rs = resource.getResourceSet();
} else {
rs = null;
}
return rs;
}
/**
* Get the Resource's ResourceSet of the data associated to the given Match, and for the given side of the
* comparison.
*
* @param match
* the given Match.
* @param side
* the given side of the comparison.
* @return the Resource's ResourceSet of the data associated to the given Match.
*/
public static ResourceSet getDataResourceSet(Match match, MergeViewerSide side) {
final ResourceSet rs;
final Resource resource;
if (MergeViewerSide.LEFT == side && match != null && match.getLeft() != null) {
resource = match.getLeft().eResource();
} else if (MergeViewerSide.RIGHT == side && match != null && match.getRight() != null) {
resource = match.getRight().eResource();
} else if (MergeViewerSide.ANCESTOR == side && match != null && match.getOrigin() != null) {
resource = match.getOrigin().eResource();
} else {
resource = null;
}
if (resource != null) {
rs = resource.getResourceSet();
} else {
rs = null;
}
return rs;
}
/**
* Check if the given list of TreeNodes contains at least two nodes that have NotLoadedFragmentMatch for
* data.
*
* @param nodes
* the given list of TreeNodes.
* @return <code>true</code> if the given list of TreeNodes contains at least two nodes that have
* NotLoadedFragmentMatch for data, false otherwise.
*/
public static boolean containsNotLoadedFragmentNodes(Collection<TreeNode> nodes) {
int notLoadedFragments = 0;
for (TreeNode node : nodes) {
EObject data = TREE_NODE_DATA.apply(node);
if (data instanceof NotLoadedFragmentMatch) {
notLoadedFragments++;
}
}
return notLoadedFragments > 1;
}
/**
* Get from the given list of {@link IMergeViewerItem}s, the NotLoadedFragmentMatchs.
*
* @param items
* the given list of IMergeViewerItems.
* @return a list of Match.
*/
public static Collection<Match> getNotLoadedFragmentMatches(Collection<IMergeViewerItem> items) {
final Collection<Match> notLoadedFragmentMatches = Lists.newArrayList();
for (IMergeViewerItem item : items) {
// If an IMergeViewerItem contains NotLoadedFragmentMatch, this is the same NotLoadedFragmentMatch
// on left, right and ancestor sides.
Object left = item.getLeft();
if (left instanceof NotLoadedFragmentMatch) {
notLoadedFragmentMatches.add((NotLoadedFragmentMatch)left);
}
}
return notLoadedFragmentMatches;
}
/**
* Get the resource's name associated with the data of the given NotLoadedFragmentMatch. If it is a
* NotLoadedFragmentMatch containing others NotLoadedFragmentMatch, then it returns an empty string.
*
* @param match
* the given NotLoadedFragmentMatch.
* @return the resource's name associated with the data of the given NotLoadedFragmentMatch.
*/
public static String getResourceName(NotLoadedFragmentMatch match) {
final String name;
Collection<? extends Match> children = match.getChildren();
if (Iterables.size(children) == 1) {
URI uri = getDataURI(children.iterator().next());
name = uri.lastSegment();
} else {
name = ""; //$NON-NLS-1$
}
return name;
}
/**
* Filters, from the root matches of the given comparison, those who will children matches of the given
* match if all fragments of the whole models involved in comparison had been loaded, for the given side
* of the comparison.
*
* @param comparison
* the given comparison, cannot be <code>null</code>.
* @param match
* the given match, can be <code>null</code>.
* @param side
* the given side of the comparison.
* @return a list of Matches.
*/
public static Collection<Match> getChildrenMatchWithNotLoadedParent(Comparison comparison, Match match,
MergeViewerSide side) {
if (match == null) {
return emptyList();
}
final Collection<Match> childrenMatches = Sets.newLinkedHashSet();
final Collection<Match> matches = comparison.getMatches();
final IGraphView<URI> graph = getResourcesURIGraph();
if (graph == null) {
return childrenMatches;
}
ResourceSet rs = getDataResourceSet(match, side);
if (rs == null) {
return childrenMatches;
}
for (Match rootMatch : matches) {
if (isFragment(rootMatch, side)) {
URI uri = getDataURI(rootMatch, side);
Resource resourceParent = getParent(rs, uri);
URI parentData = graph.getParentData(uri);
boolean _continue = true;
while (resourceParent == null && _continue == true) {
if (parentData != null) {
resourceParent = getParent(rs, parentData.trimFragment());
parentData = graph.getParentData(parentData.trimFragment());
} else {
_continue = false;
}
}
if (resourceParent != null && parentData != null) {
EObject eObjectParent = resourceParent.getEObject(parentData.fragment());
Match matchParent = match.getComparison().getMatch(eObjectParent);
if (matchParent != null && matchParent.equals(match)) {
childrenMatches.add(rootMatch);
}
}
}
}
return childrenMatches;
}
/**
* Check if the given URI is a child (directly or not) of one of the given list of URIs.
*
* @param uri
* the given URI.
* @param uris
* the given list of URIs.
* @return true if the given URI is a child (directly or not) of one of the given list of URIs, false
* otherwise.
*/
public static boolean isChildOf(URI uri, Collection<URI> uris) {
final IGraphView<URI> graph = getResourcesURIGraph();
URI parentData = graph.getParentData(uri);
while (parentData != null) {
URI parent = parentData.trimFragment();
if (uris.contains(parent)) {
return true;
}
parentData = graph.getParentData(parent);
}
return false;
}
/**
* Constructs a {@link org.eclipse.emf.compare.match.impl.NotLoadedFragmentMatch} from the given
* {@link org.eclipse.emf.compare.Match} and then return the
* {@link org.eclipse.emf.compare.rcp.ui.mergeviewer.item.IMergeViewerItem} corresponding to this
* NotLoadedFragmentMatch.
*
* @param match
* the given Match.
* @param side
* the side of the Match.
* @param comparison
* the comparison object that contains the Match.
* @param adapterFactory
* the adapter factory used to create the merge viewer item.
* @return an IMergeViewerItem.
*/
public static IMergeViewerItem createItemForNotLoadedFragmentMatch(Match match, MergeViewerSide side,
Comparison comparison, AdapterFactory adapterFactory) {
final MergeViewerItem.Container container;
ResourceSet rs = getDataResourceSet(match, side);
URI uri = getDataURI(match, side);
EObject firstLoadedParent = getEObjectParent(rs, uri);
if (firstLoadedParent == null) {
NotLoadedFragmentMatch notLoadedFragmentMatch = new NotLoadedFragmentMatch(match);
container = new MergeViewerItem.Container(comparison, null, notLoadedFragmentMatch,
notLoadedFragmentMatch, notLoadedFragmentMatch, side, adapterFactory);
} else if (isRootResource(firstLoadedParent.eResource().getURI())) {
Match matchParent = comparison.getMatch(firstLoadedParent);
if (matchParent != null) {
if (!comparison.getMatches().contains(matchParent)) {
container = new MergeViewerItem.Container(comparison, null, match.getLeft(),
match.getRight(), match.getOrigin(), side, adapterFactory);
} else {
container = null;
}
} else {
NotLoadedFragmentMatch notLoadedFragmentMatch = new NotLoadedFragmentMatch(match);
container = new MergeViewerItem.Container(comparison, null, notLoadedFragmentMatch,
notLoadedFragmentMatch, notLoadedFragmentMatch, side, adapterFactory);
}
} else {
container = null;
}
return container;
}
/**
* Adds a new parent container to the given list of IMergeViewerItems if needed and returns it. If the
* given items don't need a new parent, return null.
*
* @param items
* the given IMergeViewerItems.
* @param side
* the side of the Match.
* @param comparison
* the comparison object that contains the Match.
* @param adapterFactory
* the adapter factory used to create the merge viewer item.
* @return an IMergeViewerItem, or null.
*/
public static IMergeViewerItem addNewContainerForNotLoadedFragmentMatches(
Collection<IMergeViewerItem> items, MergeViewerSide side, Comparison comparison,
AdapterFactory adapterFactory) {
final MergeViewerItem.Container newContainer;
final Collection<Match> notLoadedFragmentMatches = getNotLoadedFragmentMatches(items);
if (notLoadedFragmentMatches.size() > 1) {
// Need to replace by top-container NotLoadedFragment item
NotLoadedFragmentMatch notLoadedFragmentMatch = new NotLoadedFragmentMatch(
notLoadedFragmentMatches);
for (NotLoadedFragmentMatch match : filter(notLoadedFragmentMatches,
NotLoadedFragmentMatch.class)) {
match.setName(getResourceName(match));
}
newContainer = new MergeViewerItem.Container(comparison, null, notLoadedFragmentMatch,
notLoadedFragmentMatch, notLoadedFragmentMatch, side, adapterFactory);
} else {
newContainer = null;
}
return newContainer;
}
}