/*******************************************************************************
* Copyright (c) 2013 Rene Schneider, GEBIT Solutions 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
*******************************************************************************/
package de.gebit.integrity.runner.callbacks;
import java.util.Map;
import com.google.inject.Inject;
import de.gebit.integrity.dsl.CustomOperation;
import de.gebit.integrity.dsl.NestedObject;
import de.gebit.integrity.dsl.TypedNestedObject;
import de.gebit.integrity.dsl.ValueOrEnumValueOrOperation;
import de.gebit.integrity.dsl.ValueOrEnumValueOrOperationCollection;
import de.gebit.integrity.dsl.Variable;
import de.gebit.integrity.exceptions.ThisShouldNeverHappenException;
import de.gebit.integrity.operations.UnexecutableException;
import de.gebit.integrity.parameter.conversion.ConversionContext;
import de.gebit.integrity.parameter.conversion.UnresolvableVariable;
import de.gebit.integrity.parameter.conversion.ValueConverter;
import de.gebit.integrity.parameter.resolving.ParameterResolver;
import de.gebit.integrity.parameter.variables.VariableManager;
import de.gebit.integrity.runner.results.FixtureExecutionResult;
import de.gebit.integrity.string.FormattedString;
import de.gebit.integrity.utils.ParameterUtil.UnresolvableVariableException;
/**
* Abstract base class for test runner callback implementation. Provides some generic functionality required by most
* callbacks. Using this class is optional; a callback can directly inherit from {@link TestRunnerCallback} as well.
*
* @author Rene Schneider - initial API and implementation
*
*/
public abstract class AbstractTestRunnerCallback extends TestRunnerCallback {
/**
* The value converter to use.
*/
@Inject
protected ValueConverter valueConverter;
/**
* The parameter resolver to use.
*/
@Inject
protected ParameterResolver parameterResolver;
/**
* The variable manager to use.
*/
@Inject
protected VariableManager variableManager;
/**
* Converts a result value (that is, a value returned by a fixture during a test or call) to a formatted string.
* This method uses the fixture instance, if available, to perform the conversion.
*
* @param aResultValue
* the result value to convert
* @param aResult
* the execution result which provides access to the fixture instance and method
* @param aForceIntermediateMapFlag
* whether the conversion should force the usage of an intermediate map (useful for bean types)
* @param aConversionContext
* the conversion context to use (null = default)
* @return the converted string
*/
protected FormattedString convertResultValueToFormattedStringGuarded(Object aResultValue,
FixtureExecutionResult aResult, boolean aForceIntermediateMapFlag, ConversionContext aConversionContext) {
if (aResult.getFixtureInstance() != null) {
return aResult.getFixtureInstance().performValueToFormattedStringConversion(aResultValue,
aResult.getFixtureMethod(), aForceIntermediateMapFlag, aConversionContext);
} else {
return valueConverter.convertValueToFormattedString(aResultValue, aForceIntermediateMapFlag,
aConversionContext);
}
}
/**
* Converts a result value (that is, a value returned by a fixture during a test or call) to a string. This method
* uses the fixture instance, if available, to perform the conversion.
*
* @param aResultValue
* the result value to convert
* @param aResult
* the execution result which provides access to the fixture instance and method
* @param aForceIntermediateMapFlag
* whether the conversion should force the usage of an intermediate map (useful for bean types)
* @param aConversionContext
* the conversion context to use
* @return the converted string
*/
protected String convertResultValueToStringGuarded(Object aResultValue, FixtureExecutionResult aResult,
boolean aForceIntermediateMapFlag, ConversionContext aConversionContext) {
return convertResultValueToFormattedStringGuarded(aResultValue, aResult, aForceIntermediateMapFlag,
aConversionContext).toUnformattedString();
}
/**
* Determines whether a given {@link ValueOrEnumValueOrOperationCollection} contains at least one nested object (or
* an equivalent map with key-value pairs).
*
* @param aCollection
* the collection to check
* @return true or false
*/
protected boolean containsNestedObject(ValueOrEnumValueOrOperationCollection aCollection) {
if (aCollection == null) {
return false;
}
if (containsNestedObject(aCollection.getValue())) {
return true;
} else {
for (ValueOrEnumValueOrOperation tempValue : aCollection.getMoreValues()) {
if (containsNestedObject(tempValue)) {
return true;
}
}
}
return false;
}
/**
* Determines whether a given {@link ValueOrEnumValueOrOperation} contains at least one nested object (or an
* equivalent map with key-value pairs).
*
* @param aValue
* the value to check
* @return true or false
*/
protected boolean containsNestedObject(Object aValue) {
if (aValue == null) {
return false;
}
if (aValue instanceof Variable) {
return containsNestedObject(variableManager.get(((Variable) aValue).getName()));
} else if (aValue instanceof CustomOperation) {
try {
return containsNestedObject(valueConverter.convertValue(null, aValue, null));
} catch (UnresolvableVariableException exc) {
throw new ThisShouldNeverHappenException("Unresolvable variables should be resolved to null", exc);
} catch (UnexecutableException exc) {
// ignored here
}
} else if ((aValue instanceof NestedObject) || (aValue instanceof TypedNestedObject)) {
return true;
} else if (aValue instanceof Map<?, ?>) {
return true;
}
return false;
}
/**
* Convert a value to a string intended to be included in the textual output. This most importantly converts
* {@link UnresolvableVariable} instances to a "null" string.
*
* @param aValue
* the value to stringify
* @return the string
*/
protected String valueToString(Object aValue) {
if (aValue == null || aValue == UnresolvableVariable.getInstance()) {
return "null";
} else {
return aValue.toString();
}
}
}