diff --git a/fruit,quantity b/fruit,quantity new file mode 100644 index 0000000000..972cd12f9b --- /dev/null +++ b/fruit,quantity @@ -0,0 +1 @@ +src/main/java/resources/finalReport.csv \ No newline at end of file diff --git a/src/main/java/core/basesyntax/HelloWorld.java b/src/main/java/core/basesyntax/HelloWorld.java deleted file mode 100644 index 05f94be41b..0000000000 --- a/src/main/java/core/basesyntax/HelloWorld.java +++ /dev/null @@ -1,9 +0,0 @@ -package core.basesyntax; - -/** - * Feel free to remove this class and create your own. - */ -public class HelloWorld { - // HINT: In the `public static void main(String[] args)` it is better to create instances of your classes, - // and call their methods, but do not write any business logic in the `main` method! -} diff --git a/src/main/java/core/basesyntax/Main.java b/src/main/java/core/basesyntax/Main.java new file mode 100644 index 0000000000..7f102327c6 --- /dev/null +++ b/src/main/java/core/basesyntax/Main.java @@ -0,0 +1,61 @@ +package core.basesyntax; + +import core.basesyntax.dao.FruitStorageDao; +import core.basesyntax.dao.FruitStorageDaoImpl; +import core.basesyntax.model.FruitTransaction; +import core.basesyntax.service.DataConverterService; +import core.basesyntax.service.FileReaderService; +import core.basesyntax.service.FileWriterService; +import core.basesyntax.service.ReportGenerator; +import core.basesyntax.service.ShopService; +import core.basesyntax.service.impl.CsvFormatReportGenerator; +import core.basesyntax.service.impl.DataConverterServiceImpl; +import core.basesyntax.service.impl.FileReaderServiceImpl; +import core.basesyntax.service.impl.FileWriterServiceImpl; +import core.basesyntax.service.impl.ShopServiceImpl; +import core.basesyntax.service.impl.operation.BalanceOperation; +import core.basesyntax.service.impl.operation.OperationHandler; +import core.basesyntax.service.impl.operation.PurchaseOperation; +import core.basesyntax.service.impl.operation.ReturnOperation; +import core.basesyntax.service.impl.operation.SupplyOperation; +import core.basesyntax.strategy.OperationStrategy; +import core.basesyntax.strategy.OperationStrategyImpl; +import java.util.List; +import java.util.Map; + +public class Main { + private static final String INPUT_FILE_PATH = "src/main/java/resources/reportToRead.csv"; + private static final String OUTPUT_FILE_PATH = "src/main/java/resources/finalReport.csv"; + private static final FruitStorageDao FRUIT_STORAGE_DAO = new FruitStorageDaoImpl(); + + private static final Map OPERATION_HANDLERS + = Map.of(FruitTransaction.Operation.BALANCE, new BalanceOperation(FRUIT_STORAGE_DAO), + FruitTransaction.Operation.PURCHASE, new PurchaseOperation(FRUIT_STORAGE_DAO), + FruitTransaction.Operation.RETURN, new ReturnOperation(FRUIT_STORAGE_DAO), + FruitTransaction.Operation.SUPPLY, new SupplyOperation(FRUIT_STORAGE_DAO)); + + public static void main(String[] arg) { + // 1. Read the data from the input CSV file + FileReaderService fileReader = new FileReaderServiceImpl(); + List inputReport = fileReader.read(INPUT_FILE_PATH); + + // 2. Convert the incoming data into FruitTransactions list + DataConverterService dataConverter = new DataConverterServiceImpl(); + List transactions = dataConverter.convertToTransaction(inputReport); + + // 3. Creating OperationStrategy with OperationHandlers + OperationStrategy operationStrategy = new OperationStrategyImpl(OPERATION_HANDLERS); + + // 4. Process the incoming transactions with applicable OperationHandler implementations + ShopService shopService = new ShopServiceImpl(operationStrategy); + shopService.process(transactions); + + // 5.Generate report based on the current Storage state + ReportGenerator reportGenerator = new CsvFormatReportGenerator(FRUIT_STORAGE_DAO); + String resultingReport = reportGenerator.create(); + + // 6. Write the received report into the destination file + FileWriterService fileWriter = new FileWriterServiceImpl(); + fileWriter.writeToFile(OUTPUT_FILE_PATH, resultingReport); + } +} diff --git a/src/main/java/core/basesyntax/dao/FruitStorageDao.java b/src/main/java/core/basesyntax/dao/FruitStorageDao.java new file mode 100644 index 0000000000..dd7b6666db --- /dev/null +++ b/src/main/java/core/basesyntax/dao/FruitStorageDao.java @@ -0,0 +1,12 @@ +package core.basesyntax.dao; + +import java.util.Map; +import java.util.Set; + +public interface FruitStorageDao { + void setQuantity(String fruitName, Integer quantity); + + Integer getQuantity(String fruitName); + + Set> getAll(); +} diff --git a/src/main/java/core/basesyntax/dao/FruitStorageDaoImpl.java b/src/main/java/core/basesyntax/dao/FruitStorageDaoImpl.java new file mode 100644 index 0000000000..996d21aad6 --- /dev/null +++ b/src/main/java/core/basesyntax/dao/FruitStorageDaoImpl.java @@ -0,0 +1,25 @@ +package core.basesyntax.dao; + +import core.basesyntax.dp.Storage; +import java.util.Map; +import java.util.Set; + +public class FruitStorageDaoImpl implements FruitStorageDao { + @Override + public void setQuantity(String fruitName, Integer quantity) { + Storage.storage.put(fruitName, quantity); + } + + @Override + public Integer getQuantity(String fruitName) { + if (!Storage.storage.containsKey(fruitName)) { + throw new RuntimeException("There is no " + fruitName + " in the storage"); + } + return Storage.storage.get(fruitName); + } + + @Override + public Set> getAll() { + return Map.copyOf(Storage.storage).entrySet(); + } +} diff --git a/src/main/java/core/basesyntax/dp/Storage.java b/src/main/java/core/basesyntax/dp/Storage.java new file mode 100644 index 0000000000..d9b6331006 --- /dev/null +++ b/src/main/java/core/basesyntax/dp/Storage.java @@ -0,0 +1,7 @@ +package core.basesyntax.dp; + +import java.util.HashMap; + +public class Storage { + public static final HashMap storage = new HashMap<>(); +} diff --git a/src/main/java/core/basesyntax/model/FruitTransaction.java b/src/main/java/core/basesyntax/model/FruitTransaction.java new file mode 100644 index 0000000000..b66108b890 --- /dev/null +++ b/src/main/java/core/basesyntax/model/FruitTransaction.java @@ -0,0 +1,76 @@ +package core.basesyntax.model; + +import java.util.Arrays; + +public class FruitTransaction { + private Operation operation; + private String fruit; + private int quantity; + + public FruitTransaction(Operation operation, String fruit, int quantity) { + this.operation = operation; + this.fruit = fruit; + this.quantity = quantity; + } + + public enum Operation { + BALANCE("b"), + SUPPLY("s"), + PURCHASE("p"), + RETURN("r"); + + private final String letter; + + Operation(String operation) { + this.letter = operation; + } + + public String getLetter() { + return letter; + } + + public static FruitTransaction.Operation getOperation(String letter) { + return Arrays.stream(FruitTransaction.Operation.values()) + .filter(a -> a.getLetter().equals(letter)) + .findFirst() + .orElseThrow( + () -> new IllegalArgumentException( + "No enum constant for letter: " + letter + ) + ); + } + } + + public Operation getOperation() { + return operation; + } + + public void setOperation(Operation operation) { + this.operation = operation; + } + + public String getFruit() { + return fruit; + } + + public void setFruit(String fruit) { + this.fruit = fruit; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + + @Override + public String toString() { + return "FruitTransaction{" + + "operation=" + operation + + ", fruit='" + fruit + '\'' + + ", quantity=" + quantity + + '}'; + } +} diff --git a/src/main/java/core/basesyntax/service/DataConverterService.java b/src/main/java/core/basesyntax/service/DataConverterService.java new file mode 100644 index 0000000000..31e5664c9d --- /dev/null +++ b/src/main/java/core/basesyntax/service/DataConverterService.java @@ -0,0 +1,8 @@ +package core.basesyntax.service; + +import core.basesyntax.model.FruitTransaction; +import java.util.List; + +public interface DataConverterService { + List convertToTransaction(List data); +} diff --git a/src/main/java/core/basesyntax/service/FileReaderService.java b/src/main/java/core/basesyntax/service/FileReaderService.java new file mode 100644 index 0000000000..04234fd030 --- /dev/null +++ b/src/main/java/core/basesyntax/service/FileReaderService.java @@ -0,0 +1,7 @@ +package core.basesyntax.service; + +import java.util.List; + +public interface FileReaderService { + List read(String pathToFile); +} diff --git a/src/main/java/core/basesyntax/service/FileWriterService.java b/src/main/java/core/basesyntax/service/FileWriterService.java new file mode 100644 index 0000000000..ec208c2aae --- /dev/null +++ b/src/main/java/core/basesyntax/service/FileWriterService.java @@ -0,0 +1,5 @@ +package core.basesyntax.service; + +public interface FileWriterService { + void writeToFile(String pathToFile, String report); +} diff --git a/src/main/java/core/basesyntax/service/ReportGenerator.java b/src/main/java/core/basesyntax/service/ReportGenerator.java new file mode 100644 index 0000000000..e02d95802b --- /dev/null +++ b/src/main/java/core/basesyntax/service/ReportGenerator.java @@ -0,0 +1,5 @@ +package core.basesyntax.service; + +public interface ReportGenerator { + String create(); +} diff --git a/src/main/java/core/basesyntax/service/ShopService.java b/src/main/java/core/basesyntax/service/ShopService.java new file mode 100644 index 0000000000..f964d7a652 --- /dev/null +++ b/src/main/java/core/basesyntax/service/ShopService.java @@ -0,0 +1,8 @@ +package core.basesyntax.service; + +import core.basesyntax.model.FruitTransaction; +import java.util.List; + +public interface ShopService { + void process(List transactions); +} diff --git a/src/main/java/core/basesyntax/service/impl/CsvFormatReportGenerator.java b/src/main/java/core/basesyntax/service/impl/CsvFormatReportGenerator.java new file mode 100644 index 0000000000..a150eb0dbe --- /dev/null +++ b/src/main/java/core/basesyntax/service/impl/CsvFormatReportGenerator.java @@ -0,0 +1,30 @@ +package core.basesyntax.service.impl; + +import core.basesyntax.dao.FruitStorageDao; +import core.basesyntax.service.ReportGenerator; +import java.util.Map; + +public class CsvFormatReportGenerator implements ReportGenerator { + private static final String LINE_SEPARATOR = System.lineSeparator(); + private static final String CSV_SEPARATOR = ","; + private static final String START_MESSAGE = "fruit,quantity"; + private final FruitStorageDao storageDao; + + public CsvFormatReportGenerator(FruitStorageDao storageDao) { + this.storageDao = storageDao; + } + + @Override + public String create() { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(START_MESSAGE); + + storageDao.getAll().stream() + .map(Map.Entry::getKey) + .forEach(e -> stringBuilder.append(LINE_SEPARATOR) + .append(e) + .append(CSV_SEPARATOR) + .append(storageDao.getQuantity(e))); + return stringBuilder.toString(); + } +} diff --git a/src/main/java/core/basesyntax/service/impl/DataConverterServiceImpl.java b/src/main/java/core/basesyntax/service/impl/DataConverterServiceImpl.java new file mode 100644 index 0000000000..ea3dbd2763 --- /dev/null +++ b/src/main/java/core/basesyntax/service/impl/DataConverterServiceImpl.java @@ -0,0 +1,44 @@ +package core.basesyntax.service.impl; + +import core.basesyntax.model.FruitTransaction; +import core.basesyntax.service.DataConverterService; +import java.util.ArrayList; +import java.util.List; + +public class DataConverterServiceImpl implements DataConverterService { + public static final String SEPARATOR = ","; + public static final int ELEMENTS_IN_A_ROW = 3; + + @Override + public List convertToTransaction(List data) { + List transactions = new ArrayList<>(); + + for (int i = 1; i < data.size(); i++) { + String line = data.get(i); + String[] parts = line.split(SEPARATOR); + + if (parts.length != ELEMENTS_IN_A_ROW) { + throw new IllegalArgumentException( + "Invalid data format: " + + line + ". Should be " + + ELEMENTS_IN_A_ROW + " elements in a row." + ); + } + + try { + FruitTransaction.Operation operation = + FruitTransaction.Operation.getOperation(parts[0].trim().toLowerCase()); + + String fruit = parts[1].trim(); + int quantity = Integer.parseInt(parts[2].trim()); + + FruitTransaction transaction = new FruitTransaction(operation, fruit, quantity); + transactions.add(transaction); + } catch (IllegalArgumentException e) { + throw new RuntimeException("Error parsing line: " + line, e); + } + } + + return transactions; + } +} diff --git a/src/main/java/core/basesyntax/service/impl/FileReaderServiceImpl.java b/src/main/java/core/basesyntax/service/impl/FileReaderServiceImpl.java new file mode 100644 index 0000000000..04c2c5e70d --- /dev/null +++ b/src/main/java/core/basesyntax/service/impl/FileReaderServiceImpl.java @@ -0,0 +1,19 @@ +package core.basesyntax.service.impl; + +import core.basesyntax.service.FileReaderService; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +public class FileReaderServiceImpl implements FileReaderService { + + @Override + public List read(String pathToFile) { + try { + return Files.readAllLines(Path.of(pathToFile)); + } catch (IOException e) { + throw new RuntimeException("Can't read from file: " + pathToFile, e); + } + } +} diff --git a/src/main/java/core/basesyntax/service/impl/FileWriterServiceImpl.java b/src/main/java/core/basesyntax/service/impl/FileWriterServiceImpl.java new file mode 100644 index 0000000000..7d71aaab2a --- /dev/null +++ b/src/main/java/core/basesyntax/service/impl/FileWriterServiceImpl.java @@ -0,0 +1,19 @@ +package core.basesyntax.service.impl; + +import core.basesyntax.service.FileWriterService; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; + +public class FileWriterServiceImpl implements FileWriterService { + @Override + public void writeToFile(String pathToFile, String report) { + Path path = Path.of(pathToFile); + try { + Files.writeString(path, report, StandardOpenOption.CREATE); + } catch (IOException e) { + throw new RuntimeException("Can't write file " + path, e); + } + } +} diff --git a/src/main/java/core/basesyntax/service/impl/ShopServiceImpl.java b/src/main/java/core/basesyntax/service/impl/ShopServiceImpl.java new file mode 100644 index 0000000000..55c2275ae0 --- /dev/null +++ b/src/main/java/core/basesyntax/service/impl/ShopServiceImpl.java @@ -0,0 +1,31 @@ +package core.basesyntax.service.impl; + +import core.basesyntax.model.FruitTransaction; +import core.basesyntax.service.ShopService; +import core.basesyntax.service.impl.operation.OperationHandler; +import core.basesyntax.strategy.OperationStrategy; +import java.util.List; + +public class ShopServiceImpl implements ShopService { + private final OperationStrategy operationStrategy; + + public ShopServiceImpl(OperationStrategy operationStrategy) { + this.operationStrategy = operationStrategy; + } + + @Override + public void process(List transactions) { + for (FruitTransaction transaction : transactions) { + OperationHandler handler = + operationStrategy.getOperationHandler(transaction.getOperation()); + + if (handler == null) { + throw new IllegalArgumentException( + "No handler found for operation: " + transaction.getOperation() + ); + } + + handler.doOperation(transaction.getFruit(), transaction.getQuantity()); + } + } +} diff --git a/src/main/java/core/basesyntax/service/impl/operation/BalanceOperation.java b/src/main/java/core/basesyntax/service/impl/operation/BalanceOperation.java new file mode 100644 index 0000000000..65ecaae3fc --- /dev/null +++ b/src/main/java/core/basesyntax/service/impl/operation/BalanceOperation.java @@ -0,0 +1,16 @@ +package core.basesyntax.service.impl.operation; + +import core.basesyntax.dao.FruitStorageDao; + +public class BalanceOperation implements OperationHandler { + private final FruitStorageDao storageDao; + + public BalanceOperation(FruitStorageDao storageDao) { + this.storageDao = storageDao; + } + + @Override + public void doOperation(String fruitName, Integer quantity) { + storageDao.setQuantity(fruitName, quantity); + } +} diff --git a/src/main/java/core/basesyntax/service/impl/operation/OperationHandler.java b/src/main/java/core/basesyntax/service/impl/operation/OperationHandler.java new file mode 100644 index 0000000000..1ec1c70f57 --- /dev/null +++ b/src/main/java/core/basesyntax/service/impl/operation/OperationHandler.java @@ -0,0 +1,5 @@ +package core.basesyntax.service.impl.operation; + +public interface OperationHandler { + void doOperation(String fruitName, Integer quantity); +} diff --git a/src/main/java/core/basesyntax/service/impl/operation/PurchaseOperation.java b/src/main/java/core/basesyntax/service/impl/operation/PurchaseOperation.java new file mode 100644 index 0000000000..3d0f51cdb3 --- /dev/null +++ b/src/main/java/core/basesyntax/service/impl/operation/PurchaseOperation.java @@ -0,0 +1,22 @@ +package core.basesyntax.service.impl.operation; + +import core.basesyntax.dao.FruitStorageDao; + +public class PurchaseOperation implements OperationHandler { + private final FruitStorageDao storageDao; + + public PurchaseOperation(FruitStorageDao storageDao) { + this.storageDao = storageDao; + } + + @Override + public void doOperation(String fruitName, Integer quantity) { + Integer fruitQuantity = storageDao.getQuantity(fruitName); + if (fruitQuantity < quantity) { + throw new RuntimeException("Not enough " + fruitName + " in the storage"); + } + Integer previousQuantity = storageDao.getQuantity(fruitName); + Integer newQuantity = previousQuantity - quantity; + storageDao.setQuantity(fruitName, newQuantity); + } +} diff --git a/src/main/java/core/basesyntax/service/impl/operation/ReturnOperation.java b/src/main/java/core/basesyntax/service/impl/operation/ReturnOperation.java new file mode 100644 index 0000000000..5ca77ba24c --- /dev/null +++ b/src/main/java/core/basesyntax/service/impl/operation/ReturnOperation.java @@ -0,0 +1,23 @@ +package core.basesyntax.service.impl.operation; + +import core.basesyntax.dao.FruitStorageDao; +import java.util.Objects; + +public class ReturnOperation implements OperationHandler { + private final FruitStorageDao storageDao; + + public ReturnOperation(FruitStorageDao storageDao) { + this.storageDao = storageDao; + } + + @Override + public void doOperation(String fruitName, Integer quantity) { + Integer previousQuantity; + Integer currentQuantity = storageDao.getQuantity(fruitName); + + previousQuantity = Objects.requireNonNullElse(currentQuantity, 0); + + Integer newQuantity = previousQuantity + quantity; + storageDao.setQuantity(fruitName, newQuantity); + } +} diff --git a/src/main/java/core/basesyntax/service/impl/operation/SupplyOperation.java b/src/main/java/core/basesyntax/service/impl/operation/SupplyOperation.java new file mode 100644 index 0000000000..d2c1349e05 --- /dev/null +++ b/src/main/java/core/basesyntax/service/impl/operation/SupplyOperation.java @@ -0,0 +1,23 @@ +package core.basesyntax.service.impl.operation; + +import core.basesyntax.dao.FruitStorageDao; +import java.util.Objects; + +public class SupplyOperation implements OperationHandler { + private final FruitStorageDao storageDao; + + public SupplyOperation(FruitStorageDao storageDao) { + this.storageDao = storageDao; + } + + @Override + public void doOperation(String fruitName, Integer quantity) { + Integer previousQuantity; + Integer currentQuantity = storageDao.getQuantity(fruitName); + + previousQuantity = Objects.requireNonNullElse(currentQuantity, 0); + + Integer newQuantity = previousQuantity + quantity; + storageDao.setQuantity(fruitName, newQuantity); + } +} diff --git a/src/main/java/core/basesyntax/strategy/OperationStrategy.java b/src/main/java/core/basesyntax/strategy/OperationStrategy.java new file mode 100644 index 0000000000..1438d77248 --- /dev/null +++ b/src/main/java/core/basesyntax/strategy/OperationStrategy.java @@ -0,0 +1,8 @@ +package core.basesyntax.strategy; + +import core.basesyntax.model.FruitTransaction; +import core.basesyntax.service.impl.operation.OperationHandler; + +public interface OperationStrategy { + OperationHandler getOperationHandler(FruitTransaction.Operation operation); +} diff --git a/src/main/java/core/basesyntax/strategy/OperationStrategyImpl.java b/src/main/java/core/basesyntax/strategy/OperationStrategyImpl.java new file mode 100644 index 0000000000..b94cde1c4b --- /dev/null +++ b/src/main/java/core/basesyntax/strategy/OperationStrategyImpl.java @@ -0,0 +1,25 @@ +package core.basesyntax.strategy; + +import core.basesyntax.model.FruitTransaction; +import core.basesyntax.service.impl.operation.OperationHandler; +import java.util.Map; + +public class OperationStrategyImpl implements OperationStrategy { + private final Map operationOperationsHandlerMap; + + public OperationStrategyImpl(Map operationOperationsHandlerMap) { + this.operationOperationsHandlerMap = operationOperationsHandlerMap; + } + + @Override + public OperationHandler getOperationHandler(FruitTransaction.Operation operation) { + OperationHandler handler = operationOperationsHandlerMap.get(operation); + + if (handler == null) { + throw new IllegalArgumentException("No handler found for operation: " + operation); + } + + return handler; + } +} diff --git a/src/main/java/resources/finalReport.csv b/src/main/java/resources/finalReport.csv new file mode 100644 index 0000000000..df0f0f8d6c --- /dev/null +++ b/src/main/java/resources/finalReport.csv @@ -0,0 +1,3 @@ +fruit,quantity +apple,90 +banana,152 \ No newline at end of file diff --git a/src/main/java/resources/reportToRead.csv b/src/main/java/resources/reportToRead.csv new file mode 100644 index 0000000000..c771f259d3 --- /dev/null +++ b/src/main/java/resources/reportToRead.csv @@ -0,0 +1,9 @@ +type,fruit,quantity +b,banana,20 +b,apple,100 +s,banana,100 +p,banana,13 +r,apple,10 +p,apple,20 +p,banana,5 +s,banana,50 \ No newline at end of file