package interpreter; import environment.RuntimeEnvironment; import error.CriticalLispException; import error.ErrorManager; import interpreter.LispInterpreter.LanguageFile; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; import java.util.function.Function; import static util.Path.getPathPrefix; public class LispInterpreterBuilderImpl implements LispInterpreterBuilder { private static LispInterpreterBuilder uniqueInstance = new LispInterpreterBuilderImpl(); public static LispInterpreterBuilder getInstance() { return uniqueInstance; } private String inputName; private InputStream inputStream; private PrintStream outputStream; private PrintStream errorOutputStream; private Runnable terminationFunction; private Runnable errorTerminationFunction; private List languageFiles; private Function promptDecorator; private Function valueOutputDecorator; private Function warningOutputDecorator; private Function errorOutputDecorator; private Function criticalOutputDecorator; private RuntimeEnvironment environment; private boolean isInteractive; private boolean isFileBased; protected boolean isBuilt; protected LispInterpreterBuilderImpl() { this.environment = RuntimeEnvironment.getInstance(); this.inputName = ""; this.isInteractive = true; this.isFileBased = false; this.isBuilt = false; this.languageFiles = new ArrayList<>(); this.promptDecorator = s -> s; this.valueOutputDecorator = s -> s; this.warningOutputDecorator = s -> s; this.errorOutputDecorator = s -> s; this.criticalOutputDecorator = s -> s; } @Override public void setInput(InputStream inputStream, String inputName) { this.inputStream = inputStream; this.inputName = inputName; } @Override public void setOutput(PrintStream outputStream) { this.outputStream = outputStream; } @Override public void setErrorOutput(PrintStream errorOutputStream) { this.errorOutputStream = errorOutputStream; } @Override public void setTerminationFunction(Runnable terminationFunction) { this.terminationFunction = terminationFunction; } @Override public void setErrorTerminationFunction(Runnable errorTerminationFunction) { this.errorTerminationFunction = errorTerminationFunction; } @Override public void setNotInteractive() { this.isInteractive = false; } @Override public void useFile(String fileName) { this.isFileBased = true; this.inputName = fileName; this.setNotInteractive(); } @Override public void setLanguageFileNames(String... languageFileNames) { ClassLoader classLoader = getClass().getClassLoader(); languageFiles = new ArrayList<>(); for (String fileName : languageFileNames) languageFiles.add(new LanguageFile(classLoader.getResourceAsStream(fileName), fileName)); } @Override public void setPromptDecorator(Function decorator) { this.promptDecorator = decorator; } @Override public void setValueOutputDecorator(Function decorator) { this.valueOutputDecorator = decorator; } @Override public void setWarningOutputDecorator(Function decorator) { this.warningOutputDecorator = decorator; } @Override public void setErrorOutputDecorator(Function decorator) { this.errorOutputDecorator = decorator; } @Override public void setCriticalOutputDecorator(Function decorator) { this.criticalOutputDecorator = decorator; } @Override public LispInterpreter build() { if (!isBuilt) return buildInterpreter(); else throw new InterpreterAlreadyBuiltException(); } private LispInterpreter buildInterpreter() { configureRuntimeEnvironment(); LispInterpreter lispInterpreter = createInterpreter(); lispInterpreter.interpretLanguageFiles(languageFiles); isBuilt = true; return lispInterpreter; } private void configureRuntimeEnvironment() { ErrorManager errorManager = new ErrorManager(); environment.setOutput(outputStream); environment.setErrorOutput(errorOutputStream); environment.setErrorManager(errorManager); environment.setTerminationFunction(terminationFunction); environment.setErrorTerminationFunction(errorTerminationFunction); environment.setPromptDecorator(promptDecorator); environment.setValueOutputDecorator(valueOutputDecorator); environment.setWarningOutputDecorator(warningOutputDecorator); environment.setErrorOutputDecorator(errorOutputDecorator); environment.setCriticalOutputDecorator(criticalOutputDecorator); configurePath(); configureInput(errorManager); } private void configurePath() { if (isFileBased) environment.setPath(getPathPrefix(inputName)); else environment.setPath(""); } private void configureInput(ErrorManager errorManager) { environment.setInputName(inputName); try { environment.setInput(getInputStream()); } catch (FileNotFoundException e) { errorManager.handle(new LispFileNotFoundException(e)); } } private InputStream getInputStream() throws FileNotFoundException { return isFileBased ? new FileInputStream(inputName) : inputStream; } private LispInterpreter createInterpreter() { if (isFileBased) return new FileLispInterpreter(); else if (isInteractive) return new InteractiveLispInterpreter(); return new LispInterpreter(); } public static class InterpreterAlreadyBuiltException extends CriticalLispException { private static final long serialVersionUID = 1L; @Override public String getMessage() { return "Refusing to build more than one interpreter."; } } public static class LispFileNotFoundException extends CriticalLispException { private static final long serialVersionUID = 1L; private String message; public LispFileNotFoundException(FileNotFoundException e) { this.message = e.getMessage(); } @Override public String getMessage() { return message; } } }