/**
* Copyright (c) 2013, 2016 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.tests.framework.junit.internal;
import java.lang.reflect.Constructor;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.BasicMonitor;
import org.eclipse.emf.common.util.Monitor;
import org.eclipse.emf.compare.Comparison;
import org.eclipse.emf.compare.conflict.IConflictDetector;
import org.eclipse.emf.compare.conflict.MatchBasedConflictDetector;
import org.eclipse.emf.compare.diff.DefaultDiffEngine;
import org.eclipse.emf.compare.diff.DiffBuilder;
import org.eclipse.emf.compare.diff.IDiffEngine;
import org.eclipse.emf.compare.match.DefaultComparisonFactory;
import org.eclipse.emf.compare.match.DefaultEqualityHelperFactory;
import org.eclipse.emf.compare.match.DefaultMatchEngine;
import org.eclipse.emf.compare.match.IMatchEngine;
import org.eclipse.emf.compare.match.eobject.EcoreWeightProvider;
import org.eclipse.emf.compare.match.eobject.EditionDistance;
import org.eclipse.emf.compare.match.eobject.IEObjectMatcher;
import org.eclipse.emf.compare.match.eobject.IdentifierEObjectMatcher;
import org.eclipse.emf.compare.match.eobject.ProximityEObjectMatcher;
import org.eclipse.emf.compare.scope.DefaultComparisonScope;
import org.eclipse.emf.compare.scope.IComparisonScope;
import org.eclipse.emf.compare.tests.framework.NotifierTuple;
import org.eclipse.emf.compare.tests.framework.junit.annotation.ConflictTest;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
/**
* This implementation of a {@link Statement} allows us to call methods annotated with {@link ConflictTest} on
* the result of a conflict detection process.
*
* @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
*/
public class ConflictStatement extends Statement {
/** Target of the test. */
private final Object testObject;
/**
* The statement that should be executed to retrieve the {@link NotifierTuple} we are to match.
*/
private final ResultStatement<NotifierTuple> tupleStatement;
/** The actual test method. */
private final FrameworkMethod test;
/**
* Instantiates our statement given its target object and tuple as well as the test methods.
*
* @param testObject
* Target of the test.
* @param tupleStatement
* The statement that should be executed to retrieve the {@link NotifierTuple} we are to match
* and diff.
* @param test
* The actual test method.
*/
public ConflictStatement(Object testObject, ResultStatement<NotifierTuple> tupleStatement,
FrameworkMethod test) {
this.testObject = testObject;
this.tupleStatement = tupleStatement;
this.test = test;
}
/**
* {@inheritDoc}
*
* @see org.junit.runners.model.Statement#evaluate()
*/
@Override
public void evaluate() throws Throwable {
tupleStatement.evaluate();
final NotifierTuple tuple = tupleStatement.getResult();
final ConflictTest annotation = test.getAnnotation(ConflictTest.class);
final IComparisonScope scope = createComparisonScope(tuple, annotation);
final IMatchEngine matchEngine = createMatchEngine(annotation);
final IDiffEngine diffEngine = createDiffEngine(annotation);
final IConflictDetector detector = new MatchBasedConflictDetector();
final Monitor monitor = new BasicMonitor();
final Comparison comparison = matchEngine.match(scope, new BasicMonitor());
diffEngine.diff(comparison, new BasicMonitor());
detector.detect(comparison, monitor);
test.invokeExplosively(testObject, scope, comparison);
}
/**
* Creates the match engine specified by the given annotation if it has a public no-arg constructor, use
* the {@link DefaultMatchEngine default} otherwise.
*
* @param annotation
* The annotation on which is defined the match engine we are to use.
* @return An instance of the specified match engine if it has a public no-arg constructor; an instance of
* the {@link DefaultMatchEngine} otherwise.
*/
private static IMatchEngine createMatchEngine(ConflictTest annotation) {
final Class<? extends IMatchEngine> engineClass = annotation.matchEngine();
IMatchEngine engine = null;
try {
engine = engineClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
// Swallow : we'll create a default engine instead.
}
if (engine == null) {
final IEObjectMatcher contentMatcher = new ProximityEObjectMatcher(
EditionDistance.builder().weightProvider(new EcoreWeightProvider()).build());
final IEObjectMatcher matcher = new IdentifierEObjectMatcher(contentMatcher);
engine = new DefaultMatchEngine(matcher,
new DefaultComparisonFactory(new DefaultEqualityHelperFactory()));
}
return engine;
}
/**
* Creates the diff engine specified by the given annotation if it has a public no-arg constructor, use
* the {@link DefaultDiffEngine default} otherwise.
*
* @param annotation
* The annotation on which is defined the diff engine we are to use.
* @return An instance of the specified diff engine if it has a public no-arg constructor; an instance of
* the {@link DefaultDiffEngine} otherwise.
*/
private static IDiffEngine createDiffEngine(ConflictTest annotation) {
final Class<? extends IDiffEngine> engineClass = annotation.diffEngine();
IDiffEngine engine = null;
try {
engine = engineClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
// Swallow : we'll create a default engine instead.
}
if (engine == null) {
engine = new DefaultDiffEngine(new DiffBuilder());
}
return engine;
}
/**
* Creates the comparison scope specified by the given annotation if it has a public constructor taking
* three {@link Notifier}s as parameters. We'll return an instance of the {@link DefaultComparisonScope}
* otherwise.
*
* @param tuple
* The tuple for which we need a comparison scope.
* @param annotation
* The annotation on which is specified the desired scope's class.
* @return An instance of the specified comparison scope it it has the expected constructor, an instance
* of the {@link DefaultComparisonScope} otherwise.
*/
private static IComparisonScope createComparisonScope(NotifierTuple tuple, ConflictTest annotation) {
final Class<? extends IComparisonScope> scopeClass = annotation.scope();
IComparisonScope scope = null;
try {
final Constructor<? extends IComparisonScope> constructor = scopeClass
.getConstructor(Notifier.class, Notifier.class, Notifier.class);
scope = constructor.newInstance(tuple.getLeft(), tuple.getRight(), tuple.getOrigin());
// CHECKSTYLE:OFF invoking a constructor requires 7 catches. Since
// we're swallowing all exceptions, we simply catch everything.
} catch (Exception e) {
// CHECKSTYLE:ON
// Swallow : we'll create a default engine instead.
}
if (scope == null) {
scope = new DefaultComparisonScope(tuple.getLeft(), tuple.getRight(), tuple.getOrigin());
}
return scope;
}
}