/*******************************************************************************
* 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.part;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.edt.compiler.binding.IRPartBinding;
import org.eclipse.edt.compiler.core.ast.Class;
import org.eclipse.edt.compiler.core.ast.Name;
import org.eclipse.edt.compiler.core.ast.NestedFunction;
import org.eclipse.edt.compiler.internal.core.builder.IProblemRequestor;
import org.eclipse.edt.compiler.internal.core.lookup.ICompilerOptions;
import org.eclipse.edt.compiler.internal.core.validation.ExpressionValidator;
import org.eclipse.edt.compiler.internal.core.validation.annotation.AnnotationValidator;
import org.eclipse.edt.compiler.internal.core.validation.name.EGLNameValidator;
import org.eclipse.edt.compiler.internal.util.BindingUtil;
import org.eclipse.edt.mof.egl.EGLClass;
import org.eclipse.edt.mof.egl.Function;
import org.eclipse.edt.mof.egl.FunctionParameter;
import org.eclipse.edt.mof.egl.NamedElement;
import org.eclipse.edt.mof.egl.StructPart;
import org.eclipse.edt.mof.egl.Type;
import org.eclipse.edt.mof.utils.NameUtile;
public class ClassValidator extends FunctionContainerValidator {
IRPartBinding irBinding;
org.eclipse.edt.mof.egl.EGLClass classBinding;
Class clazz;
public ClassValidator(IProblemRequestor problemRequestor, IRPartBinding irBinding, ICompilerOptions compilerOptions) {
super(problemRequestor, irBinding, compilerOptions);
this.irBinding = irBinding;
this.classBinding = (org.eclipse.edt.mof.egl.EGLClass)irBinding.getIrPart();
}
@Override
public boolean visit(Class clazz) {
this.clazz = clazz;
partNode = clazz;
EGLNameValidator.validate(clazz.getName(), EGLNameValidator.CLASS, problemRequestor, compilerOptions);
new AnnotationValidator(problemRequestor, compilerOptions).validateAnnotationTarget(clazz);
clazz.accept(new ExpressionValidator(partBinding, problemRequestor, compilerOptions));
checkImplements(clazz.getImplementedInterfaces());
checkInterfaceFunctionsOverriden(classBinding);
checkAbstractFunctionsOverriden(classBinding);
checkImplicitConstructor(clazz);
if (checkExtends()) {
checkCycles();
}
return true;
}
private boolean checkExtends() {
Name extended = clazz.getExtends();
if (extended != null) {
Type type = extended.resolveType();
if (type != null && !(type instanceof org.eclipse.edt.mof.egl.EGLClass)) {
problemRequestor.acceptProblem(
extended,
IProblemRequestor.CLASS_MUST_EXTEND_CLASS,
new String[] {
type.getTypeSignature()
});
return false;
}
else if (type != null && classBinding.equals(type)) {
problemRequestor.acceptProblem(
extended,
IProblemRequestor.PART_CANNOT_EXTEND_ITSELF,
new String[] {
type.getTypeSignature()
});
return false;
}
}
return true;
}
private void checkCycles() {
Name name = clazz.getExtends();
if (name != null) {
Type type = name.resolveType();
if (type instanceof org.eclipse.edt.mof.egl.EGLClass
&& checkCycles((org.eclipse.edt.mof.egl.EGLClass)type, new HashSet<org.eclipse.edt.mof.egl.EGLClass>())) {
problemRequestor.acceptProblem(
name,
IProblemRequestor.RECURSIVE_LOOP_IN_EXTENDS,
new String[] {classBinding.getCaseSensitiveName(), name.toString()});
}
}
}
private boolean checkCycles(org.eclipse.edt.mof.egl.EGLClass classType, Set<org.eclipse.edt.mof.egl.EGLClass> seen) {
// Sometimes the binding won't be resolved yet.
classType = (org.eclipse.edt.mof.egl.EGLClass)BindingUtil.realize(classType);
if (seen.contains(classType)) {
return false;
}
if (classBinding.equals(classType)) {
return true;
}
seen.add(classType);
for (StructPart superType : classType.getSuperTypes()) {
if (superType instanceof org.eclipse.edt.mof.egl.EGLClass) {
if (checkCycles((org.eclipse.edt.mof.egl.EGLClass)superType, seen)) {
return true;
}
}
}
return false;
}
@Override
public boolean visit(NestedFunction nestedFunction) {
Function function = (Function)nestedFunction.getName().resolveElement();
if (function != null) {
Function override = getOverriddenFunction(function, classBinding);
if (override != null) {
//Check that the function does not reduce visibility of the overridden function
if (BindingUtil.isPrivate(function) && !BindingUtil.isPrivate(override)) {
problemRequestor.acceptProblem(
nestedFunction.getName(),
IProblemRequestor.CANNOT_REDUCE_VISIBILITY,
new String[] {override.getCaseSensitiveName(), ((NamedElement)override.getContainer()).getCaseSensitiveName()});
}
//Check that the function returns the same type as the overridden function
if (!BindingUtil.typesAreIdentical(function.getReturnType(), override.getReturnType()) || function.isNullable() != override.isNullable()) {
problemRequestor.acceptProblem(
nestedFunction.getName(),
IProblemRequestor.RETURN_TYPES_NOT_COMPATIBLE,
new String[] {override.getCaseSensitiveName(), ((NamedElement)override.getContainer()).getCaseSensitiveName()});
}
}
}
return super.visit(nestedFunction);
}
private Function getOverriddenFunction(Function function, EGLClass classBinding) {
//Check the hierarchy for a function with the same parm types as the one provided
if (classBinding == null) {
return null;
}
for (StructPart superType : classBinding.getSuperTypes()) {
if (superType instanceof org.eclipse.edt.mof.egl.EGLClass) {
Function override = getOverriddenFunction(function, ((EGLClass)superType).getFunctions());
if (override != null) {
return override;
}
return getOverriddenFunction(function, (EGLClass)superType);
}
}
return null;
}
private Function getOverriddenFunction(Function function, List<Function> functions) {
for (Function newFunc : functions) {
if (BindingUtil.functionSignituresAreIdentical(function, newFunc, false, false)) {
return newFunc;
}
}
return null;
}
}