/*
* ******************************************************************************
* MontiCore Language Workbench
* Copyright (c) 2015, MontiCore, All rights reserved.
*
* This project 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.0 of the License, or (at your option) any later version.
* This library 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 project. If not, see <http://www.gnu.org/licenses/>.
* ******************************************************************************
*/
package de.monticore.symboltable;
import de.monticore.ModelNameCalculator;
import de.monticore.ModelingLanguage;
import de.monticore.ModelingLanguageFamily;
import de.monticore.ast.ASTNode;
import de.monticore.io.paths.ModelPath;
import de.monticore.modelloader.ModelingLanguageModelLoader;
import de.monticore.symboltable.modifiers.AccessModifier;
import de.monticore.symboltable.resolving.AdaptedResolvingFilter;
import de.monticore.symboltable.resolving.ResolvingFilter;
import de.monticore.symboltable.resolving.ResolvingInfo;
import de.se_rwth.commons.logging.Log;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* @author Pedram Mir Seyed Nazari
*/
public final class GlobalScope extends CommonScope {
private final ModelPath modelPath;
private final ResolvingConfiguration resolvingConfiguration;
private final Set<ModelingLanguage> modelingLanguages = new LinkedHashSet<>();
private final Map<String, Set<ModelingLanguageModelLoader<? extends ASTNode>>> modelName2ModelLoaderCache = new HashMap<>();
public GlobalScope(final ModelPath modelPath, final Collection <ModelingLanguage> modelingLanguages,
final ResolvingConfiguration resolvingConfiguration) {
super(Optional.empty(), true);
this.modelPath = Log.errorIfNull(modelPath);
this.resolvingConfiguration = Log.errorIfNull(resolvingConfiguration);
this.modelingLanguages.addAll(Log.errorIfNull(modelingLanguages));
if (modelingLanguages.isEmpty()) {
Log.warn(GlobalScope.class.getSimpleName() + ": 0xA1044 No model loaders defined. This hampers the "
+ "loading of models.");
}
setResolvingFilters(resolvingConfiguration.getTopScopeResolvingFilters());
}
public GlobalScope(final ModelPath modelPath, final ModelingLanguage language,
ResolvingConfiguration resolvingConfiguration) {
this(modelPath, Collections.singletonList(language), resolvingConfiguration);
}
public GlobalScope(final ModelPath modelPath, final ModelingLanguage language) {
this(modelPath, language, new ResolvingConfiguration());
resolvingConfiguration.addTopScopeResolvers(language.getResolvingFilters());
setResolvingFilters(resolvingConfiguration.getTopScopeResolvingFilters());
}
public GlobalScope(final ModelPath modelPath, ModelingLanguageFamily languageFamily) {
this(modelPath, languageFamily.getModelingLanguages(), new ResolvingConfiguration());
resolvingConfiguration.addTopScopeResolvers(languageFamily.getAllResolvers());
setResolvingFilters(resolvingConfiguration.getTopScopeResolvingFilters());
}
@Override
public Optional<String> getName() {
return Optional.empty();
}
@Override
public <T extends Symbol> Collection<T> resolveMany(final ResolvingInfo resolvingInfo,
final String symbolName, final SymbolKind kind, final AccessModifier modifier, final Predicate<Symbol> predicate) {
resolvingInfo.addInvolvedScope(this);
// First, try to resolve the symbol in the current scope and its sub scopes.
Collection<T> resolvedSymbol = resolveDownMany(resolvingInfo, symbolName, kind, modifier, predicate);
if (!resolvedSymbol.isEmpty()) {
return resolvedSymbol;
}
// Symbol not found: try to load corresponding model and build its symbol table
loadModels(resolvingInfo.getResolvingFilters(), symbolName, kind);
// Maybe the symbol now exists in this scope (or its sub scopes). So, resolve down, again.
resolvedSymbol = resolveDownMany(new ResolvingInfo(getResolvingFilters()), symbolName, kind, modifier, predicate);
return resolvedSymbol;
}
protected void loadModels(final Collection<ResolvingFilter<? extends Symbol>> resolvingFilters,
final String symbolName, final SymbolKind kind) {
for (final ModelingLanguage lang : modelingLanguages) {
final ModelNameCalculator modelNameCalculator = lang.getModelNameCalculator();
final ModelingLanguageModelLoader<? extends ASTNode> modelLoader = lang.getModelLoader();
final Set<SymbolKind> possibleSymbolKinds = calculatePossibleSymbolKinds(resolvingFilters, kind);
for (final SymbolKind kindForCalc : possibleSymbolKinds) {
final Set<String> calculatedModelNames = modelNameCalculator.calculateModelNames(symbolName, kindForCalc);
for (String calculatedModelName : calculatedModelNames) {
if (continueWithModelLoader(calculatedModelName, modelLoader)) {
modelLoader.loadModelsIntoScope(calculatedModelName, modelPath, this, resolvingConfiguration);
cache(modelLoader, calculatedModelNames.iterator().next());
}
else {
Log.debug("Already tried to load model for '" + symbolName + "'. If model exists, continue with cached version.", GlobalScope.class.getSimpleName());
}
}
}
}
}
protected Set<SymbolKind> calculatePossibleSymbolKinds(Collection<ResolvingFilter<? extends Symbol>> resolvingFilters, SymbolKind kind) {
final Collection<ResolvingFilter<? extends Symbol>> resolversForKind = ResolvingFilter
.getFiltersForTargetKind(resolvingFilters, kind);
return resolversForKind.stream()
.map(resolvingFilter -> getSymbolKindByResolvingFilter(kind, resolvingFilter))
.collect(Collectors.toCollection(LinkedHashSet::new));
}
public void cache(ModelingLanguageModelLoader<? extends ASTNode> modelLoader, String calculatedModelName) {
if (modelName2ModelLoaderCache.containsKey(calculatedModelName)) {
modelName2ModelLoaderCache.get(calculatedModelName).add(modelLoader);
}
else {
final Set<ModelingLanguageModelLoader<? extends ASTNode>> ml = new LinkedHashSet<>();
ml.add(modelLoader);
modelName2ModelLoaderCache.put(calculatedModelName, ml);
}
}
/**
* Adapted resolving filters search for a source symbol kind and adapt it
* to a symbol of the target kind. E.g., Class -> State means, search for
* a class symbol and adapt it to a state symbol. In that case, the model
* name calculator must know the kind of the source symbol (i.e., class symbol).
*
* @param kind
* @param resolvingFilter
* @return
*/
private SymbolKind getSymbolKindByResolvingFilter(SymbolKind kind, ResolvingFilter<? extends Symbol> resolvingFilter) {
SymbolKind kindForCalc;
if (resolvingFilter instanceof AdaptedResolvingFilter) {
kindForCalc = ((AdaptedResolvingFilter) resolvingFilter).getSourceKind();
}
else {
kindForCalc = kind;
}
return kindForCalc;
}
/**
* Model loading continues with the given <code>modelLoader</code>, if
* <code>calculatedModelName</code> has not been already loaded with that loader.
*
* @return true, if it should be continued with the model loader
*/
protected boolean continueWithModelLoader(final String calculatedModelName, final ModelingLanguageModelLoader<? extends ASTNode> modelLoader) {
return !modelName2ModelLoaderCache.containsKey(calculatedModelName)
|| !modelName2ModelLoaderCache.get(calculatedModelName).contains(modelLoader);
}
@Override
protected boolean checkIfContinueAsSubScope(String symbolName, SymbolKind kind) {
return false;
}
}