blob: 1052994e47536707e9c87f703d247e309c4d0b20 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2016 Willink Transformations 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:
* E.D.Willink - Initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.pivot.library;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.ocl.pivot.evaluation.IterationManager;
/**
* AbstractIteration realizes shared characteristics of library iterations by providing a
* default iteration algorithm with a call-back at each iteration step.
*/
public abstract class AbstractIteration extends AbstractFeature implements LibraryIteration.LibraryIterationExtension
{
/**
* An out-of-band value that can be returned by {@link #updateAccumulator} to signal
* that the iteration should carry on rather than terminate using the returned value.
*/
protected static final @NonNull Object CARRY_ON = new Object() {
@Override
public String toString() {
return "<<CARRY_ON>>";
}
};
/**
* A MutableObject may be used as an iteration accumulatior containing a single changing value.
*/
public static class MutableObject
{
private @Nullable Object value;
public MutableObject(@Nullable Object value) {
this.value = value;
}
public @Nullable Object get() {
return value;
}
public void set(@Nullable Object value) {
this.value = value;
}
}
/**
* The default iteration algorithm steps through the iteration domain by invoking
* {@link IterationManager#hasCurrent()} and {@link IterationManager#advanceIterators()}.
* At each step {@link #updateAccumulator(IterationManager)} is invoked to update the
* accumulator for that step. A non-null return causes a premature exit and forms the
* return from the overall evaluation. If all steps complete {@link #resolveTerminalValue(IterationManager)}
* is invoked to provide the return value.
* <p>
* Derived classes may override this method to change the iteration algorithm or override
* the call-backs to customize the default iteration.
*/
@Override
public @Nullable Object evaluateIteration(@NonNull IterationManager iterationManager) {
try {
while (true) {
if (!iterationManager.hasCurrent()) {
return resolveTerminalValue(iterationManager);
}
Object resultVal = updateAccumulator(iterationManager);
if (resultVal != CARRY_ON) {
return resultVal;
}
iterationManager.advanceIterators();
}
}
finally {
iterationManager.dispose();
}
}
/**
* Return the final result at the end of an iteration over all the source elements. The
* default implementation just returns the accumulator. Derived iterations should
* override.
* <br>
* This method is bypassed if the iteration ends prematurely.
*
* @param iterationManager the iteration context
* @return the result
*/
protected @Nullable Object resolveTerminalValue(@NonNull IterationManager iterationManager) {
return iterationManager.getAccumulatorValue();
}
/**
* Update the accumulatorValue with the bodyValue resulting from the current iteration
* for which the iterators define the context in the environment.
*
* @param iterationManager the iteration context
* @return non-CARRY_ON premature result of iteration, or CARRY_ON if complete
*/
protected abstract @Nullable Object updateAccumulator(@NonNull IterationManager iterationManager);
}