/** * Copyright (C) 2009 STMicroelectronics * Copyright (C) 2014 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 Leclercq * Contributors: Stephane Seyvoz */ package org.ow2.mind.adl; import static org.ow2.mind.adl.ast.ASTHelper.isComposite; import static org.ow2.mind.adl.ast.ASTHelper.isPrimitive; import static org.ow2.mind.adl.ast.ASTHelper.isType; import java.util.Map; import org.objectweb.fractal.adl.ADLException; import org.objectweb.fractal.adl.CompilerError; import org.objectweb.fractal.adl.Definition; import org.objectweb.fractal.adl.Loader; import org.objectweb.fractal.adl.Node; import org.objectweb.fractal.adl.components.ComponentErrors; import org.objectweb.fractal.adl.error.GenericErrors; import org.objectweb.fractal.adl.error.NodeErrorLocator; import org.objectweb.fractal.adl.merger.MergeException; import org.objectweb.fractal.adl.merger.NodeMerger; import org.ow2.mind.adl.ast.ASTHelper; import org.ow2.mind.adl.ast.AbstractDefinition; import org.ow2.mind.adl.ast.DefinitionReference; import org.ow2.mind.adl.ast.ExtendsDecoration; import org.ow2.mind.adl.ast.MindDefinition; import org.ow2.mind.adl.ast.SubDefinitionsDecoration; import org.ow2.mind.error.ErrorManager; import com.google.inject.Inject; import com.google.inject.name.Named; /** * This delegating loader merges a definitions with the definitions it extends. */ public class ExtendsLoader extends AbstractDelegatingLoader { @Inject protected ErrorManager errorManagerItf; /** * The name of the {@link DefinitionReferenceResolver} to be injected in this * class. */ public static final String EXTENDS_DEFINITION_RESOLVER = "ExtendsDefinitionResolver"; @Inject @Named(EXTENDS_DEFINITION_RESOLVER) protected DefinitionReferenceResolver definitionReferenceResolverItf; /** * The name of the {@link NodeMerger} to be injected in this class. */ public static final String EXTENDS_NODE_MERGER = "ExtendsNodeMerger"; @Inject @Named(EXTENDS_NODE_MERGER) protected NodeMerger nodeMergerItf; /** * The name of the {@link #nameAttributes} to be injected in this class. */ public static final String ADL_ID_ATTRIBUTES = "ADL_ID_Attributes"; /** * The names of the "name" attribute for each AST node type. This map * associates the names of the "name" attribute, used to detect overridden * elements, to each AST node type that has such an attribute. */ @Inject @Named(ADL_ID_ATTRIBUTES) protected Map<String, String> nameAttributes; /** * Used to retrieve super types Definitions from DefinitionReferences. */ @Inject protected Loader loaderItf; // --------------------------------------------------------------------------- // Implementation of the Loader interface // --------------------------------------------------------------------------- public Definition load(final String name, final Map<Object, Object> context) throws ADLException { Definition d = clientLoader.load(name, context); if (d instanceof MindDefinition) d = resolveExtends((MindDefinition) d, context); return d; } protected MindDefinition resolveExtends(MindDefinition d, final Map<Object, Object> context) throws ADLException { if (d.getExtends() == null) return d; // abstract attribute is not inherited. Store the "abstract" attribute // before merging final boolean isAbstract = ASTHelper.isAbstract(d); final DefinitionReference[] extendedDefs = d.getExtends() .getDefinitionReferences(); // save "sub-definitions" before merge final Object subDefsObject = d.astGetDecoration("sub-definitions"); SubDefinitionsDecoration savedSubDefinitionsDecoration = null; if (subDefsObject instanceof SubDefinitionsDecoration) savedSubDefinitionsDecoration = (SubDefinitionsDecoration) subDefsObject; // keep inheritance information as a decoration, since the direct "extends" // is removed afterwards final ExtendsDecoration list = new ExtendsDecoration(); for (final DefinitionReference extend : extendedDefs) { /* * allow retrieving super-types from a definition */ list.add(extend); /* * allow retrieving sub-types from parent */ decorateParentDefinitionWithSubDefinition(extend, d, context); } d.astSetDecoration("extends", list); // cleanup d.setExtends(null); if (extendedDefs.length > 0) { final Kind kind = Kind.fromDefinition(d); // first resolve extended list Definition superDef = null; for (final DefinitionReference extend : extendedDefs) { final Definition extendedDefinition = definitionReferenceResolverItf .resolve(extend, d, context); // if the definition has not been resolved correctly, ignore it. if (ASTHelper.isUnresolvedDefinitionNode(extendedDefinition)) continue; final Kind extendedKind = Kind.fromDefinition(extendedDefinition); switch (kind) { case TYPE : if (extendedKind == Kind.PRIMITIVE) { errorManagerItf.logError( ADLErrors.INVALID_EXTENDS_TYPE_EXTENDS_PRIMITIVE, extend.getName()); continue; } else if (extendedKind == Kind.COMPOSITE) { errorManagerItf.logError( ADLErrors.INVALID_EXTENDS_TYPE_EXTENDS_COMPOSITE, extend.getName()); continue; } break; case PRIMITIVE : if (extendedKind == Kind.COMPOSITE) { errorManagerItf.logError( ADLErrors.INVALID_EXTENDS_PRIMITIVE_EXTENDS_COMPOSITE, extend.getName()); continue; } break; case COMPOSITE : if (extendedKind == Kind.PRIMITIVE) { errorManagerItf.logError( ADLErrors.INVALID_EXTENDS_COMPOSITE_EXTENDS_PRIMITIVE, extend.getName()); continue; } break; } if (superDef == null) superDef = extendedDefinition; else superDef = mergeDef(extendedDefinition, superDef, extend); } if (superDef != null) { // second merge d with superDef only if superDef has been correctly // resolved. d = (MindDefinition) mergeDef(d, superDef, extendedDefs[extendedDefs.length - 1]); } // restore original sub-definition decoration instead of the merge // result one d.astSetDecoration("sub-definitions", savedSubDefinitionsDecoration); // restore the "abstract" attribute if (d instanceof AbstractDefinition) { if (isAbstract) ((AbstractDefinition) d).setIsAbstract(AbstractDefinition.TRUE); else ((AbstractDefinition) d).setIsAbstract(null); } } return d; } private void decorateParentDefinitionWithSubDefinition( final DefinitionReference extend, final MindDefinition d, final Map<Object, Object> context) throws ADLException { SubDefinitionsDecoration subDefinitionsDecoration = null; Definition extendedDefinition = definitionReferenceResolverItf.resolve( extend, d, context); // It was a Generic Definition, the resolve will return the merged // definition according to its TypeArguments, not the generic one. // We needed resolve for fully qualified name according to the "d" // scope however (if it was used with short name and imports). // Note: Checking the original definitionReference, not the merge final int i = extendedDefinition.getName().indexOf('<'); // -1 = not found, no name can start with '<', and avoid empty string if (i > 0) { final String templateName = extendedDefinition.getName().substring(0, i); extendedDefinition = loaderItf.load(templateName, context); } // if the definition has not been resolved correctly, ignore it. if (ASTHelper.isUnresolvedDefinitionNode(extendedDefinition)) return; final Object subDefsDecorationObject = extendedDefinition .astGetDecoration("sub-definitions"); if (subDefsDecorationObject != null) { if (subDefsDecorationObject instanceof SubDefinitionsDecoration) { subDefinitionsDecoration = (SubDefinitionsDecoration) subDefsDecorationObject; subDefinitionsDecoration.add(d); } } else { subDefinitionsDecoration = new SubDefinitionsDecoration(); subDefinitionsDecoration.add(d); extendedDefinition.astSetDecoration("sub-definitions", subDefinitionsDecoration); } } private static enum Kind { TYPE, PRIMITIVE, COMPOSITE; static Kind fromDefinition(final Definition d) { if (isType(d)) { return Kind.TYPE; } else if (isPrimitive(d)) { return Kind.PRIMITIVE; } else if (isComposite(d)) { return Kind.COMPOSITE; } else { throw new CompilerError(GenericErrors.INTERNAL_ERROR, new NodeErrorLocator(d), "Unknown definition type: " + d.astGetType()); } } } protected Definition mergeDef(final Definition def, Definition superDef, final Node locator) throws ADLException, CompilerError { try { superDef = (Definition) nodeMergerItf .merge(def, superDef, nameAttributes); } catch (final MergeException e) { if (e instanceof InvalidMergeException) { errorManagerItf.logError(((InvalidMergeException) e).getError()); } else { throw new CompilerError(ComponentErrors.MERGE_ERROR, new NodeErrorLocator(def), e, def.getName()); } } return superDef; } }