blob: 5db4483be4ca21756e48d39d7c0b115441d3d82c [file] [log] [blame]
/**
* Copyright (c) 2017 Christian W. Damus and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Christian W. Damus - initial API and implementation
*/
package org.eclipse.emf.ecp.view.spi.rule.model.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.databinding.observable.Observables;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecp.view.spi.model.VDomainModelReference;
import org.eclipse.emf.ecp.view.spi.rule.model.Activator;
import org.eclipse.emf.ecp.view.spi.rule.model.Condition;
import org.eclipse.emf.ecp.view.spi.rule.model.IterateCondition;
import org.eclipse.emf.ecp.view.spi.rule.model.Quantifier;
import org.eclipse.emf.ecp.view.spi.rule.model.RulePackage;
import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedException;
/**
* <!-- begin-user-doc -->
* An implementation of the model object '<em><b>Iterate Condition</b></em>'.
* <!-- end-user-doc -->
* <p>
* The following features are implemented:
* </p>
* <ul>
* <li>{@link org.eclipse.emf.ecp.view.spi.rule.model.impl.IterateConditionImpl#getQuantifier <em>Quantifier</em>}</li>
* <li>{@link org.eclipse.emf.ecp.view.spi.rule.model.impl.IterateConditionImpl#isIfEmpty <em>If Empty</em>}</li>
* <li>{@link org.eclipse.emf.ecp.view.spi.rule.model.impl.IterateConditionImpl#getItemReference <em>Item
* Reference</em>}</li>
* <li>{@link org.eclipse.emf.ecp.view.spi.rule.model.impl.IterateConditionImpl#getItemCondition <em>Item
* Condition</em>}</li>
* </ul>
*
* @generated
*/
public class IterateConditionImpl extends ConditionImpl implements IterateCondition {
/**
* The default value of the '{@link #getQuantifier() <em>Quantifier</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @see #getQuantifier()
* @generated
* @ordered
*/
protected static final Quantifier QUANTIFIER_EDEFAULT = Quantifier.ALL;
/**
* The cached value of the '{@link #getQuantifier() <em>Quantifier</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @see #getQuantifier()
* @generated
* @ordered
*/
protected Quantifier quantifier = QUANTIFIER_EDEFAULT;
/**
* The default value of the '{@link #isIfEmpty() <em>If Empty</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @see #isIfEmpty()
* @generated
* @ordered
*/
protected static final boolean IF_EMPTY_EDEFAULT = false;
/**
* The cached value of the '{@link #isIfEmpty() <em>If Empty</em>}' attribute.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @see #isIfEmpty()
* @generated
* @ordered
*/
protected boolean ifEmpty = IF_EMPTY_EDEFAULT;
/**
* The cached value of the '{@link #getItemReference() <em>Item Reference</em>}' containment reference.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @see #getItemReference()
* @generated
* @ordered
*/
protected VDomainModelReference itemReference;
/**
* The cached value of the '{@link #getItemCondition() <em>Item Condition</em>}' containment reference.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @see #getItemCondition()
* @generated
* @ordered
*/
protected Condition itemCondition;
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
protected IterateConditionImpl() {
super();
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
@Override
protected EClass eStaticClass() {
return RulePackage.Literals.ITERATE_CONDITION;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
@Override
public Quantifier getQuantifier() {
return quantifier;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
@Override
public void setQuantifier(Quantifier newQuantifier) {
final Quantifier oldQuantifier = quantifier;
quantifier = newQuantifier == null ? QUANTIFIER_EDEFAULT : newQuantifier;
if (eNotificationRequired()) {
eNotify(new ENotificationImpl(this, Notification.SET, RulePackage.ITERATE_CONDITION__QUANTIFIER,
oldQuantifier, quantifier));
}
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
@Override
public boolean isIfEmpty() {
return ifEmpty;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
@Override
public void setIfEmpty(boolean newIfEmpty) {
final boolean oldIfEmpty = ifEmpty;
ifEmpty = newIfEmpty;
if (eNotificationRequired()) {
eNotify(new ENotificationImpl(this, Notification.SET, RulePackage.ITERATE_CONDITION__IF_EMPTY, oldIfEmpty,
ifEmpty));
}
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
@Override
public VDomainModelReference getItemReference() {
return itemReference;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
public NotificationChain basicSetItemReference(VDomainModelReference newItemReference, NotificationChain msgs) {
final VDomainModelReference oldItemReference = itemReference;
itemReference = newItemReference;
if (eNotificationRequired()) {
final ENotificationImpl notification = new ENotificationImpl(this, Notification.SET,
RulePackage.ITERATE_CONDITION__ITEM_REFERENCE, oldItemReference, newItemReference);
if (msgs == null) {
msgs = notification;
} else {
msgs.add(notification);
}
}
return msgs;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
@Override
public void setItemReference(VDomainModelReference newItemReference) {
if (newItemReference != itemReference) {
NotificationChain msgs = null;
if (itemReference != null) {
msgs = ((InternalEObject) itemReference).eInverseRemove(this,
EOPPOSITE_FEATURE_BASE - RulePackage.ITERATE_CONDITION__ITEM_REFERENCE, null, msgs);
}
if (newItemReference != null) {
msgs = ((InternalEObject) newItemReference).eInverseAdd(this,
EOPPOSITE_FEATURE_BASE - RulePackage.ITERATE_CONDITION__ITEM_REFERENCE, null, msgs);
}
msgs = basicSetItemReference(newItemReference, msgs);
if (msgs != null) {
msgs.dispatch();
}
} else if (eNotificationRequired()) {
eNotify(new ENotificationImpl(this, Notification.SET, RulePackage.ITERATE_CONDITION__ITEM_REFERENCE,
newItemReference, newItemReference));
}
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
@Override
public Condition getItemCondition() {
return itemCondition;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
public NotificationChain basicSetItemCondition(Condition newItemCondition, NotificationChain msgs) {
final Condition oldItemCondition = itemCondition;
itemCondition = newItemCondition;
if (eNotificationRequired()) {
final ENotificationImpl notification = new ENotificationImpl(this, Notification.SET,
RulePackage.ITERATE_CONDITION__ITEM_CONDITION, oldItemCondition, newItemCondition);
if (msgs == null) {
msgs = notification;
} else {
msgs.add(notification);
}
}
return msgs;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
@Override
public void setItemCondition(Condition newItemCondition) {
if (newItemCondition != itemCondition) {
NotificationChain msgs = null;
if (itemCondition != null) {
msgs = ((InternalEObject) itemCondition).eInverseRemove(this,
EOPPOSITE_FEATURE_BASE - RulePackage.ITERATE_CONDITION__ITEM_CONDITION, null, msgs);
}
if (newItemCondition != null) {
msgs = ((InternalEObject) newItemCondition).eInverseAdd(this,
EOPPOSITE_FEATURE_BASE - RulePackage.ITERATE_CONDITION__ITEM_CONDITION, null, msgs);
}
msgs = basicSetItemCondition(newItemCondition, msgs);
if (msgs != null) {
msgs.dispatch();
}
} else if (eNotificationRequired()) {
eNotify(new ENotificationImpl(this, Notification.SET, RulePackage.ITERATE_CONDITION__ITEM_CONDITION,
newItemCondition, newItemCondition));
}
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
@Override
public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, NotificationChain msgs) {
switch (featureID) {
case RulePackage.ITERATE_CONDITION__ITEM_REFERENCE:
return basicSetItemReference(null, msgs);
case RulePackage.ITERATE_CONDITION__ITEM_CONDITION:
return basicSetItemCondition(null, msgs);
}
return super.eInverseRemove(otherEnd, featureID, msgs);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
@Override
public Object eGet(int featureID, boolean resolve, boolean coreType) {
switch (featureID) {
case RulePackage.ITERATE_CONDITION__QUANTIFIER:
return getQuantifier();
case RulePackage.ITERATE_CONDITION__IF_EMPTY:
return isIfEmpty();
case RulePackage.ITERATE_CONDITION__ITEM_REFERENCE:
return getItemReference();
case RulePackage.ITERATE_CONDITION__ITEM_CONDITION:
return getItemCondition();
}
return super.eGet(featureID, resolve, coreType);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
@Override
public void eSet(int featureID, Object newValue) {
switch (featureID) {
case RulePackage.ITERATE_CONDITION__QUANTIFIER:
setQuantifier((Quantifier) newValue);
return;
case RulePackage.ITERATE_CONDITION__IF_EMPTY:
setIfEmpty((Boolean) newValue);
return;
case RulePackage.ITERATE_CONDITION__ITEM_REFERENCE:
setItemReference((VDomainModelReference) newValue);
return;
case RulePackage.ITERATE_CONDITION__ITEM_CONDITION:
setItemCondition((Condition) newValue);
return;
}
super.eSet(featureID, newValue);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
@Override
public void eUnset(int featureID) {
switch (featureID) {
case RulePackage.ITERATE_CONDITION__QUANTIFIER:
setQuantifier(QUANTIFIER_EDEFAULT);
return;
case RulePackage.ITERATE_CONDITION__IF_EMPTY:
setIfEmpty(IF_EMPTY_EDEFAULT);
return;
case RulePackage.ITERATE_CONDITION__ITEM_REFERENCE:
setItemReference((VDomainModelReference) null);
return;
case RulePackage.ITERATE_CONDITION__ITEM_CONDITION:
setItemCondition((Condition) null);
return;
}
super.eUnset(featureID);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
@Override
public boolean eIsSet(int featureID) {
switch (featureID) {
case RulePackage.ITERATE_CONDITION__QUANTIFIER:
return quantifier != QUANTIFIER_EDEFAULT;
case RulePackage.ITERATE_CONDITION__IF_EMPTY:
return ifEmpty != IF_EMPTY_EDEFAULT;
case RulePackage.ITERATE_CONDITION__ITEM_REFERENCE:
return itemReference != null;
case RulePackage.ITERATE_CONDITION__ITEM_CONDITION:
return itemCondition != null;
}
return super.eIsSet(featureID);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
*
* @generated
*/
@Override
public String toString() {
if (eIsProxy()) {
return super.toString();
}
final StringBuffer result = new StringBuffer(super.toString());
result.append(" (quantifier: "); //$NON-NLS-1$
result.append(quantifier);
result.append(", ifEmpty: "); //$NON-NLS-1$
result.append(ifEmpty);
result.append(')');
return result.toString();
}
@Override
public boolean evaluate(EObject domainModel) {
final VDomainModelReference path = getItemReference();
final Collection<? extends EObject> subjects = get(domainModel, path);
return doEvaluate(subjects);
}
/**
* Evaluates me by iteration of the given {@code subjects}.
*
* @param subjects the objects over which to iterate my {@link #getItemCondition() condition}
* @return the result of the iteration
*/
protected boolean doEvaluate(Collection<? extends EObject> subjects) {
boolean result = isIfEmpty();
for (final EObject next : subjects) {
result = getItemCondition().evaluate(next);
if (!result && getQuantifier() == Quantifier.ALL) {
break; // Short-circuit
}
if (result && getQuantifier() == Quantifier.ANY) {
break; // Short-circuit
}
}
return result;
}
/**
* Obtains the objects referenced by a domain-model reference, from the given
* {@code owner} object.
*
* @param owner the owner of the reference
* @param dmr the domain-model reference to follow
*
* @return the referenced objects
*/
List<? extends EObject> get(EObject owner, VDomainModelReference dmr) {
try {
@SuppressWarnings("unchecked")
IObservableList<? extends EObject> result = Activator.getDefault().getEMFFormsDatabinding()
.getObservableList(dmr, owner);
// We have to have EObjects, which means an EReference
if (result == null || !(result.getElementType() instanceof EReference)) {
result = Observables.emptyObservableList();
}
return result;
} catch (final DatabindingFailedException e) {
Activator.log(e);
return Collections.emptyList();
}
}
@Override
public boolean evaluateChangedValues(EObject domainModel,
Map<EStructuralFeature.Setting, Object> possibleNewValues) {
// First, partition the proposed changes into
// - changes to the set of objects that I iterate over (subjects)
// - changes to the features of my subjects
final List<Collection<? extends EObject>> proposedNewSubjects = new ArrayList<Collection<? extends EObject>>();
final Map<EStructuralFeature.Setting, Object> proposedChangesToSubjects = new HashMap<EStructuralFeature.Setting, Object>();
for (final Map.Entry<EStructuralFeature.Setting, Object> next : possibleNewValues.entrySet()) {
final EStructuralFeature.Setting setting = next.getKey();
final Object proposedValue = next.getValue();
if (setting.getEObject() == domainModel) {
// Changing the set of objects to iterate
@SuppressWarnings("unchecked")
final List<? extends EObject> newValues = (List<? extends EObject>) next.getValue();
proposedNewSubjects.add(newValues);
} else {
// Change features of an existing subject
proposedChangesToSubjects.put(setting, proposedValue);
}
}
// Now, evaluate the two partitions. Within each, evaluate the disjuction
// but between the partitions it's a conjuction
boolean result = true;
for (final Collection<? extends EObject> newValues : proposedNewSubjects) {
result = doEvaluate(newValues);
if (result) {
break;
}
}
if (result) {
for (final Map.Entry<EStructuralFeature.Setting, Object> next : proposedChangesToSubjects.entrySet()) {
final EStructuralFeature.Setting setting = next.getKey();
final Object proposedValue = next.getValue();
result = getItemCondition().evaluateChangedValues(setting.getEObject(),
Collections.singletonMap(setting, proposedValue));
if (!result && getQuantifier() == Quantifier.ALL) {
break; // Short-circuit
}
if (result && getQuantifier() == Quantifier.ANY) {
break; // Short-circuit
}
}
}
return result;
}
} // IterateConditionImpl