diff --git a/homework-g597-kirilenko/pom.xml b/homework-g597-kirilenko/pom.xml
index 481d50ef5..0b4dcc2f0 100644
--- a/homework-g597-kirilenko/pom.xml
+++ b/homework-g597-kirilenko/pom.xml
@@ -9,6 +9,10 @@
4.0.0
+
+ 1.4.2.RELEASE
+
+
homework-g597-kirilenko
@@ -24,6 +28,33 @@
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}
+
+
+ com.zaxxer
+ HikariCP
+ 2.5.1
+
+
+ com.h2database
+ h2
+ 1.4.193
+
+
com.google.guava
guava
diff --git a/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task1/MyCalculator.java b/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task1/MyCalculator.java
index 2e0e4c5e9..191853d20 100644
--- a/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task1/MyCalculator.java
+++ b/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task1/MyCalculator.java
@@ -221,4 +221,4 @@ private boolean checkIncorrectExpression(String expres) {
return true;
}
-}
+}
\ No newline at end of file
diff --git a/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/BillingDao.java b/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/BillingDao.java
new file mode 100644
index 000000000..b4a799a65
--- /dev/null
+++ b/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/BillingDao.java
@@ -0,0 +1,57 @@
+package ru.mipt.java2016.homework.g597.kirilenko.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 javax.annotation.PostConstruct;
+import javax.sql.DataSource;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+@Repository
+public class BillingDao {
+ private static final Logger LOG = LoggerFactory.getLogger(BillingDao.class);
+
+ @Autowired
+ private DataSource dataSource;
+
+ private JdbcTemplate jdbcTemplate;
+
+ @PostConstruct
+ public void postConstruct() {
+ jdbcTemplate = new JdbcTemplate(dataSource, false);
+ initSchema();
+ }
+
+ public void initSchema() {
+ LOG.trace("Initializing schema");
+ jdbcTemplate.execute("CREATE SCHEMA IF NOT EXISTS billing");
+ jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS billing.users " +
+ "(username VARCHAR PRIMARY KEY, password VARCHAR, enabled BOOLEAN)");
+ jdbcTemplate.update("INSERT INTO billing.users VALUES ('username', 'password', TRUE)");
+ }
+
+
+ public BillingUser loadUser(String username) throws EmptyResultDataAccessException {
+ LOG.trace("Querying for user " + username);
+ return jdbcTemplate.queryForObject(
+ "SELECT username, password, enabled FROM billing.users WHERE username = ?",
+ new Object[]{username},
+ new RowMapper() {
+ @Override
+ public BillingUser mapRow(ResultSet rs, int rowNum) throws SQLException {
+ return new BillingUser(
+ rs.getString("username"),
+ rs.getString("password"),
+ rs.getBoolean("enabled")
+ );
+ }
+ }
+ );
+ }
+}
\ No newline at end of file
diff --git a/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/BillingDatabaseConfiguration.java b/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/BillingDatabaseConfiguration.java
new file mode 100644
index 000000000..dda68971e
--- /dev/null
+++ b/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/BillingDatabaseConfiguration.java
@@ -0,0 +1,26 @@
+package ru.mipt.java2016.homework.g597.kirilenko.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;
+
+@Configuration
+public class BillingDatabaseConfiguration {
+ @Bean
+ public DataSource billingDataSource(
+ @Value("${ru.mipt.java2016.homework.g597.kirilenko.task4.username:}") String username,
+ @Value("${ru.mipt.java2016.homework.g597.kirilenko.task4.password:}") String password
+ ) {
+ HikariConfig config = new HikariConfig();
+ config.setDriverClassName(org.h2.Driver.class.getName());
+ String jdbcUrl = "jdbc:h2:~/task4.db";
+ config.setJdbcUrl(jdbcUrl);
+ config.setUsername(username);
+ config.setPassword(password);
+ return new HikariDataSource(config);
+ }
+}
diff --git a/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/BillingUser.java b/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/BillingUser.java
new file mode 100644
index 000000000..1308c8bc5
--- /dev/null
+++ b/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/BillingUser.java
@@ -0,0 +1,68 @@
+package ru.mipt.java2016.homework.g597.kirilenko.task4;
+
+public class BillingUser {
+ private final String username;
+ private final String password;
+ private final boolean enabled;
+
+ public BillingUser(String username, String password, boolean enabled) {
+ 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;
+ this.enabled = enabled;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public String toString() {
+ return "BillingUser{" +
+ "username='" + username + '\'' +
+ ", password='" + password + '\'' +
+ ", enabled=" + enabled +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ BillingUser that = (BillingUser) o;
+
+ if (enabled != that.enabled) {
+ return false;
+ }
+ 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();
+ result = 31 * result + (enabled ? 1 : 0);
+ return result;
+ }
+}
diff --git a/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/CalculatorController.java b/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/CalculatorController.java
new file mode 100644
index 000000000..97264b97d
--- /dev/null
+++ b/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/CalculatorController.java
@@ -0,0 +1,83 @@
+package ru.mipt.java2016.homework.g597.kirilenko.task4;
+
+import javafx.util.Pair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import ru.mipt.java2016.homework.base.task1.ParsingException;
+
+import java.util.ArrayList;
+
+@RestController
+public class CalculatorController {
+ private static final Logger LOG = LoggerFactory.getLogger(CalculatorController.class);
+ @Autowired
+ private MyCalculator calculator;
+
+
+ @RequestMapping(path = "/variable/{variableName}", method = RequestMethod.GET)
+ public String getVariable(@PathVariable String variableName) {
+ String result = calculator.getVariableExpression(variableName);
+ return result;
+ }
+
+ @RequestMapping(path = "/function/{functionName}", method = RequestMethod.GET)
+ public String getFunction(@PathVariable String functionName) {
+ Pair, String> info = calculator.getFunctionInfo(functionName);
+ String result = info.getValue() + "&";
+ for (int i = 0; i < info.getKey().size(); i++) {
+ result += info.getKey().get(i);
+ if (i != info.getKey().size() - 1) {
+ result += ",";
+ }
+ }
+ return result + "\n";
+ }
+
+ @RequestMapping(path = "/variable/{variableName}", method = RequestMethod.PUT,
+ consumes = "text/plain", produces = "text/plain")
+ public void putVariable(@PathVariable String variableName,
+ @RequestBody String expr) throws ParsingException {
+ calculator.setVariableExpression(variableName, expr);
+ }
+
+ @RequestMapping(path = "/variable/{variableName}", method = RequestMethod.DELETE)
+ public void deleteVariable(@PathVariable String variableName) {
+ calculator.deleteVariable(variableName);
+ }
+
+ @RequestMapping(path = "/variable", method = RequestMethod.GET)
+ public ArrayList getVariables() {
+ return calculator.getAllVariables();
+ }
+
+
+ @RequestMapping(path = "/function/{functionName}", method = RequestMethod.PUT)
+ public void putFunction(@PathVariable String functionName,
+ @RequestParam(value = "args") ArrayList args,
+ @RequestBody String functionBody) throws ParsingException {
+ calculator.setFunction(functionName, new ArrayList<>(args), functionBody);
+ }
+
+ @RequestMapping(path = "/function/{functionName}", method = RequestMethod.DELETE)
+ public Boolean deleteFunction(@PathVariable String functionName) {
+ return calculator.deleteFunction(functionName);
+ }
+
+ @RequestMapping(path = "/function", method = RequestMethod.GET)
+ public ArrayList getFunctionsNames() {
+ return calculator.getAllFunctions();
+ }
+
+ @RequestMapping(path = "/ping", method = RequestMethod.GET, produces = "text/plain")
+ public String echo() {
+ return "OK\n";
+ }
+
+ @RequestMapping(path = "/eval", method = RequestMethod.POST, consumes = "text/plain", produces = "text/plain")
+ public String eval(@RequestBody String expression) throws ParsingException {
+ double result = calculator.evaluateExpression(expression);
+ return Double.toString(result) + "\n";
+ }
+}
\ No newline at end of file
diff --git a/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/MyCalculator.java b/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/MyCalculator.java
new file mode 100644
index 000000000..1c25aa379
--- /dev/null
+++ b/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/MyCalculator.java
@@ -0,0 +1,529 @@
+package ru.mipt.java2016.homework.g597.kirilenko.task4;
+
+
+import javafx.util.Pair;
+import ru.mipt.java2016.homework.base.task1.Calculator;
+import ru.mipt.java2016.homework.base.task1.ParsingException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Stack;
+
+import static java.lang.Math.*;
+
+
+public class MyCalculator implements Calculator {
+ private Stack numbers;
+ private Stack operations;
+
+ private final Map variablesExpressions = new HashMap();
+ private final Map, String>> functions = new HashMap();
+
+ private double calculateStandardExpression(String functionName, ArrayList arguments)
+ throws ParsingException {
+ if (functionName.equals("sin")) {
+ if (arguments.size() != 1) {
+ throw new ParsingException("Incorrect expression");
+ }
+ return sin(arguments.get(0));
+ }
+ if (functionName.equals("cos")) {
+ if (arguments.size() != 1) {
+ throw new ParsingException("Incorrect expression");
+ }
+ return cos(arguments.get(0));
+ }
+ if (functionName.equals("tg")) {
+ if (arguments.size() != 1) {
+ throw new ParsingException("Incorrect expression");
+ }
+ return tan(arguments.get(0));
+ }
+ if (functionName.equals("sqrt")) {
+ if (arguments.size() != 1) {
+ throw new ParsingException("Incorrect expression");
+ }
+ return sqrt(arguments.get(0));
+ }
+ if (functionName.equals("abs")) {
+ if (arguments.size() != 1) {
+ throw new ParsingException("Incorrect expression");
+ }
+ return abs(arguments.get(0));
+ }
+ if (functionName.equals("max")) {
+ if (arguments.size() != 2) {
+ throw new ParsingException("Incorrect expression");
+ }
+ return max(arguments.get(0), arguments.get(1));
+ }
+ if (functionName.equals("min")) {
+ if (arguments.size() != 2) {
+ throw new ParsingException("Incorrect expression");
+ }
+ return min(arguments.get(0), arguments.get(1));
+ }
+ if (functionName.equals("pow")) {
+ if (arguments.size() != 2) {
+ throw new ParsingException("Incorrect expression");
+ }
+ return pow(arguments.get(0), arguments.get(1));
+ }
+ if (functionName.equals("log")) {
+ if (arguments.size() != 2) {
+ throw new ParsingException("Incorrect expression");
+ }
+ return log(arguments.get(0)) / log(arguments.get(1));
+ }
+ if (functionName.equals("log2")) {
+ if (arguments.size() != 1) {
+ throw new ParsingException("Incorrect expression");
+ }
+ return log(arguments.get(0)) / log(2);
+ }
+ if (functionName.equals("rnd")) {
+ if (arguments.size() != 0) {
+ throw new ParsingException("Incorrect expression");
+ }
+ return random();
+ }
+ if (functionName.equals("sign")) {
+ if (arguments.size() != 1) {
+ throw new ParsingException("Incorrect expression");
+ }
+ return signum(arguments.get(0));
+ }
+ throw new ParsingException("Not standard expression.");
+ }
+
+ private boolean isStandardFunction(String functionName) {
+ return functionName.equals("sin") ||
+ functionName.equals("cos") ||
+ functionName.equals("tg") ||
+ functionName.equals("sqrt") ||
+ functionName.equals("pow") ||
+ functionName.equals("abs") ||
+ functionName.equals("sign") ||
+ functionName.equals("log") ||
+ functionName.equals("log2") ||
+ functionName.equals("rnd") ||
+ functionName.equals("max") ||
+ functionName.equals("min");
+
+ }
+
+ public String getVariableExpression(String variable) {
+ if (!variablesExpressions.keySet().contains(variable)) {
+ return null;
+ }
+ return variablesExpressions.get(variable);
+ }
+
+ public void setVariableExpression(String variable, String expression) {
+ variablesExpressions.put(variable, expression);
+ }
+
+ public void deleteVariable(String variable) {
+ variablesExpressions.remove(variable);
+ }
+
+ public ArrayList getAllVariables() {
+ return new ArrayList<>(variablesExpressions.keySet());
+ }
+
+ public Pair, String> getFunctionInfo(String functionName) {
+ return functions.get(functionName);
+ }
+
+ public boolean setFunction(String functionName, ArrayList arguments, String expression) {
+ if (isStandardFunction(functionName)) {
+ return false;
+ }
+ functions.put(functionName, new Pair<>(arguments, expression));
+ return true;
+ }
+
+ public boolean deleteFunction(String functionName) {
+ if (isStandardFunction(functionName)) {
+ return false;
+ }
+ functions.remove(functionName);
+ return true;
+ }
+
+ public ArrayList getAllFunctions() {
+ return new ArrayList<>(functions.keySet());
+ }
+
+ private boolean isDigit(char c) {
+ return (c >= '0' && c <= '9');
+ }
+
+ private boolean isLatinSymbol(char c) {
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+ }
+
+ private boolean isUnderscore(char c) {
+ return c == '_';
+ }
+
+ private boolean isOpeningBracket(char c) {
+ return c == '(';
+ }
+
+
+ private Pair getValueLexem(String expression, Integer startIndex) {
+ Integer firstIndex = startIndex;
+ while (firstIndex < expression.length() && !isUnderscore(expression.charAt(firstIndex)) &&
+ !isLatinSymbol(expression.charAt(firstIndex))) {
+ firstIndex += 1;
+ }
+ if (firstIndex >= expression.length()) {
+ return null;
+ }
+ Integer secondIndex = firstIndex;
+ while (secondIndex < expression.length() && (isUnderscore(expression.charAt(secondIndex)) ||
+ isLatinSymbol(expression.charAt(secondIndex)) || isDigit(expression.charAt(secondIndex)))) {
+ secondIndex += 1;
+ }
+ if (secondIndex == expression.length() || expression.charAt(secondIndex) != '(') {
+ return new Pair<>(firstIndex, secondIndex);
+ } else {
+ return getValueLexem(expression, secondIndex + 1);
+ }
+ }
+
+ private Pair>, ArrayList> getFunctionLexem(String expression) {
+ Integer firstIndex = 0;
+ while (firstIndex < expression.length() && !isUnderscore(expression.charAt(firstIndex)) &&
+ !isLatinSymbol(expression.charAt(firstIndex))) {
+ firstIndex += 1;
+ }
+ if (firstIndex >= expression.length()) {
+ return null;
+ }
+ Integer secondIndex = firstIndex;
+ while (secondIndex < expression.length() && (isUnderscore(expression.charAt(secondIndex)) ||
+ isLatinSymbol(expression.charAt(secondIndex)) || isDigit(expression.charAt(secondIndex)))) {
+ secondIndex += 1;
+ }
+ String name = expression.substring(firstIndex, secondIndex);
+ Pair, Integer> arg = getArguments(expression, secondIndex);
+ ArrayList arguments = arg.getKey();
+ Integer lastIndex = arg.getValue();
+ Pair indices = new Pair<>(firstIndex, lastIndex);
+ return new Pair<>(new Pair<>(name, indices), arguments);
+ }
+
+ private Pair, Integer> getArguments(String expression, Integer startIndex) {
+ ArrayList arguments = new ArrayList<>();
+ Integer lastIndex = 0;
+ Integer balance = 1;
+ Integer currentIndex = startIndex + 1;
+ String currentArgument = "";
+ while (balance != 0) { //выражение уже проверено на корректность -> exception не выбрасываем
+ if (expression.charAt(currentIndex) == '(') {
+ balance += 1;
+ currentArgument += expression.charAt(currentIndex);
+ currentIndex++;
+ } else if (expression.charAt(currentIndex) == ')') {
+ balance -= 1;
+ currentArgument += expression.charAt(currentIndex);
+ currentIndex++;
+ } else if (balance == 1 && expression.charAt(currentIndex) == ',') {
+ arguments.add(currentArgument);
+ currentArgument = "";
+ currentIndex++;
+ } else {
+ currentArgument += expression.charAt(currentIndex);
+ currentIndex++;
+ }
+ }
+ arguments.add(currentArgument.substring(0, currentArgument.length() - 1));
+ return new Pair<>(arguments, currentIndex);
+ }
+
+ private Pair findArgument(String expression, String argument) {
+ int lastIndex = expression.indexOf(argument, 0);
+ if (lastIndex == -1) {
+ return null;
+ }
+ return new Pair<>(lastIndex, lastIndex + argument.length());
+ }
+
+ private Double evaluateFunction(String expression, ArrayList argumentsList,
+ ArrayList arguments) throws ParsingException {
+ for (int i = 0; i < argumentsList.size(); i++) {
+ String argument = argumentsList.get(i);
+ Pair argCoord = findArgument(expression, argument);
+ while (argCoord != null) {
+ expression = expression.substring(0, argCoord.getKey()) + arguments.get(i).toString() +
+ expression.substring(argCoord.getValue(), expression.length());
+ argCoord = findArgument(expression, argument);
+ }
+ }
+ try {
+ return evaluateExpression(expression);
+ } catch (ParsingException e) {
+ throw e;
+ }
+ }
+
+
+
+ public double evaluateExpression(String expression) throws ParsingException {
+ expression = deleteSpaces(expression);
+
+
+ try {
+ Pair valueLexem = getValueLexem(expression, 0);
+ while (valueLexem != null) {
+ Integer start = valueLexem.getKey();
+ Integer end = valueLexem.getValue();
+ String valueName = expression.substring(start, end);
+ String valExpr = getVariableExpression(valueName);
+ Double result = evaluateExpression(valExpr);
+ expression = expression.substring(0, start) + result.toString() +
+ expression.substring(end, expression.length());
+ valueLexem = getValueLexem(expression, 0);
+ }
+ Pair>, ArrayList> functionLexem =
+ getFunctionLexem(expression);
+ while (functionLexem != null) {
+ String functionName = functionLexem.getKey().getKey();
+ Integer start = functionLexem.getKey().getValue().getKey();
+ Integer end = functionLexem.getKey().getValue().getValue();
+ ArrayList arguments = functionLexem.getValue();
+ ArrayList calculatedArguments = new ArrayList<>();
+ for (int i = 0; i < arguments.size(); i++) {
+ calculatedArguments.add(evaluateExpression(arguments.get(i)));
+ }
+ Double result = Double.NaN;
+ if (isStandardFunction(functionName)) {
+ result = calculateStandardExpression(functionName, calculatedArguments);
+ } else {
+ Pair, String> func = getFunctionInfo(functionName);
+ result = evaluateFunction(func.getValue(), func.getKey(), calculatedArguments);
+ }
+ expression = expression.substring(0, start) + result.toString() +
+ expression.substring(end, expression.length());
+ functionLexem = getFunctionLexem(expression);
+ }
+ return calculate(expression);
+ } catch (ParsingException p) {
+ throw p;
+ }
+ }
+
+ @Override
+ public double calculate(String expression) throws ParsingException {
+ numbers = new Stack<>();
+ operations = new Stack<>();
+ try {
+ if (expression == null) {
+ throw new ParsingException("Incorrect expression");
+ }
+ if (!checkForConsequentNumbers(expression)) {
+ throw new ParsingException("Incorrect expression");
+ }
+ if (!checkIncorrectExpression(expression)) {
+ throw new ParsingException("Incorrect expression");
+ }
+ return toRPH(expression);
+ } finally {
+ numbers = null;
+ operations = null;
+ }
+
+ }
+
+ private boolean checkForConsequentNumbers(String expres) {
+ //между любыми двумя числами должен стоять оператор (тесты вида 1 2, 1(2)
+ boolean opBetween = true;
+ boolean notString = true;
+ for (int i = 0; i < expres.length(); i++) {
+ char c = expres.charAt(i);
+ if (c == '*' || c == '/' || c == '+' || c == '-') {
+ opBetween = true;
+ notString = true;
+ } else if (Character.isDigit(c) || c == '.') {
+ if (notString && !opBetween) {
+ return false;
+ }
+ notString = false;
+ opBetween = false;
+ } else {
+ notString = true;
+ }
+ }
+ return true;
+ }
+
+ private int priority(char c) {
+ if (c == '+' || c == '-') {
+ return 1;
+ } else if (c == '*' || c == '/') {
+ return 2;
+ } else if (c == 'M') {
+ return 3;
+ } else {
+ return -1;
+ }
+ }
+
+ private void calculationOperator(char c) {
+ if (c == 'M') {
+ double a = numbers.pop();
+ numbers.push(-a);
+ return;
+ }
+ double a = numbers.pop();
+ double b = numbers.pop();
+ if (c == '+') {
+ numbers.push(a + b);
+ } else if (c == '-') {
+ numbers.push(b - a);
+ } else if (c == '*') {
+ numbers.push(b * a);
+ } else if (c == '/') {
+ numbers.push(b / a);
+ }
+ }
+
+ private double toRPH(String expression) throws ParsingException {
+ boolean isUnary = true; //перед унарным минусом стоит либо операция, либо (
+ for (int i = 0; i < expression.length(); ++i) {
+ char c = expression.charAt(i);
+ if (c == '(') {
+ isUnary = true;
+ operations.push(c);
+ } else if (c == ')') {
+ //вычиляем значение в скобках
+ while (operations.peek() != '(') {
+ calculationOperator(operations.peek());
+ operations.pop();
+ }
+ isUnary = false;
+ //после ')' не может быть унарного минуса
+ operations.pop();
+
+ } else if (c == '+' || c == '-' || c == '*' || c == '/') {
+ if (isUnary && c == '-') {
+ c = 'M';
+ }
+ //сначала выполняем операции с большим приоритетом
+ while (!operations.isEmpty() && ((c != 'M' &&
+ priority(operations.peek()) >= priority(c)) || (c == 'M'
+ && priority(operations.peek()) > priority(c)))) {
+ calculationOperator(operations.peek());
+ operations.pop();
+ }
+ operations.push(c);
+ isUnary = true;
+ } else {
+ String operand = "";
+ //находим десятичное число и добавляем его в вектор чисел
+ while (i < expression.length() &&
+ (Character.isDigit(expression.charAt(i))
+ || expression.charAt(i) == '.')) {
+ operand += expression.charAt(i);
+ i++;
+ }
+ i--;
+ numbers.push(Double.parseDouble(operand));
+ isUnary = false;
+ //после числа не может стоять унарый минус
+ }
+ }
+ //выполняем оставшиеся операции над получившимися числами из numbers
+ while (!operations.isEmpty()) {
+ calculationOperator(operations.peek());
+ operations.pop();
+ }
+ if (numbers.size() != 1) {
+ throw new ParsingException("Invalid expression.");
+ }
+ return numbers.peek();
+ }
+
+ private String deleteSpaces(String expression) {
+ String expres = "";
+ for (int i = 0; i < expression.length(); ++i) {
+ if (expression.charAt(i) != ' ' && expression.charAt(i) != '\t' && expression.charAt(i) != '\n') {
+ expres += Character.toString(expression.charAt(i));
+ }
+ }
+ return expres;
+ }
+
+ private boolean checkIncorrectExpression(String expres) {
+ int bracketResult = 0;
+ //выражение непусто
+ //на первом месте не стоят бинарные операции
+ //на последнем месте либо цифра, либо ')'
+ if (expres.length() == 0 || expres.charAt(0) == '*'
+ || expres.charAt(0) == '/' || expres.charAt(0) == '+'
+ || !(Character.isDigit(expres.charAt(expres.length() - 1))
+ || expres.charAt(expres.length() - 1) == ')')) {
+ return false;
+ }
+ for (int i = 0; i < expres.length(); ++i) {
+ if (expres.charAt(i) == '(') {
+ bracketResult += 1;
+ }
+ if (expres.charAt(i) == ')') {
+ bracketResult -= 1;
+ }
+ //после оператора не стоит бинарный оператор(то есть не *, /, +)
+ if (expres.charAt(i) == '-' || expres.charAt(i) == '+'
+ || expres.charAt(i) == '/' || expres.charAt(i) == '*') {
+ if (i + 1 >= expres.length() || expres.charAt(i + 1) == '+'
+ || expres.charAt(i + 1) == '/' || expres.charAt(i + 1) == '*') {
+ return false;
+ }
+ }
+ //проверка на некорректные символы
+ if (!(Character.isDigit(expres.charAt(i)) || expres.charAt(i) == '.'
+ || expres.charAt(i) == '(' || expres.charAt(i) == ')'
+ || expres.charAt(i) == '+' || expres.charAt(i) == '-' ||
+ expres.charAt(i) == '*' || expres.charAt(i) == '/')) {
+ return false;
+ }
+ //проверка на неотрицательный скобочный итог
+ if (bracketResult < 0) {
+ return false;
+ }
+ //*, /, + не являются бинарными операторами, то есть они не могут стоять после '('
+ //также пустые скобки считаются некорретным выражением
+ if (expres.charAt(i) == '(') {
+ if (i + 1 >= expres.length() || (expres.charAt(i + 1) == '+'
+ || expres.charAt(i + 1) == '*' ||
+ expres.charAt(i + 1) == '/' || expres.charAt(i + 1) == ')')) {
+ return false;
+ }
+ }
+ }
+ if (bracketResult != 0) {
+ return false;
+ }
+ //проверка на корректность десятичного выражения(в каждом числе не больше одной '.')
+ int dot = 0;
+ int i = 0;
+ while (i < expres.length() && dot < 2) {
+ if (expres.charAt(i) == '+' || expres.charAt(i) == '-'
+ || expres.charAt(i) == '/' || expres.charAt(i) == '*') {
+ dot = 0;
+ }
+ if (expres.charAt(i) == '.') {
+ dot += 1;
+ }
+ i++;
+ }
+ if (dot >= 2) {
+ return false;
+ }
+ return true;
+
+ }
+}
diff --git a/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/MySpringApplication.java b/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/MySpringApplication.java
new file mode 100644
index 000000000..19eea25ee
--- /dev/null
+++ b/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/MySpringApplication.java
@@ -0,0 +1,41 @@
+package ru.mipt.java2016.homework.g597.kirilenko.task4;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.Banner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * curl http://localhost:9001/eval \
+ * -X POST \
+ * -H "Content-Type: text/plain" \
+ * -H "Authorization: Basic $(echo -n "username:password" | base64)" \
+ * --data-raw "44*3+2"
+ */
+
+@EnableAutoConfiguration
+@Configuration
+@ComponentScan(basePackageClasses = MySpringApplication.class)
+public class MySpringApplication {
+
+ @Bean
+ public MyCalculator calculator() {
+ return new MyCalculator();
+ }
+
+ @Bean
+ public EmbeddedServletContainerCustomizer customizer(
+ @Value("${ru.mipt.java2016.homework.g597.kirilenko.task4.httpPort:9001}") int port) {
+ return container -> container.setPort(port);
+ }
+
+ public static void main(String[] args) {
+ SpringApplication application = new SpringApplication(MySpringApplication.class);
+ application.setBannerMode(Banner.Mode.OFF);
+ application.run(args);
+ }
+}
diff --git a/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/SecurityServiceConfiguration.java b/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/SecurityServiceConfiguration.java
new file mode 100644
index 000000000..6f7f1d196
--- /dev/null
+++ b/homework-g597-kirilenko/src/main/java/ru/mipt/java2016/homework/g597/kirilenko/task4/SecurityServiceConfiguration.java
@@ -0,0 +1,55 @@
+package ru.mipt.java2016.homework.g597.kirilenko.task4;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityServiceConfiguration extends WebSecurityConfigurerAdapter {
+ private static final Logger LOG = LoggerFactory.getLogger(SecurityServiceConfiguration.class);
+
+ @Autowired
+ private BillingDao billingDao;
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ LOG.info("Configuring security");
+ http
+ .httpBasic().realmName("Calculator").and()
+ .formLogin().disable()
+ .logout().disable()
+ .csrf().disable()
+ .authorizeRequests()
+ .antMatchers("/**").authenticated()
+ .anyRequest().permitAll();
+ }
+
+ @Autowired
+ public void registerGlobalAuthentication(AuthenticationManagerBuilder auth) throws Exception {
+ LOG.info("Registering global user details service");
+ auth.userDetailsService(username -> {
+ try {
+ BillingUser user = billingDao.loadUser(username);
+ return new User(
+ user.getUsername(),
+ user.getPassword(),
+ Collections.singletonList(() -> "AUTH")
+ );
+ } catch (EmptyResultDataAccessException e) {
+ LOG.warn("No such user: " + username);
+ throw new UsernameNotFoundException(username);
+ }
+ });
+ }
+}
diff --git a/homework-g597-kirilenko/src/test/java/ru/mipt/java2016/homework/g597/kirilenko/task4/tests.txt b/homework-g597-kirilenko/src/test/java/ru/mipt/java2016/homework/g597/kirilenko/task4/tests.txt
new file mode 100644
index 000000000..c2107ab3f
--- /dev/null
+++ b/homework-g597-kirilenko/src/test/java/ru/mipt/java2016/homework/g597/kirilenko/task4/tests.txt
@@ -0,0 +1,18 @@
+
+curl http://localhost:9001/function/myfunc1?args=x,y -X PUT -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "username:password" | base64)" --data-raw "log2(x) + y"
+curl http://localhost:9001/function/sum2?args=x,y -X PUT -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "username:password" | base64)" --data-raw "x+2* y"
+
+curl http://localhost:9001/variable/xy -X PUT -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "username:password" | base64)" --data-raw "sqrt(9)+sign(2)"
+curl http://localhost:9001/variable/_2xy -X PUT -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "username:password" | base64)" --data-raw "log2(xy*sign(xy)) *2"
+
+curl http://localhost:9001/function/myfunc1 -X GET -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "username:password" | base64)"
+curl http://localhost:9001/function/sum2 -X GET -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "username:password" | base64)"
+
+curl http://localhost:9001/function -X GET -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "username:password" | base64)"
+curl http://localhost:9001/variable -X GET -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "username:password" | base64)"
+
+// Маленький
+curl http://localhost:9001/eval -X POST -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "username:password" | base64)" --data-raw "sqrt(_2xy*sqrt(16)) - sin(0)"
+
+// Большой тест
+curl http://localhost:9001/eval -X POST -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "username:password" | base64)" --data-raw "(sum2(myfunc1(xy, _2xy), sum2(4, sqrt(16)))*cos(0)"
\ No newline at end of file