package scriptingLanguage.frames;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
import scriptingLanguage.Interpreter;
import scriptingLanguage.Token;
import scriptingLanguage.errors.InterpreterException;
import scriptingLanguage.errors.UnexpectedTypeException;
import scriptingLanguage.frames.errors.FrameAccessException;
import scriptingLanguage.variables.AbstractClass;
import scriptingLanguage.variables.JavaClass;
public class RootFrame extends AbstractFrame {
private final LinkedHashMap<String, AbstractClass<?>> types;
private final LinkedHashMap<String, String> imports;
public RootFrame(AbstractFrame previous, Interpreter interpreter) {
super(previous, interpreter);
types = new LinkedHashMap<>();
imports = new LinkedHashMap<>();
}
public RootFrame(RootFrame base, RootFrame previous) {
super(base, previous);
types = new LinkedHashMap<>(base.types);
imports = new LinkedHashMap<>(base.imports);
variables = new LinkedHashMap<>(base.variables);
}
@Override
public void addImport(String name) {
int dot = name.lastIndexOf('.');
if (name.charAt(dot + 1) == '*') {
List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
classLoadersList.add(ClasspathHelper.contextClassLoader());
classLoadersList.add(ClasspathHelper.staticClassLoader());
Reflections reflections = new Reflections(new ConfigurationBuilder().setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
.setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0]))).filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix(name.substring(0, dot)))));
Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);
for (Class<?> clazz : classes) {
name = clazz.getName();
dot = name.lastIndexOf('.');
imports.put(name.substring(dot + 1), name);
}
}
else
imports.put(name.substring(dot + 1), name);
}
@Override
public String getImport(String name) throws FrameAccessException {
return imports.containsKey(name) ? imports.get(name) : (previous != null ? previous.getImport(name) : name);
}
@Override
protected AbstractClass<?> getType0(String type) throws UnexpectedTypeException, FrameAccessException {
RootFrame frame = this;
do {
if (frame.types.containsKey(type))
return frame.types.get(type);
else if (frame.imports.containsKey(type) || frame.imports.containsValue(type)) {
try {
AbstractClass<?> output;
frame.types.put(type, (output = new JavaClass<Object>(type, Class.forName(getImport(type)))));
return output;
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
} while (frame.previous instanceof RootFrame && (frame = (RootFrame) frame.previous) != null);
throw new UnexpectedTypeException(type + " is not a valid type.");
}
@Override
protected boolean hasType0(String type) throws FrameAccessException {
return types.containsKey(getImport(type)) || (previous != null && previous.hasType0(type));
}
@Override
public Token makeNew(AbstractClass<?> caller, String type, Token parameters, AbstractFrame call) throws InterpreterException, ArrayIndexOutOfBoundsException {
if (type.equals("Integer") || type.equals("String") || type.equals("Character") || type.equals("Double") || type.equals("Boolean") || type.equals("Complex") || type.equals("Long")
|| type.equals("Short") || type.equals("Byte"))
return interpreter.eval(caller, parameters, call);
AbstractClass<?> t = getType(type);
return t.eval(caller, parameters, call);
}
@Override
public boolean writeVariable(String variable, Token value, boolean isNew) throws InterpreterException {
throw new FrameAccessException("Cannot write to a root frame after its creation.");
}
@Override
public void addType(String name, AbstractClass<?> type) throws FrameAccessException {
types.put(name, type);
}
}