package org.jnario.typing;
import static org.jnario.jvmmodel.DoubleArrowSupport.isDoubleArrow;
import java.util.List;
import org.eclipse.xtend.core.typesystem.XtendTypeComputer;
import org.eclipse.xtext.common.types.JvmOperation;
import org.eclipse.xtext.common.types.JvmPrimitiveType;
import org.eclipse.xtext.common.types.JvmTypeReference;
import org.eclipse.xtext.common.types.util.TypeReferences;
import org.eclipse.xtext.xbase.XAbstractFeatureCall;
import org.eclipse.xtext.xbase.XBinaryOperation;
import org.eclipse.xtext.xbase.XExpression;
import org.eclipse.xtext.xbase.XNullLiteral;
import org.eclipse.xtext.xbase.typesystem.computation.IFeatureLinkingCandidate;
import org.eclipse.xtext.xbase.typesystem.computation.ITypeComputationState;
import org.eclipse.xtext.xbase.typesystem.conformance.ConformanceHint;
import org.eclipse.xtext.xbase.typesystem.util.CommonTypeComputationServices;
import org.jnario.Assertion;
import org.jnario.Should;
import org.jnario.ShouldThrow;
import com.google.inject.Inject;
public class JnarioTypeComputer extends XtendTypeComputer {
@Inject
CommonTypeComputationServices services;
@Inject
TypeReferences typeReferences;
@Override
public void computeTypes(XExpression expression, ITypeComputationState state) {
if (expression instanceof Assertion) {
_computeType((Assertion) expression, state);
} else if (expression instanceof ShouldThrow) {
_computeType((ShouldThrow) expression, state);
} else {
super.computeTypes(expression, state);
}
}
private void _computeType(ShouldThrow expression,
ITypeComputationState state) {
ITypeComputationState expressionState = state.withoutExpectation();
expressionState.computeTypes(expression.getExpression());
state.acceptActualType(getPrimitiveVoid(state),
ConformanceHint.NO_IMPLICIT_RETURN);
}
private void _computeType(Assertion assertion, ITypeComputationState state) {
ITypeComputationState conditionExpectation = state
.withExpectation(getTypeForName(Boolean.TYPE, state));
conditionExpectation.computeTypes(assertion.getExpression());
}
protected void _computeTypes(final XAbstractFeatureCall featureCall,
ITypeComputationState state) {
if (isShouldBeNull(featureCall) || isDoubleArrowNull(featureCall)) {
List<? extends IFeatureLinkingCandidate> candidates = state.getLinkingCandidates(featureCall);
for (IFeatureLinkingCandidate candidate : candidates) {
if (candidate.getFeature() instanceof JvmOperation) {
JvmOperation operation = (JvmOperation) candidate.getFeature();
if(canHandleNullArg(operation)){
candidate.applyToComputationState();
return;
}
}
}
}
super._computeTypes(featureCall, state);
}
private boolean canHandleNullArg(JvmOperation operation) {
if(operation.getParameters().size() != 2){
return false;
}
JvmTypeReference parameterType = operation.getParameters().get(1).getParameterType();
if(parameterType == null){
return false;
}
return !(parameterType.getType() instanceof JvmPrimitiveType);
}
private boolean isDoubleArrowNull(final XAbstractFeatureCall featureCall) {
return isDoubleArrow(featureCall) && ((XBinaryOperation)featureCall).getRightOperand() instanceof XNullLiteral;
}
private boolean isShouldBeNull(final XAbstractFeatureCall featureCall) {
return featureCall instanceof Should && ((Should) featureCall).getRightOperand() instanceof XNullLiteral;
}
}