/******************************************************************************* * Copyright © 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.mof.eglx.jtopen.validation; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.edt.compiler.binding.IRPartBinding; import org.eclipse.edt.compiler.binding.IValidationProxy; import org.eclipse.edt.compiler.core.ast.NestedFunction; import org.eclipse.edt.compiler.core.ast.ReturnsDeclaration; import org.eclipse.edt.compiler.core.ast.Statement; import org.eclipse.edt.compiler.internal.core.builder.IMarker; import org.eclipse.edt.compiler.internal.core.builder.IProblemRequestor; import org.eclipse.edt.compiler.internal.core.validation.AbstractFunctionValidator; import org.eclipse.edt.compiler.internal.core.validation.annotation.AnnotationValidator; import org.eclipse.edt.compiler.internal.util.BindingUtil; import org.eclipse.edt.mof.egl.Annotation; import org.eclipse.edt.mof.egl.ArrayType; import org.eclipse.edt.mof.egl.Field; import org.eclipse.edt.mof.egl.Function; import org.eclipse.edt.mof.egl.FunctionParameter; import org.eclipse.edt.mof.egl.Handler; import org.eclipse.edt.mof.egl.IrFactory; import org.eclipse.edt.mof.egl.Library; import org.eclipse.edt.mof.egl.Member; import org.eclipse.edt.mof.egl.NamedElement; import org.eclipse.edt.mof.egl.NullLiteral; import org.eclipse.edt.mof.egl.Part; import org.eclipse.edt.mof.egl.Program; import org.eclipse.edt.mof.egl.Record; import org.eclipse.edt.mof.egl.Service; import org.eclipse.edt.mof.egl.Type; import org.eclipse.edt.mof.egl.utils.TypeUtils; import org.eclipse.edt.mof.eglx.jtopen.ext.Utils; import org.eclipse.edt.mof.eglx.jtopen.messages.IBMiResourceKeys; import org.eclipse.edt.mof.impl.AbstractVisitor; import org.eclipse.edt.mof.utils.EList; import org.eclipse.edt.mof.utils.NameUtile; public class IBMiFunctionValidator extends AbstractFunctionValidator{ private Annotation annotation; private NestedFunction nestedFunction; private Map<Object, Object> parameterAnnotations; public boolean visit(NestedFunction nestedFunction){ if (nestedFunction.getName().resolveMember() instanceof Function) { validateContainerIsCorrect(((IRPartBinding)declaringPart).getIrPart(), nestedFunction); validateFunctionBodyIsEmpty((Function)nestedFunction.getName().resolveMember(), nestedFunction); this.nestedFunction = nestedFunction; annotation = nestedFunction.getName().resolveMember().getAnnotation("eglx.jtopen.annotations.IBMiProgram"); Object obj = annotation.getValue("parameterAnnotations"); if(((EList<?>)obj).size() > 0){ if (((EList<?>)obj).size() != nestedFunction.getFunctionParameters().size()) { problemRequestor.acceptProblem(nestedFunction, IBMiResourceKeys.WRONG_NUMBER_OF_PARAMETER_ANNOTATIONS, IMarker.SEVERITY_ERROR, new String[] {nestedFunction.getName().getCaseSensitiveIdentifier()}, IBMiResourceKeys.getResourceBundleForKeys()); } } else{ for(Object o : nestedFunction.getFunctionParameters()){ ((EList)obj).add(IrFactory.INSTANCE.createNullLiteral()); } } int size = Math.min(((EList<?>)obj).size(), nestedFunction.getFunctionParameters().size()); parameterAnnotations = new HashMap<Object, Object>(size); for(int idx = 0; idx < size; idx++){ parameterAnnotations.put(nestedFunction.getFunctionParameters().get(idx), ((EList<?>)obj).get(idx)); } } return true; } @Override public boolean visit(final org.eclipse.edt.compiler.core.ast.FunctionParameter functionParameter) { Member parm = functionParameter.getName().resolveMember(); if (parm.getType() != null) { Object parmAnn = parameterAnnotations.get(functionParameter); //if a parameterAnnotationArray entry exists for the parm, the type will have already been validated boolean requiresAS400TypeAnnotationTest = true; if (parmAnn != null && !(parmAnn instanceof NullLiteral)) { if(parmAnn instanceof Annotation){ IValidationProxy proxy = AnnotationValidator.getValidationProxy((Annotation)parmAnn); if (proxy != null) { for (org.eclipse.edt.compiler.binding.AnnotationValidationRule rule : proxy.getAnnotationValidators()) { requiresAS400TypeAnnotationTest = false; Map<String, Object> annotations = new HashMap<String, Object>(1); annotations.put(NameUtile.getAsName(((Annotation) parmAnn).getEClass().getETypeSignature()), parmAnn); rule.validate(functionParameter, functionParameter, parm, annotations, problemRequestor, compilerOptions); } } } else{ requiresAS400TypeAnnotationTest = false; problemRequestor.acceptProblem(functionParameter, IBMiResourceKeys.PARAMETER_ANNOTATION_INVALID, IMarker.SEVERITY_ERROR, new String[] {parmAnn.toString(), functionParameter.getName().getCaseSensitiveIdentifier()}, IBMiResourceKeys.getResourceBundleForKeys()); } } if(requiresAS400TypeAnnotationTest && Utils.requiresAS400TypeAnnotation(parm.getType())) { problemRequestor.acceptProblem(functionParameter, IBMiResourceKeys.PROGRAM_PARAMETER_ANNOTATION_REQUIRED, IMarker.SEVERITY_ERROR, new String[] {parm.getCaseSensitiveName()}, IBMiResourceKeys.getResourceBundleForKeys()); } if (!Utils.isValidAS400Type(parm.getType())) { problemRequestor.acceptProblem(functionParameter, IBMiResourceKeys.IBMIPROGRAM_PARM_TYPE_INVALID, IMarker.SEVERITY_ERROR, new String[] {parm.getCaseSensitiveName()}, IBMiResourceKeys.getResourceBundleForKeys()); } if (parm.isNullable()) { problemRequestor.acceptProblem(functionParameter, IBMiResourceKeys.IBMIPROGRAM_NULLABLE_PARM_INVALID, IMarker.SEVERITY_ERROR, new String[] {BindingUtil.getTypeName(parm), parm.getCaseSensitiveName()}, IBMiResourceKeys.getResourceBundleForKeys()); } if (parm.getType() instanceof ArrayType && ((ArrayType)parm.getType()).getElementType() != null){ if (((ArrayType)parm.getType()).elementsNullable()) { problemRequestor.acceptProblem(functionParameter, IBMiResourceKeys.IBMIPROGRAM_ARRAY_NULLABLE_PARM_INVALID, IMarker.SEVERITY_ERROR, new String[] {BindingUtil.getShortTypeString(((ArrayType)parm.getType()).getElementType(), true), parm.getCaseSensitiveName()}, IBMiResourceKeys.getResourceBundleForKeys()); } } if(parm.getType() instanceof Record || parm.getType() instanceof Handler || parm.getType() instanceof ArrayType){ parm.getType().accept(new ComplexTypes((FunctionParameter)parm, functionParameter)); } } return false; } protected class ComplexTypes extends AbstractVisitor{ private FunctionParameter parameter; private org.eclipse.edt.compiler.core.ast.FunctionParameter functionParameter; private List<String> analyzedTypes = new ArrayList<String>(); public ComplexTypes(FunctionParameter parameter, org.eclipse.edt.compiler.core.ast.FunctionParameter functionParameter) { this.parameter = parameter; this.functionParameter = functionParameter; } public boolean visit(Type type) { if(!analyzedTypes.contains(type.getTypeSignature()) && (type instanceof Record || type instanceof Handler)){ analyzedTypes.add(type.getTypeSignature()); return true; } return false; } public boolean visit(ArrayType arrayType) { arrayType.getElementType().accept(this); return false; } public boolean visit(Field field){ validateField(parameter, field); return true; } private void validateField(FunctionParameter parm, Field field) { if (field.getType() == null) { return; } String containerName = ""; if(field.getContainer() instanceof NamedElement){ containerName = ((NamedElement)field.getContainer()).getCaseSensitiveName(); } if (!Utils.isValidAS400Type(field.getType())) { problemRequestor.acceptProblem(functionParameter, IBMiResourceKeys.IBMIPROGRAM_PARM_STRUCT_TYPE_INVALID, IMarker.SEVERITY_ERROR, new String[] {parm.getCaseSensitiveName(), containerName, field.getCaseSensitiveName(), BindingUtil.getTypeName(field)}, IBMiResourceKeys.getResourceBundleForKeys()); return; } if (field.isNullable()) { problemRequestor.acceptProblem(functionParameter, IBMiResourceKeys.IBMIPROGRAM_NULLABLE_PARM_STRUCT_INVALID, IMarker.SEVERITY_ERROR, new String[] {parm.getCaseSensitiveName(), containerName, field.getCaseSensitiveName(), BindingUtil.getTypeName(field)}, IBMiResourceKeys.getResourceBundleForKeys()); return; } if (field.getType() instanceof ArrayType && ((ArrayType)field.getType()).getElementType() != null){ if (((ArrayType)field.getType()).elementsNullable()) { problemRequestor.acceptProblem(functionParameter, IBMiResourceKeys.IBMIPROGRAM_ARRAY_NULLABLE_PARM_STRUCT_INVALID, IMarker.SEVERITY_ERROR, new String[] {parm.getCaseSensitiveName(), containerName, field.getCaseSensitiveName(), BindingUtil.getTypeName(field)}, IBMiResourceKeys.getResourceBundleForKeys()); return; } } if (!hasStructAnnotation(field) && Utils.requiresAS400TypeAnnotation(field.getType())) { problemRequestor.acceptProblem(functionParameter, IBMiResourceKeys.IBMIPROGRAM_PARM_STRUCT_REQUIRES_AS400, IMarker.SEVERITY_ERROR, new String[] {parm.getCaseSensitiveName(), containerName, field.getCaseSensitiveName()}, IBMiResourceKeys.getResourceBundleForKeys()); } } private boolean hasStructAnnotation(Field binding) { for(Annotation annotation : binding.getAnnotations()) { if(NameUtile.equals("eglx.jtopen.annotations", annotation.getEClass().getPackageName())){ return true; } } return false; } } @Override public boolean visit(ReturnsDeclaration returnsDeclaration) { Type returnType = returnsDeclaration.getType() != null ? returnsDeclaration.getType().resolveType() : null; if (returnType != null) { //Returns is only valid for service programs Object isServiceProgram = annotation.getValue("isServiceProgram"); if (isServiceProgram == null || (isServiceProgram instanceof Boolean && !(Boolean)isServiceProgram) || (isServiceProgram instanceof org.eclipse.edt.compiler.core.Boolean && !((org.eclipse.edt.compiler.core.Boolean)isServiceProgram).booleanValue())) { problemRequestor.acceptProblem(returnsDeclaration, IBMiResourceKeys.IBMIPROGRAM_ONLY_SERVICE_CAN_RETURN, IMarker.SEVERITY_ERROR, new String[] {nestedFunction.getName().getCaseSensitiveIdentifier()}, IBMiResourceKeys.getResourceBundleForKeys()); } if (!TypeUtils.Type_INT.equals(returnType)) { problemRequestor.acceptProblem(returnsDeclaration, IBMiResourceKeys.IBMIPROGRAM_CAN_ONLY_RETURN_INT, IMarker.SEVERITY_ERROR, new String[] {nestedFunction.getName().getCaseSensitiveIdentifier()}, IBMiResourceKeys.getResourceBundleForKeys()); } } return false; } private void validateFunctionBodyIsEmpty(Function function, NestedFunction nestedFunction) { if (nestedFunction != null && nestedFunction.getStmts() != null) { for(Statement stmt : nestedFunction.getStmts()) { problemRequestor.acceptProblem((Statement)stmt, IProblemRequestor.PROXY_FUNCTIONS_CANNOT_HAVE_STMTS, IMarker.SEVERITY_ERROR, new String[] {function.getCaseSensitiveName()}); } } } private void validateContainerIsCorrect(Part part, NestedFunction errorNode) { // Only allowed on function of Programs, Libraries, Services, and Basic Handlers if (part != null) { if (part instanceof Program) { return; } if (part instanceof Library) { return; } if (part instanceof Service) { return; } if (part instanceof org.eclipse.edt.mof.egl.Class) { return; } if (part instanceof Handler && ((Handler) part).getStereotype() == null) { //must be basic handler! return; } } //If we got this far, the container is invalid! problemRequestor.acceptProblem(errorNode, IBMiResourceKeys.IBMIPROGRAM_CONTAINER_INVALID, IMarker.SEVERITY_ERROR, new String[] {errorNode.getName().getCaseSensitiveIdentifier()}, IBMiResourceKeys.getResourceBundleForKeys()); } }