/** * Copyright (c) 2014 itemis AG 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: * itemis AG - initial API and implementation * */ package org.yakindu.base.expressions.validation; import java.util.Set; import org.eclipse.emf.common.util.EList; import org.eclipse.xtext.validation.Check; import org.yakindu.base.expressions.expressions.Expression; import org.yakindu.base.types.ComplexType; import org.yakindu.base.types.GenericElement; import org.yakindu.base.types.Type; import org.yakindu.base.types.TypeParameter; import org.yakindu.base.types.TypeSpecifier; import org.yakindu.base.types.TypesPackage; import org.yakindu.base.types.inferrer.ITypeSystemInferrer; import org.yakindu.base.types.typesystem.ITypeSystem; import org.yakindu.base.types.validation.IValidationIssueAcceptor; import com.google.common.collect.Sets; import com.google.inject.Inject; /** * * @author andreas muelder - Initial contribution and API * */ public class ExpressionsJavaValidator extends org.yakindu.base.expressions.validation.AbstractExpressionsJavaValidator implements IValidationIssueAcceptor { public static final String WARNING_IS_RAW_CODE = "WarningRaw"; public static final String WARNING_IS_RAW_MSG = "%s is a raw type. References to generic type %s should be parameterized."; public static final String ERROR_NOT_GENERIC_CODE = "TypeNotGeneric"; public static final String ERROR_NOT_GENERIC_MSG = "The type %s is not generic; it cannot be parameterized with arguments %s."; public static final String ERROR_ARGUMENTED_SPECIFIER_INCORRECT_ARGUMENT_NR_CODE = "IncorrectNrOfArguments"; public static final String ERROR_ARGUMENTED_SPECIFIER_INCORRECT_ARGUMENT_NR_MSG = "Incorrect number of arguments for type %s; it cannot be parameterized with arguments %s."; public static final String ERROR_BOUND_MISSMATCH_CODE = "TypeParameterBoundMissmatch"; public static final String ERROR_BOUND_MISSMATCH_MSG = "Bound mismatch: The type %s is not a valid substitute for the bounded parameter %s of the type %s."; public static final String ERROR_DUPLICATE_TYPE_PARAMETER_CODE = "DuplicateTypeParameter"; public static final String ERROR_DUPLICATE_TYPE_PARAMETER_MSG = "Duplicate Type Parameter %s."; public static final String ERROR_CYCLE_DETECTED_CODE = "TypeExtendsItself"; public static final String ERROR_CYCLE_DETECTED_MSG = "Cycle detected: the type %s cannot extend itself."; @Inject private GenericsPrettyPrinter printer; @Inject private ITypeSystemInferrer typeInferrer; @Inject private ITypeSystem typeSystem; @Check public void checkExpression(Expression expression) { //Only infer root expressions since inferType infers the expression containment hierarchy if(!(expression.eContainer() instanceof Expression)) typeInferrer.infer(expression, this); } public void accept(ValidationIssue issue) { switch (issue.getSeverity()) { case ERROR: error(issue.getMessage(), null, issue.getIssueCode()); break; case WARNING: warning(issue.getMessage(), null, issue.getIssueCode()); break; case INFO: break; } } @Check public void checkIsRaw(TypeSpecifier typedElement) { Type type = typedElement.getType(); if (!(type instanceof GenericElement)) return; EList<TypeParameter> typeParameter = ((GenericElement) type).getTypeParameters(); if (typedElement.getTypeArguments().size() == 0 && typeParameter.size() > 0) { String s1 = typedElement.getType().getName(); String s2 = s1 + printer.concatTypeParameter(typeParameter); warning(String.format(WARNING_IS_RAW_MSG, s1, s2), typedElement, TypesPackage.Literals.TYPE_SPECIFIER__TYPE, WARNING_IS_RAW_CODE); } } @Check public void checkTypedElementNotGeneric(TypeSpecifier typedElement) { if (typedElement.getTypeArguments().size() > 0 && ((!(typedElement.getType() instanceof GenericElement)) || ((GenericElement) typedElement .getType()).getTypeParameters().size() == 0)) { String s1 = typedElement.getType().getName(); String s2 = printer.concatTypeArguments(typedElement.getTypeArguments()); error(String.format(ERROR_NOT_GENERIC_MSG, s1, s2), typedElement, TypesPackage.Literals.TYPE_SPECIFIER__TYPE, ERROR_NOT_GENERIC_CODE); } } @Check public void checkNofArguments(TypeSpecifier typedElement) { if (!(typedElement.getType() instanceof GenericElement)) { return; } GenericElement type = (GenericElement) typedElement.getType(); EList<TypeParameter> typeParameter = type.getTypeParameters(); if (typedElement.getTypeArguments().size() > 0 && (typedElement.getTypeArguments().size() != typeParameter.size()) && typeParameter.size() > 0) { String s1 = type.getName() + printer.concatTypeParameter(typeParameter); String s2 = printer.concatTypeArguments(typedElement.getTypeArguments()); error(String.format(ERROR_ARGUMENTED_SPECIFIER_INCORRECT_ARGUMENT_NR_MSG, s1, s2), typedElement, TypesPackage.Literals.TYPE_SPECIFIER__TYPE, ERROR_ARGUMENTED_SPECIFIER_INCORRECT_ARGUMENT_NR_CODE); } } @Check public void checkDuplicateTypeParameter(GenericElement type) { Set<String> names = Sets.newHashSet(); EList<TypeParameter> typeParameter = type.getTypeParameters(); for (TypeParameter param : typeParameter) { String name = param.getName(); if (names.contains(name)) { error(String.format(ERROR_DUPLICATE_TYPE_PARAMETER_MSG, name), type, TypesPackage.Literals.GENERIC_ELEMENT__TYPE_PARAMETERS, ERROR_DUPLICATE_TYPE_PARAMETER_CODE); } names.add(name); } } @Check public void checkTypeParameterBounds(TypeSpecifier typedElement) { if (!(typedElement.getType() instanceof GenericElement)) { return; } GenericElement type = (GenericElement) typedElement.getType(); EList<TypeParameter> typeParameter = type.getTypeParameters(); if (typedElement.getTypeArguments().size() == 0 || (typedElement.getTypeArguments().size() != typeParameter.size())) return; for (int i = 0; i < typeParameter.size(); i++) { TypeParameter parameter = typeParameter.get(i); if (parameter.getBound() != null) { Type argument = typedElement.getTypeArguments().get(i).getType(); if (!typeSystem.isSuperType(argument, parameter.getBound())) { error(String.format(ERROR_BOUND_MISSMATCH_MSG, argument.getName(), (parameter.getBound()).getName(), type.getName()), typedElement, TypesPackage.Literals.TYPE_SPECIFIER__TYPE_ARGUMENTS, i, ERROR_BOUND_MISSMATCH_CODE); } } } } @Check public void checkTypeNotExtendsItself(ComplexType type) { EList<ComplexType> superTypes = type.getSuperTypes(); for (ComplexType superType : superTypes) { if (superType.equals(type)) { error(String.format(ERROR_CYCLE_DETECTED_MSG, type.getName()), type, TypesPackage.Literals.COMPLEX_TYPE__SUPER_TYPES, ERROR_CYCLE_DETECTED_CODE); } } } }