From 5133ddd3bba1332a42b4d3b77cc7aa9b2eebaed9 Mon Sep 17 00:00:00 2001 From: syroforce Date: Thu, 15 Dec 2016 21:45:19 +0300 Subject: [PATCH 01/13] FINALISED: OptimisedByteStorage --- ...sedByteKeyValueStoragePerformanceTest.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/homework-g595-topilskiy/src/test/java/ru/mipt/java2016/homework/g595/topilskiy/task3/OptimisedByteKeyValueStoragePerformanceTest.java b/homework-g595-topilskiy/src/test/java/ru/mipt/java2016/homework/g595/topilskiy/task3/OptimisedByteKeyValueStoragePerformanceTest.java index 035212023..99af68671 100644 --- a/homework-g595-topilskiy/src/test/java/ru/mipt/java2016/homework/g595/topilskiy/task3/OptimisedByteKeyValueStoragePerformanceTest.java +++ b/homework-g595-topilskiy/src/test/java/ru/mipt/java2016/homework/g595/topilskiy/task3/OptimisedByteKeyValueStoragePerformanceTest.java @@ -12,6 +12,7 @@ import ru.mipt.java2016.homework.g595.topilskiy.task2.Serializer.*; import org.junit.Test; +import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; @@ -40,6 +41,27 @@ public void testAdler32() throws IOException { } } + @Test + public void testLockingStorageFile() throws IOException { + final String TEST_DIR_PATH = "/tmp/java_test"; + + File test_directory = new File(TEST_DIR_PATH); + test_directory.mkdir(); + + Runnable task = () -> { + OptimisedByteKeyValueStorage storage = + new OptimisedByteKeyValueStorage<>( + TEST_DIR_PATH, + IntegerSerializerSingleton.getInstance(), + IntegerSerializerSingleton.getInstance()); + }; + + new Thread(task).start(); + new Thread(task).start(); + + FileUtils.deleteDirectory(test_directory); + } + @Override protected KeyValueStorage buildStringsStorage(String path) throws MalformedDataException { return new OptimisedByteKeyValueStorage<>(path, StringSerializerSingleton.getInstance(), From e13b2d8c1639997f6bd8c07a5dd8023d65afd2b5 Mon Sep 17 00:00:00 2001 From: syroforce Date: Fri, 16 Dec 2016 06:46:38 +0300 Subject: [PATCH 02/13] INITIALIZED: files for task04 --- homework-g595-topilskiy/pom.xml | 42 ++++++++++++ .../calculator/CachedJEvalCalculator.java | 30 +++++++++ .../task4/calculator/JEvalCalculator.java | 32 +++++++++ .../task4/server/CalculatorApplication.java | 43 ++++++++++++ .../task4/server/CalculatorController.java | 42 ++++++++++++ .../topilskiy/task4/server/CalculatorDao.java | 58 +++++++++++++++++ .../CalculatorDatabaseConfiguration.java | 26 ++++++++ .../task4/server/CalculatorUser.java | 65 +++++++++++++++++++ .../server/SecurityServiceConfiguration.java | 56 ++++++++++++++++ .../src/main/resources/logback.xml | 13 ++++ 10 files changed, 407 insertions(+) create mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/CachedJEvalCalculator.java create mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/JEvalCalculator.java create mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorApplication.java create mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java create mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorDao.java create mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorDatabaseConfiguration.java create mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorUser.java create mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java create mode 100644 homework-g595-topilskiy/src/main/resources/logback.xml diff --git a/homework-g595-topilskiy/pom.xml b/homework-g595-topilskiy/pom.xml index d1dfd5ac0..95e74790c 100644 --- a/homework-g595-topilskiy/pom.xml +++ b/homework-g595-topilskiy/pom.xml @@ -10,8 +10,13 @@ 4.0.0 homework-g595-topilskiy + pom 1.0.0 + + 1.4.2.RELEASE + + ru.mipt.java2016 @@ -19,6 +24,43 @@ 1.0.0 + + net.sourceforge.jeval + jeval + 0.9.4 + + + + + 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 + + ru.mipt.java2016 homework-tests diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/CachedJEvalCalculator.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/CachedJEvalCalculator.java new file mode 100644 index 000000000..e992eb428 --- /dev/null +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/CachedJEvalCalculator.java @@ -0,0 +1,30 @@ +package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator; + +import net.sourceforge.jeval.Evaluator; +import ru.mipt.java2016.homework.base.task1.Calculator; + +/** + * Версия {@link JEvalCalculator}, которая не создает новый {@link Evaluator} каждый раз, + * а переиспользует один и тот же экземпляр. + * + * @author Fedor S. Lavrentyev + * @since 28.09.16 + */ +public class CachedJEvalCalculator extends JEvalCalculator { + public static final Calculator INSTANCE = new CachedJEvalCalculator(); + + private CachedJEvalCalculator() { + } + + private final ThreadLocal evaluator = new ThreadLocal() { + @Override + protected Evaluator initialValue() { + return CachedJEvalCalculator.super.buildEvaluator(); + } + }; + + @Override + protected Evaluator buildEvaluator() { + return evaluator.get(); + } +} diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/JEvalCalculator.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/JEvalCalculator.java new file mode 100644 index 000000000..0cb1d4268 --- /dev/null +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/JEvalCalculator.java @@ -0,0 +1,32 @@ +package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator; + +import net.sourceforge.jeval.EvaluationConstants; +import net.sourceforge.jeval.EvaluationException; +import net.sourceforge.jeval.Evaluator; +import ru.mipt.java2016.homework.base.task1.Calculator; +import ru.mipt.java2016.homework.base.task1.ParsingException; + +/** + * Пример реализации калькулятора средствами JEval. + * + * @author Fedor S. Lavrentyev + * @since 28.09.16 + */ +public class JEvalCalculator implements Calculator { + protected Evaluator buildEvaluator() { + return new Evaluator(EvaluationConstants.SINGLE_QUOTE, false, false, false, false); + } + + public double calculate(String expression) throws ParsingException { + if (expression == null) { + throw new ParsingException("Null expression"); + } + try { + Evaluator evaluator = buildEvaluator(); + String result = evaluator.evaluate(expression); + return Double.parseDouble(result); + } catch (EvaluationException e) { + throw new ParsingException("Invalid expression", e.getCause()); + } + } +} diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorApplication.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorApplication.java new file mode 100644 index 000000000..4985df9b1 --- /dev/null +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorApplication.java @@ -0,0 +1,43 @@ +package ru.mipt.java2016.homework.g595.topilskiy.task4.server; + +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; + +import ru.mipt.java2016.homework.base.task1.Calculator; +import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.CachedJEvalCalculator; + +/** + * curl http://localhost:9001/eval \ + * -X POST \ + * -H "Content-Type: text/plain" \ + * -H "Authorization: Basic $(echo -n "supersanic:gottagofast" | base64)" \ + * --data-raw "44*3+2" + */ +@EnableAutoConfiguration +@Configuration +@ComponentScan(basePackageClasses = CalculatorApplication.class) +public class CalculatorApplication { + + @Bean + public Calculator calculator() { + return CachedJEvalCalculator.INSTANCE; + } + + @Bean + public EmbeddedServletContainerCustomizer customizer( + @Value("${ru.mipt.java2016.homework.g595.topilskiy.task4.server.httpPort:9001}") int port) { + return container -> container.setPort(port); + } + + public static void main(String[] args) { + SpringApplication application = new SpringApplication(CalculatorApplication.class); + application.setBannerMode(Banner.Mode.OFF); + application.run(args); + } +} diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java new file mode 100644 index 000000000..6c205719d --- /dev/null +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java @@ -0,0 +1,42 @@ +package ru.mipt.java2016.homework.g595.topilskiy.task4.server; + +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.Calculator; +import ru.mipt.java2016.homework.base.task1.ParsingException; + +@RestController +public class CalculatorController { + private static final Logger LOG = LoggerFactory.getLogger(CalculatorController.class); + + @Autowired + private Calculator calculator; + + @RequestMapping(path = "/ping", method = RequestMethod.GET, produces = "text/plain") + public String echo() { + return "OK\n"; + } + + @RequestMapping(path = "/", method = RequestMethod.GET, produces = "text/html") + public String main(@RequestParam(required = false) String name) { + if (name == null) { + name = "world"; + } + + return "" + + "CalculatorApp" + + "

Hello, " + name + "!

" + + ""; + } + + @RequestMapping(path = "/eval", method = RequestMethod.POST, consumes = "text/plain", produces = "text/plain") + public String eval(@RequestBody String expression) throws ParsingException { + LOG.debug("Evaluation request: [" + expression + "]"); + double result = calculator.calculate(expression); + LOG.trace("Result: " + result); + return Double.toString(result) + "\n"; + } +} diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorDao.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorDao.java new file mode 100644 index 000000000..add5fc540 --- /dev/null +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorDao.java @@ -0,0 +1,58 @@ +package ru.mipt.java2016.homework.g595.topilskiy.task4.server; + +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 CalculatorDao { + private static final Logger LOG = LoggerFactory.getLogger(CalculatorDao.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 calculator"); + jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS calculator.users " + + "(username VARCHAR PRIMARY KEY, password VARCHAR, enabled BOOLEAN)"); + //jdbcTemplate.update("INSERT INTO calculator.users VALUES ('supersanic', 'gottagofast', TRUE)"); + } + + + public CalculatorUser loadUser(String username) throws EmptyResultDataAccessException { + LOG.trace("Querying for user " + username); + return jdbcTemplate.queryForObject( + "SELECT username, password, enabled FROM calculator.users 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"), + rs.getBoolean("enabled") + ); + } + } + ); + } +} diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorDatabaseConfiguration.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorDatabaseConfiguration.java new file mode 100644 index 000000000..0843bcbf4 --- /dev/null +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorDatabaseConfiguration.java @@ -0,0 +1,26 @@ +package ru.mipt.java2016.homework.g595.topilskiy.task4.server; + +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 CalculatorDatabaseConfiguration { + @Bean + public DataSource billingDataSource( + @Value("${ru.mipt.java2016.homework.g595.topilskiy.task4.server.jdbcUrl}") String jdbcUrl, + @Value("${ru.mipt.java2016.homework.g595.topilskiy.task4.server.username:}") String username, + @Value("${ru.mipt.java2016.homework.g595.topilskiy.task4.server.password:}") String password + ) { + HikariConfig config = new HikariConfig(); + config.setDriverClassName(org.h2.Driver.class.getName()); + config.setJdbcUrl(jdbcUrl); + config.setUsername(username); + config.setPassword(password); + return new HikariDataSource(config); + } +} diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorUser.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorUser.java new file mode 100644 index 000000000..6ee5f9194 --- /dev/null +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorUser.java @@ -0,0 +1,65 @@ +package ru.mipt.java2016.homework.g595.topilskiy.task4.server; + +public class CalculatorUser { + private final String username; + private final String password; + private final boolean enabled; + + public CalculatorUser(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 "CalculatorUser{" + + "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; + } + + CalculatorUser that = (CalculatorUser) o; + + return enabled == that.enabled && + username.equals(that.username) && + 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-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java new file mode 100644 index 000000000..e38fde268 --- /dev/null +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java @@ -0,0 +1,56 @@ +package ru.mipt.java2016.homework.g595.topilskiy.task4.server; + +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 CalculatorDao calculatorDao; + + @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("/eval/**").authenticated() + .antMatchers("/").authenticated() + .anyRequest().permitAll(); + } + + @Autowired + public void registerGlobalAuthentication(AuthenticationManagerBuilder auth) throws Exception { + LOG.info("Registering global user details service"); + auth.userDetailsService(username -> { + try { + CalculatorUser user = calculatorDao.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-g595-topilskiy/src/main/resources/logback.xml b/homework-g595-topilskiy/src/main/resources/logback.xml new file mode 100644 index 000000000..9c97742d2 --- /dev/null +++ b/homework-g595-topilskiy/src/main/resources/logback.xml @@ -0,0 +1,13 @@ + + + + %date %-5p %20.20c{20} %msg%n + + + + + + + + + \ No newline at end of file From f0f591daefaeed00fdfb1b184ff91ca63d97da1e Mon Sep 17 00:00:00 2001 From: syroforce Date: Fri, 16 Dec 2016 15:16:28 +0300 Subject: [PATCH 03/13] COMPLETED: New Token Calculator (tested on junit-task01) --- .../calculator/CachedJEvalCalculator.java | 30 --- .../task4/calculator/JEvalCalculator.java | 32 --- .../topilskiy/task4/calculator/Token.java | 64 ++++++ .../task4/calculator/TokenCalculator.java | 213 ++++++++++++++++++ .../task4/server/CalculatorApplication.java | 4 +- 5 files changed, 279 insertions(+), 64 deletions(-) delete mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/CachedJEvalCalculator.java delete mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/JEvalCalculator.java create mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/Token.java create mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/TokenCalculator.java diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/CachedJEvalCalculator.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/CachedJEvalCalculator.java deleted file mode 100644 index e992eb428..000000000 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/CachedJEvalCalculator.java +++ /dev/null @@ -1,30 +0,0 @@ -package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator; - -import net.sourceforge.jeval.Evaluator; -import ru.mipt.java2016.homework.base.task1.Calculator; - -/** - * Версия {@link JEvalCalculator}, которая не создает новый {@link Evaluator} каждый раз, - * а переиспользует один и тот же экземпляр. - * - * @author Fedor S. Lavrentyev - * @since 28.09.16 - */ -public class CachedJEvalCalculator extends JEvalCalculator { - public static final Calculator INSTANCE = new CachedJEvalCalculator(); - - private CachedJEvalCalculator() { - } - - private final ThreadLocal evaluator = new ThreadLocal() { - @Override - protected Evaluator initialValue() { - return CachedJEvalCalculator.super.buildEvaluator(); - } - }; - - @Override - protected Evaluator buildEvaluator() { - return evaluator.get(); - } -} diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/JEvalCalculator.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/JEvalCalculator.java deleted file mode 100644 index 0cb1d4268..000000000 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/JEvalCalculator.java +++ /dev/null @@ -1,32 +0,0 @@ -package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator; - -import net.sourceforge.jeval.EvaluationConstants; -import net.sourceforge.jeval.EvaluationException; -import net.sourceforge.jeval.Evaluator; -import ru.mipt.java2016.homework.base.task1.Calculator; -import ru.mipt.java2016.homework.base.task1.ParsingException; - -/** - * Пример реализации калькулятора средствами JEval. - * - * @author Fedor S. Lavrentyev - * @since 28.09.16 - */ -public class JEvalCalculator implements Calculator { - protected Evaluator buildEvaluator() { - return new Evaluator(EvaluationConstants.SINGLE_QUOTE, false, false, false, false); - } - - public double calculate(String expression) throws ParsingException { - if (expression == null) { - throw new ParsingException("Null expression"); - } - try { - Evaluator evaluator = buildEvaluator(); - String result = evaluator.evaluate(expression); - return Double.parseDouble(result); - } catch (EvaluationException e) { - throw new ParsingException("Invalid expression", e.getCause()); - } - } -} diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/Token.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/Token.java new file mode 100644 index 000000000..65982f64e --- /dev/null +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/Token.java @@ -0,0 +1,64 @@ +package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator; + +/** + * Token class for parsing the expression in TokenCalculator + * + * @author Artem K. Topilskiy + * @since 16.12.16. + */ +public class Token { + /** + * Enum to describe the type of Data in Token + */ + static public enum TokenType { + PLUS, MINUS, MULTIPLY, DIVIDE, + NUMBER, NAME, + LEFT_BRACE, RIGHT_BRACE, COMMA, + UNKNOWN + } + + /** + * Data + */ + private final TokenType type; + private Double number = null; + private String name = null; + + /** + * Constructors + */ + public Token(TokenType type) { + this.type = type; + } + + public Token(String name) { + this.name = name; + this.type = TokenType.NAME; + } + + public Token(Double number) { + this.number = number; + this.type = TokenType.NUMBER; + } + + + /** + * Getters + */ + public TokenType getType() { + return type; + } + + public Double getNumber() { + return number; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return String.format("Type: %s, Number: %s, Name: %s \n", type, number, name); + } +} diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/TokenCalculator.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/TokenCalculator.java new file mode 100644 index 000000000..704aea749 --- /dev/null +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/TokenCalculator.java @@ -0,0 +1,213 @@ +package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator; + +import ru.mipt.java2016.homework.base.task1.Calculator; +import ru.mipt.java2016.homework.base.task1.ParsingException; + +import java.util.ArrayList; + +/** + * Calculator that evaluates the expression by the way of tokens + * Expression is defined as the following (in extended Backus-Naur Form \) + * expression = multiple, { ('+' | '-') multiple } + * multiple = braced_expression, { ('*' | '/') braced_expression } + * braced_expression = '(' expression ')' | number_expression + * number_expression = '-' braced_expression | double_number + * double_number = '[1-9]' { '[0-9]' } [ '.' ] { '[0-9]' }+ + * + * @author Artem K. Topilskiy + * @since 16.12.16. + */ +public class TokenCalculator implements Calculator { + /** + * Data + */ + private final ArrayList tokens = new ArrayList<>(); + private int tokensIndex = 0; + + + /** + * Private Token-generate Functions + */ + private void parse(String expression) throws ParsingException { + if (expression == null) { + throw new ParsingException("Expression is null."); + } + + for (int expressionIndex = 0; expressionIndex < expression.length(); ++expressionIndex) { + Token.TokenType currentTokenType; + Double currentNumber = null; + + if (Character.isWhitespace(expression.charAt(expressionIndex)) || + Character.isSpaceChar(expression.charAt(expressionIndex))) { + continue; + } + + switch(expression.charAt(expressionIndex)) { + case '+': + currentTokenType = Token.TokenType.PLUS; + break; + + case '-': + currentTokenType = Token.TokenType.MINUS; + break; + + case '*': + currentTokenType = Token.TokenType.MULTIPLY; + break; + + case '/': + currentTokenType = Token.TokenType.DIVIDE; + break; + + case '(': + currentTokenType = Token.TokenType.LEFT_BRACE; + break; + + case ')': + currentTokenType = Token.TokenType.RIGHT_BRACE; + break; + + default: + if (!Character.isDigit(expression.charAt(expressionIndex))) { + throw new ParsingException(String.format("Unexpected symbol at %d", expressionIndex)); + } + + boolean readDot = false; + int numberStartIndex = expressionIndex; + for (; expressionIndex < expression.length(); ++expressionIndex) { + Character currentCharacter = expression.charAt(expressionIndex); + if (currentCharacter == '.' && !readDot) { + readDot = true; + } else if (!Character.isDigit(currentCharacter)) { + break; + } + } + + currentNumber = Double.parseDouble(expression.substring(numberStartIndex, expressionIndex)); + --expressionIndex; + currentTokenType = Token.TokenType.NUMBER; + break; + } + + if (currentTokenType != Token.TokenType.NUMBER) { + tokens.add(new Token(currentTokenType)); + } else { + tokens.add(new Token(currentNumber)); + } + } + } + + + /** + * Private Token-read Functions + */ + private void regressTokensIndex() { + --tokensIndex; + } + + private Token progressTokens() { + if (tokensIndex >= tokens.size()) { + return null; + } + + return tokens.get(tokensIndex++); + } + + + /** + * Private Token-calculate Functions + */ + private Double expression() throws ParsingException { + Double result = multiple(); + + for (Token token = progressTokens(); token != null; token = progressTokens()) { + if (token.getType() == Token.TokenType.PLUS) { + result += multiple(); + } else if (token.getType() == Token.TokenType.MINUS) { + result -= multiple(); + } else { + regressTokensIndex(); + break; + } + } + + return result; + } + + private Double multiple() throws ParsingException { + Double result = bracedExpression(); + + for (Token token = progressTokens(); token != null; token = progressTokens()) { + if (token.getType() == Token.TokenType.MULTIPLY) { + result *= bracedExpression(); + } else if (token.getType() == Token.TokenType.DIVIDE) { + result /= bracedExpression(); + } else { + regressTokensIndex(); + break; + } + } + + return result; + } + + private Double bracedExpression() throws ParsingException { + Double result; + + Token token = progressTokens(); + if (token == null) { + throw new ParsingException("Invalid amount of numbers."); + } + + if (token.getType() == Token.TokenType.LEFT_BRACE) { + result = expression(); + token = progressTokens(); + + if (token == null || token.getType() != Token.TokenType.RIGHT_BRACE) { + throw new ParsingException("Wrong number of left/right braces"); + } + } else { + regressTokensIndex(); + result = numberExpression(); + } + + return result; + } + + private Double numberExpression() throws ParsingException { + Double result; + + Token token = progressTokens(); + if (token == null) { + throw new ParsingException("Invalid amount of numbers"); + } + + if (token.getType() == Token.TokenType.MINUS) { + result = -expression(); + } else if (token.getType() == Token.TokenType.NUMBER) { + result = token.getNumber(); + } else { + throw new ParsingException("Invalid order of operations"); + } + + return result; + } + + + /** + * Public calculate interface + */ + public double calculate(String expression) throws ParsingException { + tokens.clear(); + tokensIndex = 0; + + parse(expression); + Double result = expression(); + + if (tokensIndex != tokens.size()) { + throw new ParsingException("Invalid number of tokens (too many)."); + } + + return result; + } +} diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorApplication.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorApplication.java index 4985df9b1..1beadede2 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorApplication.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorApplication.java @@ -10,7 +10,7 @@ import org.springframework.context.annotation.Configuration; import ru.mipt.java2016.homework.base.task1.Calculator; -import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.CachedJEvalCalculator; +import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.TokenCalculator; /** * curl http://localhost:9001/eval \ @@ -26,7 +26,7 @@ public class CalculatorApplication { @Bean public Calculator calculator() { - return CachedJEvalCalculator.INSTANCE; + return new TokenCalculator(); } @Bean From b84e1964bb6580fbfce7be7dcdd71ef48256a368 Mon Sep 17 00:00:00 2001 From: syroforce Date: Fri, 16 Dec 2016 15:23:55 +0300 Subject: [PATCH 04/13] FIX: CalculatorUserDB initialization --- .../homework/g595/topilskiy/task4/server/CalculatorDao.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorDao.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorDao.java index add5fc540..e82210785 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorDao.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorDao.java @@ -34,7 +34,11 @@ public void initSchema() { jdbcTemplate.execute("CREATE SCHEMA IF NOT EXISTS calculator"); jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS calculator.users " + "(username VARCHAR PRIMARY KEY, password VARCHAR, enabled BOOLEAN)"); - //jdbcTemplate.update("INSERT INTO calculator.users VALUES ('supersanic', 'gottagofast', TRUE)"); + try { + loadUser("supersanic"); + } catch (EmptyResultDataAccessException e) { + jdbcTemplate.update("INSERT INTO calculator.users VALUES ('supersanic', 'gottagofast', TRUE)"); + } } From c4332017038285cb9c8278c6ce652b91045727f3 Mon Sep 17 00:00:00 2001 From: syroforce Date: Fri, 16 Dec 2016 23:56:25 +0300 Subject: [PATCH 05/13] IN PROCESS: Functions for REST-Calculator MORE DIRECTORIES --- .../rest/IFunctionalCalculator.java | 69 ++++++++++ .../rest/function/CalculateableFunction.java | 10 ++ .../function/CalculatorFunctionObject.java | 80 +++++++++++ .../rest/function/IEvaluateableFunction.java | 23 ++++ .../rest/function/PredefinedFunction.java | 128 ++++++++++++++++++ .../task4/calculator/{ => vanilla}/Token.java | 2 +- .../{ => vanilla}/TokenCalculator.java | 2 +- .../task4/server/CalculatorApplication.java | 2 +- 8 files changed, 313 insertions(+), 3 deletions(-) create mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/IFunctionalCalculator.java create mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculateableFunction.java create mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculatorFunctionObject.java create mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/IEvaluateableFunction.java create mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/PredefinedFunction.java rename homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/{ => vanilla}/Token.java (99%) rename homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/{ => vanilla}/TokenCalculator.java (99%) diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/IFunctionalCalculator.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/IFunctionalCalculator.java new file mode 100644 index 000000000..b078acca3 --- /dev/null +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/IFunctionalCalculator.java @@ -0,0 +1,69 @@ +package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest; + +import ru.mipt.java2016.homework.base.task1.ParsingException; +import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.function.CalculatorFunctionObject; + +import java.util.List; + +/** + * Interface for a Calculator which can hold user-defined Functions and Variables + * + * @author Artem K. Topilskiy + * @since 16.12.16. + */ +public interface IFunctionalCalculator { + /** + * Methods of interacting with calculator VARIABLES + */ + /** + * @return the double value under the alias of variableAlias + */ + Double getVariable(String variableAlias); + + /** + * Make the alias of variableAlias reflect to the double value + */ + boolean putVariable(String variableAlias, Double value); + + /** + * Delete the alias of variableAlias and its held value + */ + boolean deleteVariable(String variableAlias); + + /** + * @return the list of aliases of variables in the calculator + */ + List getVariableList(); + + + /** + * Methods of interacting with calculator FUNCTIONS + */ + /** + * @return a CalculatorFunction object under the alias of functionAlias + * NOTE: predefined functions cannot be dealiased + */ + CalculatorFunctionObject getFunction(String functionAlias); + + /** + * Make the alias of functionAlias reflect to CalculatorFunction(expression, arguments) + */ + boolean putFunction(String functionAlias, String expression, List arguments) throws ParsingException; + + /** + * Delete the alias of functionAlias and its held function + */ + boolean deleteFunction(String functionAlias); + + /** + * @return the list of aliases of functions in the calculator + */ + List getFunctionList(); + + + /** + * Methods of CALCULATION of the value of expression + * (using the kept function and variable sets) + */ + Double calculate(String expression) throws ParsingException; +} diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculateableFunction.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculateableFunction.java new file mode 100644 index 000000000..391e1ac8c --- /dev/null +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculateableFunction.java @@ -0,0 +1,10 @@ +package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.function; + +/** + * Calculator functions which can be evaluated in runtime + * + * @author Artem K. Topilskiy + * @since 16.12.16. + */ +public class CalculateableFunction implements IEvaluateableFunction { +} diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculatorFunctionObject.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculatorFunctionObject.java new file mode 100644 index 000000000..1ec8e94f5 --- /dev/null +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculatorFunctionObject.java @@ -0,0 +1,80 @@ +package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.function; + +import java.util.ArrayList; +import java.util.List; + +/** + * The Function-Objects held in FunctionalCalculators + * which hold the argument list and the expression of the function + * + * @author Artem K. Topilskiy + * @since 16.12.16. + */ +public class CalculatorFunctionObject { + private String expression; + private List arguments; + + /** + * CONSTRUCTORS + */ + public CalculatorFunctionObject() { + expression = ""; + arguments = new ArrayList<>(); + } + + public CalculatorFunctionObject(String expression, List arguments) { + this.expression = expression; + this.arguments = arguments; + } + + + /** + * GETTERS + */ + public String getExpression() { + return expression; + } + + public List getArguments() { + return arguments; + } + + + /** + * SETTERS + */ + public void setExpression(String expression) { + this.expression = expression; + } + + public void setArguments(List arguments) { + this.arguments = arguments; + } + + /** + * OVERRIDDEN + */ + @Override + public String toString() { + /* NOTE FOR THYSELF: + Array -> stream -(reduce)-> joined strings + -(format)-> concatenate expression+arguments + */ + return String.format(expression, arguments.stream().reduce("", String::join)); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof CalculatorFunctionObject)) { + return false; + } + CalculatorFunctionObject comparedCalculatorFunction = (CalculatorFunctionObject) obj; + return expression.equals(comparedCalculatorFunction.expression) && + arguments.equals(comparedCalculatorFunction.arguments); + } + + @Override + public int hashCode() { + return expression.hashCode() * 31 + arguments.hashCode(); + } +} diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/IEvaluateableFunction.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/IEvaluateableFunction.java new file mode 100644 index 000000000..e43cd54d3 --- /dev/null +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/IEvaluateableFunction.java @@ -0,0 +1,23 @@ +package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.function; + +import ru.mipt.java2016.homework.base.task1.ParsingException; + +import java.util.List; + +/** + * Interface for a Function which can be evaluated on a set of set arguments + * + * @author Artem K. Topilskiy + * @since 16.12.16. + */ +public interface IEvaluateableFunction { + /** + * @return the result of the function evalueated on the given set of arguments + */ + Double evaluate() throws ParsingException; + + /** + * Sets the arguments up for the evaluation of the Function + */ + void setArguments(List arguments) throws ParsingException; +} diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/PredefinedFunction.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/PredefinedFunction.java new file mode 100644 index 000000000..1dd059767 --- /dev/null +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/PredefinedFunction.java @@ -0,0 +1,128 @@ +package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.function; + +import ru.mipt.java2016.homework.base.task1.ParsingException; + +import java.util.*; + +/** + * Calculator functions that are agreed upon to be predefined + * + * @author Artem K. Topilskiy + * @since 16.12.16. + */ +public class PredefinedFunction implements IEvaluateableFunction { + /** + * PUBLIC STATIC DATA + */ + public enum PredefinedFunctionType { + SIN, COS, TG, SQRT, POW, ABS, SIGN, LOG, LOG2, RND, MAX, MIN + } + + public static final Map PREDEFINED_FUNCTION_TYPE_NUM_ARGUMENTS_MAP; + static { + Map predefinedFunctionTypeNumArguments = new HashMap<>(); + predefinedFunctionTypeNumArguments.put(PredefinedFunctionType.SIN, 1); + predefinedFunctionTypeNumArguments.put(PredefinedFunctionType.COS, 1); + predefinedFunctionTypeNumArguments.put(PredefinedFunctionType.TG, 1); + predefinedFunctionTypeNumArguments.put(PredefinedFunctionType.SQRT, 1); + predefinedFunctionTypeNumArguments.put(PredefinedFunctionType.POW, 2); + predefinedFunctionTypeNumArguments.put(PredefinedFunctionType.ABS, 1); + predefinedFunctionTypeNumArguments.put(PredefinedFunctionType.SIGN, 1); + predefinedFunctionTypeNumArguments.put(PredefinedFunctionType.LOG, 1); + predefinedFunctionTypeNumArguments.put(PredefinedFunctionType.LOG2, 1); + predefinedFunctionTypeNumArguments.put(PredefinedFunctionType.RND, 0); + predefinedFunctionTypeNumArguments.put(PredefinedFunctionType.MAX, 2); + predefinedFunctionTypeNumArguments.put(PredefinedFunctionType.MIN, 2); + PREDEFINED_FUNCTION_TYPE_NUM_ARGUMENTS_MAP = + Collections.unmodifiableMap(predefinedFunctionTypeNumArguments); + } + + /** + * PRIVATE STATIC DATA + */ + private static final Random RANDOM_NUMBER_GENERATOR = new Random(1337); + + + /** + * DATA + */ + private final PredefinedFunctionType functionType; + private final List arguments = new ArrayList<>(); + + + /** + * CONSTRUCTOR + */ + public PredefinedFunction(PredefinedFunctionType functionType) { + this.functionType = functionType; + for (int numArgumentsCounter = 0; + numArgumentsCounter < PREDEFINED_FUNCTION_TYPE_NUM_ARGUMENTS_MAP.get(functionType); + ++numArgumentsCounter) { + arguments.add(0.0); + } + } + + + /** + * INTERFACE: IEvaluateableFunction + */ + @Override + public Double evaluate() throws ParsingException { + Double result = 0.0; + + switch (functionType) { + case SIN: + result = Math.sin(arguments.get(0)); + break; + case COS: + result = Math.cos(arguments.get(0)); + break; + case TG: + result = Math.tan(arguments.get(0)); + break; + case SQRT: + result = Math.sqrt(arguments.get(0)); + break; + case POW: + result = Math.pow(arguments.get(0), arguments.get(1)); + break; + case ABS: + result = Math.abs(arguments.get(0)); + break; + case SIGN: + result = Math.signum(arguments.get(0)); + break; + case LOG: + result = Math.log(arguments.get(0)); + break; + case LOG2: + result = Math.log(arguments.get(0)) / Math.log(2); + break; + case RND: + synchronized (RANDOM_NUMBER_GENERATOR) { + result = RANDOM_NUMBER_GENERATOR.nextDouble(); + } + break; + case MAX: + result = Math.max(arguments.get(0), arguments.get(1)); + break; + case MIN: + result = Math.min(arguments.get(0), arguments.get(1)); + break; + default: + break; + } + + return result; + } + + @Override + public void setArguments(List arguments) throws ParsingException { + if (this.arguments.size() != arguments.size()) { + throw new ParsingException("Number of arguments to be set is invalid."); + } + for (int argumentsIndex = 0; argumentsIndex < arguments.size(); ++argumentsIndex) { + this.arguments.set(argumentsIndex, arguments.get(argumentsIndex)); + } + } +} diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/Token.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/Token.java similarity index 99% rename from homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/Token.java rename to homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/Token.java index 65982f64e..92cbe37c2 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/Token.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/Token.java @@ -1,4 +1,4 @@ -package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator; +package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.vanilla; /** * Token class for parsing the expression in TokenCalculator diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/TokenCalculator.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/TokenCalculator.java similarity index 99% rename from homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/TokenCalculator.java rename to homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/TokenCalculator.java index 704aea749..3044edf10 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/TokenCalculator.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/TokenCalculator.java @@ -1,4 +1,4 @@ -package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator; +package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.vanilla; import ru.mipt.java2016.homework.base.task1.Calculator; import ru.mipt.java2016.homework.base.task1.ParsingException; diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorApplication.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorApplication.java index 1beadede2..7713b2521 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorApplication.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorApplication.java @@ -10,7 +10,7 @@ import org.springframework.context.annotation.Configuration; import ru.mipt.java2016.homework.base.task1.Calculator; -import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.TokenCalculator; +import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.vanilla.TokenCalculator; /** * curl http://localhost:9001/eval \ From 815ffec6aaafd602e2de0e0f1cdcc29430f28081 Mon Sep 17 00:00:00 2001 From: syroforce Date: Sat, 17 Dec 2016 06:19:59 +0300 Subject: [PATCH 06/13] IN PROCESS: FINISHED: Functions that underlie RESTCalculator --- .../task4/calculator/rest/RESTCalculator.java | 10 + .../rest/function/CalculateableFunction.java | 355 ++++++++++++++++++ .../rest/function/IEvaluateableFunction.java | 7 + .../rest/function/PredefinedFunction.java | 5 + .../calculator/vanilla/TokenCalculator.java | 3 +- 5 files changed, 379 insertions(+), 1 deletion(-) create mode 100644 homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/RESTCalculator.java diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/RESTCalculator.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/RESTCalculator.java new file mode 100644 index 000000000..937250e51 --- /dev/null +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/RESTCalculator.java @@ -0,0 +1,10 @@ +package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest; + +/** + * REST-ready Calculator, which can work with user-defined functions/arguments + * + * @author Artem K. Topilskiy + * @since 16.12.16. + */ +public class RESTCalculator { +} diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculateableFunction.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculateableFunction.java index 391e1ac8c..89e97175a 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculateableFunction.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculateableFunction.java @@ -1,10 +1,365 @@ package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.function; +import ru.mipt.java2016.homework.base.task1.ParsingException; +import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.vanilla.Token; + +import java.util.*; +import java.util.stream.Collectors; + /** * Calculator functions which can be evaluated in runtime * + * Expression is defined as the following (in extended Backus-Naur Form) + * expression = multiple, { ('+' | '-') multiple } + * multiple = braced_expression, { ('*' | '/') braced_expression } + * braced_expression = '(' expression ')' | combined_expression + * combined_expression = '-' braced_expression | double_number | function_call | variable + * function_call = function_name '(' function_parameters_called ')' + * function_parameters_called = expression { ',' expression } + * + * double_number = '[1-9]' { '[0-9]' } [ '.' ] { '[0-9]' }+ + * variable = '[a-zA-Z]' { '[a-zA-Z0-9]' } + * function_definition = function_name '(' function_parameters ')' + * function_parameters = variable { ',' variable } + * * @author Artem K. Topilskiy * @since 16.12.16. */ public class CalculateableFunction implements IEvaluateableFunction { + /** + * Global Function Data + */ + private Map functions; + private Map variables; + + /** + * Function Data + */ + private final ArrayList tokens = new ArrayList<>(); + private int tokensIndex = 0; + private String functionExpression; + + /** + * Function Parameter Data + */ + private Map parameterNumberToParameterString = new HashMap<>(); + private Map parameters = new HashMap<>(); + + /** + * CONSTRUCTOR + */ + public CalculateableFunction(String functionExpression, List functionParameters, + Map functions, + Map variables) throws ParsingException { + if (functionParameters != null) { + for (int i = 0; i < functionParameters.size(); ++i) { + parameterNumberToParameterString.put(i, functionParameters.get(i)); + } + } + + this.functionExpression = functionExpression; + this.functions = functions; + this.variables = variables; + + parse(functionExpression); + } + + /** + * Private Token-generate Functions + */ + private void parse(String expression) throws ParsingException { + if (expression == null) { + throw new ParsingException("Expression is null."); + } + + for (int expressionIndex = 0; expressionIndex < expression.length(); ++expressionIndex) { + Token currentToken; + + if (Character.isWhitespace(expression.charAt(expressionIndex)) || + Character.isSpaceChar(expression.charAt(expressionIndex))) { + continue; + } + + switch(expression.charAt(expressionIndex)) { + case '+': + currentToken = new Token(Token.TokenType.PLUS); + break; + + case '-': + currentToken = new Token(Token.TokenType.MINUS); + break; + + case '*': + currentToken = new Token(Token.TokenType.MULTIPLY); + break; + + case '/': + currentToken = new Token(Token.TokenType.DIVIDE); + break; + + case '(': + currentToken = new Token(Token.TokenType.LEFT_BRACE); + break; + + case ')': + currentToken = new Token(Token.TokenType.RIGHT_BRACE); + break; + + default: + if (Character.isDigit(expression.charAt(expressionIndex))) { + boolean readDot = false; + int numberStartIndex = expressionIndex; + for (; expressionIndex < expression.length(); ++expressionIndex) { + Character currentCharacter = expression.charAt(expressionIndex); + if (currentCharacter == '.' && !readDot) { + readDot = true; + } else if (!Character.isDigit(currentCharacter)) { + break; + } + } + + Double currentNumber = + Double.parseDouble(expression.substring(numberStartIndex, expressionIndex)); + --expressionIndex; + currentToken = new Token(currentNumber); + + } else if (Character.isAlphabetic(expression.charAt(expressionIndex))) { + int nameStartIndex = expressionIndex; + for (; expressionIndex < expression.length(); ++expressionIndex) { + Character currentCharacter = expression.charAt(expressionIndex); + if (!Character.isAlphabetic(currentCharacter) && + !Character.isDigit(currentCharacter) && + currentCharacter != '_') { + break; + } + } + + --expressionIndex; + currentToken = new Token(functionExpression.substring(nameStartIndex, expressionIndex)); + } else { + throw new ParsingException(String.format("Unexpected symbol at %d", expressionIndex)); + } + break; + } + + tokens.add(currentToken); + } + } + + + /** + * Private Token-read Functions + */ + private void regressTokensIndex() { + --tokensIndex; + } + + private Token progressTokens() { + if (tokensIndex >= tokens.size()) { + return null; + } + + return tokens.get(tokensIndex++); + } + + + /** + * Private Token-calculate Functions + */ + private Double expression() throws ParsingException { + Double result = multiple(); + + for (Token token = progressTokens(); token != null; token = progressTokens()) { + if (token.getType() == Token.TokenType.PLUS) { + result += multiple(); + } else if (token.getType() == Token.TokenType.MINUS) { + result -= multiple(); + } else { + regressTokensIndex(); + break; + } + } + + return result; + } + + private Double multiple() throws ParsingException { + Double result = bracedExpression(); + + for (Token token = progressTokens(); token != null; token = progressTokens()) { + if (token.getType() == Token.TokenType.MULTIPLY) { + result *= bracedExpression(); + } else if (token.getType() == Token.TokenType.DIVIDE) { + result /= bracedExpression(); + } else { + regressTokensIndex(); + break; + } + } + + return result; + } + + private Double bracedExpression() throws ParsingException { + Double result; + + Token token = progressTokens(); + if (token == null) { + throw new ParsingException("Invalid amount of numbers."); + } + + if (token.getType() == Token.TokenType.LEFT_BRACE) { + result = expression(); + token = progressTokens(); + + if (token == null || token.getType() != Token.TokenType.RIGHT_BRACE) { + throw new ParsingException("Wrong number of left/right braces"); + } + } else { + regressTokensIndex(); + result = combinedExpression(); + } + + return result; + } + + private Double combinedExpression() throws ParsingException { + Double result; + + Token token = progressTokens(); + if (token == null) { + throw new ParsingException("Invalid amount of numbers"); + } + + if (token.getType() == Token.TokenType.MINUS) { + result = -bracedExpression(); + } else if (token.getType() == Token.TokenType.NUMBER) { + result = token.getNumber(); + } else if (token.getType() == Token.TokenType.NAME) { + regressTokensIndex(); + result = namedExpression(); + } else { + throw new ParsingException("Invalid order of operations"); + } + + return result; + } + + private Double namedExpression() throws ParsingException { + Double result; + + Token token = progressTokens(); + if (token == null) { + throw new ParsingException("Invalid amount of numbers"); + } + String tokenName = token.getName(); + + Token nextToken = progressTokens(); + if (nextToken != null && nextToken.getType() == Token.TokenType.LEFT_BRACE) { + if (!functions.containsKey(tokenName)) { + throw new ParsingException("Unexpected symbol. Function call impossible."); + } + + result = functionCall(tokenName); + } else { + if (nextToken != null) { + regressTokensIndex(); + } + + if (parameters.containsKey(tokenName)) { + result = parameters.get(tokenName); + } else { + if (!variables.containsKey(tokenName)) { + throw new ParsingException("Unexpected symbol. No such variable."); + } + result = variables.get(tokenName); + } + } + + return result; + } + + private Double functionCall(String functionName) throws ParsingException { + Double result; + + IEvaluateableFunction function = functions.get(functionName); + List functionArguments = new ArrayList<>(); + + for (Token token = progressTokens(); + token.getType() != Token.TokenType.RIGHT_BRACE; + token = progressTokens()) { + + if (token == null) { + throw new ParsingException("Unexpected end of function expression."); + } else { + if (token.getType() == Token.TokenType.COMMA) { + throw new ParsingException("Unexpected comma - no parameter."); + } else if (token.getType() == Token.TokenType.RIGHT_BRACE) { + break; + } else { + regressTokensIndex(); + } + } + + functionArguments.add(expression()); + + token = progressTokens(); + if (token == null || + (token.getType() != Token.TokenType.COMMA && token.getType() != Token.TokenType.RIGHT_BRACE)) { + throw new ParsingException("Unexpected end of function expression."); + } + } + + function.setArguments(functionArguments); + result = function.evaluate(); + return result; + } + + + /** + * OVERRIDE + */ + @Override + public Double evaluate() throws ParsingException { + tokensIndex = 0; + Double result = expression(); + + if (tokensIndex != tokens.size()) { + throw new ParsingException("Invalid number of tokens (too many)."); + } + + return result; + } + + @Override + public void setArguments(List arguments) throws ParsingException { + if (arguments.size() != parameterNumberToParameterString.size()) { + throw new ParsingException("Number of arguments to be set is invalid."); + } + for (int argumentsIndex = 0; argumentsIndex < arguments.size(); ++argumentsIndex) { + parameters.put(parameterNumberToParameterString.get(argumentsIndex), arguments.get(argumentsIndex)); + } + } + + /** + * GETTERS + */ + public String getFunctionExpression() { + return functionExpression; + } + + public List getParameterList() { + List result = new ArrayList<>(); + + List> entries = + parameterNumberToParameterString.entrySet() + .stream() + .sorted(Comparator.comparingInt(Map.Entry::getKey)) + .collect(Collectors.toList()); + + for (Map.Entry entry : entries) { + result.add(entry.getValue()); + } + return result; + } } diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/IEvaluateableFunction.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/IEvaluateableFunction.java index e43cd54d3..a97cd8bcc 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/IEvaluateableFunction.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/IEvaluateableFunction.java @@ -20,4 +20,11 @@ public interface IEvaluateableFunction { * Sets the arguments up for the evaluation of the Function */ void setArguments(List arguments) throws ParsingException; + + /** + * @return whether the current function is predefined + */ + default boolean isPredefined() { + return false; + } } diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/PredefinedFunction.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/PredefinedFunction.java index 1dd059767..ce74f460e 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/PredefinedFunction.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/PredefinedFunction.java @@ -125,4 +125,9 @@ public void setArguments(List arguments) throws ParsingException { this.arguments.set(argumentsIndex, arguments.get(argumentsIndex)); } } + + @Override + public boolean isPredefined() { + return true; + } } diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/TokenCalculator.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/TokenCalculator.java index 3044edf10..62dcc7978 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/TokenCalculator.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/TokenCalculator.java @@ -7,7 +7,8 @@ /** * Calculator that evaluates the expression by the way of tokens - * Expression is defined as the following (in extended Backus-Naur Form \) + * + * Expression is defined as the following (in extended Backus-Naur Form) * expression = multiple, { ('+' | '-') multiple } * multiple = braced_expression, { ('*' | '/') braced_expression } * braced_expression = '(' expression ')' | number_expression From 4913c82c9c5a06540c5744e01ae1f6509e932d2b Mon Sep 17 00:00:00 2001 From: syroforce Date: Sat, 17 Dec 2016 06:47:16 +0300 Subject: [PATCH 07/13] IN PROCESS: FINISHED: RESTCalculator --- .../rest/IFunctionalCalculator.java | 3 +- .../task4/calculator/rest/RESTCalculator.java | 168 +++++++++++++++++- 2 files changed, 169 insertions(+), 2 deletions(-) diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/IFunctionalCalculator.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/IFunctionalCalculator.java index b078acca3..382a0a9ce 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/IFunctionalCalculator.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/IFunctionalCalculator.java @@ -48,7 +48,8 @@ public interface IFunctionalCalculator { /** * Make the alias of functionAlias reflect to CalculatorFunction(expression, arguments) */ - boolean putFunction(String functionAlias, String expression, List arguments) throws ParsingException; + boolean putFunction(String functionAlias, String expression, List arguments) + throws ParsingException; /** * Delete the alias of functionAlias and its held function diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/RESTCalculator.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/RESTCalculator.java index 937250e51..6b4dd6fdc 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/RESTCalculator.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/RESTCalculator.java @@ -1,10 +1,176 @@ package ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest; +import ru.mipt.java2016.homework.base.task1.ParsingException; +import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.function.CalculateableFunction; +import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.function.CalculatorFunctionObject; +import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.function.IEvaluateableFunction; +import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.function.PredefinedFunction; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.stream.Collectors; + /** * REST-ready Calculator, which can work with user-defined functions/arguments * * @author Artem K. Topilskiy * @since 16.12.16. */ -public class RESTCalculator { +public class RESTCalculator implements IFunctionalCalculator { + /** + * Stored Function and Variable Data + */ + private Map functions = new ConcurrentHashMap<>(); + private Map variables = new ConcurrentHashMap<>(); + + /** + * Initialization functions + */ + private void initializePredefinedFunctions() { + functions.put("sin", new PredefinedFunction(PredefinedFunction.PredefinedFunctionType.SIN)); + functions.put("cos", new PredefinedFunction(PredefinedFunction.PredefinedFunctionType.COS)); + functions.put("tg", new PredefinedFunction(PredefinedFunction.PredefinedFunctionType.TG)); + functions.put("sqrt", new PredefinedFunction(PredefinedFunction.PredefinedFunctionType.SQRT)); + functions.put("pow", new PredefinedFunction(PredefinedFunction.PredefinedFunctionType.POW)); + functions.put("abs", new PredefinedFunction(PredefinedFunction.PredefinedFunctionType.ABS)); + functions.put("sign", new PredefinedFunction(PredefinedFunction.PredefinedFunctionType.SIGN)); + functions.put("log", new PredefinedFunction(PredefinedFunction.PredefinedFunctionType.LOG)); + functions.put("log2", new PredefinedFunction(PredefinedFunction.PredefinedFunctionType.LOG2)); + functions.put("rnd", new PredefinedFunction(PredefinedFunction.PredefinedFunctionType.RND)); + functions.put("max", new PredefinedFunction(PredefinedFunction.PredefinedFunctionType.MAX)); + functions.put("min", new PredefinedFunction(PredefinedFunction.PredefinedFunctionType.MIN)); + } + + /** + * CONSTRUCTOR + */ + public RESTCalculator() { + initializePredefinedFunctions(); + } + + /** + * OVERRIDE + */ + + /** + * Methods of interacting with calculator VARIABLES + */ + /** + * @return the double value under the alias of variableAlias + */ + @Override + public Double getVariable(String variableAlias) { + return variables.get(variableAlias); + } + + /** + * Make the alias of variableAlias reflect to the double value + */ + @Override + public boolean putVariable(String variableAlias, Double value) { + if (functions.containsKey(variableAlias)) { + return false; + } else { + variables.put(variableAlias, value); + return true; + } + } + + /** + * Delete the alias of variableAlias and its held value + */ + @Override + public boolean deleteVariable(String variableAlias) { + if (variables.containsKey(variableAlias)) { + variables.remove(variableAlias); + return true; + } + + return false; + } + + /** + * @return the list of aliases of variables in the calculator + */ + @Override + public List getVariableList() { + return variables.keySet().stream().collect(Collectors.toList()); + } + + + /** + * Methods of interacting with calculator FUNCTIONS + */ + /** + * @return a CalculatorFunction object under the alias of functionAlias + * NOTE: predefined functions cannot be dealiased + */ + @Override + public CalculatorFunctionObject getFunction(String functionAlias) { + if (!functions.containsKey(functionAlias) || functions.get(functionAlias).isPredefined()) { + return null; + } + + CalculateableFunction function = (CalculateableFunction) functions.get(functionAlias); + return new CalculatorFunctionObject(function.getFunctionExpression(), function.getParameterList()); + } + + /** + * Make the alias of functionAlias reflect to CalculatorFunction(expression, arguments) + */ + @Override + public boolean putFunction(String functionAlias, String expression, List arguments) + throws ParsingException { + if (variables.containsKey(functionAlias) || + (functions.containsKey(functionAlias) && functions.get(functionAlias).isPredefined())) { + return false; + } + + try { + functions.put(functionAlias, new CalculateableFunction(expression, arguments, functions, variables)); + } catch (ParsingException e) { + return false; + } + + return true; + } + + /** + * Delete the alias of functionAlias and its held function + */ + @Override + public boolean deleteFunction(String functionAlias) { + if (functions.containsKey(functionAlias)) { + IEvaluateableFunction function = functions.get(functionAlias); + if (function == null || function.isPredefined()) { + return false; + } else { + functions.remove(functionAlias); + return true; + } + } + + return false; + } + + /** + * @return the list of aliases of functions in the calculator + */ + @Override + public List getFunctionList() { + return functions.keySet().stream().collect(Collectors.toList()); + } + + + /** + * Methods of CALCULATION of the value of expression + * (using the kept function and variable sets) + */ + @Override + public Double calculate(String expression) throws ParsingException { + return new CalculateableFunction(expression, new ArrayList<>(), functions, variables).evaluate(); + } } From 126356655405d8f090091519bfc0527ae2958168 Mon Sep 17 00:00:00 2001 From: syroforce Date: Sat, 17 Dec 2016 08:40:25 +0300 Subject: [PATCH 08/13] FINISHED: RESTCalculator --- .../rest/IFunctionalCalculator.java | 3 +- .../task4/calculator/rest/RESTCalculator.java | 3 +- .../rest/function/CalculateableFunction.java | 14 +- .../task4/server/CalculatorApplication.java | 8 +- .../task4/server/CalculatorController.java | 142 +++++++++++++++++- .../server/SecurityServiceConfiguration.java | 3 +- 6 files changed, 151 insertions(+), 22 deletions(-) diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/IFunctionalCalculator.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/IFunctionalCalculator.java index 382a0a9ce..9ce167b2c 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/IFunctionalCalculator.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/IFunctionalCalculator.java @@ -48,8 +48,7 @@ public interface IFunctionalCalculator { /** * Make the alias of functionAlias reflect to CalculatorFunction(expression, arguments) */ - boolean putFunction(String functionAlias, String expression, List arguments) - throws ParsingException; + boolean putFunction(String functionAlias, String expression, List arguments); /** * Delete the alias of functionAlias and its held function diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/RESTCalculator.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/RESTCalculator.java index 6b4dd6fdc..4f1a54cdf 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/RESTCalculator.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/RESTCalculator.java @@ -122,8 +122,7 @@ public CalculatorFunctionObject getFunction(String functionAlias) { * Make the alias of functionAlias reflect to CalculatorFunction(expression, arguments) */ @Override - public boolean putFunction(String functionAlias, String expression, List arguments) - throws ParsingException { + public boolean putFunction(String functionAlias, String expression, List arguments) { if (variables.containsKey(functionAlias) || (functions.containsKey(functionAlias) && functions.get(functionAlias).isPredefined())) { return false; diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculateableFunction.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculateableFunction.java index 89e97175a..e94af70c0 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculateableFunction.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculateableFunction.java @@ -105,6 +105,10 @@ private void parse(String expression) throws ParsingException { currentToken = new Token(Token.TokenType.RIGHT_BRACE); break; + case ',': + currentToken = new Token(Token.TokenType.COMMA); + break; + default: if (Character.isDigit(expression.charAt(expressionIndex))) { boolean readDot = false; @@ -134,8 +138,8 @@ private void parse(String expression) throws ParsingException { } } - --expressionIndex; currentToken = new Token(functionExpression.substring(nameStartIndex, expressionIndex)); + --expressionIndex; } else { throw new ParsingException(String.format("Unexpected symbol at %d", expressionIndex)); } @@ -285,9 +289,9 @@ private Double functionCall(String functionName) throws ParsingException { IEvaluateableFunction function = functions.get(functionName); List functionArguments = new ArrayList<>(); - for (Token token = progressTokens(); - token.getType() != Token.TokenType.RIGHT_BRACE; - token = progressTokens()) { + Token token; + do { + token = progressTokens(); if (token == null) { throw new ParsingException("Unexpected end of function expression."); @@ -308,7 +312,7 @@ private Double functionCall(String functionName) throws ParsingException { (token.getType() != Token.TokenType.COMMA && token.getType() != Token.TokenType.RIGHT_BRACE)) { throw new ParsingException("Unexpected end of function expression."); } - } + } while (token.getType() != Token.TokenType.RIGHT_BRACE); function.setArguments(functionArguments); result = function.evaluate(); diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorApplication.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorApplication.java index 7713b2521..1a3bade0f 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorApplication.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorApplication.java @@ -9,8 +9,8 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; -import ru.mipt.java2016.homework.base.task1.Calculator; -import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.vanilla.TokenCalculator; +import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.IFunctionalCalculator; +import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.RESTCalculator; /** * curl http://localhost:9001/eval \ @@ -25,8 +25,8 @@ public class CalculatorApplication { @Bean - public Calculator calculator() { - return new TokenCalculator(); + public IFunctionalCalculator calculator() { + return new RESTCalculator(); } @Bean diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java index 6c205719d..fb7cf23fb 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java @@ -3,18 +3,26 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import ru.mipt.java2016.homework.base.task1.Calculator; import ru.mipt.java2016.homework.base.task1.ParsingException; +import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.IFunctionalCalculator; +import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.function.CalculatorFunctionObject; + +import java.util.List; @RestController -public class CalculatorController { +public class CalculatorController implements IFunctionalCalculator { private static final Logger LOG = LoggerFactory.getLogger(CalculatorController.class); @Autowired - private Calculator calculator; + private IFunctionalCalculator calculator; + /** + * STANDARD Functions + */ @RequestMapping(path = "/ping", method = RequestMethod.GET, produces = "text/plain") public String echo() { return "OK\n"; @@ -32,11 +40,129 @@ public String main(@RequestParam(required = false) String name) { ""; } + + /** + * REST Functions (OVERRIDE) + */ + + /** + * Methods of interacting with calculator VARIABLES + */ + /** + * @return the double value under the alias of variableAlias + */ + @Override + @RequestMapping(path = "/variable/{variableAlias}", method = RequestMethod.GET, produces = "text/plain") + public Double getVariable(@PathVariable String variableAlias) { + return calculator.getVariable(variableAlias); + } + + /** + * Make the alias of variableAlias reflect to the double value + */ + @Override + public boolean putVariable(String variableAlias, Double value) { + return calculator.putVariable(variableAlias, value); + } + + @RequestMapping(path = "/variable/{variableAlias}", method = RequestMethod.PUT) + public boolean putVariable(@PathVariable String variableAlias, @RequestBody String value) { + Double variable = 0.0; + try { + variable = Double.parseDouble(value); + } catch (NullPointerException | NumberFormatException e) { + return false; + } + return calculator.putVariable(variableAlias, variable); + } + + /** + * Delete the alias of variableAlias and its held value + */ + @Override + @RequestMapping(path = "/variable/{variableAlias}", method = RequestMethod.DELETE) + public boolean deleteVariable(@PathVariable String variableAlias) { + return calculator.deleteVariable(variableAlias); + } + + /** + * @return the list of aliases of variables in the calculator + */ + @Override + @RequestMapping(path = "/variable", method = RequestMethod.GET) + @ResponseBody + public List getVariableList() { + return calculator.getVariableList(); + } + + + /** + * Methods of interacting with calculator FUNCTIONS + */ + /** + * @return a CalculatorFunction object under the alias of functionAlias + * NOTE: predefined functions cannot be dealiased + */ + @Override + @RequestMapping(path = "/function/{functionAlias}", method = RequestMethod.GET) + public CalculatorFunctionObject getFunction(@PathVariable String functionAlias) { + return calculator.getFunction(functionAlias); + } + + /** + * Make the alias of functionAlias reflect to CalculatorFunction(expression, arguments) + */ + @Override + @RequestMapping(path = "/function/{functionAlias}", method = RequestMethod.PUT) + public boolean putFunction(@PathVariable String functionAlias, + @RequestBody String expression, + @RequestParam(value = "args") List arguments) { + return calculator.putFunction(functionAlias, expression, arguments); + } + + /** + * Delete the alias of functionAlias and its held function + */ + @Override + @RequestMapping(path = "/function/{functionAlias}", method = RequestMethod.DELETE) + public boolean deleteFunction(@PathVariable String functionAlias) { + return calculator.deleteFunction(functionAlias); + } + + /** + * @return the list of aliases of functions in the calculator + */ + @Override + @RequestMapping(path = "/function", method = RequestMethod.GET) + @ResponseBody + public List getFunctionList() { + return calculator.getFunctionList(); + } + + + /** + * Methods of CALCULATION of the value of expression + * (using the kept function and variable sets) + */ + @Override + public Double calculate(String expression) throws ParsingException { + return calculator.calculate(expression); + } + @RequestMapping(path = "/eval", method = RequestMethod.POST, consumes = "text/plain", produces = "text/plain") - public String eval(@RequestBody String expression) throws ParsingException { - LOG.debug("Evaluation request: [" + expression + "]"); - double result = calculator.calculate(expression); - LOG.trace("Result: " + result); - return Double.toString(result) + "\n"; + public ResponseEntity eval(@RequestBody String expression) { + ResponseEntity response; + + try { + LOG.debug("Evaluation request: [" + expression + "]"); + Double result = calculator.calculate(expression); + LOG.trace("Result: " + result); + response = new ResponseEntity(result, HttpStatus.OK); + } catch (ParsingException e) { + LOG.trace("Evaluation Failed: " + e.getMessage()); + response = new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR); + } + + return response; } } diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java index e38fde268..37b672082 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java @@ -32,7 +32,8 @@ protected void configure(HttpSecurity http) throws Exception { .csrf().disable() .authorizeRequests() .antMatchers("/eval/**").authenticated() - .antMatchers("/").authenticated() + .antMatchers("/variable/**").authenticated() + .antMatchers("/function/**").authenticated() .anyRequest().permitAll(); } From f4c97e8b1b4b49289f5133252b5d96016f80356e Mon Sep 17 00:00:00 2001 From: syroforce Date: Sat, 17 Dec 2016 08:47:23 +0300 Subject: [PATCH 09/13] CODESTYLE --- .../g595/topilskiy/task4/calculator/rest/RESTCalculator.java | 1 - .../task4/calculator/rest/function/CalculateableFunction.java | 2 +- .../task4/calculator/rest/function/PredefinedFunction.java | 1 + .../homework/g595/topilskiy/task4/calculator/vanilla/Token.java | 2 +- .../topilskiy/task4/calculator/vanilla/TokenCalculator.java | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/RESTCalculator.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/RESTCalculator.java index 4f1a54cdf..ba5b47d48 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/RESTCalculator.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/RESTCalculator.java @@ -10,7 +10,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; import java.util.stream.Collectors; /** diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculateableFunction.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculateableFunction.java index e94af70c0..34f85c620 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculateableFunction.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculateableFunction.java @@ -80,7 +80,7 @@ private void parse(String expression) throws ParsingException { continue; } - switch(expression.charAt(expressionIndex)) { + switch (expression.charAt(expressionIndex)) { case '+': currentToken = new Token(Token.TokenType.PLUS); break; diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/PredefinedFunction.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/PredefinedFunction.java index ce74f460e..f84d2cf61 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/PredefinedFunction.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/PredefinedFunction.java @@ -19,6 +19,7 @@ public enum PredefinedFunctionType { } public static final Map PREDEFINED_FUNCTION_TYPE_NUM_ARGUMENTS_MAP; + static { Map predefinedFunctionTypeNumArguments = new HashMap<>(); predefinedFunctionTypeNumArguments.put(PredefinedFunctionType.SIN, 1); diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/Token.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/Token.java index 92cbe37c2..4193e6ae1 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/Token.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/Token.java @@ -10,7 +10,7 @@ public class Token { /** * Enum to describe the type of Data in Token */ - static public enum TokenType { + public enum TokenType { PLUS, MINUS, MULTIPLY, DIVIDE, NUMBER, NAME, LEFT_BRACE, RIGHT_BRACE, COMMA, diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/TokenCalculator.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/TokenCalculator.java index 62dcc7978..b373cd196 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/TokenCalculator.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/vanilla/TokenCalculator.java @@ -43,7 +43,7 @@ private void parse(String expression) throws ParsingException { continue; } - switch(expression.charAt(expressionIndex)) { + switch (expression.charAt(expressionIndex)) { case '+': currentTokenType = Token.TokenType.PLUS; break; From 5671e8e1376293fe10fac1c22a71348edd20db9d Mon Sep 17 00:00:00 2001 From: syroforce Date: Sat, 17 Dec 2016 11:02:04 +0300 Subject: [PATCH 10/13] REVIEW READY (RESTCalculator) FIXED: Logging and server replies --- .../function/CalculatorFunctionObject.java | 7 +- .../task4/server/CalculatorController.java | 127 ++++++++++++++---- .../server/SecurityServiceConfiguration.java | 6 +- .../g595/topilskiy/task4/CURL_COMMANDS.txt | 0 4 files changed, 105 insertions(+), 35 deletions(-) create mode 100644 homework-g595-topilskiy/src/test/java/ru/mipt/java2016/homework/g595/topilskiy/task4/CURL_COMMANDS.txt diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculatorFunctionObject.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculatorFunctionObject.java index 1ec8e94f5..44b2c2c81 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculatorFunctionObject.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/calculator/rest/function/CalculatorFunctionObject.java @@ -56,11 +56,8 @@ public void setArguments(List arguments) { */ @Override public String toString() { - /* NOTE FOR THYSELF: - Array -> stream -(reduce)-> joined strings - -(format)-> concatenate expression+arguments - */ - return String.format(expression, arguments.stream().reduce("", String::join)); + return "Expression: " + expression + '\n' + + "Arguments: " + String.join(", ", arguments); } @Override diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java index fb7cf23fb..e3696c6d0 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java @@ -3,8 +3,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import ru.mipt.java2016.homework.base.task1.ParsingException; @@ -52,11 +50,23 @@ public String main(@RequestParam(required = false) String name) { * @return the double value under the alias of variableAlias */ @Override - @RequestMapping(path = "/variable/{variableAlias}", method = RequestMethod.GET, produces = "text/plain") - public Double getVariable(@PathVariable String variableAlias) { + public Double getVariable(String variableAlias) { return calculator.getVariable(variableAlias); } + @RequestMapping(path = "/variable/{variableAlias}", method = RequestMethod.GET, produces = "text/plain") + public String getVariableServer(@PathVariable String variableAlias) { + LOG.debug("Attempting to get variable: " + variableAlias); + Double result = calculator.getVariable(variableAlias); + if (result == null) { + LOG.debug("No such variable: " + variableAlias); + return "No such variable: " + variableAlias; + } else { + LOG.debug("Variable: " + variableAlias + " = " + result); + return "Variable: " + variableAlias + " = " + result; + } + } + /** * Make the alias of variableAlias reflect to the double value */ @@ -66,35 +76,61 @@ public boolean putVariable(String variableAlias, Double value) { } @RequestMapping(path = "/variable/{variableAlias}", method = RequestMethod.PUT) - public boolean putVariable(@PathVariable String variableAlias, @RequestBody String value) { + public String putVariableServer(@PathVariable String variableAlias, @RequestBody String value) { + LOG.debug("Attempting to put variable: " + variableAlias + " = " + value); + Double variable = 0.0; try { variable = Double.parseDouble(value); } catch (NullPointerException | NumberFormatException e) { - return false; + LOG.debug("Failed to decode into double the value given: " + value); + return "Failed to decode into double the value given: " + value; + } + + if (calculator.putVariable(variableAlias, variable)) { + LOG.debug("Variable put successfully: " + variableAlias); + return "Variable put successfully: " + variableAlias; + } else { + LOG.debug("Failed to put successfully: " + variableAlias); + return "Failed to put successfully: " + variableAlias; } - return calculator.putVariable(variableAlias, variable); } /** * Delete the alias of variableAlias and its held value */ @Override - @RequestMapping(path = "/variable/{variableAlias}", method = RequestMethod.DELETE) - public boolean deleteVariable(@PathVariable String variableAlias) { + public boolean deleteVariable(String variableAlias) { return calculator.deleteVariable(variableAlias); } + @RequestMapping(path = "/variable/{variableAlias}", method = RequestMethod.DELETE) + public String deleteVariableServer(@PathVariable String variableAlias) { + LOG.debug("Attempting to delete variable: " + variableAlias); + if (calculator.deleteVariable(variableAlias)) { + LOG.debug("Variable deleted successfully: " + variableAlias); + return "Variable deleted successfully: " + variableAlias; + } else { + LOG.debug("Failed to delete successfully: " + variableAlias); + return "Failed to delete successfully: " + variableAlias; + } + } + /** * @return the list of aliases of variables in the calculator */ @Override - @RequestMapping(path = "/variable", method = RequestMethod.GET) - @ResponseBody public List getVariableList() { return calculator.getVariableList(); } + @RequestMapping(path = "/variable", method = RequestMethod.GET) + public String getVariableServer() { + String result = String.join(", ", calculator.getVariableList()); + LOG.trace("Output VariableList: " + result); + return "Currently defined variables:\n" + result + '\n'; + } + /** * Methods of interacting with calculator FUNCTIONS @@ -104,41 +140,82 @@ public List getVariableList() { * NOTE: predefined functions cannot be dealiased */ @Override - @RequestMapping(path = "/function/{functionAlias}", method = RequestMethod.GET) public CalculatorFunctionObject getFunction(@PathVariable String functionAlias) { return calculator.getFunction(functionAlias); } + @RequestMapping(path = "/function/{functionAlias}", method = RequestMethod.GET) + public String getFunctionServer(@PathVariable String functionAlias) { + LOG.debug("Attempting to get function: " + functionAlias); + CalculatorFunctionObject function = calculator.getFunction(functionAlias); + if (function == null) { + LOG.debug("No such function: " + functionAlias); + return "No such function: " + functionAlias; + } else { + LOG.debug("Function: " + functionAlias + "\n" + function.toString()); + return "Function: " + functionAlias + "\n" + function.toString(); + } + } + /** * Make the alias of functionAlias reflect to CalculatorFunction(expression, arguments) */ @Override - @RequestMapping(path = "/function/{functionAlias}", method = RequestMethod.PUT) - public boolean putFunction(@PathVariable String functionAlias, - @RequestBody String expression, - @RequestParam(value = "args") List arguments) { + public boolean putFunction(String functionAlias, String expression, List arguments) { return calculator.putFunction(functionAlias, expression, arguments); } + @RequestMapping(path = "/function/{functionAlias}", method = RequestMethod.PUT) + public String putFunctionServer(@PathVariable String functionAlias, + @RequestBody String expression, + @RequestParam(value = "args") List arguments) { + LOG.debug("Attempting to put function: " + functionAlias); + if (calculator.putFunction(functionAlias, expression, arguments)) { + LOG.debug("Function put successfully: " + functionAlias); + return "Function put successfully: " + functionAlias + '\n' + + calculator.getFunction(functionAlias).toString(); + } else { + LOG.debug("Failed to put successfully: " + functionAlias); + return "Failed to put successfully: " + functionAlias; + } + } + /** * Delete the alias of functionAlias and its held function */ @Override - @RequestMapping(path = "/function/{functionAlias}", method = RequestMethod.DELETE) - public boolean deleteFunction(@PathVariable String functionAlias) { + public boolean deleteFunction(String functionAlias) { return calculator.deleteFunction(functionAlias); } + @RequestMapping(path = "/function/{functionAlias}", method = RequestMethod.DELETE) + public String deleteFunctionServer(@PathVariable String functionAlias) { + LOG.debug("Attempting to delete function: " + functionAlias); + if (calculator.deleteFunction(functionAlias)) { + LOG.debug("Function deleted successfully: " + functionAlias); + return "Function deleted successfully: " + functionAlias; + } else { + LOG.debug("Failed to delete successfully: " + functionAlias); + return "Failed to delete successfully: " + functionAlias; + } + } + /** * @return the list of aliases of functions in the calculator */ @Override - @RequestMapping(path = "/function", method = RequestMethod.GET) - @ResponseBody public List getFunctionList() { return calculator.getFunctionList(); } + @RequestMapping(path = "/function", method = RequestMethod.GET) + @ResponseBody + public String getFunctionListServer() { + String result = String.join(", ", calculator.getFunctionList()); + LOG.trace("Output FunctionList: " + result); + return "Currently defined functions:\n" + result + '\n'; + } + /** * Methods of CALCULATION of the value of expression @@ -150,19 +227,15 @@ public Double calculate(String expression) throws ParsingException { } @RequestMapping(path = "/eval", method = RequestMethod.POST, consumes = "text/plain", produces = "text/plain") - public ResponseEntity eval(@RequestBody String expression) { - ResponseEntity response; - + public String eval(@RequestBody String expression) { try { LOG.debug("Evaluation request: [" + expression + "]"); Double result = calculator.calculate(expression); LOG.trace("Result: " + result); - response = new ResponseEntity(result, HttpStatus.OK); + return "Result: " + result + '\n'; } catch (ParsingException e) { LOG.trace("Evaluation Failed: " + e.getMessage()); - response = new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR); + return "Evaluation Failed: " + e.getMessage() + '\n'; } - - return response; } } diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java index 37b672082..2535e0e06 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java @@ -31,9 +31,9 @@ protected void configure(HttpSecurity http) throws Exception { .logout().disable() .csrf().disable() .authorizeRequests() - .antMatchers("/eval/**").authenticated() - .antMatchers("/variable/**").authenticated() - .antMatchers("/function/**").authenticated() +// .antMatchers("/eval/**").authenticated() +// .antMatchers("/variable/**").authenticated() +// .antMatchers("/function/**").authenticated() .anyRequest().permitAll(); } diff --git a/homework-g595-topilskiy/src/test/java/ru/mipt/java2016/homework/g595/topilskiy/task4/CURL_COMMANDS.txt b/homework-g595-topilskiy/src/test/java/ru/mipt/java2016/homework/g595/topilskiy/task4/CURL_COMMANDS.txt new file mode 100644 index 000000000..e69de29bb From f5d6ca7d57e7a0b4a04deba36826731826a97eae Mon Sep 17 00:00:00 2001 From: syroforce Date: Sat, 17 Dec 2016 13:13:53 +0300 Subject: [PATCH 11/13] REVIEW READY (RESTCalculator AND Authorization) --- .../task4/server/CalculatorController.java | 115 +++++++++++++----- .../topilskiy/task4/server/CalculatorDao.java | 25 +++- .../server/SecurityServiceConfiguration.java | 7 +- .../g595/topilskiy/task4/CURL_COMMANDS.txt | 13 ++ 4 files changed, 126 insertions(+), 34 deletions(-) diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java index e3696c6d0..16dfe5ce7 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java @@ -3,20 +3,33 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +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.g595.topilskiy.task4.calculator.rest.IFunctionalCalculator; +import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.RESTCalculator; import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.function.CalculatorFunctionObject; +import java.util.HashMap; import java.util.List; +import java.util.Map; + +import static ru.mipt.java2016.homework.g595.topilskiy.task4.server.CalculatorDao.ADMIN_USERNAME; @RestController public class CalculatorController implements IFunctionalCalculator { private static final Logger LOG = LoggerFactory.getLogger(CalculatorController.class); @Autowired - private IFunctionalCalculator calculator; + CalculatorDao calculatorDao; + @Autowired + static private Map userCalculators = new HashMap<>(); + + static { + userCalculators.put(ADMIN_USERNAME, new RESTCalculator()); + } + /** * STANDARD Functions @@ -51,13 +64,16 @@ public String main(@RequestParam(required = false) String name) { */ @Override public Double getVariable(String variableAlias) { - return calculator.getVariable(variableAlias); + return userCalculators.get(ADMIN_USERNAME).getVariable(variableAlias); } @RequestMapping(path = "/variable/{variableAlias}", method = RequestMethod.GET, produces = "text/plain") - public String getVariableServer(@PathVariable String variableAlias) { + public String getVariableServer(Authentication authentication, + @PathVariable String variableAlias) { + String requesterUsername = authentication.getName(); + LOG.debug("Attempting to get variable: " + variableAlias); - Double result = calculator.getVariable(variableAlias); + Double result = userCalculators.get(requesterUsername).getVariable(variableAlias); if (result == null) { LOG.debug("No such variable: " + variableAlias); return "No such variable: " + variableAlias; @@ -72,11 +88,14 @@ public String getVariableServer(@PathVariable String variableAlias) { */ @Override public boolean putVariable(String variableAlias, Double value) { - return calculator.putVariable(variableAlias, value); + return userCalculators.get(ADMIN_USERNAME).putVariable(variableAlias, value); } @RequestMapping(path = "/variable/{variableAlias}", method = RequestMethod.PUT) - public String putVariableServer(@PathVariable String variableAlias, @RequestBody String value) { + public String putVariableServer(Authentication authentication, + @PathVariable String variableAlias, + @RequestBody String value) { + String requesterUsername = authentication.getName(); LOG.debug("Attempting to put variable: " + variableAlias + " = " + value); Double variable = 0.0; @@ -87,7 +106,7 @@ public String putVariableServer(@PathVariable String variableAlias, @RequestBody return "Failed to decode into double the value given: " + value; } - if (calculator.putVariable(variableAlias, variable)) { + if (userCalculators.get(requesterUsername).putVariable(variableAlias, variable)) { LOG.debug("Variable put successfully: " + variableAlias); return "Variable put successfully: " + variableAlias; } else { @@ -101,13 +120,15 @@ public String putVariableServer(@PathVariable String variableAlias, @RequestBody */ @Override public boolean deleteVariable(String variableAlias) { - return calculator.deleteVariable(variableAlias); + return userCalculators.get(ADMIN_USERNAME).deleteVariable(variableAlias); } @RequestMapping(path = "/variable/{variableAlias}", method = RequestMethod.DELETE) - public String deleteVariableServer(@PathVariable String variableAlias) { + public String deleteVariableServer(Authentication authentication, @PathVariable String variableAlias) { + String requesterUsername = authentication.getName(); + LOG.debug("Attempting to delete variable: " + variableAlias); - if (calculator.deleteVariable(variableAlias)) { + if (userCalculators.get(requesterUsername).deleteVariable(variableAlias)) { LOG.debug("Variable deleted successfully: " + variableAlias); return "Variable deleted successfully: " + variableAlias; } else { @@ -121,12 +142,13 @@ public String deleteVariableServer(@PathVariable String variableAlias) { */ @Override public List getVariableList() { - return calculator.getVariableList(); + return userCalculators.get(ADMIN_USERNAME).getVariableList(); } @RequestMapping(path = "/variable", method = RequestMethod.GET) - public String getVariableServer() { - String result = String.join(", ", calculator.getVariableList()); + public String getVariableServer(Authentication authentication) { + String requesterUsername = authentication.getName(); + String result = String.join(", ", userCalculators.get(requesterUsername).getVariableList()); LOG.trace("Output VariableList: " + result); return "Currently defined variables:\n" + result + '\n'; } @@ -141,13 +163,15 @@ public String getVariableServer() { */ @Override public CalculatorFunctionObject getFunction(@PathVariable String functionAlias) { - return calculator.getFunction(functionAlias); + return userCalculators.get(ADMIN_USERNAME).getFunction(functionAlias); } @RequestMapping(path = "/function/{functionAlias}", method = RequestMethod.GET) - public String getFunctionServer(@PathVariable String functionAlias) { + public String getFunctionServer(Authentication authentication, + @PathVariable String functionAlias) { + String requesterUsername = authentication.getName(); LOG.debug("Attempting to get function: " + functionAlias); - CalculatorFunctionObject function = calculator.getFunction(functionAlias); + CalculatorFunctionObject function = userCalculators.get(requesterUsername).getFunction(functionAlias); if (function == null) { LOG.debug("No such function: " + functionAlias); return "No such function: " + functionAlias; @@ -162,18 +186,21 @@ public String getFunctionServer(@PathVariable String functionAlias) { */ @Override public boolean putFunction(String functionAlias, String expression, List arguments) { - return calculator.putFunction(functionAlias, expression, arguments); + return userCalculators.get(ADMIN_USERNAME).putFunction(functionAlias, expression, arguments); } @RequestMapping(path = "/function/{functionAlias}", method = RequestMethod.PUT) - public String putFunctionServer(@PathVariable String functionAlias, + public String putFunctionServer(Authentication authentication, + @PathVariable String functionAlias, @RequestBody String expression, @RequestParam(value = "args") List arguments) { + String requesterUsername = authentication.getName(); + LOG.debug("Attempting to put function: " + functionAlias); - if (calculator.putFunction(functionAlias, expression, arguments)) { + if (userCalculators.get(requesterUsername).putFunction(functionAlias, expression, arguments)) { LOG.debug("Function put successfully: " + functionAlias); return "Function put successfully: " + functionAlias + '\n' + - calculator.getFunction(functionAlias).toString(); + userCalculators.get(requesterUsername).getFunction(functionAlias).toString(); } else { LOG.debug("Failed to put successfully: " + functionAlias); return "Failed to put successfully: " + functionAlias; @@ -185,13 +212,15 @@ public String putFunctionServer(@PathVariable String functionAlias, */ @Override public boolean deleteFunction(String functionAlias) { - return calculator.deleteFunction(functionAlias); + return userCalculators.get(ADMIN_USERNAME).deleteFunction(functionAlias); } @RequestMapping(path = "/function/{functionAlias}", method = RequestMethod.DELETE) - public String deleteFunctionServer(@PathVariable String functionAlias) { + public String deleteFunctionServer(Authentication authentication, + @PathVariable String functionAlias) { + String requesterUsername = authentication.getName(); LOG.debug("Attempting to delete function: " + functionAlias); - if (calculator.deleteFunction(functionAlias)) { + if (userCalculators.get(requesterUsername).deleteFunction(functionAlias)) { LOG.debug("Function deleted successfully: " + functionAlias); return "Function deleted successfully: " + functionAlias; } else { @@ -205,13 +234,15 @@ public String deleteFunctionServer(@PathVariable String functionAlias) { */ @Override public List getFunctionList() { - return calculator.getFunctionList(); + return userCalculators.get(ADMIN_USERNAME).getFunctionList(); } @RequestMapping(path = "/function", method = RequestMethod.GET) @ResponseBody - public String getFunctionListServer() { - String result = String.join(", ", calculator.getFunctionList()); + public String getFunctionListServer(Authentication authentication) { + String requesterUsername = authentication.getName(); + + String result = String.join(", ", userCalculators.get(requesterUsername).getFunctionList()); LOG.trace("Output FunctionList: " + result); return "Currently defined functions:\n" + result + '\n'; } @@ -223,14 +254,16 @@ public String getFunctionListServer() { */ @Override public Double calculate(String expression) throws ParsingException { - return calculator.calculate(expression); + return userCalculators.get(ADMIN_USERNAME).calculate(expression); } @RequestMapping(path = "/eval", method = RequestMethod.POST, consumes = "text/plain", produces = "text/plain") - public String eval(@RequestBody String expression) { + public String eval(Authentication authentication, @RequestBody String expression) { + String requesterUsername = authentication.getName(); + try { LOG.debug("Evaluation request: [" + expression + "]"); - Double result = calculator.calculate(expression); + Double result = userCalculators.get(requesterUsername).calculate(expression); LOG.trace("Result: " + result); return "Result: " + result + '\n'; } catch (ParsingException e) { @@ -238,4 +271,28 @@ public String eval(@RequestBody String expression) { return "Evaluation Failed: " + e.getMessage() + '\n'; } } + + + /** + * User interactions + */ + @RequestMapping(path = "/user/add/{username}", method = RequestMethod.PUT) + public String addUser(Authentication authentication, + @PathVariable String username, + @RequestParam String password) { + String requesterUsername = authentication.getName(); + + if (!requesterUsername.equals(requesterUsername)) { + LOG.trace("User " + requesterUsername + " is not an admin."); + return "You are not an admin. Cannot add users."; + } else { + LOG.debug("Attempting to userAdd: " + "[" + username + "," + password + "]"); + if (calculatorDao.addUserDao(username, password, true)) { + userCalculators.put(username, new RESTCalculator()); + return "User " + username + " successfully created."; + } else { + return "User " + username + " already exists."; + } + } + } } diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorDao.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorDao.java index e82210785..018299abc 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorDao.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorDao.java @@ -8,14 +8,20 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; +import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.IFunctionalCalculator; +import ru.mipt.java2016.homework.g595.topilskiy.task4.calculator.rest.RESTCalculator; import javax.annotation.PostConstruct; import javax.sql.DataSource; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.HashMap; @Repository public class CalculatorDao { + public static final String ADMIN_USERNAME = "supersanic"; + public static final String ADMIN_PASSWORD = "gottagofast"; + private static final Logger LOG = LoggerFactory.getLogger(CalculatorDao.class); @Autowired @@ -31,16 +37,31 @@ public void postConstruct() { public void initSchema() { LOG.trace("Initializing schema"); + jdbcTemplate.execute("DROP SCHEMA calculator"); jdbcTemplate.execute("CREATE SCHEMA IF NOT EXISTS calculator"); jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS calculator.users " + "(username VARCHAR PRIMARY KEY, password VARCHAR, enabled BOOLEAN)"); + addUserDao(ADMIN_USERNAME, ADMIN_PASSWORD, true); + } + + public boolean addUserDao(String username, String password, Boolean isEnabled) { try { - loadUser("supersanic"); + loadUser(username); + LOG.debug("User " + username + " already exists."); + return false; } catch (EmptyResultDataAccessException e) { - jdbcTemplate.update("INSERT INTO calculator.users VALUES ('supersanic', 'gottagofast', TRUE)"); + jdbcTemplate.update("INSERT INTO calculator.users VALUES (?, ?, ?)", + new Object[]{username, password, isEnabled}); + LOG.debug("User " + username + " successfully created."); + return true; } } + public HashMap getUserCalculators() { + HashMap userCalculators = new HashMap<>(); + userCalculators.put(ADMIN_USERNAME, new RESTCalculator()); + return userCalculators; + } public CalculatorUser loadUser(String username) throws EmptyResultDataAccessException { LOG.trace("Querying for user " + username); diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java index 2535e0e06..3ffa3cd25 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java @@ -31,9 +31,10 @@ protected void configure(HttpSecurity http) throws Exception { .logout().disable() .csrf().disable() .authorizeRequests() -// .antMatchers("/eval/**").authenticated() -// .antMatchers("/variable/**").authenticated() -// .antMatchers("/function/**").authenticated() + .antMatchers("/user/**").authenticated() + .antMatchers("/eval/**").authenticated() + .antMatchers("/variable/**").authenticated() + .antMatchers("/function/**").authenticated() .anyRequest().permitAll(); } diff --git a/homework-g595-topilskiy/src/test/java/ru/mipt/java2016/homework/g595/topilskiy/task4/CURL_COMMANDS.txt b/homework-g595-topilskiy/src/test/java/ru/mipt/java2016/homework/g595/topilskiy/task4/CURL_COMMANDS.txt index e69de29bb..63be610e2 100644 --- a/homework-g595-topilskiy/src/test/java/ru/mipt/java2016/homework/g595/topilskiy/task4/CURL_COMMANDS.txt +++ b/homework-g595-topilskiy/src/test/java/ru/mipt/java2016/homework/g595/topilskiy/task4/CURL_COMMANDS.txt @@ -0,0 +1,13 @@ +curl http://localhost:9001/eval -X POST -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "supersanic:gottagofast" | base64)" --data-raw "44*3+2" + +curl http://localhost:9001/user/add/krusher99?password=mlgpro -X PUT -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "supersanic:gottagofast" | base64)" + +curl http://localhost:9001/eval -X POST -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "krusher99:mlgpro" | base64)" --data-raw "44*3+2" + +curl http://localhost:9001/variable/mlgrank -X PUT -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "krusher99:mlgpro" | base64)" --data-raw "1" + +curl http://localhost:9001/variable/mlgrank -X PUT -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "supersanic:gottagofast" | base64)" --data-raw "99999" + +curl http://localhost:9001/variable/mlgrank -X GET -H "Authorization: Basic $(echo -n "krusher99:mlgpro" | base64)" + +curl http://localhost:9001/variable/mlgrank -X GET -H "Authorization: Basic $(echo -n "supersanic:gottagofast" | base64)" From 7c38b828c59aedebbeefa6fa8d2b6283eda0f608 Mon Sep 17 00:00:00 2001 From: syroforce Date: Sat, 17 Dec 2016 13:55:01 +0300 Subject: [PATCH 12/13] ACCEPTED: TASK04 --- .../task4/server/CalculatorController.java | 44 +++++++++---------- .../server/SecurityServiceConfiguration.java | 6 +-- .../g595/topilskiy/task4/CURL_COMMANDS.txt | 13 ++++++ 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java index 16dfe5ce7..0d40c613e 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java @@ -22,7 +22,7 @@ public class CalculatorController implements IFunctionalCalculator { private static final Logger LOG = LoggerFactory.getLogger(CalculatorController.class); @Autowired - CalculatorDao calculatorDao; + private CalculatorDao calculatorDao; @Autowired static private Map userCalculators = new HashMap<>(); @@ -76,10 +76,10 @@ public String getVariableServer(Authentication authentication, Double result = userCalculators.get(requesterUsername).getVariable(variableAlias); if (result == null) { LOG.debug("No such variable: " + variableAlias); - return "No such variable: " + variableAlias; + return "No such variable: " + variableAlias + '\n'; } else { LOG.debug("Variable: " + variableAlias + " = " + result); - return "Variable: " + variableAlias + " = " + result; + return "Variable: " + variableAlias + " = " + result + '\n'; } } @@ -100,18 +100,18 @@ public String putVariableServer(Authentication authentication, Double variable = 0.0; try { - variable = Double.parseDouble(value); - } catch (NullPointerException | NumberFormatException e) { - LOG.debug("Failed to decode into double the value given: " + value); - return "Failed to decode into double the value given: " + value; + variable = userCalculators.get(requesterUsername).calculate(value); + } catch (ParsingException e) { + LOG.debug("Failed to decode into double the value given: " + value + '\n' + e.getMessage()); + return "Failed to decode into double the value given: " + value + '\n' + e.getMessage(); } if (userCalculators.get(requesterUsername).putVariable(variableAlias, variable)) { - LOG.debug("Variable put successfully: " + variableAlias); - return "Variable put successfully: " + variableAlias; + LOG.debug("Variable put successfully: " + variableAlias + " = " + variable); + return "Variable put successfully: " + variableAlias + " = " + variable + '\n'; } else { LOG.debug("Failed to put successfully: " + variableAlias); - return "Failed to put successfully: " + variableAlias; + return "Failed to put successfully: " + variableAlias + '\n'; } } @@ -130,10 +130,10 @@ public String deleteVariableServer(Authentication authentication, @PathVariable LOG.debug("Attempting to delete variable: " + variableAlias); if (userCalculators.get(requesterUsername).deleteVariable(variableAlias)) { LOG.debug("Variable deleted successfully: " + variableAlias); - return "Variable deleted successfully: " + variableAlias; + return "Variable deleted successfully: " + variableAlias + '\n'; } else { LOG.debug("Failed to delete successfully: " + variableAlias); - return "Failed to delete successfully: " + variableAlias; + return "Failed to delete successfully: " + variableAlias + '\n'; } } @@ -174,10 +174,10 @@ public String getFunctionServer(Authentication authentication, CalculatorFunctionObject function = userCalculators.get(requesterUsername).getFunction(functionAlias); if (function == null) { LOG.debug("No such function: " + functionAlias); - return "No such function: " + functionAlias; + return "No such function: " + functionAlias + '\n'; } else { - LOG.debug("Function: " + functionAlias + "\n" + function.toString()); - return "Function: " + functionAlias + "\n" + function.toString(); + LOG.debug("Function: " + functionAlias + '\n' + function.toString()); + return "Function: " + functionAlias + '\n' + function.toString() + '\n'; } } @@ -200,10 +200,10 @@ public String putFunctionServer(Authentication authentication, if (userCalculators.get(requesterUsername).putFunction(functionAlias, expression, arguments)) { LOG.debug("Function put successfully: " + functionAlias); return "Function put successfully: " + functionAlias + '\n' + - userCalculators.get(requesterUsername).getFunction(functionAlias).toString(); + userCalculators.get(requesterUsername).getFunction(functionAlias).toString() + '\n'; } else { LOG.debug("Failed to put successfully: " + functionAlias); - return "Failed to put successfully: " + functionAlias; + return "Failed to put successfully: " + functionAlias + '\n'; } } @@ -222,10 +222,10 @@ public String deleteFunctionServer(Authentication authentication, LOG.debug("Attempting to delete function: " + functionAlias); if (userCalculators.get(requesterUsername).deleteFunction(functionAlias)) { LOG.debug("Function deleted successfully: " + functionAlias); - return "Function deleted successfully: " + functionAlias; + return "Function deleted successfully: " + functionAlias + '\n'; } else { LOG.debug("Failed to delete successfully: " + functionAlias); - return "Failed to delete successfully: " + functionAlias; + return "Failed to delete successfully: " + functionAlias + '\n'; } } @@ -284,14 +284,14 @@ public String addUser(Authentication authentication, if (!requesterUsername.equals(requesterUsername)) { LOG.trace("User " + requesterUsername + " is not an admin."); - return "You are not an admin. Cannot add users."; + return "You are not an admin. Cannot add users." + '\n'; } else { LOG.debug("Attempting to userAdd: " + "[" + username + "," + password + "]"); if (calculatorDao.addUserDao(username, password, true)) { userCalculators.put(username, new RESTCalculator()); - return "User " + username + " successfully created."; + return "User " + username + " successfully created." + '\n'; } else { - return "User " + username + " already exists."; + return "User " + username + " already exists." + '\n'; } } } diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java index 3ffa3cd25..254b965a0 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/SecurityServiceConfiguration.java @@ -31,11 +31,7 @@ protected void configure(HttpSecurity http) throws Exception { .logout().disable() .csrf().disable() .authorizeRequests() - .antMatchers("/user/**").authenticated() - .antMatchers("/eval/**").authenticated() - .antMatchers("/variable/**").authenticated() - .antMatchers("/function/**").authenticated() - .anyRequest().permitAll(); + .anyRequest().authenticated(); } @Autowired diff --git a/homework-g595-topilskiy/src/test/java/ru/mipt/java2016/homework/g595/topilskiy/task4/CURL_COMMANDS.txt b/homework-g595-topilskiy/src/test/java/ru/mipt/java2016/homework/g595/topilskiy/task4/CURL_COMMANDS.txt index 63be610e2..f31b8acf9 100644 --- a/homework-g595-topilskiy/src/test/java/ru/mipt/java2016/homework/g595/topilskiy/task4/CURL_COMMANDS.txt +++ b/homework-g595-topilskiy/src/test/java/ru/mipt/java2016/homework/g595/topilskiy/task4/CURL_COMMANDS.txt @@ -11,3 +11,16 @@ curl http://localhost:9001/variable/mlgrank -X PUT -H "Content-Type: text/plain" curl http://localhost:9001/variable/mlgrank -X GET -H "Authorization: Basic $(echo -n "krusher99:mlgpro" | base64)" curl http://localhost:9001/variable/mlgrank -X GET -H "Authorization: Basic $(echo -n "supersanic:gottagofast" | base64)" + +curl http://localhost:9001/variable/ml -X PUT -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "supersanic:gottagofast" | base64)" --data-raw "max(1.010, 1.009)" + +curl http://localhost:9001/function/myfunc?args=x&args=y -X PUT -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "krusher99:mlgpro" | base64)" --data-raw "pow(2, x)" + +curl http://localhost:9001/function/myfunc1?args=x,y -X PUT -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "krusher99:mlgpro" | base64)" --data-raw "log2(x) + y" + +curl http://localhost:9001/eval -X POST -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "krusher99:mlgpro" | base64)" --data-raw "15 + myfunc1(64, 4)" + +curl http://localhost:9001/eval -X POST -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "supersanic:gottagofast" | base64)" --data-raw "15 + myfunc1(64, 4)" + +curl http://localhost:9001/function/myfunc1 -X GET -H "Content-Type: text/plain" -H "Authorization: Basic $(echo -n "supersanic:gottagofast" | base64)" + From 5d380ccb10dda6b9b4d093514ab4aaf9bf2d6f70 Mon Sep 17 00:00:00 2001 From: syroforce Date: Sat, 17 Dec 2016 13:58:40 +0300 Subject: [PATCH 13/13] CHECKSTYLE --- .../g595/topilskiy/task4/server/CalculatorController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java index 0d40c613e..514b2ec65 100644 --- a/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java +++ b/homework-g595-topilskiy/src/main/java/ru/mipt/java2016/homework/g595/topilskiy/task4/server/CalculatorController.java @@ -24,7 +24,7 @@ public class CalculatorController implements IFunctionalCalculator { @Autowired private CalculatorDao calculatorDao; @Autowired - static private Map userCalculators = new HashMap<>(); + private static Map userCalculators = new HashMap<>(); static { userCalculators.put(ADMIN_USERNAME, new RESTCalculator());