Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlgoubet2008-04-03 12:35:22 +0000
committerlgoubet2008-04-03 12:35:22 +0000
commit1f3aa351d2fbb13666536b710beda5e46de6d278 (patch)
treede38b8834d6edc00eb0a22ad9b7db9612b8c2c60 /plugins/org.eclipse.emf.compare.ui/src/org/eclipse/emf/compare/ui/internal/ModelComparator.java
parentef309bac28c707faaad415b86deed13c5b2b0c8b (diff)
downloadorg.eclipse.emf.compare-1f3aa351d2fbb13666536b710beda5e46de6d278.tar.gz
org.eclipse.emf.compare-1f3aa351d2fbb13666536b710beda5e46de6d278.tar.xz
org.eclipse.emf.compare-1f3aa351d2fbb13666536b710beda5e46de6d278.zip
Altering API for specific team integration
Diffstat (limited to 'plugins/org.eclipse.emf.compare.ui/src/org/eclipse/emf/compare/ui/internal/ModelComparator.java')
-rw-r--r--plugins/org.eclipse.emf.compare.ui/src/org/eclipse/emf/compare/ui/internal/ModelComparator.java454
1 files changed, 454 insertions, 0 deletions
diff --git a/plugins/org.eclipse.emf.compare.ui/src/org/eclipse/emf/compare/ui/internal/ModelComparator.java b/plugins/org.eclipse.emf.compare.ui/src/org/eclipse/emf/compare/ui/internal/ModelComparator.java
new file mode 100644
index 000000000..3af4d0517
--- /dev/null
+++ b/plugins/org.eclipse.emf.compare.ui/src/org/eclipse/emf/compare/ui/internal/ModelComparator.java
@@ -0,0 +1,454 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2007, 2008 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.ui.internal;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.IStreamContentAccessor;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.ResourceNode;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.compare.EMFCompareException;
+import org.eclipse.emf.compare.EMFComparePlugin;
+import org.eclipse.emf.compare.diff.metamodel.DiffFactory;
+import org.eclipse.emf.compare.diff.metamodel.DiffModel;
+import org.eclipse.emf.compare.diff.metamodel.ModelInputSnapshot;
+import org.eclipse.emf.compare.diff.service.DiffService;
+import org.eclipse.emf.compare.match.api.MatchOptions;
+import org.eclipse.emf.compare.match.metamodel.MatchModel;
+import org.eclipse.emf.compare.match.service.MatchService;
+import org.eclipse.emf.compare.ui.EMFCompareUIMessages;
+import org.eclipse.emf.compare.ui.util.EMFCompareConstants;
+import org.eclipse.emf.compare.util.EMFCompareMap;
+import org.eclipse.emf.compare.util.ModelUtils;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * This class will allow model comparison given a CompareConfiguration while allowing specific handling of
+ * resource loading when team providers require it.
+ *
+ * @author Laurent Goubet <a href="mailto:laurent.goubet@obeo.fr">laurent.goubet@obeo.fr</a>
+ */
+public final class ModelComparator {
+ /** Keeps track of the team handlers declared for the extension point. */
+ private static final Set<TeamHandlerDescriptor> CACHED_HANDLERS = new HashSet<TeamHandlerDescriptor>();
+
+ /**
+ * This will contain instances of comparators associated to given CompareConfiguration.
+ */
+ private static final Map<CompareConfiguration, ModelComparator> INSTANCES = new HashMap<CompareConfiguration, ModelComparator>();
+
+ /** Name of the extension point to parse for team handlers. */
+ private static final String TEAM_HANDLERS_EXTENSION_POINT = "org.eclipse.emf.compare.ui.internal.team.handler"; //$NON-NLS-1$
+
+ /** This will hold the result of these resources' comparison. */
+ protected ModelInputSnapshot comparisonResult;
+
+ /** Resource of the ancestor model used in this comparison. */
+ private Resource ancestorResource;
+
+ /** This will keep track of the handler used by this comparison. */
+ private AbstractTeamHandler comparisonHandler;
+
+ /**
+ * Indicates that the left compared model is remote and shouldn't be modified.
+ */
+ private boolean leftIsRemote;
+
+ /** Resource of the left model used in this comparison. */
+ private Resource leftResource;
+
+ /** ResourceSet which will be used to load our resources. */
+ private final ResourceSet resourceSet = new ResourceSetImpl();
+
+ /**
+ * Indicates that the right compared model is remote and shouldn't be modified. This will only happen if
+ * we couldn't load a local resource when comparing with repository.
+ */
+ private boolean rightIsRemote;
+
+ /** Resource of the right model used in this comparison. */
+ private Resource rightResource;
+
+ static {
+ parseExtensionMetaData();
+ }
+
+ /**
+ * Model comparators will only be instantiated via {@link #getComparator(CompareConfiguration)}.
+ */
+ private ModelComparator() {
+ // prevents external instantiation
+ }
+
+ /**
+ * This will return the ModelComparator associated to the given CompareConfiguration.
+ *
+ * @param configuration
+ * CompareConfiguration of this comparator.
+ * @return The comparator for this configuration.
+ */
+ public static ModelComparator getComparator(CompareConfiguration configuration) {
+ if (!INSTANCES.containsKey(configuration))
+ INSTANCES.put(configuration, new ModelComparator());
+ return INSTANCES.get(configuration);
+ }
+
+ /**
+ * Removes the comparator corresponding to a no longer used configuration.
+ *
+ * @param configuration
+ * CompareConfiguration which comparator should be removed.
+ */
+ public static void removeComparator(CompareConfiguration configuration) {
+ INSTANCES.remove(configuration);
+ }
+
+ /**
+ * This will parse {@link #TEAM_HANDLERS_EXTENSION_POINT} for team handlers.
+ */
+ private static void parseExtensionMetaData() {
+ final IExtension[] extensions = Platform.getExtensionRegistry().getExtensionPoint(
+ TEAM_HANDLERS_EXTENSION_POINT).getExtensions();
+ for (IExtension extension : extensions) {
+ for (IConfigurationElement configElement : extension.getConfigurationElements()) {
+ final TeamHandlerDescriptor descriptor = new TeamHandlerDescriptor(configElement);
+ CACHED_HANDLERS.add(descriptor);
+ }
+ }
+ }
+
+ /**
+ * This will run the comparison process and return the resulting {@link ModelInputSnapshot snapshot}.
+ *
+ * @param configuration
+ * Compared configuration of this comparison. Properties will be set on this to hold comparison
+ * data.
+ * @return Result of the comparison of the loaded resources.
+ */
+ public ModelInputSnapshot compare(CompareConfiguration configuration) {
+ if (comparisonResult == null) {
+ comparisonResult = DiffFactory.eINSTANCE.createModelInputSnapshot();
+ final Date start = Calendar.getInstance().getTime();
+
+ try {
+ PlatformUI.getWorkbench().getProgressService().busyCursorWhile(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InterruptedException {
+ final Map<String, Object> options = new EMFCompareMap<String, Object>();
+ options.put(MatchOptions.OPTION_PROGRESS_MONITOR, monitor);
+ final MatchModel match;
+ if (getAncestorResource() == null)
+ match = MatchService.doResourceMatch(getLeftResource(), getRightResource(),
+ options);
+ else
+ match = MatchService.doResourceMatch(getLeftResource(), getRightResource(),
+ getAncestorResource(), options);
+ final DiffModel diff = DiffService.doDiff(match, getAncestorResource() != null);
+
+ comparisonResult.setDate(Calendar.getInstance().getTime());
+ comparisonResult.setDiff(diff);
+ comparisonResult.setMatch(match);
+ }
+ });
+ } catch (InterruptedException e) {
+ throw new EMFCompareException(e.getMessage());
+ } catch (InvocationTargetException e) {
+ EMFComparePlugin.log(e, true);
+ }
+
+ final Date end = Calendar.getInstance().getTime();
+ configuration.setProperty(EMFCompareConstants.PROPERTY_COMPARISON_TIME, end.getTime()
+ - start.getTime());
+ configuration.setLeftEditable(!isLeftRemote());
+ configuration.setRightEditable(!isRightRemote());
+ if (isLeftRemote()) {
+ configuration.setLeftLabel(EMFCompareUIMessages.getString("comparison.label.remoteResource")); //$NON-NLS-1$
+ configuration.setRightLabel(EMFCompareUIMessages.getString("comparison.label.localResource")); //$NON-NLS-1$
+ }
+ }
+ return comparisonResult;
+ }
+
+ /**
+ * Returns the compared resources ancestor.
+ *
+ * @return The compared resources ancestor.
+ */
+ public Resource getAncestorResource() {
+ if (comparisonHandler != null)
+ return comparisonHandler.getAncestorResource();
+ return ancestorResource;
+ }
+
+ /**
+ * This will return the comparison result.
+ *
+ * @return The comparison result. <code>null</code> if no comparison has been done since last loading
+ * resources.
+ */
+ public ModelInputSnapshot getComparisonResult() {
+ return comparisonResult;
+ }
+
+ /**
+ * Returns the left compared resource.
+ *
+ * @return The left compared resource.
+ */
+ public Resource getLeftResource() {
+ if (comparisonHandler != null)
+ return comparisonHandler.getLeftResource();
+ return leftResource;
+ }
+
+ /**
+ * Returns the right compared resource.
+ *
+ * @return The right compared resource.
+ */
+ public Resource getRightResource() {
+ if (comparisonHandler != null)
+ return comparisonHandler.getRightResource();
+ return rightResource;
+ }
+
+ /**
+ * Indicates that the left resource is remote and shouldn't be modified.
+ *
+ * @return <code>true</code> if the left compared resource is remote, <code>false</code> otherwise.
+ */
+ public boolean isLeftRemote() {
+ if (comparisonHandler != null)
+ return comparisonHandler.isLeftRemote();
+ return leftIsRemote;
+ }
+
+ /**
+ * Indicates that the right resource is remote and shouldn't be modified. Note that this will never return
+ * <code>true</code> unless we failed to load a local resource for comparison.
+ *
+ * @return <code>true</code> if the right compared resource is remote, <code>false</code> otherwise.
+ */
+ public boolean isRightRemote() {
+ if (comparisonHandler != null)
+ return comparisonHandler.isRightRemote();
+ return rightIsRemote;
+ }
+
+ /**
+ * This will load the resources held by <code>input</code>.
+ *
+ * @param input
+ * CompareInput which holds the resources to be loaded.
+ * @return <code>true</code> if the given models have been successfully loaded, <code>false</code>
+ * otherwise.
+ */
+ public boolean loadResources(ICompareInput input) {
+ clear();
+ final ITypedElement left = input.getLeft();
+ final ITypedElement right = input.getRight();
+ final ITypedElement ancestor = input.getAncestor();
+
+ try {
+ // This will be sufficient when comparing local resources
+ boolean result = handleLocalResources(left, right, ancestor);
+ // If resources weren't local, iterates through the registry to find
+ // a proper team handler
+ if (!result) {
+ final Iterator<TeamHandlerDescriptor> handlerDescriptorIterator = CACHED_HANDLERS.iterator();
+ while (handlerDescriptorIterator.hasNext()) {
+ final AbstractTeamHandler handler = handlerDescriptorIterator.next().getHandlerInstance();
+ result |= handler.loadResources(input, resourceSet);
+ if (result) {
+ comparisonHandler = handler;
+ break;
+ }
+ }
+ }
+ // We didn't found a proper handler, use a generic one
+ if (!result)
+ result |= handleGenericResources(left, right, ancestor);
+ return result;
+ } catch (IOException e) {
+ EMFComparePlugin.log(e, true);
+ } catch (CoreException e) {
+ EMFComparePlugin.log(e.getStatus());
+ }
+ return false;
+ }
+
+ /**
+ * Clears all loaded resources from the resource set.
+ */
+ private void clear() {
+ leftResource = null;
+ rightResource = null;
+ ancestorResource = null;
+ final Iterator<Resource> resourcesIterator = resourceSet.getResources().iterator();
+ while (resourcesIterator.hasNext()) {
+ final Resource resource = resourcesIterator.next();
+ resource.unload();
+ }
+ resourceSet.getResources().clear();
+ comparisonResult = null;
+ comparisonHandler = null;
+ }
+
+ /**
+ * This generic handler should be able to load resources passed by any team plug-in. Using this handler
+ * instead of {@link #handleSubversiveResources(ITypedElement, ITypedElement, ITypedElement)} for
+ * comparison via subversive will result in unsaveable merge operations.
+ *
+ * @param left
+ * Handler of the left compared model.
+ * @param right
+ * Handler of the right compared model.
+ * @param ancestor
+ * Handler of these two models' common ancestor.
+ * @return <code>true</code> If all resources have been loaded by this handler, <code>false</code>
+ * otherwise.
+ * @throws IOException
+ * Thrown if the right resource cannot be loaded.
+ * @throws CoreException
+ * Thrown if exceptions occur when loading the remote resources (left and ancestor).
+ */
+ private boolean handleGenericResources(ITypedElement left, ITypedElement right, ITypedElement ancestor)
+ throws IOException, CoreException {
+ if (left instanceof ResourceNode && right instanceof IStreamContentAccessor) {
+ if (((ResourceNode)left).getResource().isAccessible())
+ rightResource = ModelUtils
+ .load(((ResourceNode)left).getResource().getFullPath(), resourceSet).eResource();
+ else
+ rightResource = ModelUtils.createResource(URI.createPlatformResourceURI(((ResourceNode)left)
+ .getResource().getFullPath().toOSString(), true));
+ leftResource = ModelUtils.load(((IStreamContentAccessor)right).getContents(), right.getName(),
+ resourceSet).eResource();
+ leftIsRemote = true;
+ if (ancestor != null)
+ ancestorResource = ModelUtils.load(((IStreamContentAccessor)ancestor).getContents(),
+ ancestor.getName(), resourceSet).eResource();
+ return true;
+ }
+ /*
+ * We *should* never be here. There always is a local resource when comparing with CVS, this code will
+ * be executed if we couldn't manage to handle this *local* resource as such. Though the resource will
+ * be loaded with this generic handler, note that it will not be saveable.
+ */
+ boolean result = false;
+ if (left instanceof IStreamContentAccessor && right instanceof IStreamContentAccessor) {
+ rightResource = ModelUtils.load(((IStreamContentAccessor)left).getContents(), left.getName(),
+ resourceSet).eResource();
+ leftResource = ModelUtils.load(((IStreamContentAccessor)right).getContents(), right.getName(),
+ resourceSet).eResource();
+ rightIsRemote = true;
+ leftIsRemote = true;
+ if (ancestor != null)
+ ancestorResource = ModelUtils.load(((IStreamContentAccessor)ancestor).getContents(),
+ ancestor.getName(), resourceSet).eResource();
+ result = true;
+ }
+ return result;
+ }
+
+ /**
+ * This will try and load the given element as being local resources.
+ *
+ * @param left
+ * Handler of the left compared model.
+ * @param right
+ * Handler of the right compared model.
+ * @param ancestor
+ * Handler of these two models' common ancestor.
+ * @return <code>true</code> If all resources have been loaded by this handler, <code>false</code>
+ * otherwise.
+ * @throws IOException
+ * Thrown if resources cannot be loaded.
+ */
+ private boolean handleLocalResources(ITypedElement left, ITypedElement right, ITypedElement ancestor)
+ throws IOException {
+ if (left instanceof ResourceNode && right instanceof ResourceNode) {
+ leftResource = ModelUtils.load(((ResourceNode)left).getResource().getFullPath(), resourceSet)
+ .eResource();
+ rightResource = ModelUtils.load(((ResourceNode)right).getResource().getFullPath(), resourceSet)
+ .eResource();
+ if (ancestor != null)
+ ancestorResource = ModelUtils.load(((ResourceNode)ancestor).getResource().getFullPath(),
+ resourceSet).eResource();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Describes a team handler registered from a plug-in's extension point.
+ *
+ * @author Laurent Goubet <a href="mailto:laurent.goubet@obeo.fr">laurent.goubet@obeo.fr</a>
+ */
+ private static final class TeamHandlerDescriptor {
+ /**
+ * Name of the extension point attribute corresponding to the handler's class.
+ */
+ private static final String ATTRIBUTE_HANDLER_CLASS = "class"; //$NON-NLS-1$
+
+ /**
+ * Keeps a reference to the configuration element that describes this handler.
+ */
+ private final IConfigurationElement element;
+
+ /** This descriptor's wrapped {@link AbstractTeamHandler handler}. */
+ private AbstractTeamHandler handler;
+
+ /**
+ * Constructs a new descriptor from an IConfigurationElement.
+ *
+ * @param configuration
+ * Configuration of the team handler.
+ */
+ public TeamHandlerDescriptor(IConfigurationElement configuration) {
+ element = configuration;
+ }
+
+ /**
+ * Returns an instance of the described handler.
+ *
+ * @return Instance of the handler.
+ */
+ public AbstractTeamHandler getHandlerInstance() {
+ if (handler == null) {
+ try {
+ handler = (AbstractTeamHandler)element.createExecutableExtension(ATTRIBUTE_HANDLER_CLASS);
+ } catch (CoreException e) {
+ EMFComparePlugin.log(e, true);
+ }
+ }
+ return handler;
+ }
+ }
+}

Back to the top