blob: 82dd91691352d1d4ff5dc99697195bcef7b4f801 [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 org.eclipse.emf.emfstore.internal.modelmutator.mutation.MutationPredicates.HAS_FEATURE_MAP_ENTRY_TYPE;
import static org.eclipse.emf.emfstore.internal.modelmutator.mutation.MutationPredicates.IS_MULTI_VALUED;
import static org.eclipse.emf.emfstore.internal.modelmutator.mutation.MutationPredicates.IS_MUTABLE_ATTRIBUTE;
import static org.eclipse.emf.emfstore.internal.modelmutator.mutation.MutationPredicates.IS_NON_EMPTY_VALUE_OR_LIST;
import java.util.List;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.emfstore.modelmutator.ESAttributeChangeMutation;
import org.eclipse.emf.emfstore.modelmutator.ESModelMutatorUtil;
import org.eclipse.emf.emfstore.modelmutator.ESMutationException;
import org.eclipse.emf.emfstore.modelmutator.ESRandomChangeMode;
import com.google.common.base.Predicate;
/**
* A mutation, which adds, deletes, or reorders an attribute value of an object.
*
* @author Philip Langer
*
*/
public class AttributeChangeMutation extends StructuralFeatureMutation<ESAttributeChangeMutation> implements
ESAttributeChangeMutation {
private Object newValue;
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 AttributeChangeMutation(ESModelMutatorUtil util) {
super(util);
addGeneralAttributeChangeMutationPredicates();
}
/**
* 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 AttributeChangeMutation(ESModelMutatorUtil util, MutationTargetSelector selector) {
super(util, selector);
addGeneralAttributeChangeMutationPredicates();
}
private void addGeneralAttributeChangeMutationPredicates() {
addTargetFeatureAttributePredicate();
addAttributeTypeNotFeatureMapPredicate();
addAttributeTypeNotEEnumeratorPredicate();
}
private void addTargetFeatureAttributePredicate() {
getTargetContainerSelector().getTargetFeaturePredicates().add(IS_MUTABLE_ATTRIBUTE);
}
private void addAttributeTypeNotFeatureMapPredicate() {
getTargetContainerSelector().getTargetFeaturePredicates().add(not(HAS_FEATURE_MAP_ENTRY_TYPE));
}
private void addAttributeTypeNotEEnumeratorPredicate() {
getTargetContainerSelector().getTargetFeaturePredicates().add(
not(new Predicate<EStructuralFeature>() {
public boolean apply(EStructuralFeature input) {
return input != null && EcorePackage.eINSTANCE.getEEnumerator().equals(input.getEType());
}
}));
}
/**
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.modelmutator.mutation.Mutation#clone()
*/
@Override
public Mutation clone() {
return new AttributeChangeMutation(getUtil(), getTargetContainerSelector());
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.internal.modelmutator.mutation.Mutation#apply()
*/
@Override
public void apply() throws ESMutationException {
switch (getRandomChangeMode()) {
case ADD:
doAddAttributeValue();
break;
case DELETE:
doDeleteAttributeValue();
break;
case REORDER:
doReorderAttributeValue();
break;
default:
break;
}
}
private boolean doAddAttributeValue() throws ESMutationException {
getTargetContainerSelector().doSelection();
final EObject eObject = getTargetContainerSelector().getTargetObject();
final EAttribute eAttribute = (EAttribute) getTargetContainerSelector().getTargetFeature();
Object newValue = createNewValue(eAttribute);
if (newValue != null && eAttribute.isID() && !getUtil().getModelMutatorConfiguration().isAllowDuplicateIDs()) {
while (!getUtil().isUniqueID(newValue)) {
newValue = createNewValue(eAttribute);
}
getUtil().registerID(newValue);
}
if (eAttribute.isMany()) {
final int insertionIndex = getTargetContainerSelector()
.getRandomIndexFromTargetObjectAndFeatureValueRange();
getUtil().setPerCommand(eObject, eAttribute, newValue, insertionIndex);
} else {
getUtil().setPerCommand(eObject, eAttribute, newValue);
}
return true;
}
private boolean doDeleteAttributeValue() throws ESMutationException {
makeSureWeHaveAValueInSelectedObjectAtSelectedFeature();
getTargetContainerSelector().doSelection();
final EObject eObject = getTargetContainerSelector().getTargetObject();
final EAttribute eAttribute = (EAttribute) getTargetContainerSelector().getTargetFeature();
if (eAttribute.isMany()) {
final List<?> currentValues = (List<?>) eObject.eGet(eAttribute);
final int deletionIndex = getTargetContainerSelector().getRandomIndexFromTargetObjectAndFeatureValueRange();
currentValues.remove(deletionIndex);
getUtil().setPerCommand(eObject, eAttribute, currentValues);
} else {
getUtil().setPerCommand(eObject, eAttribute, SetCommand.UNSET_VALUE);
}
return true;
}
private void makeSureWeHaveAValueInSelectedObjectAtSelectedFeature() {
getTargetContainerSelector().getOriginalFeatureValuePredicates().add(IS_NON_EMPTY_VALUE_OR_LIST);
}
private boolean doReorderAttributeValue() throws ESMutationException {
final boolean success;
makeSureAttributeIsMutliValued();
makeSureWeHaveAValueInSelectedObjectAtSelectedFeature();
getTargetContainerSelector().doSelection();
final EObject eObject = getTargetContainerSelector().getTargetObject();
final EAttribute eAttribute = (EAttribute) getTargetContainerSelector().getTargetFeature();
if (eAttribute.isMany()) {
final List<?> currentValues = (List<?>) eObject.eGet(eAttribute);
final int numberOfCurrentValues = currentValues.size();
final int pickIndex = getRandom().nextInt(numberOfCurrentValues);
final int putIndex = getRandom().nextInt(numberOfCurrentValues);
getUtil().movePerCommand(eObject, eAttribute, currentValues.get(pickIndex), putIndex);
success = true;
} else {
success = false;
}
return success;
}
private void makeSureAttributeIsMutliValued() {
getTargetContainerSelector().getTargetFeaturePredicates().add(IS_MULTI_VALUED);
}
/**
* Creates a new value suitable to be set for the specified {@code eAttribute}.
*
* @param eAttribute The attribute to create a value for.
* @return The created new value suitable for {@code eAttribute}.
*/
protected Object createNewValue(EAttribute eAttribute) {
if (newValue != null) {
return newValue;
}
return getUtil().createNewAttribute(eAttribute);
}
/**
* 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.ESAttributeChangeMutation#setNewValue(java.lang.Object)
*/
public ESAttributeChangeMutation setNewValue(Object newValue) {
this.newValue = newValue;
return this;
}
/**
*
* {@inheritDoc}
*
* @see org.eclipse.emf.emfstore.modelmutator.ESAttributeChangeMutation#setRandomChangeMode(org.eclipse.emf.emfstore.modelmutator.ESRandomChangeMode)
*/
public ESAttributeChangeMutation setRandomChangeMode(ESRandomChangeMode mode) {
randomChangeMode = mode;
return this;
}
}