ModuleExpression.java
package org.sterling.runtime.expression;
import static org.sterling.runtime.expression.ExpressionConversions.convertSymbol;
import static org.sterling.runtime.expression.ExpressionFactory.declaration;
import static org.sterling.runtime.expression.ExpressionFactory.symbol;
import static org.sterling.util.StringUtil.stringify;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import org.sterling.SterlingException;
import org.sterling.runtime.GlobalModule;
import org.sterling.runtime.exception.UndefinedMemberException;
public class ModuleExpression extends Expression {
private static Map<Symbol, DeclaredExpression> processMembers(Set<DeclaredExpression> members) {
Map<Symbol, DeclaredExpression> map = new HashMap<>();
for (DeclaredExpression declaration : members) {
map.put(declaration.getSymbol(), declaration);
}
return map;
}
private final Symbol symbol;
private final Map<Symbol, DeclaredExpression> members;
private final Set<Symbol> loadedMembers;
private final GlobalModule loader;
public ModuleExpression(Symbol symbol, GlobalModule loader, Set<DeclaredExpression> members) {
this.symbol = symbol;
this.loader = loader;
this.members = new ConcurrentHashMap<>(processMembers(members));
this.loadedMembers = new CopyOnWriteArraySet<>();
}
@Override
public Expression access(Expression member) throws SterlingException {
Symbol symbol = convertSymbol(member);
if (!isDefined(symbol)) {
try {
return super.access(member);
} catch (UndefinedMemberException exception) {
load(symbol);
}
}
return members.get(symbol).getExpression();
}
@Override
public String toString() {
return stringify(this, symbol);
}
private Symbol getModuleName(Symbol subModule) {
return symbol(symbol.getValue() + "/" + subModule.getValue());
}
private boolean isDefined(Symbol symbol) {
return members.containsKey(symbol);
}
private void load(Symbol subModule) throws SterlingException {
if (!loadedMembers.contains(subModule)) {
loadedMembers.add(subModule);
members.put(subModule, declaration(subModule, loader.loadModule(getModuleName(subModule))));
}
}
}