/*******************************************************************************
* Copyright © 2012, 2013 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*
*******************************************************************************/
package org.eclipse.edt.compiler.internal.core.validation.type;
import java.util.List;
import java.util.Map;
import org.eclipse.edt.compiler.ASTValidator;
import org.eclipse.edt.compiler.binding.IPartBinding;
import org.eclipse.edt.compiler.binding.IRPartBinding;
import org.eclipse.edt.compiler.binding.ITypeBinding;
import org.eclipse.edt.compiler.core.ast.AbstractASTExpressionVisitor;
import org.eclipse.edt.compiler.core.ast.AbstractASTVisitor;
import org.eclipse.edt.compiler.core.ast.ArrayType;
import org.eclipse.edt.compiler.core.ast.Expression;
import org.eclipse.edt.compiler.core.ast.ParenthesizedExpression;
import org.eclipse.edt.compiler.core.ast.TernaryExpression;
import org.eclipse.edt.compiler.core.ast.Type;
import org.eclipse.edt.compiler.internal.core.builder.IProblemRequestor;
import org.eclipse.edt.compiler.internal.core.lookup.ICompilerOptions;
import org.eclipse.edt.compiler.internal.util.BindingUtil;
import org.eclipse.edt.mof.egl.AnnotationType;
import org.eclipse.edt.mof.egl.Constructor;
import org.eclipse.edt.mof.egl.ParameterizedType;
import org.eclipse.edt.mof.egl.StructPart;
import org.eclipse.edt.mof.egl.utils.TypeUtils;
public class TypeValidator {
public static void validate(Type type, final IPartBinding declaringPart, final IProblemRequestor problemRequestor, final ICompilerOptions compilerOptions) {
if (type == null) {
return;
}
// First make sure the type can be used by EGL code (e.g. you cannot declare a field whose type is a program).
org.eclipse.edt.mof.egl.Type typeBinding = type.getBaseType().resolveType();
if (typeBinding != null) {
boolean valid = !TypeUtils.isStaticType(typeBinding);
if (valid) {
// Annotations can be fields only inside other annotations.
valid = !(typeBinding instanceof AnnotationType) || (declaringPart instanceof IRPartBinding && ((IRPartBinding)declaringPart).getIrPart() instanceof AnnotationType);
}
if (!valid) {
problemRequestor.acceptProblem(type,
IProblemRequestor.DATA_DECLARATION_HAS_INCORRECT_TYPE,
new String[] {typeBinding.getTypeSignature()});
return;
}
}
type.accept(new AbstractASTVisitor() {
@Override
public boolean visit(org.eclipse.edt.compiler.core.ast.NameType nameType) {
List<ASTValidator> validators = declaringPart.getEnvironment().getCompiler().getValidatorsFor(nameType);
if (validators != null && validators.size() > 0) {
for (ASTValidator validator : validators) {
validator.validate(nameType, declaringPart, problemRequestor, compilerOptions);
}
}
return false;
};
});
}
public static void validateInstantiatable(Type type, IPartBinding declaringPart, boolean isNullable, IProblemRequestor problemRequestor) {
// Non-nullable reference types must have a public default constructor in order to be instantiatable. They also cannot be abstract.
if (!isNullable) {
boolean isValid = true;
org.eclipse.edt.mof.egl.Type typeBinding = type.resolveType();
if (typeBinding != null && !(typeBinding instanceof ParameterizedType)
&& TypeUtils.isReferenceType(typeBinding) && !hasPublicDefaultConstructor(typeBinding)) {
// Don't need to throw error if the field is in an ExternalType, or if the field's type is the same as the declaring part's type.
if (declaringPart == null || (declaringPart.getKind() != ITypeBinding.EXTERNALTYPE_BINDING
&& !(declaringPart instanceof IRPartBinding && typeBinding.equals(((IRPartBinding)declaringPart).getIrPart())))) {
isValid = false;
}
}
if (isValid && BindingUtil.isAbstract(typeBinding)) {
isValid = false;
}
if (!isValid) {
problemRequestor.acceptProblem(type,
IProblemRequestor.TYPE_NOT_INSTANTIABLE,
new String[] {type.getCanonicalName()});
}
}
}
public static boolean hasPublicDefaultConstructor(org.eclipse.edt.mof.egl.Type typeBinding) {
if (typeBinding != null && typeBinding.getClassifier() instanceof StructPart) {
List<Constructor> constructors = ((StructPart)typeBinding.getClassifier()).getConstructors();
// Lack of explicit constructors means it has a default.
if (constructors.size() == 0) {
return true;
}
for (Constructor con : constructors) {
if (con.getParameters().size() == 0 && !BindingUtil.isPrivate(con)) {
return true;
}
}
}
return false;
}
public static void validateTypeDeclaration(Type type, IPartBinding declaringPart, IProblemRequestor problemRequestor) {
if (type.isArrayType()) {
while (type.isArrayType()) {
if (((ArrayType)type).hasInitialSize()) {
problemRequestor.acceptProblem(
type,
IProblemRequestor.ARRAY_DIMENSION_NOT_ALLOWED);
}
type = ((ArrayType)type).getElementType();
}
}
}
/**
* Finds all the expressions that should be validated against another type. This is typically just the
* expr as-is, except that for ternary expressions we need to check both the second and third exprs against
* some other type (including any nested ternary exprs - e.g. second expr is itself another ternary). Any generic
* types for the expressions will already be resolved.
*/
public static void collectExprsForTypeCompatibility(Expression rootExpr, final Map<Expression, org.eclipse.edt.mof.egl.Type> exprMap) {
rootExpr.accept(new AbstractASTExpressionVisitor() {
@Override
public boolean visit(TernaryExpression ternaryExpression) {
ternaryExpression.getSecondExpr().accept(this);
ternaryExpression.getThirdExpr().accept(this);
return false;
};
@Override
public boolean visit(ParenthesizedExpression parenthesizedExpression) {
return true;
};
@Override
public boolean visitExpression(Expression expression) {
exprMap.put(expression, BindingUtil.resolveGenericType(expression.resolveType(), expression));
return false;
}
});
}
}