Refactored error severity
This commit is contained in:
parent
bdcb2227c9
commit
6fa132313d
|
@ -0,0 +1,21 @@
|
||||||
|
package error;
|
||||||
|
|
||||||
|
import static error.ErrorManager.Severity.CRITICAL;
|
||||||
|
|
||||||
|
import error.ErrorManager.Severity;
|
||||||
|
import file.FilePosition;
|
||||||
|
|
||||||
|
public abstract class CriticalLineColumnException extends LineColumnException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public CriticalLineColumnException(FilePosition position) {
|
||||||
|
super(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Severity getSeverity() {
|
||||||
|
return CRITICAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package error;
|
||||||
|
|
||||||
|
import static error.ErrorManager.Severity.CRITICAL;
|
||||||
|
|
||||||
|
import error.ErrorManager.Severity;
|
||||||
|
|
||||||
|
public abstract class CriticalLispException extends LispException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Severity getSeverity() {
|
||||||
|
return CRITICAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
package error;
|
package error;
|
||||||
|
|
||||||
|
import static error.ErrorManager.Severity.*;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
|
|
||||||
import environment.RuntimeEnvironment;
|
import environment.RuntimeEnvironment;
|
||||||
|
@ -9,11 +12,10 @@ import environment.RuntimeEnvironment;
|
||||||
*/
|
*/
|
||||||
public class ErrorManager {
|
public class ErrorManager {
|
||||||
|
|
||||||
public static final int CRITICAL_LEVEL = 3;
|
|
||||||
|
|
||||||
private static final String ANSI_RESET = "\u001B[0m";
|
private static final String ANSI_RESET = "\u001B[0m";
|
||||||
private static final String ANSI_RED = "\u001B[31m";
|
private static final String ANSI_RED = "\u001B[31m";
|
||||||
private static final String ANSI_PURPLE = "\u001B[35m";
|
private static final String ANSI_PURPLE = "\u001B[35m";
|
||||||
|
private static final String ANSI_YELLOW = "\u001B[33m";
|
||||||
|
|
||||||
private RuntimeEnvironment environment;
|
private RuntimeEnvironment environment;
|
||||||
|
|
||||||
|
@ -21,26 +23,72 @@ public class ErrorManager {
|
||||||
this.environment = RuntimeEnvironment.getInstance();
|
this.environment = RuntimeEnvironment.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateError(LispException lispException) {
|
public void handle(LispException lispException) {
|
||||||
printError(lispException);
|
printMessage(lispException);
|
||||||
|
|
||||||
if (isCritical(lispException))
|
if (isCritical(lispException))
|
||||||
environment.terminateExceptionally();
|
environment.terminateExceptionally();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printError(LispException lispException) {
|
private void printMessage(LispException lispException) {
|
||||||
String formattedMessage = formatMessage(lispException);
|
PrintStream output = selectOutputStream(lispException.getSeverity());
|
||||||
environment.getErrorOutput().println(formattedMessage);
|
output.println(formatMessage(lispException));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PrintStream selectOutputStream(Severity severity) {
|
||||||
|
if (severity == WARNING)
|
||||||
|
return environment.getOutput();
|
||||||
|
|
||||||
|
return environment.getErrorOutput();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String formatMessage(LispException lispException) {
|
private String formatMessage(LispException lispException) {
|
||||||
String color = isCritical(lispException) ? ANSI_PURPLE : ANSI_RED;
|
Severity severity = lispException.getSeverity();
|
||||||
|
String color = severity.getMessageColor();
|
||||||
|
String prefix = severity.toDisplayString();
|
||||||
|
|
||||||
return MessageFormat.format("{0}error: {1}{2}", color, lispException.getMessage(), ANSI_RESET);
|
return MessageFormat.format("{0}{1}: {2}{3}", color, prefix, lispException.getMessage(), ANSI_RESET);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isCritical(LispException lispException) {
|
private boolean isCritical(LispException lispException) {
|
||||||
return lispException.getSeverity() >= CRITICAL_LEVEL;
|
return lispException.getSeverity() == CRITICAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static enum Severity {
|
||||||
|
WARNING {
|
||||||
|
|
||||||
|
public String getMessageColor() {
|
||||||
|
return ANSI_YELLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toDisplayString() {
|
||||||
|
return "warning";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ERROR {
|
||||||
|
|
||||||
|
public String getMessageColor() {
|
||||||
|
return ANSI_RED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toDisplayString() {
|
||||||
|
return "error";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CRITICAL {
|
||||||
|
|
||||||
|
public String getMessageColor() {
|
||||||
|
return ANSI_PURPLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toDisplayString() {
|
||||||
|
return "critical";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public abstract String getMessageColor();
|
||||||
|
|
||||||
|
public abstract String toDisplayString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
package error;
|
package error;
|
||||||
|
|
||||||
|
import static error.ErrorManager.Severity.ERROR;
|
||||||
|
|
||||||
|
import error.ErrorManager.Severity;
|
||||||
|
|
||||||
public abstract class LispException extends RuntimeException {
|
public abstract class LispException extends RuntimeException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
public int getSeverity() {
|
public Severity getSeverity() {
|
||||||
return 0;
|
return ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package error;
|
||||||
|
|
||||||
|
import static error.ErrorManager.Severity.WARNING;
|
||||||
|
|
||||||
|
import error.ErrorManager.Severity;
|
||||||
|
|
||||||
|
public abstract class LispWarning extends LispException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Severity getSeverity() {
|
||||||
|
return WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -12,15 +12,13 @@ import sexpression.*;
|
||||||
public class LOAD extends LispFunction {
|
public class LOAD extends LispFunction {
|
||||||
|
|
||||||
private ArgumentValidator argumentValidator;
|
private ArgumentValidator argumentValidator;
|
||||||
private RuntimeEnvironment environment;
|
|
||||||
private ErrorManager errorManager;
|
private ErrorManager errorManager;
|
||||||
|
|
||||||
public LOAD() {
|
public LOAD() {
|
||||||
this.argumentValidator = new ArgumentValidator("LOAD");
|
this.argumentValidator = new ArgumentValidator("LOAD");
|
||||||
this.argumentValidator.setExactNumberOfArguments(1);
|
this.argumentValidator.setExactNumberOfArguments(1);
|
||||||
this.argumentValidator.setEveryArgumentExpectedType(LispString.class);
|
this.argumentValidator.setEveryArgumentExpectedType(LispString.class);
|
||||||
this.environment = RuntimeEnvironment.getInstance();
|
this.errorManager = RuntimeEnvironment.getInstance().getErrorManager();
|
||||||
this.errorManager = this.environment.getErrorManager();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SExpression call(Cons argumentList) {
|
public SExpression call(Cons argumentList) {
|
||||||
|
@ -52,7 +50,7 @@ public class LOAD extends LispFunction {
|
||||||
try {
|
try {
|
||||||
parser = new LispParser(new FileInputStream(fileName), fileName);
|
parser = new LispParser(new FileInputStream(fileName), fileName);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
errorManager.generateError(new CouldNotLoadFileException(fileName));
|
errorManager.handle(new CouldNotLoadFileWarning(fileName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return parser;
|
return parser;
|
||||||
|
@ -63,7 +61,7 @@ public class LOAD extends LispFunction {
|
||||||
try {
|
try {
|
||||||
EVAL.eval(parser.getNextSExpression());
|
EVAL.eval(parser.getNextSExpression());
|
||||||
} catch (LispException e) {
|
} catch (LispException e) {
|
||||||
errorManager.generateError(e);
|
errorManager.handle(e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,12 +69,12 @@ public class LOAD extends LispFunction {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class CouldNotLoadFileException extends LispException {
|
public static class CouldNotLoadFileWarning extends LispWarning {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private String fileName;
|
private String fileName;
|
||||||
|
|
||||||
public CouldNotLoadFileException(String fileName) {
|
public CouldNotLoadFileWarning(String fileName) {
|
||||||
this.fileName = fileName;
|
this.fileName = fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package function.builtin.special;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
|
|
||||||
import environment.RuntimeEnvironment;
|
import environment.RuntimeEnvironment;
|
||||||
|
import error.*;
|
||||||
import function.*;
|
import function.*;
|
||||||
import function.builtin.cons.LIST;
|
import function.builtin.cons.LIST;
|
||||||
import sexpression.*;
|
import sexpression.*;
|
||||||
|
@ -13,7 +14,7 @@ public class DEFUN extends LispFunction {
|
||||||
private ArgumentValidator argumentValidator;
|
private ArgumentValidator argumentValidator;
|
||||||
private ArgumentValidator lambdaListIsListValidator;
|
private ArgumentValidator lambdaListIsListValidator;
|
||||||
private ArgumentValidator lambdaListValidator;
|
private ArgumentValidator lambdaListValidator;
|
||||||
private RuntimeEnvironment environment;
|
private ErrorManager errorManager;
|
||||||
|
|
||||||
public DEFUN() {
|
public DEFUN() {
|
||||||
this.argumentValidator = new ArgumentValidator("DEFUN");
|
this.argumentValidator = new ArgumentValidator("DEFUN");
|
||||||
|
@ -26,7 +27,7 @@ public class DEFUN extends LispFunction {
|
||||||
this.lambdaListValidator = new ArgumentValidator("DEFUN|parameter|");
|
this.lambdaListValidator = new ArgumentValidator("DEFUN|parameter|");
|
||||||
this.lambdaListValidator.setEveryArgumentExpectedType(Symbol.class);
|
this.lambdaListValidator.setEveryArgumentExpectedType(Symbol.class);
|
||||||
|
|
||||||
this.environment = RuntimeEnvironment.getInstance();
|
this.errorManager = RuntimeEnvironment.getInstance().getErrorManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SExpression call(Cons argumentList) {
|
public SExpression call(Cons argumentList) {
|
||||||
|
@ -44,20 +45,30 @@ public class DEFUN extends LispFunction {
|
||||||
UserDefinedFunction function = new UserDefinedFunction(functionName.toString(), lambdaList, functionBody);
|
UserDefinedFunction function = new UserDefinedFunction(functionName.toString(), lambdaList, functionBody);
|
||||||
|
|
||||||
if (FunctionTable.isAlreadyDefined(functionName.toString()))
|
if (FunctionTable.isAlreadyDefined(functionName.toString()))
|
||||||
printWarning(functionName);
|
errorManager.handle(new RedefiningFunctionWarning(functionName.toString()));
|
||||||
|
|
||||||
FunctionTable.defineFunction(functionName.toString(), function);
|
FunctionTable.defineFunction(functionName.toString(), function);
|
||||||
|
|
||||||
return functionName;
|
return functionName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void printWarning(SExpression functionName) {
|
|
||||||
String message = MessageFormat.format("WARNING: redefining function {0}", functionName.toString());
|
|
||||||
environment.getOutput().println(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean evaluateArguments() {
|
public boolean evaluateArguments() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class RedefiningFunctionWarning extends LispWarning {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private String functionName;
|
||||||
|
|
||||||
|
public RedefiningFunctionWarning(String functionName) {
|
||||||
|
this.functionName = functionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return MessageFormat.format("redefining function {0}", functionName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class LispInterpreter {
|
||||||
printValueOfNextSExpressionWithException();
|
printValueOfNextSExpressionWithException();
|
||||||
} catch (LispException e) {
|
} catch (LispException e) {
|
||||||
erasePrompt();
|
erasePrompt();
|
||||||
errorManager.generateError(e);
|
errorManager.handle(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ public class LispInterpreterBuilderImpl implements LispInterpreterBuilder {
|
||||||
try {
|
try {
|
||||||
environment.setInput(getInputStream());
|
environment.setInput(getInputStream());
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
errorManager.generateError(new LispFileNotFoundException(e));
|
errorManager.handle(new LispFileNotFoundException(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,22 +112,17 @@ public class LispInterpreterBuilderImpl implements LispInterpreterBuilder {
|
||||||
return isInteractive ? new InteractiveLispInterpreter() : new LispInterpreter();
|
return isInteractive ? new InteractiveLispInterpreter() : new LispInterpreter();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class InterpreterAlreadyBuiltException extends LispException {
|
public class InterpreterAlreadyBuiltException extends CriticalLispException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSeverity() {
|
|
||||||
return ErrorManager.CRITICAL_LEVEL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return "Refusing to build more than one interpreter.";
|
return "Refusing to build more than one interpreter.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class LispFileNotFoundException extends LispException {
|
public static class LispFileNotFoundException extends CriticalLispException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private String message;
|
private String message;
|
||||||
|
@ -136,11 +131,6 @@ public class LispInterpreterBuilderImpl implements LispInterpreterBuilder {
|
||||||
this.message = e.getMessage();
|
this.message = e.getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSeverity() {
|
|
||||||
return ErrorManager.CRITICAL_LEVEL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return message;
|
return message;
|
||||||
|
|
|
@ -2,7 +2,7 @@ package scanner;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import error.*;
|
import error.CriticalLispException;
|
||||||
|
|
||||||
public interface LispInputStream {
|
public interface LispInputStream {
|
||||||
|
|
||||||
|
@ -10,17 +10,12 @@ public interface LispInputStream {
|
||||||
|
|
||||||
void unreadLastCharacter();
|
void unreadLastCharacter();
|
||||||
|
|
||||||
public static class MaximumUnreadsExceededException extends LispException {
|
public static class MaximumUnreadsExceededException extends CriticalLispException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSeverity() {
|
|
||||||
return ErrorManager.CRITICAL_LEVEL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class UncheckedIOException extends LispException {
|
public static class UncheckedIOException extends CriticalLispException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private IOException ioException;
|
private IOException ioException;
|
||||||
|
@ -29,11 +24,6 @@ public interface LispInputStream {
|
||||||
this.ioException = ioException;
|
this.ioException = ioException;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSeverity() {
|
|
||||||
return ErrorManager.CRITICAL_LEVEL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return ioException.getMessage();
|
return ioException.getMessage();
|
||||||
|
|
|
@ -11,7 +11,7 @@ public interface TokenFactory {
|
||||||
|
|
||||||
Token createEOFToken(FilePosition position);
|
Token createEOFToken(FilePosition position);
|
||||||
|
|
||||||
public static class EmptyTokenTextException extends LineColumnException {
|
public static class EmptyTokenTextException extends CriticalLineColumnException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ -19,11 +19,6 @@ public interface TokenFactory {
|
||||||
super(position);
|
super(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSeverity() {
|
|
||||||
return ErrorManager.CRITICAL_LEVEL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMessagePrefix() {
|
public String getMessagePrefix() {
|
||||||
return "empty token";
|
return "empty token";
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package error;
|
package error;
|
||||||
|
|
||||||
|
import static error.ErrorManager.Severity.*;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
@ -8,6 +9,7 @@ import java.util.*;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import environment.RuntimeEnvironment;
|
import environment.RuntimeEnvironment;
|
||||||
|
import error.ErrorManager.Severity;
|
||||||
|
|
||||||
public class ErrorManagerTester {
|
public class ErrorManagerTester {
|
||||||
|
|
||||||
|
@ -15,22 +17,24 @@ public class ErrorManagerTester {
|
||||||
private static final String MESSAGE = "message";
|
private static final String MESSAGE = "message";
|
||||||
|
|
||||||
private Set<String> indicatorSet;
|
private Set<String> indicatorSet;
|
||||||
|
private ByteArrayOutputStream errorOutputStream;
|
||||||
private ByteArrayOutputStream outputStream;
|
private ByteArrayOutputStream outputStream;
|
||||||
|
|
||||||
private ErrorManager createErrorManagerWithIndicators() {
|
private ErrorManager createErrorManagerWithIndicators() {
|
||||||
RuntimeEnvironment.getInstance().setErrorTerminationFunction(() -> indicatorSet.add(TERMINATED));
|
RuntimeEnvironment.getInstance().setErrorTerminationFunction(() -> indicatorSet.add(TERMINATED));
|
||||||
RuntimeEnvironment.getInstance().setErrorOutput(new PrintStream(outputStream));
|
RuntimeEnvironment.getInstance().setErrorOutput(new PrintStream(errorOutputStream));
|
||||||
|
RuntimeEnvironment.getInstance().setOutput(new PrintStream(outputStream));
|
||||||
|
|
||||||
return new ErrorManager();
|
return new ErrorManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
private LispException createLispException(int severity) {
|
private LispException createLispException(Severity severity) {
|
||||||
return new LispException() {
|
return new LispException() {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getSeverity() {
|
public Severity getSeverity() {
|
||||||
return severity;
|
return severity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,17 +53,26 @@ public class ErrorManagerTester {
|
||||||
assertFalse(indicatorSet.contains(TERMINATED));
|
assertFalse(indicatorSet.contains(TERMINATED));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertErrorMessageNotWritten() {
|
private void assertWarningMessageNotWritten() {
|
||||||
assertTrue(outputStream.toByteArray().length == 0);
|
assertTrue(outputStream.toByteArray().length == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertErrorMessageWritten() {
|
private void assertWarningMessageWritten() {
|
||||||
assertTrue(outputStream.toByteArray().length > 0);
|
assertTrue(outputStream.toByteArray().length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertErrorMessageNotWritten() {
|
||||||
|
assertTrue(errorOutputStream.toByteArray().length == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertErrorMessageWritten() {
|
||||||
|
assertTrue(errorOutputStream.toByteArray().length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
this.indicatorSet = new HashSet<>();
|
this.indicatorSet = new HashSet<>();
|
||||||
|
this.errorOutputStream = new ByteArrayOutputStream();
|
||||||
this.outputStream = new ByteArrayOutputStream();
|
this.outputStream = new ByteArrayOutputStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,24 +80,43 @@ public class ErrorManagerTester {
|
||||||
public void givenCriticalExceptionSeverity_RunsProvidedTerminationFunction() {
|
public void givenCriticalExceptionSeverity_RunsProvidedTerminationFunction() {
|
||||||
ErrorManager errorManager = createErrorManagerWithIndicators();
|
ErrorManager errorManager = createErrorManagerWithIndicators();
|
||||||
|
|
||||||
errorManager.generateError(createLispException(ErrorManager.CRITICAL_LEVEL));
|
errorManager.handle(createLispException(CRITICAL));
|
||||||
assertTerminated();
|
assertTerminated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenNonCriticalExceptionSeverity_DoesNotRunProvidedTerminationFunction() {
|
public void givenWarningExceptionSeverity_DoesNotRunProvidedTerminationFunction() {
|
||||||
ErrorManager errorManager = createErrorManagerWithIndicators();
|
ErrorManager errorManager = createErrorManagerWithIndicators();
|
||||||
|
|
||||||
errorManager.generateError(createLispException(0));
|
errorManager.handle(createLispException(WARNING));
|
||||||
assertNotTerminated();
|
assertNotTerminated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void usesOutputFunctionToDisplayMessages_NoTermination() {
|
public void givenErrorExceptionSeverity_DoesNotRunProvidedTerminationFunction() {
|
||||||
ErrorManager errorManager = createErrorManagerWithIndicators();
|
ErrorManager errorManager = createErrorManagerWithIndicators();
|
||||||
|
|
||||||
errorManager.generateError(createLispException(0));
|
errorManager.handle(createLispException(ERROR));
|
||||||
assertNotTerminated();
|
assertNotTerminated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void usesOutputToDisplayWarningMessage() {
|
||||||
|
ErrorManager errorManager = createErrorManagerWithIndicators();
|
||||||
|
|
||||||
|
errorManager.handle(createLispException(WARNING));
|
||||||
|
assertNotTerminated();
|
||||||
|
assertErrorMessageNotWritten();
|
||||||
|
assertWarningMessageWritten();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void usesErrorOutputToDisplayErrorMessage() {
|
||||||
|
ErrorManager errorManager = createErrorManagerWithIndicators();
|
||||||
|
|
||||||
|
errorManager.handle(createLispException(ERROR));
|
||||||
|
assertNotTerminated();
|
||||||
|
assertWarningMessageNotWritten();
|
||||||
assertErrorMessageWritten();
|
assertErrorMessageWritten();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,9 +132,16 @@ public class ErrorManagerTester {
|
||||||
public void usesOutputFunctionToDisplayMessages_WithTermination() {
|
public void usesOutputFunctionToDisplayMessages_WithTermination() {
|
||||||
ErrorManager errorManager = createErrorManagerWithIndicators();
|
ErrorManager errorManager = createErrorManagerWithIndicators();
|
||||||
|
|
||||||
errorManager.generateError(createLispException(ErrorManager.CRITICAL_LEVEL));
|
errorManager.handle(createLispException(CRITICAL));
|
||||||
assertTerminated();
|
assertTerminated();
|
||||||
assertErrorMessageWritten();
|
assertErrorMessageWritten();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void severityCoverage() {
|
||||||
|
Severity.valueOf(WARNING.toString());
|
||||||
|
Severity.valueOf(ERROR.toString());
|
||||||
|
Severity.valueOf(CRITICAL.toString());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package function;
|
package function;
|
||||||
|
|
||||||
|
import static error.ErrorManager.Severity.ERROR;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import error.ErrorManager;
|
|
||||||
import function.ArgumentValidator.*;
|
import function.ArgumentValidator.*;
|
||||||
import sexpression.*;
|
import sexpression.*;
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ public class ArgumentValidatorTester {
|
||||||
public void tooManyArgumentsException_HasCorrectSeverity() {
|
public void tooManyArgumentsException_HasCorrectSeverity() {
|
||||||
TooManyArgumentsException e = new TooManyArgumentsException("TEST", Nil.getInstance());
|
TooManyArgumentsException e = new TooManyArgumentsException("TEST", Nil.getInstance());
|
||||||
|
|
||||||
assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL);
|
assertEquals(ERROR, e.getSeverity());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -90,7 +90,7 @@ public class ArgumentValidatorTester {
|
||||||
public void tooFewArgumentsException_HasCorrectSeverity() {
|
public void tooFewArgumentsException_HasCorrectSeverity() {
|
||||||
TooFewArgumentsException e = new TooFewArgumentsException("TEST", Nil.getInstance());
|
TooFewArgumentsException e = new TooFewArgumentsException("TEST", Nil.getInstance());
|
||||||
|
|
||||||
assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL);
|
assertEquals(ERROR, e.getSeverity());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -105,7 +105,7 @@ public class ArgumentValidatorTester {
|
||||||
public void BadArgumentTypeException_HasCorrectSeverity() {
|
public void BadArgumentTypeException_HasCorrectSeverity() {
|
||||||
BadArgumentTypeException e = new BadArgumentTypeException("TEST", Nil.getInstance(), SExpression.class);
|
BadArgumentTypeException e = new BadArgumentTypeException("TEST", Nil.getInstance(), SExpression.class);
|
||||||
|
|
||||||
assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL);
|
assertEquals(ERROR, e.getSeverity());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -188,7 +188,7 @@ public class ArgumentValidatorTester {
|
||||||
public void DottedArgumentListException_HasCorrectSeverity() {
|
public void DottedArgumentListException_HasCorrectSeverity() {
|
||||||
DottedArgumentListException e = new DottedArgumentListException("TEST", Nil.getInstance());
|
DottedArgumentListException e = new DottedArgumentListException("TEST", Nil.getInstance());
|
||||||
|
|
||||||
assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL);
|
assertEquals(ERROR, e.getSeverity());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -1,39 +1,51 @@
|
||||||
package function.builtin;
|
package function.builtin;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static testutil.TestUtilities.*;
|
import static testutil.TestUtilities.evaluateString;
|
||||||
|
import static testutil.TypeAssertions.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import environment.RuntimeEnvironment;
|
import environment.RuntimeEnvironment;
|
||||||
|
import error.ErrorManager;
|
||||||
import function.ArgumentValidator.*;
|
import function.ArgumentValidator.*;
|
||||||
import sexpression.*;
|
|
||||||
|
|
||||||
public class LOADTester {
|
public class LOADTester {
|
||||||
|
|
||||||
private ByteArrayOutputStream outputStream;
|
private ByteArrayOutputStream outputStream;
|
||||||
|
private ByteArrayOutputStream errorOutputStream;
|
||||||
|
|
||||||
private void assertSomethingPrinted() {
|
private void assertWarningMessagePrinted() {
|
||||||
assertTrue(outputStream.toByteArray().length > 0);
|
assertTrue(outputStream.toByteArray().length > 0);
|
||||||
|
assertTrue(errorOutputStream.toByteArray().length == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertErrorMessagePrinted() {
|
||||||
|
assertTrue(errorOutputStream.toByteArray().length > 0);
|
||||||
|
assertTrue(outputStream.toByteArray().length == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertNothingPrinted() {
|
private void assertNothingPrinted() {
|
||||||
assertTrue(outputStream.toByteArray().length == 0);
|
assertTrue(outputStream.toByteArray().length == 0);
|
||||||
|
assertTrue(outputStream.toByteArray().length == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
this.outputStream = new ByteArrayOutputStream();
|
this.outputStream = new ByteArrayOutputStream();
|
||||||
RuntimeEnvironment.getInstance().setErrorOutput(new PrintStream(outputStream));
|
this.errorOutputStream = new ByteArrayOutputStream();
|
||||||
|
RuntimeEnvironment.getInstance().setOutput(new PrintStream(outputStream));
|
||||||
|
RuntimeEnvironment.getInstance().setErrorOutput(new PrintStream(errorOutputStream));
|
||||||
|
RuntimeEnvironment.getInstance().setErrorManager(new ErrorManager());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loadGoodFile_ReturnsTAndPrintsNothing() {
|
public void loadGoodFile_ReturnsTAndPrintsNothing() {
|
||||||
String input = "(load \"test/function/builtin/test-files/load-good.lisp\")";
|
String input = "(load \"test/function/builtin/test-files/load-good.lisp\")";
|
||||||
|
|
||||||
assertSExpressionsMatch(Symbol.T, evaluateString(input));
|
assertT(evaluateString(input));
|
||||||
assertNothingPrinted();
|
assertNothingPrinted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,16 +53,16 @@ public class LOADTester {
|
||||||
public void loadBadFile_ReturnsNilAndPrintsError() {
|
public void loadBadFile_ReturnsNilAndPrintsError() {
|
||||||
String input = "(load \"test/function/builtin/test-files/load-bad.lisp\")";
|
String input = "(load \"test/function/builtin/test-files/load-bad.lisp\")";
|
||||||
|
|
||||||
assertSExpressionsMatch(Nil.getInstance(), evaluateString(input));
|
assertNil(evaluateString(input));
|
||||||
assertSomethingPrinted();
|
assertErrorMessagePrinted();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loadNonExistentFile_ReturnsNilAndPrintsError() {
|
public void loadNonExistentFile_ReturnsNilAndPrintsError() {
|
||||||
String input = "(load \"doesNotExist.lisp\")";
|
String input = "(load \"doesNotExist.lisp\")";
|
||||||
|
|
||||||
assertSExpressionsMatch(Nil.getInstance(), evaluateString(input));
|
assertNil(evaluateString(input));
|
||||||
assertSomethingPrinted();
|
assertWarningMessagePrinted();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = BadArgumentTypeException.class)
|
@Test(expected = BadArgumentTypeException.class)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package function.builtin.special;
|
package function.builtin.special;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static testutil.TestUtilities.*;
|
import static testutil.TestUtilities.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
@ -8,6 +8,7 @@ import java.io.*;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import environment.RuntimeEnvironment;
|
import environment.RuntimeEnvironment;
|
||||||
|
import error.ErrorManager;
|
||||||
import function.ArgumentValidator.*;
|
import function.ArgumentValidator.*;
|
||||||
import table.FunctionTable;
|
import table.FunctionTable;
|
||||||
|
|
||||||
|
@ -15,14 +16,15 @@ public class DEFUNTester {
|
||||||
|
|
||||||
private ByteArrayOutputStream outputStream;
|
private ByteArrayOutputStream outputStream;
|
||||||
|
|
||||||
private void assertPrinted(String expected) {
|
private void assertSomethingPrinted() {
|
||||||
assertEquals(expected, outputStream.toString());
|
assertTrue(outputStream.toByteArray().length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
this.outputStream = new ByteArrayOutputStream();
|
this.outputStream = new ByteArrayOutputStream();
|
||||||
RuntimeEnvironment.getInstance().setOutput(new PrintStream(outputStream));
|
RuntimeEnvironment.getInstance().setOutput(new PrintStream(outputStream));
|
||||||
|
RuntimeEnvironment.getInstance().setErrorManager(new ErrorManager());
|
||||||
FunctionTable.reset();
|
FunctionTable.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +55,7 @@ public class DEFUNTester {
|
||||||
evaluateString(input);
|
evaluateString(input);
|
||||||
evaluateString(input);
|
evaluateString(input);
|
||||||
|
|
||||||
assertPrinted("WARNING: redefining function MYFUNCTION\n");
|
assertSomethingPrinted();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -61,7 +63,7 @@ public class DEFUNTester {
|
||||||
evaluateString("(defun myFunction2 () nil)");
|
evaluateString("(defun myFunction2 () nil)");
|
||||||
evaluateString("(defun myFunction2 () T)");
|
evaluateString("(defun myFunction2 () T)");
|
||||||
|
|
||||||
assertPrinted("WARNING: redefining function MYFUNCTION2\n");
|
assertSomethingPrinted();
|
||||||
assertSExpressionsMatch(parseString("t"), evaluateString("(myFunction2)"));
|
assertSExpressionsMatch(parseString("t"), evaluateString("(myFunction2)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package parser;
|
package parser;
|
||||||
|
|
||||||
|
import static error.ErrorManager.Severity.ERROR;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
import static testutil.TestUtilities.*;
|
import static testutil.TestUtilities.*;
|
||||||
import static testutil.TypeAssertions.*;
|
import static testutil.TypeAssertions.*;
|
||||||
|
@ -8,7 +9,7 @@ import java.io.InputStream;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import error.*;
|
import error.LispException;
|
||||||
import scanner.LispInputStream.UncheckedIOException;
|
import scanner.LispInputStream.UncheckedIOException;
|
||||||
import scanner.LispScanner.UnterminatedStringException;
|
import scanner.LispScanner.UnterminatedStringException;
|
||||||
import token.Eof.EofEncounteredException;
|
import token.Eof.EofEncounteredException;
|
||||||
|
@ -188,7 +189,7 @@ public class LispParserTester {
|
||||||
try {
|
try {
|
||||||
parser.getNextSExpression();
|
parser.getNextSExpression();
|
||||||
} catch (BadCharacterException e) {
|
} catch (BadCharacterException e) {
|
||||||
assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL);
|
assertEquals(ERROR, e.getSeverity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +232,7 @@ public class LispParserTester {
|
||||||
try {
|
try {
|
||||||
parser.getNextSExpression();
|
parser.getNextSExpression();
|
||||||
} catch (EofEncounteredException e) {
|
} catch (EofEncounteredException e) {
|
||||||
assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL);
|
assertEquals(ERROR, e.getSeverity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,7 +267,7 @@ public class LispParserTester {
|
||||||
try {
|
try {
|
||||||
parser.getNextSExpression();
|
parser.getNextSExpression();
|
||||||
} catch (StartsWithRightParenthesisException e) {
|
} catch (StartsWithRightParenthesisException e) {
|
||||||
assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL);
|
assertEquals(ERROR, e.getSeverity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package scanner;
|
package scanner;
|
||||||
|
|
||||||
|
import static error.ErrorManager.Severity.CRITICAL;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
import static testutil.TestUtilities.*;
|
import static testutil.TestUtilities.*;
|
||||||
|
|
||||||
|
@ -7,7 +8,6 @@ import java.io.InputStream;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import error.ErrorManager;
|
|
||||||
import scanner.LispInputStream.*;
|
import scanner.LispInputStream.*;
|
||||||
|
|
||||||
public class LispCommentRemovingInputStreamTester {
|
public class LispCommentRemovingInputStreamTester {
|
||||||
|
@ -188,7 +188,7 @@ public class LispCommentRemovingInputStreamTester {
|
||||||
try {
|
try {
|
||||||
lispInputStream.unreadLastCharacter();
|
lispInputStream.unreadLastCharacter();
|
||||||
} catch (MaximumUnreadsExceededException e) {
|
} catch (MaximumUnreadsExceededException e) {
|
||||||
assertTrue(e.getSeverity() >= ErrorManager.CRITICAL_LEVEL);
|
assertEquals(CRITICAL, e.getSeverity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ public class LispCommentRemovingInputStreamTester {
|
||||||
try {
|
try {
|
||||||
lispInputStream.read();
|
lispInputStream.read();
|
||||||
} catch (UncheckedIOException e) {
|
} catch (UncheckedIOException e) {
|
||||||
assertTrue(e.getSeverity() >= ErrorManager.CRITICAL_LEVEL);
|
assertEquals(CRITICAL, e.getSeverity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package scanner;
|
package scanner;
|
||||||
|
|
||||||
|
import static error.ErrorManager.Severity.ERROR;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
import static testutil.TestUtilities.createInputStreamFromString;
|
import static testutil.TestUtilities.createInputStreamFromString;
|
||||||
|
|
||||||
|
@ -8,7 +9,6 @@ import java.util.*;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import error.ErrorManager;
|
|
||||||
import scanner.LispScanner.UnterminatedStringException;
|
import scanner.LispScanner.UnterminatedStringException;
|
||||||
import token.*;
|
import token.*;
|
||||||
import token.Number;
|
import token.Number;
|
||||||
|
@ -56,7 +56,7 @@ public class LispScannerTypeTester {
|
||||||
try {
|
try {
|
||||||
assertTokenTypesMatch(input);
|
assertTokenTypesMatch(input);
|
||||||
} catch (BadCharacterException e) {
|
} catch (BadCharacterException e) {
|
||||||
assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL);
|
assertEquals(ERROR, e.getSeverity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ public class LispScannerTypeTester {
|
||||||
try {
|
try {
|
||||||
assertTokenTypesMatch(input);
|
assertTokenTypesMatch(input);
|
||||||
} catch (LispScanner.UnterminatedStringException e) {
|
} catch (LispScanner.UnterminatedStringException e) {
|
||||||
assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL);
|
assertEquals(ERROR, e.getSeverity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package sexpression;
|
package sexpression;
|
||||||
|
|
||||||
|
import static error.ErrorManager.Severity.ERROR;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import error.ErrorManager;
|
|
||||||
import function.UserDefinedFunction;
|
import function.UserDefinedFunction;
|
||||||
import sexpression.LispNumber.InvalidNumberException;
|
import sexpression.LispNumber.InvalidNumberException;
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ public class SExpressionTester {
|
||||||
try {
|
try {
|
||||||
new LispNumber("a");
|
new LispNumber("a");
|
||||||
} catch (InvalidNumberException e) {
|
} catch (InvalidNumberException e) {
|
||||||
assertTrue(e.getSeverity() < ErrorManager.CRITICAL_LEVEL);
|
assertEquals(ERROR, e.getSeverity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package token;
|
package token;
|
||||||
|
|
||||||
|
import static error.ErrorManager.Severity.CRITICAL;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import error.ErrorManager;
|
|
||||||
import file.FilePosition;
|
import file.FilePosition;
|
||||||
import token.TokenFactory.*;
|
import token.TokenFactory.*;
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ public class TokenFactoryTester {
|
||||||
try {
|
try {
|
||||||
createToken("");
|
createToken("");
|
||||||
} catch (EmptyTokenTextException e) {
|
} catch (EmptyTokenTextException e) {
|
||||||
assertTrue(e.getSeverity() == ErrorManager.CRITICAL_LEVEL);
|
assertEquals(CRITICAL, e.getSeverity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue