/** * Copyright (C) 2010 France Telecom * Copyright (C) 2013 Schneider-Electric * * This file is part of "Mind Compiler" is free software: you can redistribute * it and/or modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Contact: mind@ow2.org * * Authors: Matthieu ANNE * Contributors: Stephane SEYVOZ */ package org.ow2.mind.preproc; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Logger; import org.antlr.runtime.Token; import org.objectweb.fractal.adl.ADLException; import org.objectweb.fractal.adl.Definition; import org.objectweb.fractal.adl.error.BasicErrorLocator; import org.objectweb.fractal.adl.error.NodeErrorLocator; import org.objectweb.fractal.adl.interfaces.Interface; import org.objectweb.fractal.adl.interfaces.InterfaceContainer; import org.objectweb.fractal.adl.types.TypeInterface; import org.objectweb.fractal.adl.types.TypeInterfaceUtil; import org.objectweb.fractal.adl.util.FractalADLLogManager; import org.ow2.mind.adl.ast.ASTHelper; import org.ow2.mind.adl.ast.Data; import org.ow2.mind.adl.ast.DataField; import org.ow2.mind.adl.ast.ImplementationContainer; import org.ow2.mind.adl.ast.Source; import org.ow2.mind.adl.idl.InterfaceDefinitionDecorationHelper; import org.ow2.mind.adl.implementation.ImplementationLocator; import org.ow2.mind.adl.membrane.ast.Controller; import org.ow2.mind.adl.membrane.ast.ControllerContainer; import org.ow2.mind.adl.membrane.ast.ControllerInterface; import org.ow2.mind.error.ErrorManager; import org.ow2.mind.idl.ast.IDLASTHelper; import org.ow2.mind.idl.ast.InterfaceDefinition; import org.ow2.mind.io.OutputFileLocator; public class CPLChecker { protected final Definition definition; protected final ErrorManager errorManager; protected final ImplementationLocator implLocatorItf; protected final OutputFileLocator outputFileLocatorItf; protected final Data data; protected boolean prvDeclared = false; protected static Logger logger = FractalADLLogManager .getLogger("MPP"); protected Map<String, List<String>> declaredItfMethMap = new HashMap<String, List<String>>(); protected final Map<Object, Object> context; public CPLChecker(final ErrorManager errorManager, final ImplementationLocator implLocatorItf, final OutputFileLocator outputFileLocatorItf, final Definition definition, final Map<Object, Object> context) { this.errorManager = errorManager; this.implLocatorItf = implLocatorItf; this.outputFileLocatorItf = outputFileLocatorItf; this.definition = definition; this.context = context; this.data = (definition instanceof ImplementationContainer) ? ((ImplementationContainer) definition).getData() : null; } public void prvDecl(final String structContent, final String sourceFile) { prvDeclared = true; } // returns 'true' is the accessed field is a DataField declared in definition. public boolean prvAccess(final Token fieldName, final String sourceFile) throws ADLException { if (definition == null) { // Add this condition so that the testNG will not throw exceptions // (stand-alone node) return false; } if (definition instanceof ImplementationContainer) { for (final DataField dataField : ((ImplementationContainer) definition) .getDataFields()) { if (fieldName.getText().equals(dataField.getName())) { return true; } } // field name not found in DataFields if (data != null) { // component also declare a PRIVATE structure. assumes that field is in // this structure return false; } // component does not have PRIVATE structure, the field name is invalid. errorManager.logError(MPPErrors.UNKNOWN_DATAFIELD, locator(fieldName, sourceFile), fieldName.getText()); } return false; } public void serverMethDef(final Token itfName, final String itfIdx, final Token methName, final String sourceFile) throws ADLException { if (definition == null) { // Add this condition so that the testNG will not throw exceptions // (stand-alone node) return; } if (data != null && !prvDeclared) { errorManager.logError(MPPErrors.MISSING_PRIVATE_DECLARATION, (data .getPath() == null) ? new NodeErrorLocator(data) : new BasicErrorLocator(data.getPath(), -1, -1), data.getPath()); } final Interface itf = ASTHelper.getInterface(definition, itfName.getText()); if (itf == null) { errorManager.logError(MPPErrors.UNKNOWN_INTERFACE, locator(itfName, sourceFile), itfName.getText()); return; } if (!TypeInterfaceUtil.isServer(itf)) { errorManager.logError(MPPErrors.INVALID_CLIENT_INTERFACE, locator(itfName, sourceFile), itfName.getText(), methName.getText()); return; } // assume that itfDef is already loaded final InterfaceDefinition itfDef = InterfaceDefinitionDecorationHelper .getResolvedInterfaceDefinition((TypeInterface) itf, null, null); if (IDLASTHelper.getMethod(itfDef, methName.getText()) == null) { errorManager.logError(MPPErrors.UNKNOWN_METHOD, locator(methName, sourceFile), itfName.getText(), methName.getText()); } // to avoid rewriting the grammar StringBuilder idxSB = null; Integer idxInt = null; if (itfIdx != null) idxSB = new StringBuilder().append(itfIdx); checkIdx(itf, itfName, idxSB, sourceFile); if (itfIdx != null) { try { idxInt = Integer.parseInt(itfIdx); } catch (final NumberFormatException e) { // ignore, idx is not a number literal } } if (!TypeInterfaceUtil.isCollection(itf)) ImplementedMethodsHelper.addImplementedMethod(itf, methName.getText()); else ImplementedMethodsHelper.addCollectionImplementedMethod(itf, idxInt, methName.getText()); } public void itfMethCall(final Token itfName, final Token methName, final String sourceFile) throws ADLException { if (definition == null) { // Add this condition so that the testNG will not throw exceptions // (stand-alone node) return; } final Interface itf = ASTHelper.getInterface(definition, itfName.getText()); if (itf == null) { errorManager.logError(MPPErrors.UNKNOWN_INTERFACE, locator(itfName, sourceFile), itfName.getText()); return; } // assume that itfDef is already loaded final InterfaceDefinition itfDef = InterfaceDefinitionDecorationHelper .getResolvedInterfaceDefinition((TypeInterface) itf, null, null); if (IDLASTHelper.getMethod(itfDef, methName.getText()) == null) { errorManager.logError(MPPErrors.UNKNOWN_METHOD, locator(methName, sourceFile), itfName.getText(), methName.getText()); } } public void attAccess(final Token attributeName, final String sourceFile) throws ADLException { if (definition == null) { // Add this condition so that the testNG will not throw exceptions // (stand-alone node) return; } if (ASTHelper.getAttribute(definition, attributeName.getText()) == null) { errorManager.logError(MPPErrors.UNKNOWN_ATTRIBUTE, locator(attributeName, sourceFile), attributeName.getText()); } } public void collItfMethCall(final Token itfName, final Token methName, final StringBuilder idx, final String sourceFile) throws ADLException { if (definition == null) { // Add this condition so that the testNG will not throw exceptions // (stand-alone node) return; } final Interface itf = ASTHelper.getInterface(definition, itfName.getText()); if (itf == null) { errorManager.logError(MPPErrors.UNKNOWN_INTERFACE, locator(itfName, sourceFile), itfName.getText()); return; } // assume that itfDef is already loaded final InterfaceDefinition itfDef = InterfaceDefinitionDecorationHelper .getResolvedInterfaceDefinition((TypeInterface) itf, null, null); if (IDLASTHelper.getMethod(itfDef, methName.getText()) == null) { errorManager.logError(MPPErrors.UNKNOWN_METHOD, locator(methName, sourceFile), itfName.getText(), methName.getText()); } checkIdx(itf, itfName, idx, sourceFile); } public void getMyItf(final Token itfName, final StringBuilder idx, final String sourceFile) throws ADLException { if (definition == null) { // Add this condition so that the testNG will not throw exceptions // (stand-alone node) return; } final Interface itf = ASTHelper.getInterface(definition, itfName.getText()); if (itf == null) { errorManager.logError(MPPErrors.UNKNOWN_INTERFACE, locator(itfName, sourceFile), itfName.getText()); return; } checkIdx(itf, itfName, idx, sourceFile); } public void bindMyItf(final Token itfName, final StringBuilder idx, final String sourceFile) throws ADLException { if (definition == null) { // Add this condition so that the testNG will not throw exceptions // (stand-alone node) return; } final Interface itf = ASTHelper.getInterface(definition, itfName.getText()); if (itf == null) { errorManager.logError(MPPErrors.UNKNOWN_INTERFACE, locator(itfName, sourceFile), itfName.getText()); return; } checkIdx(itf, itfName, idx, sourceFile); } public void isBound(final Token itfName, final StringBuilder idx, final String sourceFile) throws ADLException { if (definition == null) { // Add this condition so that the testNG will not throw exceptions // (stand-alone node) return; } final Interface itf = ASTHelper.getInterface(definition, itfName.getText()); if (itf == null) { errorManager.logError(MPPErrors.UNKNOWN_INTERFACE, locator(itfName, sourceFile), itfName.getText()); return; } checkIdx(itf, itfName, idx, sourceFile); } public void getCollectionSize(final Token itfName, final String sourceFile) throws ADLException { if (definition == null) { // Add this condition so that the testNG will not throw exceptions // (stand-alone node) return; } final Interface itf = ASTHelper.getInterface(definition, itfName.getText()); if (itf == null) { errorManager.logError(MPPErrors.UNKNOWN_INTERFACE, locator(itfName, sourceFile), itfName.getText()); return; } } protected void checkIdx(final Interface itf, final Token itfToken, final StringBuilder idx, final String sourceFile) throws ADLException { if (idx == null) { if (TypeInterfaceUtil.isCollection(itf)) { errorManager.logError(MPPErrors.INVALID_INTERFACE_MISSING_INDEX, locator(itfToken, sourceFile), itf.getName()); } } else { if (!TypeInterfaceUtil.isCollection(itf)) { errorManager.logError(MPPErrors.INVALID_INTERFACE_NOT_A_COLLECTION, locator(itfToken, sourceFile), itf.getName()); } if (idx.length() <= 2) { return; } final String index = idx.substring(1, idx.length() - 1).trim(); try { final int i = Integer.parseInt(index); if (i < 0 || i >= ASTHelper.getNumberOfElement(itf)) { errorManager.logError(MPPErrors.INVALID_INDEX, locator(itfToken, sourceFile), itf.getName(), index); } } catch (final NumberFormatException e) { // ignore, idx is not a number literal } } } protected BasicErrorLocator locator(final Token token, final String sourceFile) { return new BasicErrorLocator(sourceFile, token.getLine(), token.getCharPositionInLine()); } public void postParseChecks(final String sourceFile) throws ADLException { // this means we aren't in standard compilation // probably CPL-Preproc direct parser tests if (definition == null) return; // handling membrane of composites if (!ASTHelper.isPrimitive(definition)) return; // mark file as visited final Source source = ImplementedMethodsHelper.getDefinitionSourceFromPath( implLocatorItf, outputFileLocatorItf, definition, sourceFile, context); // should never happen, except maybe for generated code issued from // architecture transformations, and added in definition ? if (source == null) return; ImplementedMethodsHelper.setSourceVisited(source); // was it the last one ? if yes, we run a full methods check if (definition instanceof ImplementationContainer && definition instanceof InterfaceContainer) if (ImplementedMethodsHelper .haveAllSourcesBeenVisited((ImplementationContainer) definition)) { final Map<Interface, List<String>> allUnimplementedMethods = new HashMap<Interface, List<String>>(); final Map<Interface, Map<Integer, List<String>>> allCollectionUnimplementedMethods = new HashMap<Interface, Map<Integer, List<String>>>(); logger .fine("All of " + definition.getName() + " sources visited - Now checking if all provided methods were implemented..."); for (final Interface currItf : ((InterfaceContainer) definition) .getInterfaces()) if ((currItf instanceof TypeInterface) && ((TypeInterface) currItf).getRole().equals( TypeInterface.SERVER_ROLE) && !isControllerInterface(currItf.getName())) { if (!TypeInterfaceUtil.isCollection(currItf)) { final List<String> unimplementedMethodsList = ImplementedMethodsHelper .getInterfaceUnimplementedMethods(currItf); if (!unimplementedMethodsList.isEmpty()) allUnimplementedMethods.put(currItf, unimplementedMethodsList); } else { final Map<Integer, List<String>> unimplementedMethodsMap = ImplementedMethodsHelper .getCollectionInterfaceUnimplementedMethods(currItf); if (!unimplementedMethodsMap.isEmpty()) { allCollectionUnimplementedMethods.put(currItf, unimplementedMethodsMap); } } } if (allUnimplementedMethods.isEmpty() && allCollectionUnimplementedMethods.isEmpty()) logger.fine("All methods were correctly implemented."); else if (!allUnimplementedMethods.isEmpty()) { final Set<Interface> interfaces = allUnimplementedMethods.keySet(); final Interface itf0 = (Interface) interfaces.toArray()[0]; // Show missing methods from the first concerned interface errorManager.logError(MPPErrors.MISSING_METHOD_DECLARATION, definition.getName(), itf0.getName(), allUnimplementedMethods.get(itf0)); } else if (!allCollectionUnimplementedMethods.isEmpty()) { final Set<Interface> interfaces = allCollectionUnimplementedMethods .keySet(); final Interface itf0 = (Interface) interfaces.toArray()[0]; final Map<Integer, List<String>> unimplMethsByIdxMap = allCollectionUnimplementedMethods .get(itf0); final Set<Integer> indexes = unimplMethsByIdxMap.keySet(); final Integer idx0 = (Integer) indexes.toArray()[0]; // Show missing methods from the first concerned interface errorManager.logError(MPPErrors.MISSING_COLL_METHOD_DECLARATION, definition.getName(), itf0.getName(), idx0.toString(), unimplMethsByIdxMap.get(idx0)); } } } private boolean isControllerInterface(final String currItf) { /* * Check if we host controllers (METH-s will be generated so they can't be * found in Source-s) Inspired from * AbstractControllerADLLoaderAnnotationProcessor */ if (definition instanceof ControllerContainer) { for (final Controller ctrl : ((ControllerContainer) definition) .getControllers()) { for (final ControllerInterface ctrlItf : ctrl.getControllerInterfaces()) { if (ctrlItf.getName().equals(currItf)) { return true; } } } } return false; } }