diff --git a/homework-g597-vasilyev/pom.xml b/homework-g597-vasilyev/pom.xml
index c96bf1049..2dd429437 100644
--- a/homework-g597-vasilyev/pom.xml
+++ b/homework-g597-vasilyev/pom.xml
@@ -9,6 +9,10 @@
4.0.0
+
+ 1.4.2.RELEASE
+
+
homework-g597-vasilyev
@@ -22,5 +26,42 @@
1.0.0
test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring.boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+ ${spring.boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+ ${spring.boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+ ${spring.boot.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+ ${spring.boot.version}
+
+
+ com.zaxxer
+ HikariCP
+ 2.5.1
+
+
+ com.h2database
+ h2
+ 1.4.193
+
\ No newline at end of file
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/BuiltinCommand.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/BuiltinCommand.java
new file mode 100644
index 000000000..a5cf2fb8f
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/BuiltinCommand.java
@@ -0,0 +1,30 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task1;
+
+import ru.mipt.java2016.homework.base.task1.ParsingException;
+
+import java.util.Stack;
+import java.util.function.Function;
+
+/**
+ * Created by mizabrik on 21.12.16.
+ */
+public class BuiltinCommand implements Command {
+ private final Function function;
+ private final int valency;
+
+ public BuiltinCommand(Function function, int valency) {
+ this.function = function;
+ this.valency = valency;
+ }
+
+ @Override
+ public void apply(Stack stack) throws ParsingException {
+ Double[] args = new Double[valency];
+
+ for (int i = valency - 1; i >= 0; --i) {
+ args[i] = stack.pop();
+ }
+
+ stack.push(function.apply(args));
+ }
+}
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/Command.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/Command.java
new file mode 100644
index 000000000..4a6eba04e
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/Command.java
@@ -0,0 +1,12 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task1;
+
+import ru.mipt.java2016.homework.base.task1.ParsingException;
+
+import java.util.Stack;
+
+/**
+ * Created by mizabrik on 19.12.16.
+ */
+public interface Command {
+ void apply(Stack stack) throws ParsingException;
+}
\ No newline at end of file
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/ExpressionTokenizer.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/ExpressionTokenizer.java
new file mode 100644
index 000000000..5bd5a01fe
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/ExpressionTokenizer.java
@@ -0,0 +1,148 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task1;
+
+import ru.mipt.java2016.homework.base.task1.ParsingException;
+
+import java.util.Map;
+
+/**
+ * Created by mizabrik on 18.12.16.
+ */
+public class ExpressionTokenizer {
+ public enum TokenType {
+ OPENING_BRACKET, CLOSING_BRACKET, NUMBER, IDENTIFIER, OPERATOR, COMMA
+ }
+
+ private final String expression;
+ private final Map operators;
+ private int position = 0;
+
+ public ExpressionTokenizer(String expression, Map operators) {
+ this.expression = expression;
+ this.operators = operators;
+ skipSpaces();
+ }
+
+ public TokenType peekNextType() throws ParsingException {
+ if (hasChar()) {
+ char c = peekChar();
+
+ if (Character.isDigit(c)) {
+ return TokenType.NUMBER;
+ } else if (Character.isAlphabetic(c)) {
+ return TokenType.IDENTIFIER;
+ } else if (c == '(') {
+ return TokenType.OPENING_BRACKET;
+ } else if (c == ')') {
+ return TokenType.CLOSING_BRACKET;
+ } else if (operators.containsKey(c)) {
+ return TokenType.OPERATOR;
+ } else if (c == ',') {
+ return TokenType.COMMA;
+ } else {
+ throw new ParsingException("Unexpected character " + c);
+ }
+ } else {
+ return null;
+ }
+ }
+
+ public double nextNumber() throws ParsingException {
+ if (peekNextType() != TokenType.NUMBER) {
+ throw new ParsingException("No number at postition " + Integer.toString(position));
+ }
+
+ double number = 0.0;
+
+ while (hasDigit()) {
+ number *= 10;
+ number += Character.getNumericValue(nextChar());
+ }
+
+ if (hasChar() && peekChar() == '.') {
+ nextChar();
+
+ double fraction = 1;
+ while (hasDigit()) {
+ fraction /= 10;
+ number += Character.getNumericValue(nextChar()) * fraction;
+ }
+ }
+
+ skipSpaces();
+ return number;
+ }
+
+ public String nextIdentifier() throws ParsingException {
+ if (peekNextType() != TokenType.IDENTIFIER) {
+ throw new ParsingException("No identifier at position " + Integer.toString(position));
+ }
+
+ StringBuilder builder = new StringBuilder();
+ while (hasAlphanumeric()) {
+ builder.append(nextChar());
+ }
+
+ skipSpaces();
+ return builder.toString();
+ }
+
+ public char nextBracket() throws ParsingException {
+ if (peekNextType() != TokenType.OPENING_BRACKET && peekNextType() != TokenType.CLOSING_BRACKET) {
+ throw new ParsingException("No bracket at position " + Integer.toString(position));
+ }
+
+ char bracket = nextChar();
+
+ skipSpaces();
+ return bracket;
+ }
+
+ public Operator nextOperator() throws ParsingException {
+ if (peekNextType() != TokenType.OPERATOR) {
+ throw new ParsingException("No operator at position " + Integer.toString(position));
+ }
+
+ Operator operator = operators.get(nextChar());
+
+ skipSpaces();
+ return operator;
+ }
+
+ public char nextComma() throws ParsingException {
+ if (peekNextType() != TokenType.COMMA) {
+ throw new ParsingException("No comma at position " + Integer.toString(position));
+ }
+
+ char comma = nextChar();
+
+ skipSpaces();
+ return comma;
+ }
+
+
+ private boolean hasChar() {
+ return position < expression.length();
+ }
+
+ private boolean hasAlphanumeric() {
+ return hasChar() && (Character.isAlphabetic(peekChar()) || hasDigit());
+ }
+
+ private boolean hasDigit() {
+ return hasChar() && Character.isDigit(peekChar());
+ }
+
+ private char nextChar() {
+ return expression.charAt(position++);
+ }
+
+ private char peekChar() {
+ return expression.charAt(position);
+ }
+
+ private void skipSpaces() {
+ while (hasChar() && Character.isWhitespace(peekChar())) {
+ nextChar();
+ }
+ }
+}
\ No newline at end of file
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/ExtendableCalculator.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/ExtendableCalculator.java
new file mode 100644
index 000000000..8c5b3db8a
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/ExtendableCalculator.java
@@ -0,0 +1,13 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task1;
+
+import ru.mipt.java2016.homework.base.task1.Calculator;
+import ru.mipt.java2016.homework.base.task1.ParsingException;
+
+/**
+ * Created by mizabrik on 21.12.16.
+ */
+public interface ExtendableCalculator extends Calculator {
+ boolean supportsFunction(String name);
+
+ double calculate(String expression, Scope scope) throws ParsingException;
+}
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/FunctionCommand.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/FunctionCommand.java
new file mode 100644
index 000000000..f21bc6b34
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/FunctionCommand.java
@@ -0,0 +1,21 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task1;
+
+import ru.mipt.java2016.homework.base.task1.ParsingException;
+
+import java.util.Stack;
+
+/**
+ * Created by mizabrik on 21.12.16.
+ */
+public class FunctionCommand implements Command {
+ private Command command;
+
+ public FunctionCommand(Command command) {
+ this.command = command;
+ }
+
+ @Override
+ public void apply(Stack stack) throws ParsingException {
+ command.apply(stack);
+ }
+}
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/MapScope.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/MapScope.java
new file mode 100644
index 000000000..f6111cd06
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/MapScope.java
@@ -0,0 +1,24 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task1;
+
+import java.util.Map;
+
+/**
+ * Created by mizabrik on 21.12.16.
+ */
+public class MapScope implements Scope {
+ private Map map;
+
+ public MapScope(Map map) {
+ this.map = map;
+ }
+
+ @Override
+ public Command getCommand(String name) {
+ return map.get(name);
+ }
+
+ @Override
+ public boolean hasCommand(String name) {
+ return map.containsKey(name);
+ }
+}
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/Operator.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/Operator.java
index bd4b8f646..249da9d10 100644
--- a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/Operator.java
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/Operator.java
@@ -1,13 +1,14 @@
package ru.mipt.java2016.homework.g597.vasilyev.task1;
-import java.util.Stack;
import ru.mipt.java2016.homework.base.task1.ParsingException;
+import java.util.Stack;
+
/**
* Created by mizabrik on 10.10.16.
* Arithmetic operator class.
*/
-enum Operator {
+enum Operator implements Command {
ADD(2, 2, true),
SUBTRACT(2, 2, true),
MULTIPLY(1, 2, true),
@@ -25,7 +26,8 @@ enum Operator {
}
// Apply operator to stack
- void apply(Stack numbers) throws ParsingException {
+ @Override
+ public void apply(Stack numbers) throws ParsingException {
// Too few arguments
if (numbers.size() < valency) {
throw new ParsingException("Too few operands");
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/OverridingScope.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/OverridingScope.java
new file mode 100644
index 000000000..2830cf709
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/OverridingScope.java
@@ -0,0 +1,34 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task1;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by mizabrik on 21.12.16.
+ */
+public class OverridingScope implements Scope {
+ private Scope origin;
+ private Map overrides = new HashMap<>();
+
+ public OverridingScope(Scope origin) {
+ this.origin = origin;
+ }
+
+ public void addOverride(String name, Command command) {
+ overrides.put(name, command);
+ }
+
+ @Override
+ public Command getCommand(String name) {
+ if (overrides.containsKey(name)) {
+ return overrides.get(name);
+ } else {
+ return origin.getCommand(name);
+ }
+ }
+
+ @Override
+ public boolean hasCommand(String name) {
+ return overrides.containsKey(name) || origin.hasCommand(name);
+ }
+}
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/PushNumberCommand.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/PushNumberCommand.java
new file mode 100644
index 000000000..fabe0e2b0
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/PushNumberCommand.java
@@ -0,0 +1,21 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task1;
+
+import ru.mipt.java2016.homework.base.task1.ParsingException;
+
+import java.util.Stack;
+
+/**
+ * Created by mizabrik on 19.12.16.
+ */
+public class PushNumberCommand implements Command {
+ private final double number;
+
+ public PushNumberCommand(double number) {
+ this.number = number;
+ }
+
+ @Override
+ public void apply(Stack stack) throws ParsingException {
+ stack.push(number);
+ }
+}
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/Scope.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/Scope.java
new file mode 100644
index 000000000..a3bb6e47f
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/Scope.java
@@ -0,0 +1,10 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task1;
+
+/**
+ * Created by mizabrik on 21.12.16.
+ */
+public interface Scope {
+ Command getCommand(String name);
+
+ boolean hasCommand(String name);
+}
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/ShuntingYardCalculator.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/ShuntingYardCalculator.java
index e20e59a2e..4c1799990 100644
--- a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/ShuntingYardCalculator.java
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/ShuntingYardCalculator.java
@@ -1,223 +1,227 @@
package ru.mipt.java2016.homework.g597.vasilyev.task1;
-import java.util.ArrayList;
-import java.util.Stack;
-import ru.mipt.java2016.homework.base.task1.Calculator;
import ru.mipt.java2016.homework.base.task1.ParsingException;
+import java.util.*;
+
/**
* Created by mizabrik on 08.10.16.
* Implementation using Dijkstra shunting algorithm with two stacks.
*/
-class ShuntingYardCalculator implements Calculator {
+public class ShuntingYardCalculator implements ExtendableCalculator {
+ private static final Map OPERATORS = new HashMap<>();
+ private static final Map BUILTINS = new HashMap<>();
+
+ static {
+ OPERATORS.put('+', Operator.ADD);
+ OPERATORS.put('-', Operator.SUBTRACT);
+ OPERATORS.put('*', Operator.MULTIPLY);
+ OPERATORS.put('/', Operator.DIVIDE);
+
+ BUILTINS.put("sin", new BuiltinCommand((Double[] args) -> Math.sin(args[0]), 1));
+ BUILTINS.put("cos", new BuiltinCommand((Double[] args) -> Math.cos(args[0]), 1));
+ BUILTINS.put("tg", new BuiltinCommand((Double[] args) -> Math.tan(args[0]), 1));
+ BUILTINS.put("sqrt", new BuiltinCommand((Double[] args) -> Math.sqrt(args[0]), 1));
+ BUILTINS.put("pow", new BuiltinCommand((Double[] args) -> Math.pow(args[0], args[1]), 2));
+ BUILTINS.put("abs", new BuiltinCommand((Double[] args) -> Math.abs(args[0]), 1));
+ BUILTINS.put("sign", new BuiltinCommand((Double[] args) -> Math.signum(args[0]), 1));
+ BUILTINS.put("log", new BuiltinCommand((Double[] args) -> Math.log(args[1]) / Math.log(args[0]), 2));
+ BUILTINS.put("log2", new BuiltinCommand((Double[] args) -> Math.log(args[0]) / Math.log(2), 1));
+ BUILTINS.put("log2", new BuiltinCommand((Double[] args) -> Math.log(args[0]) / Math.log(2), 1));
+ BUILTINS.put("max", new BuiltinCommand((Double[] args) -> Math.max(args[0], args[1]), 2));
+ BUILTINS.put("min", new BuiltinCommand((Double[] args) -> Math.min(args[0], args[1]), 2));
+ BUILTINS.put("rnd", new BuiltinCommand((Double[] args) -> Math.random(), 0));
+ }
+
+ public static void main(String[] args) {
+ try {
+ ExtendableCalculator calc = new ShuntingYardCalculator();
+ Scanner s = new Scanner(System.in);
+ s.useLocale(Locale.US); // use dots for fractions
+ s.useDelimiter("[,() \n]+");
+ Map definitions = new HashMap<>();
+ Scope scope = new MapScope(definitions);
+
+ while (s.hasNext()) {
+ String command = s.next();
+
+ if (command.equals("var")) {
+ String variable = s.next();
+ s.next("=");
+ Double value = s.nextDouble();
+ definitions.put(variable, new PushNumberCommand(value));
+ } else if (command.equals("def")) {
+ String function = s.next();
+ ArrayList functionArgs = new ArrayList<>();
+ while (!s.hasNext("=")) {
+ functionArgs.add(s.next());
+ }
+ s.next("=");
+ definitions.put(function, new UserCommand(s.nextLine(), functionArgs.toArray(new String[0]),
+ calc, scope));
+ } else if (command.equals("eval")) {
+ System.out.println(calc.calculate(s.nextLine(), scope));
+ } else {
+ System.out.println(command + ": no such command");
+ s.nextLine();
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
// Calculate expression.
public double calculate(String expression) throws ParsingException {
+ return calculate(expression, new MapScope(new HashMap<>()));
+ }
+
+ @Override
+ public boolean supportsFunction(String name) {
+ return BUILTINS.containsKey(name);
+ }
+
+ // Calculate expression with custom scope
+ public double calculate(String expression, Scope scope) throws ParsingException {
if (expression == null) {
throw new ParsingException("Null expression");
}
- ArrayList parsed = parse(expression);
- if (parsed.size() == 0) {
+ ArrayList commands = parse(expression, scope);
+ if (commands.size() == 0) {
throw new ParsingException("Empty expression");
}
- return evaluate(parsed);
+ return evaluate(commands);
}
- // Tokenize expression
- private ArrayList parse(String expr) throws ParsingException {
- ArrayList result = new ArrayList<>();
-
- char c;
- for (int i = 0; i < expr.length(); ++i) {
- c = expr.charAt(i);
- if (Character.isDigit(c)) {
- double number = Character.getNumericValue(c);
- double fraction = 1;
- ++i;
- while (i < expr.length() && Character.isDigit(expr.charAt(i))) {
- c = expr.charAt(i);
- number *= 10;
- number += Character.getNumericValue(c);
- ++i;
- }
- if (i < expr.length() && expr.charAt(i) == '.') {
- ++i;
- while (i < expr.length() && Character.isDigit(expr.charAt(i))) {
- fraction /= 10;
- number += Character.getNumericValue(expr.charAt(i)) * fraction;
- ++i;
- }
- }
- --i; // increased by loop expression
- result.add(new NumberToken(number));
- } else if (Character.isWhitespace(c)) {
- continue;
- } else if (isOperatorSymbol(c)) {
- result.add(new OperatorToken(c));
- } else if (c == '(' || c == ')') {
- if (c == ')' && result.size() > 0 && result.get(result.size() - 1) instanceof BracketToken
- && ((BracketToken) result.get(result.size() - 1)).getType()
- == Bracket.OPENING) {
- throw new ParsingException("Empty brackets");
- }
- result.add(new BracketToken(c));
- } else {
- throw new ParsingException("Illegal character");
- }
- }
- return result;
- }
+ // Tokenize expression
+ private ArrayList parse(String expression, Scope scope) throws ParsingException {
+ ArrayList result = new ArrayList<>();
+ Stack commandStack = new Stack<>();
+ ExpressionTokenizer tokenizer = new ExpressionTokenizer(expression, OPERATORS);
- // Get result of expression coded by consequence of tokens.
- private double evaluate(ArrayList infix) throws ParsingException {
- Stack operators = new Stack<>();
- Stack numbers = new Stack<>();
- operators.push(new BracketToken('('));
- infix.add(new BracketToken(')'));
- int bracketBalance = 1;
+ int bracketBalance = 0;
boolean gotOperand = false;
+ ExpressionTokenizer.TokenType type;
+ for (type = tokenizer.peekNextType(); type != null; type = tokenizer.peekNextType()) {
+ switch (type) {
+ case NUMBER:
+ result.add(new PushNumberCommand(tokenizer.nextNumber()));
+ popFunctions(result, commandStack);
+
+ gotOperand = true;
+ break;
+ case OPERATOR:
+ Operator operator = tokenizer.nextOperator();
+ if (!gotOperand) {
+ switch (operator) {
+ case ADD:
+ operator = Operator.UNARY_PLUS;
+ break;
+ case SUBTRACT:
+ operator = Operator.UNARY_MINUS;
+ break;
+ default:
+ throw new ParsingException("No operand for operator");
+ }
+ } else {
+ popFunctions(result, commandStack);
+ }
- for (Token token : infix) {
- if (token instanceof NumberToken) {
- numbers.push(((NumberToken) token).getNumber());
- gotOperand = true;
- } else if (token instanceof OperatorToken) {
- Operator operator = ((OperatorToken) token).getOperator();
- if (!gotOperand) {
- switch (operator) {
- case ADD:
- operator = Operator.UNARY_PLUS;
+ while (!commandStack.empty() && commandStack.peek() instanceof Operator) {
+ Operator previousOperator = (Operator) commandStack.peek();
+ if (previousOperator.priority < operator.priority
+ || (operator.hasLeftAssociativity && previousOperator.priority == operator.priority)) {
+ result.add(previousOperator);
+ commandStack.pop();
+ } else {
break;
- case SUBTRACT:
- operator = Operator.UNARY_MINUS;
- break;
- default:
- throw new ParsingException("Illegal expression");
+ }
}
- token = new OperatorToken(operator);
- }
- gotOperand = false;
- while (operators.size() > 0 && operators.peek() instanceof OperatorToken) {
- Operator previousOperator = ((OperatorToken) operators.peek()).getOperator();
- if (previousOperator.priority < operator.priority
- || (operator.hasLeftAssociativity && previousOperator.priority == operator.priority)) {
- previousOperator.apply(numbers);
- operators.pop();
+ commandStack.push(operator);
+
+ gotOperand = false;
+ break;
+ case OPENING_BRACKET:
+ ++bracketBalance;
+ tokenizer.nextBracket();
+ commandStack.push(null);
+
+ gotOperand = false;
+ break;
+ case CLOSING_BRACKET:
+ if (bracketBalance == 0) {
+ throw new ParsingException("Bad bracket balance");
+ }
+ --bracketBalance;
+ tokenizer.nextBracket();
+
+ while (commandStack.peek() != null) {
+ result.add(commandStack.pop());
+ }
+ commandStack.pop();
+ popFunctions(result, commandStack);
+
+ gotOperand = true;
+ break;
+ case IDENTIFIER:
+ String identifier = tokenizer.nextIdentifier();
+ if (BUILTINS.containsKey(identifier)) {
+ commandStack.push(new FunctionCommand(BUILTINS.get(identifier)));
+ } else if (scope.hasCommand(identifier)) {
+ commandStack.push(new FunctionCommand(scope.getCommand(identifier)));
} else {
- break;
+ throw new ParsingException("Unknown identifier " + identifier);
}
- }
- operators.push(token);
- } else {
- Bracket bracket = ((BracketToken) token).getType();
- switch (bracket) {
- case OPENING:
- operators.push(token);
- gotOperand = false;
- ++bracketBalance;
- break;
- case CLOSING:
- if (bracketBalance == 0) {
- throw new ParsingException("Bad bracket balance");
- }
- while (operators.peek() instanceof OperatorToken) {
- ((OperatorToken) operators.pop()).getOperator().apply(numbers);
- }
- operators.pop();
- --bracketBalance;
- gotOperand = true;
- break;
- default:
- throw new IllegalStateException();
- }
+
+ gotOperand = true; // well, possibly
+ break;
+ case COMMA:
+ tokenizer.nextComma();
+ while (!commandStack.empty() && commandStack.peek() != null) {
+ result.add(commandStack.pop());
+ }
+ if (commandStack.empty()) {
+ throw new ParsingException("Misplaced comma");
+ }
+
+ gotOperand = false;
+ break;
+ default:
+ throw new ParsingException("Illegal character");
}
}
if (bracketBalance != 0) {
throw new ParsingException("Bad bracket balance");
}
- if (numbers.size() != 1) {
- throw new ParsingException("Illegal expression");
- }
- infix.remove(infix.size() - 1);
-
- return numbers.pop();
- }
-
- // Apply operation according to operator to stack of numbers
- private boolean isOperatorSymbol(char c) {
- return c == '+' || c == '-' || c == '*' || c == '/';
- }
-
- private enum Bracket {
- OPENING, CLOSING
- }
-
- private class Token {
- }
-
- private class NumberToken extends Token {
- private double number;
- private NumberToken(double number) {
- this.number = number;
+ while (!commandStack.empty()) {
+ result.add(commandStack.pop());
}
- private double getNumber() {
- return number;
- }
+ return result;
}
- private class BracketToken extends Token {
- private Bracket type;
-
- private BracketToken(char bracket) {
- switch (bracket) {
- case '(':
- type = Bracket.OPENING;
- break;
- case ')':
- type = Bracket.CLOSING;
- break;
- default:
- throw new IllegalArgumentException();
- }
- }
-
- private Bracket getType() {
- return type;
+ private void popFunctions(ArrayList result, Stack stack) {
+ while (!stack.empty() && stack.peek() instanceof FunctionCommand) {
+ result.add(stack.pop());
}
}
- private class OperatorToken extends Token {
- private Operator operator;
+ // Get result of expression coded by consequence of tokens.
+ private double evaluate(ArrayList commands) throws ParsingException {
+ Stack stack = new Stack<>();
- private OperatorToken(Operator operator) {
- this.operator = operator;
+ for (Command command : commands) {
+ command.apply(stack);
}
- private OperatorToken(char operatorChar) {
- switch (operatorChar) {
- case '+':
- operator = Operator.ADD;
- break;
- case '-':
- operator = Operator.SUBTRACT;
- break;
- case '*':
- operator = Operator.MULTIPLY;
- break;
- case '/':
- operator = Operator.DIVIDE;
- break;
- default:
- throw new IllegalArgumentException();
- }
+ if (stack.size() != 1) {
+ throw new ParsingException("Illegal expression");
}
- private Operator getOperator() {
- return operator;
- }
+ return stack.pop();
}
}
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/UserCommand.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/UserCommand.java
new file mode 100644
index 000000000..bb28a4493
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task1/UserCommand.java
@@ -0,0 +1,33 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task1;
+
+import ru.mipt.java2016.homework.base.task1.ParsingException;
+
+import java.util.Stack;
+
+/**
+ * Created by mizabrik on 21.12.16.
+ */
+public class UserCommand implements Command {
+ private ExtendableCalculator calculator;
+ private String expression;
+ private String[] args;
+ private Scope oldScope;
+
+ public UserCommand(String expression, String[] args, ExtendableCalculator calculator, Scope scope) {
+ this.expression = expression;
+ this.args = args;
+ this.calculator = calculator;
+ this.oldScope = scope;
+ }
+
+ @Override
+ public void apply(Stack stack) throws ParsingException {
+ OverridingScope scope = new OverridingScope(oldScope);
+
+ for (int i = args.length - 1; i >= 0; --i) {
+ scope.addOverride(args[i], new PushNumberCommand(stack.pop()));
+ }
+
+ stack.push(calculator.calculate(expression, scope));
+ }
+}
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/CalculatorController.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/CalculatorController.java
new file mode 100644
index 000000000..f7b9b06b1
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/CalculatorController.java
@@ -0,0 +1,140 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task4;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.security.core.Authentication;
+import org.springframework.web.bind.annotation.*;
+import ru.mipt.java2016.homework.base.task1.ParsingException;
+import ru.mipt.java2016.homework.g597.vasilyev.task1.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by mizabrik on 21.12.16.
+ */
+
+@RestController
+public class CalculatorController {
+ @Autowired
+ private ExtendableCalculator calculator;
+
+ @Autowired
+ private CalculatorDao calculatorDao;
+
+ @RequestMapping(value = "/variable/", method = RequestMethod.GET, produces = MediaType.TEXT_PLAIN_VALUE)
+ public String getVariablesList(Authentication authentication) {
+ StringBuilder response = new StringBuilder();
+ for (UserVariable variable : calculatorDao.getVariables(authentication.getName())) {
+ response.append(variable.getName())
+ .append(" = ")
+ .append(variable.getValue())
+ .append('\n');
+ }
+ return response.toString();
+ }
+
+ @RequestMapping(value = "/variable/{variableName}", method = RequestMethod.GET,
+ produces = MediaType.TEXT_PLAIN_VALUE)
+ public String getVariableValue(Authentication authentication, @PathVariable String variableName) {
+ return Double.toString(calculatorDao.getVariableValue(authentication.getName(), variableName));
+ }
+
+ @RequestMapping(value = "/variable/{variableName}", method = RequestMethod.PUT,
+ consumes = MediaType.TEXT_PLAIN_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
+ public String setVariableValue(Authentication authentication,
+ @PathVariable String variableName,
+ @RequestBody String expression) throws ParsingException {
+ if (calculator.supportsFunction(variableName)) {
+ return builtinMesssage(variableName);
+ }
+ double value = calculator.calculate(expression, userScope(authentication.getName()));
+ if (calculatorDao.setVariableValue(authentication.getName(), variableName, value)) {
+ return "Variable added.\n";
+ } else {
+ return variableName + " is a function.\n";
+ }
+ }
+
+ @RequestMapping(value = "/variable/{variableName}", method = RequestMethod.DELETE,
+ produces = MediaType.TEXT_PLAIN_VALUE)
+ public String deleteVariable(Authentication authentication, @PathVariable String variableName) {
+ calculatorDao.deleteVariable(authentication.getName(), variableName);
+ return "Deleted variable.\n";
+ }
+
+ @RequestMapping(value = "/function/", method = RequestMethod.GET, produces = MediaType.TEXT_PLAIN_VALUE)
+ public String getFunctionList(Authentication authentication) {
+ StringBuilder response = new StringBuilder();
+ for (UserFunction function : calculatorDao.getFunctions(authentication.getName())) {
+ response.append(function.toString());
+ response.append('\n');
+ }
+ return response.toString();
+ }
+
+ @RequestMapping(value = "/function/{functionName}", method = RequestMethod.GET,
+ produces = MediaType.TEXT_PLAIN_VALUE)
+ public String getFunction(Authentication authentication, @PathVariable String functionName) {
+ if (calculator.supportsFunction(functionName)) {
+ return functionName + " is a builtin.\n";
+ }
+ return calculatorDao.getFunction(authentication.getName(), functionName).toString();
+ }
+
+ @RequestMapping(value = "/function/{functionName}", method = RequestMethod.PUT,
+ consumes = MediaType.TEXT_PLAIN_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
+ public String setVariableValue(Authentication authentication,
+ @PathVariable String functionName,
+ @RequestParam String[] args,
+ @RequestBody String expression) {
+ if (calculator.supportsFunction(functionName)) {
+ return builtinMesssage(functionName);
+ }
+
+ if (calculatorDao.setFunction(authentication.getName(), new UserFunction(functionName, expression, args))) {
+ return "Function added.\n";
+ } else {
+ return functionName + " is a variable.\n";
+ }
+ }
+
+ @RequestMapping(value = "/function/{functionName}", method = RequestMethod.DELETE,
+ produces = MediaType.TEXT_PLAIN_VALUE)
+ public String deleteFunction(Authentication authentication, @PathVariable String functionName) {
+ if (calculator.supportsFunction(functionName)) {
+ return "Builtins can not be deleted.\n";
+ }
+ calculatorDao.deleteFunction(authentication.getName(), functionName);
+ return "Deleted function.\n";
+ }
+
+ @RequestMapping(value = "/eval/", method = RequestMethod.POST, produces = MediaType.TEXT_PLAIN_VALUE)
+ public String evaluate(Authentication authentication, @RequestBody String expression) throws ParsingException {
+ if (authentication.isAuthenticated()) {
+ return Double.toString(calculator.calculate(expression, userScope(authentication.getName()))) + " ";
+ } else {
+ return Double.toString(calculator.calculate(expression)) + " ";
+ }
+ }
+
+ private Scope userScope(String username) {
+ Map definitions = new HashMap<>();
+ Scope scope = new MapScope(definitions);
+
+ for (UserVariable variable : calculatorDao.getVariables(username)) {
+ definitions.put(variable.getName(), new PushNumberCommand(variable.getValue()));
+ }
+
+ for (UserFunction function : calculatorDao.getFunctions(username)) {
+ definitions.put(function.getName(),
+ new UserCommand(function.getExpression(), function.getArgs(), calculator, scope));
+ }
+
+ return scope;
+ }
+
+ private String builtinMesssage(String identifier) {
+ return "Builtin function " + identifier + " is unmodifiable.";
+ }
+}
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/CalculatorDao.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/CalculatorDao.java
new file mode 100644
index 000000000..bf12c9c66
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/CalculatorDao.java
@@ -0,0 +1,159 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task4;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.PostConstruct;
+import javax.sql.DataSource;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * Created by mizabrik on 21.12.16.
+ */
+
+@Repository
+public class CalculatorDao {
+ @Autowired
+ private DataSource dataSource;
+
+ private JdbcTemplate jdbcTemplate;
+
+ private final Logger logger = LoggerFactory.getLogger(CalculatorDao.class);
+
+ @PostConstruct
+ public void postConstruct() {
+ jdbcTemplate = new JdbcTemplate(dataSource);
+ initSchema();
+ getFunction("root", "preceq");
+ }
+
+ public CalculatorUser loadUser(String username) throws EmptyResultDataAccessException {
+ return jdbcTemplate.queryForObject(
+ "SELECT username, password FROM calculator.user WHERE username = ?",
+ new Object[]{username},
+ new RowMapper() {
+ @Override
+ public CalculatorUser mapRow(ResultSet rs, int rowNum) throws SQLException {
+ return new CalculatorUser(rs.getString("username"), rs.getString("password"));
+ }
+ }
+ );
+ }
+
+ public List getVariables(String username) {
+ return jdbcTemplate.query(
+ "SELECT name, value FROM calculator.variable WHERE owner = ?",
+ new UserVariableMapper(), username);
+ }
+
+ public double getVariableValue(String username, String variableName) throws EmptyResultDataAccessException {
+ return jdbcTemplate.queryForObject(
+ "SELECT value FROM calculator.variable WHERE owner = ? AND name = ?",
+ new Object[]{username, variableName},
+ Double.class
+ );
+ }
+
+ public void deleteVariable(String username, String variableName) {
+ jdbcTemplate.update("DELETE FROM calculator.variable WHERE owner = ? AND name = ?", username, variableName);
+ logger.info("User '{}' deleted variable '{}'.", username, variableName);
+ }
+
+ @Transactional
+ public boolean setVariableValue(String username, String variableName, Double value) {
+ if (countElements("function", username, variableName) == 0) {
+ jdbcTemplate.update("MERGE INTO calculator.variable (owner, name, value) VALUES (?, ?, ?)",
+ username, variableName, value);
+ logger.info("User '{}' has set variable '{}'.", username, variableName);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public UserFunction getFunction(String username, String name) {
+ return jdbcTemplate.queryForObject(
+ "SELECT name, expression, arguments FROM calculator.function WHERE owner = ? AND name = ?",
+ new Object[]{username, name}, new UserFunctionMapper());
+ }
+
+ public List getFunctions(String username) {
+ return jdbcTemplate.query("SELECT name, expression, arguments FROM calculator.function WHERE owner = ?",
+ new UserFunctionMapper(), username);
+ }
+
+ public boolean setFunction(String username, UserFunction function) {
+ if (countElements("variable", username, function.getName()) == 0) {
+ jdbcTemplate.update("MERGE INTO calculator.function (owner, name, expression, arguments)"
+ + " VALUES (?, ?, ?, ?)",
+ username, function.getName(), function.getExpression(), function.getArgs());
+ logger.info("User '{}' has set function '{}'.", username, function.getName());
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public void deleteFunction(String username, String name) {
+ jdbcTemplate.update("DELETE FROM calculator.function WHERE owner = ? AND name = ?", username, name);
+ logger.info("User '{}' has deleted function '{}'.", username, name);
+ }
+
+ public CalculatorUser createUser(String username, String password) {
+ jdbcTemplate.update("INSERT INTO calculator.user (username, password) VALUES (?, ?)", username, password);
+ return new CalculatorUser(username, password);
+ }
+
+ private static class UserFunctionMapper implements RowMapper {
+ @Override
+ public UserFunction mapRow(ResultSet resultSet, int i) throws SQLException {
+ return new UserFunction(resultSet.getString("name"), resultSet.getString("expression"),
+ arrayToString((Object[]) resultSet.getArray("arguments").getArray()));
+ }
+ }
+
+ private static class UserVariableMapper implements RowMapper {
+ @Override
+ public UserVariable mapRow(ResultSet resultSet, int i) throws SQLException {
+ return new UserVariable(resultSet.getString("name"), resultSet.getDouble("value"));
+ }
+ }
+
+ private void initSchema() {
+ jdbcTemplate.execute("CREATE SCHEMA IF NOT EXISTS calculator");
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS calculator.user " +
+ "(username VARCHAR PRIMARY KEY, password VARCHAR)");
+ jdbcTemplate.update("MERGE INTO calculator.user (username, password) VALUES ('root', 'root')");
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS calculator.variable" +
+ "(owner VARCHAR, name VARCHAR, value DOUBLE," +
+ "PRIMARY KEY (name, owner)," +
+ "FOREIGN KEY (owner) REFERENCES calculator.user(username))");
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS calculator.function" +
+ "(owner VARCHAR, name VARCHAR, expression VARCHAR, arguments ARRAY," +
+ "PRIMARY KEY (name, owner)," +
+ "FOREIGN KEY (owner) REFERENCES calculator.user(username))");
+ }
+
+ private static String[] arrayToString(Object[] array) {
+ String[] strings = new String[array.length];
+ for (int i = 0; i < array.length; ++i) {
+ strings[i] = (String) array[i];
+ }
+
+ return strings;
+ }
+
+ private int countElements(String table, String owner, String name) {
+ return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM calculator." + table
+ + " WHERE owner = ? AND name = ?",
+ new Object[]{owner, name}, Integer.class);
+ }
+}
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/CalculatorDatabaseConfiguration.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/CalculatorDatabaseConfiguration.java
new file mode 100644
index 000000000..1da965d93
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/CalculatorDatabaseConfiguration.java
@@ -0,0 +1,29 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task4;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.sql.DataSource;
+
+/**
+ * Created by mizabrik on 21.12.16.
+ */
+@Configuration
+public class CalculatorDatabaseConfiguration {
+ @Bean
+ public DataSource calculatorDataSource(
+ @Value("${ru.mipt.java2016.homework.g597.mizabrik.task4.dbUrl}") String dbUrl,
+ @Value("${ru.mipt.java2016.homework.g597.mizabrik.task4.dbUsername:}") String dbUsername,
+ @Value("${ru.mipt.java2016.homework.g597.mizabrik.task4.dbPassword:}") String dbPassword
+ ) {
+ HikariConfig config = new HikariConfig();
+ config.setDriverClassName(org.h2.Driver.class.getName());
+ config.setJdbcUrl(dbUrl);
+ config.setUsername(dbUsername);
+ config.setPassword(dbPassword);
+ return new HikariDataSource(config);
+ }
+}
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/CalculatorUser.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/CalculatorUser.java
new file mode 100644
index 000000000..5b6a842a0
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/CalculatorUser.java
@@ -0,0 +1,60 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task4;
+
+/**
+ * Created by mizabrik on 21.12.16.
+ */
+public class CalculatorUser {
+ private final String username;
+ private final String password;
+
+ public CalculatorUser(String username, String password) {
+ if (username == null) {
+ throw new IllegalArgumentException("Null username is not allowed");
+ }
+ if (password == null) {
+ throw new IllegalArgumentException("Null password is not allowed");
+ }
+ this.username = username;
+ this.password = password;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ @Override
+ public String toString() {
+ return "BillingUser{" +
+ "username='" + username + '\'' +
+ ", password='" + password + '\'' +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ CalculatorUser that = (CalculatorUser) o;
+
+ if (!username.equals(that.username)) {
+ return false;
+ }
+ return password.equals(that.password);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = username.hashCode();
+ result = 31 * result + password.hashCode();
+ return result;
+ }
+}
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/RestfulCalculatorApplication.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/RestfulCalculatorApplication.java
new file mode 100644
index 000000000..1bccf45a6
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/RestfulCalculatorApplication.java
@@ -0,0 +1,26 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task4;
+
+import org.springframework.boot.Banner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import ru.mipt.java2016.homework.g597.vasilyev.task1.ExtendableCalculator;
+import ru.mipt.java2016.homework.g597.vasilyev.task1.ShuntingYardCalculator;
+
+/**
+ * Created by mizabrik on 21.12.16.
+ */
+
+@SpringBootApplication
+public class RestfulCalculatorApplication {
+ public static void main(String[] args) {
+ SpringApplication application = new SpringApplication(RestfulCalculatorApplication.class);
+ application.setBannerMode(Banner.Mode.OFF);
+ application.run(args);
+ }
+
+ @Bean
+ ExtendableCalculator calculator() {
+ return new ShuntingYardCalculator();
+ }
+}
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/SecurityServiceConfguration.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/SecurityServiceConfguration.java
new file mode 100644
index 000000000..cd581ef62
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/SecurityServiceConfguration.java
@@ -0,0 +1,51 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task4;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+
+import java.util.Collections;
+
+/**
+ * Created by mizabrik on 21.12.16.
+ */
+@Configuration
+@EnableWebSecurity
+public class SecurityServiceConfguration extends WebSecurityConfigurerAdapter {
+ @Autowired
+ private CalculatorDao calculatorDao;
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http
+ .httpBasic().realmName("Calculator").and()
+ .csrf().disable()
+ .authorizeRequests()
+ //.antMatchers("/eval/").authenticated()
+ .antMatchers("/variable/**").authenticated()
+ .antMatchers("/function/**").authenticated()
+ .anyRequest().permitAll();
+ }
+
+ @Autowired
+ public void registerGlobalAuthentication(AuthenticationManagerBuilder auth) throws Exception {
+ auth.userDetailsService(username -> {
+ try {
+ CalculatorUser user = calculatorDao.loadUser(username);
+ return new User(
+ user.getUsername(),
+ user.getPassword(),
+ Collections.singletonList(() -> "AUTH")
+ );
+ } catch (EmptyResultDataAccessException e) {
+ throw new UsernameNotFoundException(username);
+ }
+ });
+ }
+}
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/UserFunction.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/UserFunction.java
new file mode 100644
index 000000000..2d133e503
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/UserFunction.java
@@ -0,0 +1,39 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task4;
+
+/**
+ * Created by mizabrik on 21.12.16.
+ */
+public class UserFunction {
+ private String name;
+ private String expression;
+ private String[] args;
+
+ public UserFunction(String name, String expression, String[] args) {
+ this.name = name;
+ this.expression = expression;
+ this.args = args;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getExpression() {
+ return expression;
+ }
+
+ public String[] getArgs() {
+ return args;
+ }
+
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ return builder.append(name)
+ .append('(')
+ .append(String.join(", ", args))
+ .append(')')
+ .append(" = ")
+ .append(expression)
+ .toString();
+ }
+}
diff --git a/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/UserVariable.java b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/UserVariable.java
new file mode 100644
index 000000000..37aa867b4
--- /dev/null
+++ b/homework-g597-vasilyev/src/main/java/ru/mipt/java2016/homework/g597/vasilyev/task4/UserVariable.java
@@ -0,0 +1,22 @@
+package ru.mipt.java2016.homework.g597.vasilyev.task4;
+
+/**
+ * Created by mizabrik on 21.12.16.
+ */
+public class UserVariable {
+ private String name;
+ private double value;
+
+ public UserVariable(String name, double value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public double getValue() {
+ return value;
+ }
+}