blob: e45ffb8c2021221d081637a657e167255a05a690 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2020, 2021 Obeo.
* 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.acceleo.aql.location.aql;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.acceleo.Module;
import org.eclipse.acceleo.aql.location.common.AbstractLocationLink;
import org.eclipse.acceleo.aql.parser.AcceleoAstUtils;
import org.eclipse.acceleo.aql.parser.AcceleoParser;
import org.eclipse.acceleo.query.ast.Call;
import org.eclipse.acceleo.query.ast.ClassTypeLiteral;
import org.eclipse.acceleo.query.ast.EClassifierTypeLiteral;
import org.eclipse.acceleo.query.ast.EnumLiteral;
import org.eclipse.acceleo.query.ast.Error;
import org.eclipse.acceleo.query.ast.Expression;
import org.eclipse.acceleo.query.ast.Literal;
import org.eclipse.acceleo.query.ast.VariableDeclaration;
import org.eclipse.acceleo.query.ast.util.AstSwitch;
import org.eclipse.acceleo.query.parser.AstEvaluator;
import org.eclipse.acceleo.query.runtime.EvaluationResult;
import org.eclipse.acceleo.query.runtime.IService;
import org.eclipse.acceleo.query.runtime.IValidationResult;
import org.eclipse.acceleo.query.runtime.impl.EvaluationServices;
import org.eclipse.acceleo.query.runtime.impl.Nothing;
import org.eclipse.acceleo.query.runtime.impl.ServicesValidationResult;
import org.eclipse.acceleo.query.runtime.impl.ValidationServices;
import org.eclipse.acceleo.query.runtime.namespace.IQualifiedNameQueryEnvironment;
import org.eclipse.acceleo.query.validation.type.IType;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnumLiteral;
/**
* An {@link AstSwitch AQL Switch} to provide, for an element from an AQL AST, the {@link List} of
* {@link AbstractLocationLink} that points to its definition location(s).
*
* @author Florent Latombe
*/
public class AqlDefinitionLocator extends AstSwitch<List<AbstractLocationLink<?, ?>>> {
/**
* FIXME: move somewhere else.
*/
private static final String LOCATION_NAMESPACE = "_reserved_::to::locate";
/**
* The {@link IQualifiedNameQueryEnvironment}.
*/
private final IQualifiedNameQueryEnvironment queryEnvironment;
/**
* The {@link EvaluationServices}. It is able to lookup services and variables.
*/
private final EvaluationServices aqlEvaluationServices;
/**
* The {@link AstEvaluator}. It may be required to partially evaluate AQL Expressions so we can determine
* their types.
*/
private final AstEvaluator aqlEvaluator;
/**
* The {@link AqlVariablesLocalContext} that holds information about the variables.
*/
private final AqlVariablesLocalContext aqlVariablesContext;
/**
* The contextual {@link IValidationResult}.
*/
private final IValidationResult validationResult;
/**
* The context qualified name.
*/
private String qualifiedName;
/**
* Constructor.
*
* @param queryEnvironment
* the (non-{@code null}) {@link IQualifiedNameQueryEnvironment}.
* @param validationResult
* the (non-{@code null}) {@link IValidationResult} containing the element(s) that will be
* passed as argument to this.
* @param aqlVariablesContext
* the (non-{@code null}) {@link AqlVariablesLocalContext.
* @param qualifiedName
* the context qualified name
*/
public AqlDefinitionLocator(IQualifiedNameQueryEnvironment queryEnvironment,
IValidationResult validationResult, AqlVariablesLocalContext aqlVariablesContext,
String qualifiedName) {
this.queryEnvironment = Objects.requireNonNull(queryEnvironment);
this.validationResult = Objects.requireNonNull(validationResult);
this.aqlEvaluationServices = new EvaluationServices(this.queryEnvironment);
this.aqlEvaluator = new AstEvaluator(aqlEvaluationServices);
this.aqlVariablesContext = Objects.requireNonNull(aqlVariablesContext);
this.qualifiedName = qualifiedName;
}
/**
* For a {@link VariableDeclaration}, it is its own definition.
*
* @see org.eclipse.acceleo.query.ast.util.AstSwitch#caseVariableDeclaration(org.eclipse.acceleo.query.ast.VariableDeclaration)
*/
@Override
public List<AbstractLocationLink<?, ?>> caseVariableDeclaration(VariableDeclaration variableDeclaration) {
return Collections.singletonList(new AqlLocationLinkToAql(variableDeclaration, variableDeclaration));
}
/**
* For an AQL {@link Expression}, we 'evaluate' it using a specific runtime that binds not to the intended
* value of the expression, but the to the AST elements that define the expression term.
*
* @see org.eclipse.acceleo.query.ast.util.AstSwitch#caseExpression(org.eclipse.acceleo.query.ast.Expression)
*/
@Override
public List<AbstractLocationLink<?, ?>> caseExpression(Expression expression) {
EvaluationResult evaluationResult = aqlEvaluator.eval(this.aqlVariablesContext
.getVariableDefinitions(), expression);
Object expressionDefiningElement = evaluationResult.getResult();
if (expressionDefiningElement == null || expressionDefiningElement instanceof Nothing) {
return new ArrayList<>();
} else {
return Collections.singletonList(new AqlLocationLinkToAny(expression, expressionDefiningElement));
}
}
/**
* For most {@link Literal}, there is no "definition" to go to.
*
* @see org.eclipse.acceleo.query.ast.util.AstSwitch#caseLiteral(org.eclipse.acceleo.query.ast.Literal)
*/
@Override
public List<AbstractLocationLink<?, ?>> caseLiteral(Literal literal) {
return new ArrayList<>();
}
@Override
public List<AbstractLocationLink<?, ?>> caseClassTypeLiteral(ClassTypeLiteral classTypeLiteral) {
return Collections.singletonList(new AqlLocationLinkToAny(classTypeLiteral, classTypeLiteral
.getValue()));
}
@Override
public List<AbstractLocationLink<?, ?>> caseEClassifierTypeLiteral(
EClassifierTypeLiteral eClassifierTypeLiteral) {
final Collection<EClassifier> eClassifiers = queryEnvironment.getEPackageProvider().getTypes(
eClassifierTypeLiteral.getEPackageName(), eClassifierTypeLiteral.getEClassifierName());
return eClassifiers.stream().map(eClassifier -> new AqlLocationLinkToAny(eClassifierTypeLiteral,
eClassifier)).collect(Collectors.toList());
}
@Override
public List<AbstractLocationLink<?, ?>> caseEnumLiteral(EnumLiteral enumLiteral) {
final Collection<EEnumLiteral> eEnumLiterals = queryEnvironment.getEPackageProvider().getEnumLiterals(
enumLiteral.getEPackageName(), enumLiteral.getEEnumName(), enumLiteral.getEEnumLiteralName());
return eEnumLiterals.stream().map(eEnumLiteral -> new AqlLocationLinkToAny(enumLiteral, eEnumLiteral))
.collect(Collectors.toList());
}
@Override
public List<AbstractLocationLink<?, ?>> caseCall(Call call) {
final String contextQualifiedName = LOCATION_NAMESPACE + AcceleoParser.QUALIFIER_SEPARATOR
+ qualifiedName;
final Module module = AcceleoAstUtils.getContainerModule(AcceleoAstUtils.getContainerOfAqlAstElement(
call));
queryEnvironment.getLookupEngine().getResolver().register(contextQualifiedName, module);
queryEnvironment.getLookupEngine().pushContext(contextQualifiedName);
try {
final ValidationServices validationServices = new ValidationServices(queryEnvironment);
// Argument Types - which are expressions whose type must first be evaluated.
final List<Set<IType>> argumentTypes = call.getArguments().stream().map(argument -> {
Set<IType> argumentPossibleTypes = validationResult.getPossibleTypes(argument);
return argumentPossibleTypes;
}).collect(Collectors.toList());
final ServicesValidationResult servicesValidationResult = validationServices.call(call,
validationResult, argumentTypes);
// Retrieve all the IServices which fit the name and argument types.
final Set<IService<?>> candidateServices = servicesValidationResult.getResolvedServices();
// Return links to all the candidates.
return candidateServices.stream().map(service -> new AqlLocationLinkToAny(call, service)).collect(
Collectors.toList());
} finally {
queryEnvironment.getLookupEngine().popContext(contextQualifiedName);
queryEnvironment.getLookupEngine().getResolver().clear(Collections.singleton(
contextQualifiedName));
queryEnvironment.getLookupEngine().clearContext(contextQualifiedName);
}
}
@Override
public List<AbstractLocationLink<?, ?>> caseError(Error object) {
return new ArrayList<>();
}
}