/*
* ******************************************************************************
* 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.ast.ASTNode;
import de.monticore.symboltable.modifiers.AccessModifier;
import de.se_rwth.commons.Names;
import de.se_rwth.commons.logging.Log;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Optional;
/**
* @author Pedram Mir Seyed Nazari
*/
public abstract class CommonSymbol implements Symbol {
private final String name;
private String packageName;
private String fullName;
private Scope enclosingScope;
private ASTNode node;
private SymbolKind kind;
private AccessModifier accessModifier = AccessModifier.ALL_INCLUSION;
public CommonSymbol(String name, SymbolKind kind) {
this.name = Log.errorIfNull(name);
this.kind = Log.errorIfNull(kind);
}
public void setPackageName(String packageName) {
this.packageName = Log.errorIfNull(packageName);
}
/**
* @see Symbol#getName()
*/
@Override
public String getName() {
return name;
}
/**
* Returns the package of this symbol. All symbols within an artifact usually
* have the same package name. For example, the state chart <code>p.q.SC</code>
* and its containing states all have the package <code>p.q</code>.
*
* By default, this method determines the package name dynamically via
* {@link #determinePackageName()} } and caches the value. If the package name
* should not be cached, and hence, calculated every time this method is invoked,
* override this method and directly delegate to {@link #determinePackageName()}.
*
* @see #getFullName()
*
*/
@Override
public String getPackageName() {
if (packageName == null) {
packageName = determinePackageName();
}
return packageName;
}
/**
* Determines <b>dynamically</b> the package name of the symbol.
* @return the package name of the symbol determined dynamically
*/
protected String determinePackageName() {
Optional<? extends Scope> optCurrentScope = Optional.ofNullable(enclosingScope);
while (optCurrentScope.isPresent()) {
final Scope currentScope = optCurrentScope.get();
if (currentScope.isSpannedBySymbol()) {
// If one of the enclosing scope(s) is spanned by a symbol, take its
// package name. This check is important, since the package name of the
// enclosing symbol might be set manually.
return currentScope.getSpanningSymbol().get().getPackageName();
}
else if (currentScope instanceof ArtifactScope) {
return ((ArtifactScope)currentScope).getPackageName();
}
optCurrentScope = currentScope.getEnclosingScope();
}
return "";
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
@Override
/**
* @return the full name of a symbol. For example, the full name of a state symbol <code>s</code>
* in a state chart <code>p.q.SC</code> is <code>p.q.SC.s</code>.
*
* By default, this method determines the full name dynamically via
* {@link #determineFullName()} and caches the value. If the full name should not
* be cached, and hence, calculated every time this method is invoked, override
* this method and directly delegate to {@link #determineFullName()}.
*
* @see #getPackageName()
*/
public String getFullName() {
if (fullName == null) {
fullName = determineFullName();
}
return fullName;
}
/**
* Determines <b>dynamically</b> the full name of the symbol.
* @return the full name of the symbol determined dynamically
*/
protected String determineFullName() {
if (enclosingScope == null) {
// There should not be a symbol that is not defined in any scope. This case should only
// occur while the symbol is built (by the symbol table creator). So, here the full name
// should not be cached yet.
return name;
}
final Deque<String> nameParts = new ArrayDeque<>();
nameParts.addFirst(name);
Optional<? extends Scope> optCurrentScope = Optional.of(enclosingScope);
while (optCurrentScope.isPresent()) {
final Scope currentScope = optCurrentScope.get();
if (currentScope.isSpannedBySymbol()) {
// If one of the enclosing scope(s) is spanned by a symbol, the full name
// of that symbol is the missing prefix, and hence, the calculation
// ends here. This check is important, since the full name of the enclosing
// symbol might be set manually.
nameParts.addFirst(currentScope.getSpanningSymbol().get().getFullName());
break;
}
if (!(currentScope instanceof GlobalScope)) {
if (currentScope instanceof ArtifactScope) {
// We have reached the artifact scope. Get the package name from the
// symbol itself, since it might be set manually.
if (!getPackageName().isEmpty()) {
nameParts.addFirst(getPackageName());
}
}
else {
if (currentScope.getName().isPresent()) {
nameParts.addFirst(currentScope.getName().get());
}
// ...else stop? If one of the enclosing scopes is unnamed,
// the full name is same as the simple name.
}
}
optCurrentScope = currentScope.getEnclosingScope();
}
return Names.getQualifiedName(nameParts);
}
@Override
public SymbolKind getKind() {
return kind;
}
/**
* @param kind the kind to set
*/
protected void setKind(SymbolKind kind) {
this.kind = Log.errorIfNull(kind);
}
/**
* @see Symbol#setAstNode(de.monticore.ast.ASTNode)
*/
@Override
public void setAstNode(ASTNode node) {
this.node = node;
}
/**
* @see Symbol#getAstNode()
*/
@Override
public Optional<ASTNode> getAstNode() {
return Optional.ofNullable(node);
}
@Override
public Scope getEnclosingScope() {
return enclosingScope;
}
@Override
public void setEnclosingScope(MutableScope scope) {
this.enclosingScope = scope;
}
/**
* @see Symbol#getAccessModifier()
*/
@Override
public AccessModifier getAccessModifier() {
return accessModifier;
}
@Override
public void setAccessModifier(AccessModifier accessModifier) {
this.accessModifier = accessModifier;
}
@Override
public String toString() {
return getName();
}
}