blob: 0675883cdf55ea12dc19f69c6b0adea9255e29e4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2014 EclipseSource Muenchen GmbH and others.
*
* 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:
* Philip Langer - initial API and implementation
******************************************************************************/
package org.eclipse.emf.emfstore.internal.modelmutator.mutation;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.Iterables.get;
import static com.google.common.collect.Iterables.isEmpty;
import static com.google.common.collect.Iterables.size;
import static org.eclipse.emf.emfstore.internal.modelmutator.mutation.MutationPredicates.IS_CONTAINMENT_OR_OPPOSITE_OF_CONTAINMENT_REFERENCE;
import static org.eclipse.emf.emfstore.internal.modelmutator.mutation.MutationPredicates.IS_MULTI_VALUED;
import static org.eclipse.emf.emfstore.internal.modelmutator.mutation.MutationPredicates.IS_NON_EMPTY_EOBJECT_LIST;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.emfstore.modelmutator.ESModelMutatorUtil;
import org.eclipse.emf.emfstore.modelmutator.ESMutationException;
import org.eclipse.emf.emfstore.modelmutator.ESRandomChangeMode;
import org.eclipse.emf.emfstore.modelmutator.ESReferenceChangeMutation;
import com.google.common.base.Predicate;
/**
* A mutation, which changes reference values.
*
* @author Philip Langer
*
*/
public class ReferenceChangeMutation extends StructuralFeatureMutation<ESReferenceChangeMutation> implements
ESReferenceChangeMutation {
private EObject newReferenceValue;
private ESRandomChangeMode randomChangeMode;
/**
* Creates a new mutation with the specified {@code util}.
*
* @param util The model mutator util used for accessing the model to be mutated.
*/
public ReferenceChangeMutation(ESModelMutatorUtil util) {
super(util);
addTargetFeatureReferencePredicate();
}
/**
* Creates a new mutation with the specified {@code util} and the {@code selector}.
*
* @param util The model mutator util used for accessing the model to be mutated.
* @param selector The target selector for selecting the target container and feature.
*/
protected ReferenceChangeMutation(ESModelMutatorUtil util, MutationTargetSelector selector) {
super(util, selector);
addTargetFeatureReferencePredicate();
}
private void addTargetFeatureReferencePredicate() {
getTargetContainerSelector().getTargetFeaturePredicates().add(
MutationPredicates.IS_MUTABLE_REFERENCE);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.modelmutator.mutation.Mutation#clone()
*/
@Override
public Mutation clone() {
return new ReferenceChangeMutation(getUtil(), getTargetContainerSelector());
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.modelmutator.mutation.Mutation#apply()
*/
@Override
public void apply() throws ESMutationException {
switch (getRandomChangeMode()) {
case ADD:
doAddReferenceValue();
break;
case DELETE:
doDeleteReferenceValue();
break;
case REORDER:
doReorderReferenceValue();
break;
default:
break;
}
}
private boolean doAddReferenceValue() throws ESMutationException {
final boolean success;
makeSureChangingTargetDoesNotAffectContainmentReference();
makeSureValueForSelectedFeatureToAddExists();
getTargetContainerSelector().doSelection();
final EObject eObject = getTargetContainerSelector().getTargetObject();
final EReference eReference = (EReference) getTargetContainerSelector().getTargetFeature();
final EObject newValue = selectNewReferenceValue();
if (newValue != null) {
success = addOrSetReferenceValue(eObject, eReference, newValue);
} else {
success = false;
}
return success;
}
private boolean addOrSetReferenceValue(final EObject eObject, final EReference eReference,
final EObject newValue) {
final boolean success;
if (newValue != null) {
if (eReference.isMany()) {
final int insertionIndex = getTargetContainerSelector()
.getRandomIndexFromTargetObjectAndFeatureValueRange();
getUtil().addPerCommand(eObject, eReference, newValue, insertionIndex);
} else {
getUtil().setPerCommand(eObject, eReference, newValue);
}
success = true;
} else {
success = false;
}
return success;
}
/**
* Selects and returns a suitable value for the currently selected reference.
*
* @return The selected reference value.
* @throws ESMutationException Thrown if no valid value could be found.
*/
protected EObject selectNewReferenceValue() throws ESMutationException {
if (newReferenceValue != null) {
return newReferenceValue;
}
final EObject newReferenceValue;
final EReference eReference = (EReference) getTargetContainerSelector().getTargetFeature();
final Iterable<EObject> suitableEObjects = getUtil().getSuitableEObjectsForAvailableFeature(eReference);
final int numberOfAvailableEObjects = size(suitableEObjects);
if (numberOfAvailableEObjects < 1) {
throw new ESMutationException("No objects available as feature values to add"); //$NON-NLS-1$
}
final int randomIndex = getRandom().nextInt(numberOfAvailableEObjects);
newReferenceValue = get(suitableEObjects, randomIndex);
return newReferenceValue;
}
private boolean doDeleteReferenceValue() throws ESMutationException {
makeSureChangingTargetDoesNotAffectContainmentReference();
makeSureWeHaveValuesInSelectedObjectAtSelectedFeature();
getTargetContainerSelector().doSelection();
final EObject eObject = getTargetContainerSelector().getTargetObject();
final EReference eReference = (EReference) getTargetContainerSelector().getTargetFeature();
if (eReference.isMany()) {
final List<?> currentValues = (List<?>) eObject.eGet(eReference);
final int numberOfCurrentValues = currentValues.size();
final int deletionIndex = getRandom().nextInt(numberOfCurrentValues);
currentValues.remove(deletionIndex);
getUtil().setPerCommand(eObject, eReference, currentValues);
} else {
getUtil().setPerCommand(eObject, eReference, SetCommand.UNSET_VALUE);
}
return true;
}
private void makeSureChangingTargetDoesNotAffectContainmentReference() {
getTargetContainerSelector().getTargetFeaturePredicates().add(
not(IS_CONTAINMENT_OR_OPPOSITE_OF_CONTAINMENT_REFERENCE));
}
private void makeSureValueForSelectedFeatureToAddExists() {
getTargetContainerSelector().getTargetFeaturePredicates().add(new Predicate<EStructuralFeature>() {
public boolean apply(EStructuralFeature input) {
return input != null
&& !isEmpty(getUtil().getSuitableEObjectsForAvailableFeature(input));
}
});
}
private boolean doReorderReferenceValue() throws ESMutationException {
final boolean success;
makeSureSelectedFeatureIsMultiValued();
makeSureWeHaveValuesInSelectedObjectAtSelectedFeature();
getTargetContainerSelector().doSelection();
final EObject eObject = getTargetContainerSelector().getTargetObject();
final EReference eReference = (EReference) getTargetContainerSelector().getTargetFeature();
if (eReference.isMany()) {
@SuppressWarnings("unchecked")
final List<Object> currentValues = (List<Object>) eObject.eGet(eReference);
final int numberOfCurrentValues = currentValues.size();
final int pickIndex = getRandom().nextInt(numberOfCurrentValues);
final int putIndex = getRandom().nextInt(numberOfCurrentValues);
getUtil().movePerCommand(eObject, eReference, currentValues.get(pickIndex), putIndex);
success = true;
} else {
success = false;
}
return success;
}
private void makeSureSelectedFeatureIsMultiValued() {
getTargetContainerSelector().getTargetFeaturePredicates().add(IS_MULTI_VALUED);
}
private void makeSureWeHaveValuesInSelectedObjectAtSelectedFeature() {
getTargetContainerSelector().getOriginalFeatureValuePredicates().add(IS_NON_EMPTY_EOBJECT_LIST);
}
/**
* Returns a random change mode.
*
* @return A random change mode.
*/
protected ESRandomChangeMode getRandomChangeMode() {
if (randomChangeMode != null) {
return randomChangeMode;
}
final ESRandomChangeMode[] values = ESRandomChangeMode.values();
final int nextInt = getRandom().nextInt(values.length);
return values[nextInt];
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.modelmutator.ESReferenceChangeMutation#setRandomChangeMode(org.eclipse.emf.emfstore.modelmutator.ESRandomChangeMode)
*/
public ESReferenceChangeMutation setRandomChangeMode(ESRandomChangeMode randomChangeMode) {
this.randomChangeMode = randomChangeMode;
return this;
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.modelmutator.ESReferenceChangeMutation#setNewReferenceValue(org.eclipse.emf.ecore.EObject)
*/
public ESReferenceChangeMutation setNewReferenceValue(EObject newValue) {
if (newValue != null) {
newReferenceValue = newValue;
}
return this;
}
}