/* * ****************************************************************************** * 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 com.google.common.collect.FluentIterable; import de.monticore.symboltable.modifiers.AccessModifier; import de.monticore.symboltable.names.CommonQualifiedNamesCalculator; import de.monticore.symboltable.names.QualifiedNamesCalculator; import de.monticore.symboltable.resolving.ResolvingInfo; import de.se_rwth.commons.Joiners; import de.se_rwth.commons.Names; import de.se_rwth.commons.Splitters; import de.se_rwth.commons.logging.Log; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Predicate; import static java.util.Objects.requireNonNull; /** * Represents the scope of the whole artifact (i.e., file or compilation unit). * * @author Pedram Mir Seyed Nazari */ public class ArtifactScope extends CommonScope { private final String packageName; private final List<ImportStatement> imports; private QualifiedNamesCalculator qualifiedNamesCalculator; public ArtifactScope(final String packageName, final List<ImportStatement> imports) { this(Optional.empty(), packageName, imports); } public ArtifactScope(final Optional<MutableScope> enclosingScope, final String packageName, final List<ImportStatement> imports) { super(enclosingScope, true); setExportsSymbols(true); Log.errorIfNull(packageName); Log.errorIfNull(imports); if (!packageName.isEmpty()) { this.packageName = packageName.endsWith(".") ? packageName.substring(0, packageName.length() - 1) : packageName; } else { // default package this.packageName = ""; } this.imports = Collections.unmodifiableList(new ArrayList<>(imports)); this.qualifiedNamesCalculator = new CommonQualifiedNamesCalculator(); } @Override public Optional<String> getName() { if (!super.getName().isPresent()) { final Optional<? extends ScopeSpanningSymbol> topLevelSymbol = getTopLevelSymbol(); if (topLevelSymbol.isPresent()) { setName(topLevelSymbol.get().getName()); } } return super.getName(); } public Optional<? extends ScopeSpanningSymbol> getTopLevelSymbol() { if (getSubScopes().size() == 1) { return getSubScopes().get(0).getSpanningSymbol(); } // there is no top level symbol, if more than one sub scope exists. return Optional.empty(); } public String getPackageName() { return packageName; } /** * Starts the bottom-up inter-model resolution process. * * @param <T> * @param resolvingInfo * @param name * @param kind * @return */ @Override protected <T extends Symbol> Collection<T> continueWithEnclosingScope(final ResolvingInfo resolvingInfo, final String name, final SymbolKind kind, final AccessModifier modifier, final Predicate<Symbol> predicate) { final Collection<T> result = new LinkedHashSet<>(); if (checkIfContinueWithEnclosingScope(resolvingInfo.areSymbolsFound()) && (getEnclosingScope().isPresent())) { if (!(enclosingScope instanceof GlobalScope)) { Log.warn("0xA1039 An artifact scope should have the global scope as enclosing scope or no " + "enclosing scope at all."); } final Set<String> potentialQualifiedNames = qualifiedNamesCalculator.calculateQualifiedNames(name, packageName, imports); for (final String potentialQualifiedName : potentialQualifiedNames) { final Collection<T> resolvedFromEnclosing = enclosingScope.resolveMany(resolvingInfo, potentialQualifiedName, kind, modifier, predicate); result.addAll(resolvedFromEnclosing); } } return result; } protected String getRemainingNameForResolveDown(String symbolName) { final String packageAS = this.getPackageName(); final FluentIterable<String> packageASNameParts = FluentIterable.from(Splitters.DOT.omitEmptyStrings().split(packageAS)); final FluentIterable<String> symbolNameParts = FluentIterable.from(Splitters.DOT.split(symbolName)); String remainingSymbolName = symbolName; if (symbolNameParts.size() > packageASNameParts.size()) { remainingSymbolName = Joiners.DOT.join(symbolNameParts.skip(packageASNameParts.size())); } return remainingSymbolName; } @Override protected boolean checkIfContinueAsSubScope(String symbolName, SymbolKind kind) { if(this.exportsSymbols()) { final String symbolQualifier = Names.getQualifier(symbolName); final List<String> symbolQualifierParts = Splitters.DOT.splitToList(symbolQualifier); final List<String> packageParts = Splitters.DOT.splitToList(packageName); boolean symbolNameStartsWithPackage = true; if (packageName.isEmpty()) { // symbol qualifier always contains default package (i.e., empty string) symbolNameStartsWithPackage = true; } else if (symbolQualifierParts.size() >= packageParts.size()) { for (int i = 0; i < packageParts.size(); i++) { if (!packageParts.get(i).equals(symbolQualifierParts.get(i))) { symbolNameStartsWithPackage = false; break; } } } else { symbolNameStartsWithPackage = false; } return symbolNameStartsWithPackage; } return false; } public void setQualifiedNamesCalculator(QualifiedNamesCalculator qualifiedNamesCalculator) { this.qualifiedNamesCalculator = requireNonNull(qualifiedNamesCalculator); } public List<ImportStatement> getImports() { return Collections.unmodifiableList(imports); } }