package function; import java.text.MessageFormat; import error.LispException; import function.builtin.LENGTH; import sexpression.*; public class ArgumentValidator { private Class firstArgumentType; private Class trailingArgumentType; private String functionName; private Integer maximumNumberOfArguments; private Integer minimumNumberOfArguments; public ArgumentValidator(String functionName) { this.firstArgumentType = SExpression.class; this.trailingArgumentType = SExpression.class; this.functionName = functionName; this.minimumNumberOfArguments = null; this.maximumNumberOfArguments = null; } public void setFirstArgumentExpectedType(Class argumentType) { this.firstArgumentType = argumentType; } public void setTrailingArgumentExpectedType(Class argumentType) { this.trailingArgumentType = argumentType; } public void setEveryArgumentExpectedType(Class argumentType) { this.firstArgumentType = argumentType; this.trailingArgumentType = argumentType; } public void setMaximumNumberOfArguments(int maximumNumberOfArguments) { this.maximumNumberOfArguments = maximumNumberOfArguments; } public void setMinimumNumberOfArguments(int minimumNumberOfArguments) { this.minimumNumberOfArguments = minimumNumberOfArguments; } public void setExactNumberOfArguments(int exactNumberOfArguments) { this.minimumNumberOfArguments = exactNumberOfArguments; this.maximumNumberOfArguments = exactNumberOfArguments; } public void validate(Cons argumentList) { validateListNotDotted(argumentList); if (containsTooFewArguments(argumentList)) throw new TooFewArgumentsException(functionName, argumentList); else if (containsTooManyArguments(argumentList)) throw new TooManyArgumentsException(functionName, argumentList); validateArgumentTypes(argumentList); } private void validateListNotDotted(Cons argumentList) { Cons currentCons = argumentList; SExpression nextCons = argumentList.getCdr(); while (!nextCons.nullp()) { if (!nextCons.consp()) throw new DottedArgumentListException(functionName, argumentList); currentCons = (Cons) nextCons; nextCons = currentCons.getCdr(); } } private boolean containsTooFewArguments(Cons argumentList) { return (minimumNumberOfArguments != null) && (LENGTH.getLength(argumentList) < minimumNumberOfArguments); } private boolean containsTooManyArguments(Cons argumentList) { return (maximumNumberOfArguments != null) && (LENGTH.getLength(argumentList) > maximumNumberOfArguments); } private void validateArgumentTypes(Cons argumentList) { if (!isExpectedFirstArgumentType(argumentList.getCar())) throw new BadArgumentTypeException(functionName, argumentList.getCar(), firstArgumentType); validateRemainingArguments(argumentList); } private boolean isExpectedFirstArgumentType(SExpression firstArgument) { return firstArgumentType.isInstance(firstArgument); } private void validateRemainingArguments(Cons cons) { for (cons = (Cons) cons.getCdr(); !cons.nullp(); cons = (Cons) cons.getCdr()) if (!isExpectedRemainingArgumentType(cons.getCar())) throw new BadArgumentTypeException(functionName, cons.getCar(), trailingArgumentType); } private boolean isExpectedRemainingArgumentType(SExpression remainingArgument) { return trailingArgumentType.isInstance(remainingArgument); } public static class TooFewArgumentsException extends LispException { private static final long serialVersionUID = 1L; private String functionName; private Cons originalSExpression; public TooFewArgumentsException(String functionName, Cons argumentList) { this.functionName = functionName; this.originalSExpression = new Cons(new Symbol(this.functionName), argumentList); } @Override public String getMessage() { return MessageFormat.format("too few arguments given to {0}: {1}", functionName, originalSExpression); } } public static class TooManyArgumentsException extends LispException { private static final long serialVersionUID = 1L; private String functionName; private Cons originalSExpression; public TooManyArgumentsException(String functionName, Cons argumentList) { this.functionName = functionName; this.originalSExpression = new Cons(new Symbol(this.functionName), argumentList); } @Override public String getMessage() { return MessageFormat.format("too many arguments given to {0}: {1}", functionName, originalSExpression); } } public static class DottedArgumentListException extends LispException { private static final long serialVersionUID = 1L; private String functionName; private Cons originalSExpression; public DottedArgumentListException(String functionName, Cons argumentList) { this.functionName = functionName; this.originalSExpression = new Cons(new Symbol(this.functionName), argumentList); } @Override public String getMessage() { return MessageFormat.format("dotted argument list given to {0}: {1}", functionName, originalSExpression); } } public static class BadArgumentTypeException extends LispException { private static final long serialVersionUID = 1L; private String functionName; private String argument; private Class expected; public BadArgumentTypeException(String functionName, SExpression argument, Class expected) { this.functionName = functionName; this.argument = argument.toString(); this.expected = expected; } @Override public String getMessage() { DisplayName displayName = expected.getAnnotation(DisplayName.class); String expectedType = (displayName == null) ? "unknown" : displayName.value(); return MessageFormat.format("{0}: {1} is not the expected type of ''{2}''", functionName, argument, expectedType); } } }