/**
* Copyright (C) 2009 STMicroelectronics
*
* 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:
*/
package org.ow2.mind.adl.parameter;
import static org.ow2.mind.adl.parameter.ast.ParameterASTHelper.getInferredParameterType;
import static org.ow2.mind.adl.parameter.ast.ParameterASTHelper.setInferredParameterType;
import static org.ow2.mind.adl.parameter.ast.ParameterASTHelper.setUsedFormalParameter;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import org.objectweb.fractal.adl.ADLException;
import org.objectweb.fractal.adl.CompilerError;
import org.objectweb.fractal.adl.ContextLocal;
import org.objectweb.fractal.adl.Definition;
import org.objectweb.fractal.adl.NodeFactory;
import org.objectweb.fractal.adl.error.GenericErrors;
import org.objectweb.fractal.adl.error.NodeErrorLocator;
import org.ow2.mind.adl.ADLErrors;
import org.ow2.mind.adl.DefinitionReferenceResolver;
import org.ow2.mind.adl.DefinitionReferenceResolver.AbstractDelegatingDefinitionReferenceResolver;
import org.ow2.mind.adl.ast.ASTHelper;
import org.ow2.mind.adl.ast.DefinitionReference;
import org.ow2.mind.adl.parameter.ast.Argument;
import org.ow2.mind.adl.parameter.ast.ArgumentContainer;
import org.ow2.mind.adl.parameter.ast.FormalParameter;
import org.ow2.mind.adl.parameter.ast.FormalParameterContainer;
import org.ow2.mind.adl.parameter.ast.ParameterASTHelper.ParameterType;
import org.ow2.mind.error.ErrorManager;
import org.ow2.mind.value.ast.Reference;
import org.ow2.mind.value.ast.Value;
import com.google.inject.Inject;
/**
* This delegating {@link DefinitionReferenceResolver} checks that
* {@link Argument} nodes contained by the {@link DefinitionReference} to
* resolve, match {@link FormalParameter} contained by the resolved
* {@link Definition}.
*/
public class ParametricDefinitionReferenceResolver
extends
AbstractDelegatingDefinitionReferenceResolver {
// define a class to alias this too long generic type
protected static final class FormalParameterCache
extends
ContextLocal<Map<Definition, Map<String, FormalParameter>>> {
}
protected final FormalParameterCache contextualParameters = new FormalParameterCache();
@Inject
protected NodeFactory nodeFactoryItf;
@Inject
protected ErrorManager errorManagerItf;
// ---------------------------------------------------------------------------
// Implementation of the DefinitionReferenceResolver interface
// ---------------------------------------------------------------------------
public Definition resolve(final DefinitionReference reference,
final Definition encapsulatingDefinition,
final Map<Object, Object> context) throws ADLException {
return resolve(reference, encapsulatingDefinition,
getParameters(encapsulatingDefinition, context), context);
}
// ---------------------------------------------------------------------------
// Utility methods
// ---------------------------------------------------------------------------
protected Definition resolve(final DefinitionReference reference,
final Definition encapsulatingDefinition,
final Map<String, FormalParameter> formalParameters,
final Map<Object, Object> context) throws ADLException {
final Definition d = clientResolverItf.resolve(reference,
encapsulatingDefinition, context);
// Map argument values to formal parameters.
// argumentMap associates formal parameter names to actual arguments;
final Map<String, Argument> argumentMap = mapArguments(reference, d);
if (argumentMap != null) {
// referenced definition has formal parameters.
// Check argument values
for (final FormalParameter parameter : ((FormalParameterContainer) d)
.getFormalParameters()) {
final ParameterType type = getInferredParameterType(parameter);
final Argument argumentValue = argumentMap.get(parameter.getName());
final Value value = argumentValue.getValue();
if (value instanceof Reference) {
// the argument references a formal parameter
final String ref = ((Reference) value).getRef();
final FormalParameter referencedParameter = formalParameters.get(ref);
if (referencedParameter == null) {
errorManagerItf.logError(ADLErrors.UNDEFINED_PARAMETER, value, ref);
continue;
}
setUsedFormalParameter(referencedParameter);
final ParameterType referencedType = getInferredParameterType(referencedParameter);
if (referencedType == null) {
setInferredParameterType(referencedParameter, type);
} else if (type != null && type != referencedType) {
errorManagerItf.logError(ADLErrors.INCOMPATIBLE_ARGUMENT_TYPE,
value, ref);
}
} else if (type != null && !type.isCompatible(value)) {
errorManagerItf.logError(ADLErrors.INCOMPATIBLE_ARGUMENT_VALUE,
value, parameter.getName());
}
}
}
return d;
}
protected Map<String, Argument> mapArguments(
final DefinitionReference reference, final Definition definition)
throws ADLException {
final Argument[] arguments = (reference instanceof ArgumentContainer)
? ((ArgumentContainer) reference).getArguments()
: null;
final FormalParameter[] parameters = (definition instanceof FormalParameterContainer)
? ((FormalParameterContainer) definition).getFormalParameters()
: null;
if (parameters == null || parameters.length == 0) {
if (arguments != null && arguments.length > 0
&& !ASTHelper.isUnresolvedDefinitionNode(definition)) {
removeArguments((ArgumentContainer) reference);
errorManagerItf.logError(ADLErrors.INVALID_REFERENCE_NO_PARAMETER,
reference);
}
return null;
} else {
// there are parameters
if (arguments == null || arguments.length == 0) {
errorManagerItf.logError(ADLErrors.INVALID_REFERENCE_MISSING_ARGUMENT,
reference);
return null;
}
if (arguments.length > 0 && arguments[0].getName() == null) {
// argument values are specified by ordinal position.
if (parameters.length > arguments.length) {
// missing parameter values
removeArguments((ArgumentContainer) reference);
errorManagerItf.logError(
ADLErrors.INVALID_REFERENCE_MISSING_ARGUMENT, reference);
return null;
}
if (parameters.length < arguments.length
&& !ASTHelper.isUnresolvedDefinitionNode(definition)) {
removeArguments((ArgumentContainer) reference);
errorManagerItf.logError(
ADLErrors.INVALID_REFERENCE_TOO_MANY_ARGUMENT, reference);
return null;
}
final Map<String, Argument> result = new HashMap<String, Argument>(
parameters.length);
for (int i = 0; i < parameters.length; i++) {
final Argument value = arguments[i];
// sanity check.
if (value.getName() != null) {
throw new CompilerError(GenericErrors.INTERNAL_ERROR,
new NodeErrorLocator(value),
"Cannot mix ordinal and name-based template values.");
}
final String varName = parameters[i].getName();
value.setName(varName);
result.put(varName, value);
}
return result;
} else {
// template values are specified by name
final Map<String, Argument> valuesByName = new HashMap<String, Argument>(
arguments.length);
for (final Argument value : arguments) {
// sanity check.
if (value.getName() == null) {
throw new CompilerError(GenericErrors.INTERNAL_ERROR,
new NodeErrorLocator(value),
"Cannot mix ordinal and name-based argument values.");
}
// remove argument and re-add them latter in the formal parameter
// declaration order.
((ArgumentContainer) reference).removeArgument(value);
valuesByName.put(value.getName(), value);
}
final Map<String, Argument> result = new HashMap<String, Argument>();
for (final FormalParameter variable : parameters) {
final Argument value = valuesByName.remove(variable.getName());
if (value == null) {
// missing parameter values
removeArguments((ArgumentContainer) reference);
errorManagerItf.logError(
ADLErrors.INVALID_REFERENCE_MISSING_ARGUMENT, reference,
variable.getName());
return null;
}
result.put(variable.getName(), value);
((ArgumentContainer) reference).addArgument(value);
}
if (!valuesByName.isEmpty()) {
// too many parameter values
if (!ASTHelper.isUnresolvedDefinitionNode(definition)) {
for (final Map.Entry<String, Argument> value : valuesByName
.entrySet()) {
// remove faulty argument
((ArgumentContainer) reference).removeArgument(value.getValue());
errorManagerItf.logError(
ADLErrors.INVALID_REFERENCE_NO_SUCH_PARAMETER,
value.getValue(), value.getKey());
}
}
return null;
}
return result;
}
}
}
protected void removeArguments(final ArgumentContainer container) {
for (final Argument argument : container.getArguments()) {
container.removeArgument(argument);
}
}
protected Map<String, FormalParameter> getParameters(final Definition d,
final Map<Object, Object> context) throws ADLException {
Map<Definition, Map<String, FormalParameter>> parameters = contextualParameters
.get(context);
if (parameters == null) {
parameters = new IdentityHashMap<Definition, Map<String, FormalParameter>>();
contextualParameters.set(context, parameters);
}
Map<String, FormalParameter> result = parameters.get(d);
if (result == null) {
result = new LinkedHashMap<String, FormalParameter>();
if (d instanceof FormalParameterContainer) {
final FormalParameter[] formalParameters = ((FormalParameterContainer) d)
.getFormalParameters();
if (formalParameters.length > 0) {
for (final FormalParameter parameter : formalParameters) {
result.put(parameter.getName(), parameter);
}
}
}
parameters.put(d, result);
}
return result;
}
}