2017-03-30_23-36-40

****************************************************************
Задание 15
1. В CashMachine создай константу - путь к ресурсам.
public static final String RESOURCE_PATH = "com.javarush.test.level26.lesson15.big01.resources.";
Отрефактори загрузку всех ResourceBundle с учетом RESOURCE_PATH.

2. В классе CashMachine не должно быть инициализации ResourceBundle.
Вынеси из CashMachine сообщение о выходе в ConsoleHelper, назови метод printExitMessage.

3. Это всё! Красоту можешь наводить самостоятельно. Тестов на этот пункт не будет.
Например:
3.1. Исправить выводимые тексты
3.2. Добавить ресурсы для нескольких локалей. Например, еще и для русской.
3.3. Добавить валидацию вводимых номиналов.

Твои достижения:
1. разобрался с паттерном Command
2. подружился с Жадным алгоритмом
3. познакомился с локализацией
4. стал больше знать и уметь
5. увидел, как раскладывать задачу на подзадачи
6. продвинулся на шаг ближе к работе джава программистом
7. решил одно из тестовых заданий, которое дают на собеседовании. Только тсссс, никому об этом не говори 🙂
Если когда-то тебе дадут такое задание, то не копируй это решение, а сделай свое по аналогии.

Поздравляю!


****************************************************************
Задание 14
1. Для каждого нового ресурса замени все строки в соответствующей команде.

2. Для ресурса common_en.properties замени все строки в ConsoleHelper.


****************************************************************
Задание 13
Ты уже разобрался с ResourceBundle - это круто.
Теперь мы сможем прикрутить локализацию, т.е. поддержку нескольких языков

1. В каждую команду добавь поле private ResourceBundle res, которое инициализируй соответствующим ресурсом

2. Для каждого нового ресурса замени все строки в соответствующей команде.


****************************************************************
Задание 12
В задании 11 мы захардкодили номер кредитной карточки с пином, с которыми разрешим работать нашему банкомату.
Но юзеров может быть много. Не будем же мы их всех хардкодить! Если понадобится добавить еще одного пользователя,
то придется передеплоить наше приложение. Есть решение этой проблемы.

Смотри, добавился новый пакет resources, в котором мы будем хранить наши ресурсные файлы.
В этом пакете есть файл verifiedCards.properties, в котором заданы карточки с пинами.

1. В LoginCommand добавь поле private ResourceBundle validCreditCards.
При объявлении инициализируй это поле данными из файла verifiedCards.properties.
Почитай в инете, как это делается для ResourceBundle.

2. Замени хардкоженные данные кредитной карточки и пина на проверку наличия данных в ресурсе verifiedCards.properties.

3. Добавь обработку команды LoginCommand в начало нашего main внутрь блока try-catch


****************************************************************
Задание 11
Поздравляю, ты реализовал WithdrawCommand! Основной функционал завершен. Дальше можно допиливать и наводить красоту.
Реализуем одну плюшку. Можно и без нее, но с ней - красивее.
Это верификация кредитной карты пользователя. Нет, никакого API сторонних либ не будет. Только консольная обработка.

Итак, назовем эту операцию LOGIN и сделаем для нее команду.
1. Добавить в операции LOGIN с ординал=0
2. Запретим пользователю выбирать эту операцию из списка.
В единственном методе для поиска операций запретим доступ по ординал - бросим IllegalArgumentException.
3. Создадим LoginCommand по аналогии с другими командами, в котором захардкодим номер карточки с пином
123456789012 и 1234 соответственно.
4. Реализуйте следующую логику для команды LoginCommand:
4.1. Пока пользователь не введет валидные номер карты и пин - выполнять следующие действия
4.2. Запросить у пользователя 2 числа - номер кредитной карты, состоящий из 12 цифр, и пин - состоящий из 4 цифр
4.3. Вывести юзеру сообщение о невалидных данных, если они такими являются.
4.4. Если данные валидны, то проверить их на соответствие захардкоженным (123456789012 и 1234).
4.5. Если данные в п. 4.4. идентифицированы, то сообщить, что верификация прошла успешно.
4.6. Если данные в п. 4.4. НЕ идентифицированы, то вернуться к п.4.1.


****************************************************************
Задание 10
Сегодня мы займемся командой WithdrawCommand - это самая сложная операция.

1. Реализуйте следующий алгоритм для команды WithdrawCommand:
1.1. Считать код валюты (метод уже есть)
1.2. Получить манипулятор для этой валюты.
1.3. Пока пользователь не введет корректные данные выполнять:
1.3.1. Попросить ввести сумму
1.3.2. Если введены некорректные данные, то сообщить об этом пользователю и вернуться к п. 1.3.
1.3.3. Проверить, достаточно ли средств на счету.
Для этого в манипуляторе создайте метод boolean isAmountAvailable(int expectedAmount), который вернет true, если денег достаточно для выдачи.
Если недостаточно, то вернуться к п. 1.3.
1.3.4. Списать деньги со счета. Для этого в манируляторе создайте метод
Map<Integer, Integer> withdrawAmount(int expectedAmount), который вернет карту Map<номинал, количество>.
Подробно логику этого метода смотрите в п.2.
1.3.5. Вывести пользователю результат из п. 1.3.4. в следующем виде:
<табуляция>номинал - количество
Сортировка - от большего номинала к меньшему.
Вывести сообщение об успешной транзакции.
1.3.6. Перехватить исключение NotEnoughMoneyException, уведомить юзера о нехватке банкнот и вернуться к п. 1.3.

2. Логика основного метода withdrawAmount:
2.1. Внимание!!! Метод withdrawAmount должен возвращать минимальное количество банкнот, которыми набирается запрашиваемая сумма.
Используйте Жадный алгоритм (use google).
Если есть несколько вариантов, то использовать тот, в котором максимальное количество банкнот высшего номинала.
Если для суммы 600 результат - три банкноты: 500 + 50 + 50 = 200 + 200 + 200, то выдать первый вариант.
Пример, надо выдать 600
В манипуляторе есть следующие банкноты:
500 - 2
200 - 3
100 - 1
50 - 12
Результат должен быть таким:
500 - 1
100 - 1
т.е. всего две банкноты (это минимальное количество банкнот) номиналом 500 и 100.

2.2. Мы же не можем одни и те же банкноты выдавать несколько раз, поэтому
если мы нашли вариант выдачи денег (п.2.1. успешен), то вычесть все эти банкноты из карты в манипуляторе.

2.3. метод withdrawAmount должен кидать NotEnoughMoneyException, если купюрами невозможно выдать запрашиваемую сумму.
Пример, надо выдать 600
В манипуляторе есть следующие банкноты:
500 - 2
200 - 2
Результат - данными банкнотами невозможно выдать запрашиваемую сумму. Кинуть исключение.
Не забудьте, что в некоторых случаях картой кидается ConcurrentModificationException.


****************************************************************
Задание 9
Сегодня мы займемся командой ExitCommand.
1. Реализуйте следующую логику в команде ExitCommand:
1.1. Спросить, действительно ли пользователь хочет выйти - варианты <y,n>.
1.2. Если пользователь подтвердит свои намерения, то попрощаться с ним.

Это всё хорошо, но бывают случаи, когда срочно нужно прервать операцию, например, если пользователь ошибся с выбором операции.
Для этого у нас есть InterruptOperationException.
2.Реализуйте следующую логику:
2.1. Если пользователь в любом месте ввел текст 'EXIT' любым регистром, то выбросить InterruptOperationException.
2.2. Найдите единственное место, куда нужно вставить эту логику. Реализуйте функционал в этом единственном методе.

3. Заверните тело метода main в try-catch и обработайте исключение InterruptOperationException.
Попрощайтесь с пользователем в блоке catch используя ConsoleHelper.


****************************************************************
Задание 8
Пора привести в порядок наш main, уж очень там всего много, чего не должно быть.

1. Перенесите логику из main в DepositCommand и InfoCommand
Проверим, что там стало с main? Цикл, в котором спрашиваем операцию у пользователя, а потом вызываем метод у CommandExecutor.
И так до бесконечности... надо бы придумать условие выхода из цикла.
Исправь цикл, чтоб он стал do-while. Условие выхода - операция EXIT.

2. Давайте запустим прогу и пополним счет на EUR 100 2 и USD 20 6, и посмотрим на INFO.
Ничего не понятно, т.к. создались 2 манипулятора: первый для EUR, второй для USD.
Давайте улучшим логику InfoCommand. Надо вывести баланс по каждому манипулятору.

2.1. В классе CurrencyManipulatorFactory создайте статический метод getAllCurrencyManipulators(), который вернет Collection всех манипуляторов.
У вас все манипуляторы хранятся в карте, не так ли? Если нет, то рефакторьте.
2.2. В InfoCommand в цикле выведите [код валюты - общая сумма денег для выбранной валюты]
Запустим прогу и пополним счет на EUR 100 2 и USD 20 6, и посмотрим на INFO.
Все работает правильно?
 EUR - 200
 USD - 120
Отлично!

3. Запустим прогу и сразу первой операцией попросим INFO. Ничего не вывело? Непорядок.
Добавьте в манипулятор метод boolean hasMoney(), который будет показывать, добавлены ли какие-то банкноты или нет.

4. В InfoCommand используйте метод п.3. и выведите фразу "No money available.", если нет денег в банкомате.


****************************************************************
Задание 7
Возвращаемся к паттерну Command.

1. Создадим пакет command, в нем будут все классы, относящиеся к этой логике.
Подумайте над модификатором доступа для всех классов в этом пакете.

2. Создадим интерфейс Command с методом void execute().

3. Для каждой операции создадим класс-команду, удовлетворяющую паттерну Command.
Имена классов DepositCommand, InfoCommand, WithdrawCommand, ExitCommand

4. Создадим public класс CommandExecutor, через который можно будет взаимодействовать со всеми командами.
Создадим ему статическую карту Map<Operation, Command>, которую проинициализируем всеми известными нам операциями и командами.

4.1 Создадим метод public static final void execute(Operation operation), который будет дергать метод execute у нужной команды.
Реализуйте эту логику.
4.2. Расставьте правильно модификаторы доступа учитывая, что единственная точка входа - это метод execute.

Проверяем, чтоб структура соответствовала тестам на сервере.
Логику будем переносить в следующем таске.


****************************************************************
Задание 6
Чтобы отрефакторить код в соответствии с паттерном Command, нужно выделить в коде несколько логических блоков кода.
У нас пока два таких блока: 1) код операции DEPOSIT, 2) код операции INFO.
Они захардкожены в методе main. Нужно от этого избавиться.
Нужно сделать так, чтобы пользователь сам выбирал, какую операцию на данный момент нужно выполнять.

1. В энум Operation добавьте статический метод Operation getAllowableOperationByOrdinal(Integer i)
Должен возвращать элемент энума: для 1 - INFO, 2 - DEPOSIT, 3 - WITHDRAW, 4 - EXIT;
На некорректные данные бросать IllegalArgumentException

2. В классе ConsoleHelper реализуйте логику статического метода Operation askOperation()
Спросить у пользователя операцию.
Если пользователь вводит 1, то выбирается команда INFO, 2 - DEPOSIT, 3 - WITHDRAW, 4 - EXIT;
Используйте метод, описанный в п.1.
Обработай исключение - запроси данные об операции повторно.


****************************************************************
Задание 5
1.В предыдущем таске мы реализовали основную логику операции DEPOSIT.
Но посмотреть результат так и не удалось.
Поэтому создадим в манипуляторе метод int getTotalAmount(), который посчитает общую сумму денег для выбранной валюты.

2. Добавим вызов метода getTotalAmount() в метод main.
Всё работает верно? Тогда движемся дальше.
Видно, что метод getTotalAmount() считает то, что нам необходимо для операции INFO.
Поэтому пришло время небольшого рефакторинга.
!!Читайте паттерн Command.
Однако, перед рефакторингом нужно еще разобраться в одном вопросе. Но об этом не сейчас.


****************************************************************
Задание 4
1. Выберем операцию, с которой мы сможем начать.
Подумаем. В банкомате еще денег нет, поэтому INFO и WITHDRAW протестить не получится.
Начнем с операции DEPOSIT - поместить деньги.
Считаем с консоли код валюты, потом считаем номинал и количество банкнот, а потом добавим их в манипулятор.

2. Чтобы считать код валюты, добавим статический метод String askCurrencyCode() в ConsoleHelper.
Этот метод должен предлагать пользователю ввести код валюты, проверять, что код содержит 3 символа.
Если данные некорректны, то сообщить об этом пользователю и повторить.
Если данные валидны, то перевести код в верхний регистр и вернуть.

3. Чтобы считать номинал и количество банкнот, добавим статический метод String[] getValidTwoDigits(String currencyCode) в ConsoleHelper.
Этот метод должен предлагать пользователю ввести два целых положительных числа.
Первое число - номинал, второе - количество банкнот.
Никаких валидаторов на номинал нет. Т.е. 1200 - это нормальный номинал.
Если данные некорректны, то сообщить об этом пользователю и повторить.
Пример вводимых данных:
200 5

4. В классе CurrencyManipulator создайте метод void addAmount(int denomination, int count),
который добавит введенные номинал и количество банкнот

5. Пора уже увидеть приложение в действии.
В методе main захардкодь логику пункта 1.
Кстати, чтобы не было проблем с тестами на стороне сервера, добавь в метод main первой строчкой Locale.setDefault(Locale.ENGLISH);
Запускаем, дебажим, смотрим.


****************************************************************
Задание 3
1. Создадим класс CurrencyManipulator, который будет хранить всю информацию про выбранную валюту.
String currencyCode - код валюты, например, USD. Состоит из трех букв
Map<Integer, Integer> denominations - это Map<номинал, количество>
Чтобы можно было посмотреть, к какой валюте относится манипулятор, добавим геттер для currencyCode
Очевидно, что манипулятор никак не может функционировать без названия валюты,
поэтому добавим конструктор с этим параметром и проинициализируем currencyCode

2. Валют может быть несколько, поэтому нам понадобится фабрика, которая будет создавать и хранить манипуляторы.
Создадим класс CurrencyManipulatorFactory со статическим методом getManipulatorByCurrencyCode(String currencyCode).
В этом методе будем создавать нужный манипулятор, если он еще не существует, либо возвращать ранее созданный.
Подумайте, где лучше хранить все манипуляторы.
Сделайте так, чтобы невозможно было создавать объекты CurrencyManipulatorFactory класса


****************************************************************
Задание 2
1. Создадим в ConsoleHelper два статических метода:
1.1 writeMessage(String message), который будет писать в консоль наше сообщение
1.2 String readString(), который будет считывать с консоли строку и возвращать ее.
Если возникнет какое-то исключение при работе с консолью, то перехватим его и не будем обрабатывать.
Кстати, создадим только один экземпляр BufferedReader-а

2. Создадим пакет exception, в который поместим два checked исключения:
2.1 InterruptOperationException будем кидать, когда нужно прервать текущую операцию и выйти из приложения
2.2 NotEnoughMoneyException будем кидать, когда не сможем выдать запрашиваемую сумму


****************************************************************
Задание 1

Давай напишем эмулятор работы банкомата.
Операции, которые будем поддерживать, следующие:
поместить деньги, снять деньги, показать состояние банкомата.
Также будем поддерживать мультивалютность.
Купюрами будем оперировать теми, которые поместим в банкомат.
Если для снятия требуемой суммы будет недостаточно банкнот, то сообщим юзеру об этом.
Понятно, что всё должно быть user friendly, поэтому придется наводить рюшечки.

Итак..
1. Создайте класс CashMachine с методом main.
2. Наши операции зададим в энуме Operation: INFO, DEPOSIT, WITHDRAW
Т.к. всё должно быть user friendly, то на выход из приложения надо попрощаться с юзером.
Поэтому добавим еще операцию EXIT
3. Т.к мы будем активно общаться с юзером, то будет много выводимого текста.
Чтобы работу с консолью сосредоточить в одном месте, создадим класс ConsoleHelper
package com.javarush.test.level26.lesson15.big01.command;

import com.javarush.test.level26.lesson15.big01.exception.InterruptOperationException;
import com.javarush.test.level26.lesson15.big01.exception.NotEnoughMoneyException;

/**
 * Created by promoscow on 21.02.17.
 */
interface Command {
    void execute() throws InterruptOperationException;
}

 

package com.javarush.test.level26.lesson15.big01.command;

import com.javarush.test.level26.lesson15.big01.Operation;
import com.javarush.test.level26.lesson15.big01.exception.InterruptOperationException;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by promoscow on 21.02.17.
 */
public final class CommandExecutor {
    private static Map<Operation, Command> commands = new HashMap<>();

    static {
        commands.put(Operation.LOGIN, new LoginCommand());
        commands.put(Operation.DEPOSIT, new DepositCommand());
        commands.put(Operation.EXIT, new ExitCommand());
        commands.put(Operation.INFO, new InfoCommand());
        commands.put(Operation.WITHDRAW, new WithdrawCommand());
    }

    private CommandExecutor() {
    }

    public static final void execute(Operation operation) throws InterruptOperationException {
        if (commands.containsKey(operation)) {
            commands.get(operation).execute();
        }
    }
}

 

package com.javarush.test.level26.lesson15.big01.command;

import com.javarush.test.level26.lesson15.big01.CashMachine;
import com.javarush.test.level26.lesson15.big01.ConsoleHelper;
import com.javarush.test.level26.lesson15.big01.CurrencyManipulator;
import com.javarush.test.level26.lesson15.big01.CurrencyManipulatorFactory;
import com.javarush.test.level26.lesson15.big01.exception.InterruptOperationException;
import com.javarush.test.level26.lesson15.big01.exception.NotEnoughMoneyException;

import java.util.ResourceBundle;

/**
 * Created by promoscow on 21.02.17.
 */
class DepositCommand implements Command {
    private ResourceBundle res = ResourceBundle.getBundle(CashMachine.RESOURCE_PATH + "deposit_en");
    @Override
    public void execute() throws InterruptOperationException {

        ConsoleHelper.writeMessage(res.getString("before"));
        String currencyCode = ConsoleHelper.askCurrencyCode();
        CurrencyManipulator manipulator = CurrencyManipulatorFactory.getManipulatorByCurrencyCode(currencyCode);
        String[] digits = ConsoleHelper.getValidTwoDigits();
        manipulator.addAmount(Integer.parseInt(digits[0]), Integer.parseInt(digits[1]));
        ConsoleHelper.writeMessage(String.format(res.getString("success.format"),
                Integer.parseInt(digits[0]) * Integer.parseInt(digits[1]), currencyCode));
    }
}

 

package com.javarush.test.level26.lesson15.big01.command;

import com.javarush.test.level26.lesson15.big01.CashMachine;
import com.javarush.test.level26.lesson15.big01.ConsoleHelper;
import com.javarush.test.level26.lesson15.big01.exception.InterruptOperationException;
import com.javarush.test.level26.lesson15.big01.exception.NotEnoughMoneyException;

import java.util.ResourceBundle;

/**
 * Created by promoscow on 21.02.17.
 */
class ExitCommand implements Command {
    private ResourceBundle res = ResourceBundle.getBundle(CashMachine.RESOURCE_PATH + "exit_en");
    @Override
    public void execute() throws InterruptOperationException {
        ConsoleHelper.writeMessage(res.getString("exit.question.y.n"));
        String answer = ConsoleHelper.readString().toLowerCase();
        if (answer.equals(res.getString("yes"))) ConsoleHelper.writeMessage(res.getString("thank.message"));
    }
}

 

package com.javarush.test.level26.lesson15.big01.command;

import com.javarush.test.level26.lesson15.big01.CashMachine;
import com.javarush.test.level26.lesson15.big01.ConsoleHelper;
import com.javarush.test.level26.lesson15.big01.CurrencyManipulator;
import com.javarush.test.level26.lesson15.big01.CurrencyManipulatorFactory;

import java.util.Collection;
import java.util.ResourceBundle;

/**
 * Created by promoscow on 21.02.17.
 */
class InfoCommand implements Command {
    private ResourceBundle res = ResourceBundle.getBundle(CashMachine.RESOURCE_PATH + "info_en");
    @Override
    public void execute() {
        ConsoleHelper.writeMessage(res.getString("before"));
        Collection<CurrencyManipulator> collection = CurrencyManipulatorFactory.getAllCurrencyManipulators();
        boolean hasMoney = false;
        for (CurrencyManipulator currencyManipulator : collection) {
            hasMoney = (currencyManipulator.hasMoney() && currencyManipulator.getTotalAmount() != 0 || hasMoney);
            if (currencyManipulator.getTotalAmount() != 0) {
                ConsoleHelper.writeMessage(currencyManipulator.getCurrencyCode() + " - " + currencyManipulator.getTotalAmount());
            }
        }
        if (!hasMoney) ConsoleHelper.writeMessage(res.getString("no.money"));
    }
}

 

package com.javarush.test.level26.lesson15.big01.command;

import com.javarush.test.level26.lesson15.big01.CashMachine;
import com.javarush.test.level26.lesson15.big01.ConsoleHelper;
import com.javarush.test.level26.lesson15.big01.exception.InterruptOperationException;

import java.util.ResourceBundle;

/**
 * Created by promoscow on 30.03.17.
 */
public class LoginCommand implements Command {
    private ResourceBundle validCreditCards = ResourceBundle.getBundle(CashMachine.RESOURCE_PATH + "verifiedCards");
    private ResourceBundle res = ResourceBundle.getBundle(CashMachine.RESOURCE_PATH + "login_en");
    @Override
    public void execute() throws InterruptOperationException {
        ConsoleHelper.writeMessage(res.getString("before"));

        while (true) {
            ConsoleHelper.writeMessage(res.getString("specify.data"));
            String cardNum = ConsoleHelper.readString();
            String enterPin = ConsoleHelper.readString();
            if (cardNum.length() != 12) {
                ConsoleHelper.writeMessage(res.getString("try.again.or.exit"));
                continue;
            }
            try {
                long checkNum = Long.parseLong(cardNum);
            } catch (NumberFormatException e) {
                ConsoleHelper.writeMessage(res.getString("try.again.or.exit"));
                continue;
            }
            if (enterPin.length() != 4) {
                ConsoleHelper.writeMessage(res.getString("try.again.with.details"));
                continue;
            }
            try {
                int checkPin = Integer.parseInt(enterPin);
            } catch (NumberFormatException e) {
                ConsoleHelper.writeMessage(res.getString("try.again.with.details"));
                continue;
            }
            if (validCreditCards.containsKey(cardNum) && validCreditCards.getString(cardNum).equals(enterPin)) {
                ConsoleHelper.writeMessage(String.format(res.getString("success.format"), cardNum));
                break;
            } else ConsoleHelper.writeMessage(String.format(res.getString("not.verified.format"), cardNum));
        }
    }
}

 

package com.javarush.test.level26.lesson15.big01.command;

import com.javarush.test.level26.lesson15.big01.CashMachine;
import com.javarush.test.level26.lesson15.big01.ConsoleHelper;
import com.javarush.test.level26.lesson15.big01.CurrencyManipulator;
import com.javarush.test.level26.lesson15.big01.CurrencyManipulatorFactory;
import com.javarush.test.level26.lesson15.big01.exception.InterruptOperationException;
import com.javarush.test.level26.lesson15.big01.exception.NotEnoughMoneyException;

import java.util.*;

/**
 * Created by promoscow on 21.02.17.
 */
class WithdrawCommand implements Command {
    private ResourceBundle res = ResourceBundle.getBundle(CashMachine.RESOURCE_PATH + "withdraw_en");
    @Override
    public void execute() throws InterruptOperationException {
        ConsoleHelper.writeMessage(res.getString("before"));
        String currencyCode = ConsoleHelper.askCurrencyCode();
        CurrencyManipulator manipulator = CurrencyManipulatorFactory.getManipulatorByCurrencyCode(currencyCode);
        try {
            int amount = 0;
            while (true) {
                ConsoleHelper.writeMessage(res.getString("specify.amount"));
                try {
                    amount = Integer.parseInt(ConsoleHelper.readString());
                    if (!manipulator.isAmountAvailable(amount)) {
                        ConsoleHelper.writeMessage(res.getString("not.enough.money"));
                        continue;
                    }
                    Map<Integer, Integer> map = manipulator.withdrawAmount(amount);
                    ArrayList<Integer> list = new ArrayList<>(map.keySet());
                    Collections.sort(list);
                    Collections.reverse(list);
                    for (int i = 0; i < list.size(); i++) {
                        ConsoleHelper.writeMessage("\t" + list.get(i) + " - " + map.get(list.get(i)));
                    }
                    ConsoleHelper.writeMessage(String.format(res.getString("success.format"), amount, currencyCode));
                    break;

                } catch (NumberFormatException e) {
                    ConsoleHelper.writeMessage(res.getString("specify.not.empty.amount"));
                }
            }
        } catch (NotEnoughMoneyException e) {
            ConsoleHelper.writeMessage(res.getString("exact.amount.not.available"));
        }
    }
}

 

package com.javarush.test.level26.lesson15.big01.exception;

/**
 * Created by promoscow on 20.02.17.
 */
public class InterruptOperationException extends Exception {
    public InterruptOperationException() {
//        ConsoleHelper.writeMessage("ВСЕГО ХОРОШЕГО.");
//        System.exit(0);
    }
}

 

package com.javarush.test.level26.lesson15.big01.exception;

/**
 * Created by promoscow on 20.02.17.
 */
public class NotEnoughMoneyException extends Exception {
    public NotEnoughMoneyException() {

    }
}

 

the.end=Terminated. Thank you for visiting JavaRush cash machine. Good luck.
choose.operation=Please choose an operation desired or type 'EXIT' for exiting
operation.INFO=INFO
operation.DEPOSIT=DEPOSIT
operation.WITHDRAW=WITHDRAW
operation.EXIT=EXIT
invalid.data=Please specify valid data.
choose.currency.code=Please choose a currency code, for example USD
choose.denomination.and.count.format=Please specify integer denomination and integer count. For example '10 3' means 30 %s

 

before=Depositing...
success.format=%d %s was deposited successfully
invalid.data=Please specify valid data.

 

exit.question.y.n=Do you really want to exit? <y,n>
yes=y
thank.message=Thank you for visiting JavaRush cash machine. Good luck.

 

before=Information:
no.money=No money available.

 

before=Logging in...
specify.data=Please specify your credit card number and pin code or type 'EXIT' for exiting.
success.format=Credit card [%s] is verified successfully!
not.verified.format=Credit card [%s] is not verified.
try.again.or.exit=Please try again or type 'EXIT' for urgent exiting
try.again.with.details=Please specify valid credit card number - 12 digits, pin code - 4 digits.

 

123456789012=1234
234567890123=2345
345678901234=3456

 

before=Withdrawing...
success.format=%d %s was withdrawn successfully
specify.amount=Please specify integer amount for withdrawing.
specify.not.empty.amount=Please specify valid positive integer amount for withdrawing.
not.enough.money=Not enough money on your account, please try again
exact.amount.not.available=Exact amount is not available

 

package com.javarush.test.level26.lesson15.big01;

import com.javarush.test.level26.lesson15.big01.command.CommandExecutor;
import com.javarush.test.level26.lesson15.big01.exception.InterruptOperationException;

import java.util.Locale;

/**
 * Created by promoscow on 20.02.17.
 */
public class CashMachine {
    public static final String RESOURCE_PATH = "com.javarush.test.level26.lesson15.big01.resources.";
    public static void main(String[] args) {
        Locale.setDefault(Locale.ENGLISH);

//        CurrencyManipulator manipulator = null;
//        String currencyCode = "RUR";
//        manipulator = CurrencyManipulatorFactory.getManipulatorByCurrencyCode(currencyCode);
//        manipulator.addAmount(5000, 2);
//        manipulator.addAmount(1000, 1);
//        manipulator.addAmount(500, 3);
//        manipulator.addAmount(100, 2);
//        manipulator.addAmount(50, 3);

            Operation operation = null;
        try {
            CommandExecutor.execute(Operation.LOGIN);
            do {
                operation = ConsoleHelper.askOperation();
                CommandExecutor.execute(operation);
            } while (operation != Operation.EXIT);
        } catch (InterruptOperationException e) {
            ConsoleHelper.printExitMessage();
        }
    }
}

 

package com.javarush.test.level26.lesson15.big01;

import com.javarush.test.level26.lesson15.big01.exception.InterruptOperationException;
import com.javarush.test.level26.lesson15.big01.exception.NotEnoughMoneyException;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ResourceBundle;

/**
 * Created by promoscow on 20.02.17.
 */
public class ConsoleHelper {
    private static ResourceBundle res = ResourceBundle.getBundle(CashMachine.RESOURCE_PATH + "common_en");
    private static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

    public static void writeMessage(String message) {
        System.out.println(message);
    }

    public static String readString() throws InterruptOperationException {

        String s = null;
        try {
            s = reader.readLine();
            if (s.toLowerCase().equals("exit")) {
                throw new InterruptOperationException();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return s;
    }

    public static String askCurrencyCode() throws InterruptOperationException {
        writeMessage(res.getString("choose.currency.code"));
        String currencyCode = "";
        while (true) {
            currencyCode = readString();
            if (currencyCode.matches("^[a-zA-Z]{3}$")) break;
            writeMessage(res.getString("invalid.data"));
        }
        return currencyCode.toUpperCase();
    }

    public static String[] getValidTwoDigits() throws InterruptOperationException {
        writeMessage(res.getString("choose.denomination.and.count.format"));
        String enter = "";
        while (true) {
            enter = readString();
            if (enter.matches("\\d+ \\d+")) break;
            writeMessage(res.getString("invalid.data"));
        }
        return enter.split(" ");
    }

    public static Operation askOperation() throws InterruptOperationException {
        Operation operation = null;
        while (true) {
            writeMessage(res.getString("choose.operation"));
            writeMessage(String.format("%s (1) %s (2) %s (3) %s (4)",
                    res.getString("operation.INFO"),
                    res.getString("operation.DEPOSIT"),
                    res.getString("operation.WITHDRAW"),
                    res.getString("operation.EXIT")));
            try {
                operation = Operation.getAllowableOperationByOrdinal(Integer.valueOf(readString()));
                break;
            } catch (Exception e) {
                writeMessage(res.getString("wrong.input"));
            }
        }
        return operation;
    }

    public static void printExitMessage() {
        ConsoleHelper.writeMessage(res.getString("the.end"));
    }
}

 

package com.javarush.test.level26.lesson15.big01;

import com.javarush.test.level26.lesson15.big01.exception.NotEnoughMoneyException;

import java.util.*;

/**
 * Created by promoscow on 20.02.17.
 */
public class CurrencyManipulator {
    public Map<Integer, Integer> denominations = new HashMap<Integer, Integer>();
    private String currencyCode = "";

    public CurrencyManipulator(String currencyCode) {
        this.currencyCode = currencyCode;
    }

    public String getCurrencyCode() {
        return currencyCode;
    }

    public void addAmount(int denomination, int count) {
        if (denominations.containsKey(denomination)) {
            denominations.put(denomination, denominations.get(denomination) + count);
        } else {
            denominations.put(denomination, count);
        }

//        ArrayList<Integer> list = new ArrayList<>(denominations.keySet());
//        Collections.sort(list);
//        Collections.reverse(list);
//        for (Integer aList : list) {
//            ConsoleHelper.writeMessage("\t" + aList + " - " + denominations.get(aList));
//        }
    }

    public int getTotalAmount() {
        int count = 0;
        for (Map.Entry<Integer, Integer> entry : denominations.entrySet()) count += (entry.getKey() * entry.getValue());
        return count;
    }

    public boolean hasMoney() {
        return (!denominations.isEmpty());
    }

    public boolean isAmountAvailable(int expectedAmount) {
        return this.getTotalAmount() >= expectedAmount;
    }

    public Map<Integer, Integer> withdrawAmount(int expectedAmount) throws NotEnoughMoneyException {
        Map<Integer, Integer> map = new HashMap<>();
        ArrayList<Integer> list = new ArrayList<>(denominations.keySet());
        Collections.sort(list);
        Collections.reverse(list);
        for (Integer integer : list) {    //integer - номинал
            int x = expectedAmount / integer;    //сколько купюр данного номинала требуется в идеале
//            System.out.println("expected: denomination - " + integer + ", amount - " + x);
//            System.out.println(denominations.get(integer) + " value");
            if (x > 0) {
                if (denominations.get(integer) >= x) {
                    map.put(integer, x);
                    expectedAmount -= integer * x;
                }
                else {
                    map.put(integer, denominations.get(integer));
                    expectedAmount -= denominations.get(integer) * integer;
                }
            }
            if (expectedAmount == 0) break;
        }
        if (expectedAmount > 0) {
            throw new NotEnoughMoneyException();
        }
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            int m = denominations.get(entry.getKey());
            denominations.remove(entry.getKey());
            denominations.put(entry.getKey(), (m - entry.getValue()));
        }
        return map;
    }
}

 

package com.javarush.test.level26.lesson15.big01;

        import java.util.Collection;
        import java.util.HashMap;
        import java.util.Map;

/**
 * Created by promoscow on 20.02.17.
 */
public final class CurrencyManipulatorFactory {
    private static Map<String, CurrencyManipulator> manipulators = new HashMap<String, CurrencyManipulator>();

    private CurrencyManipulatorFactory() {
    }

    public static CurrencyManipulator getManipulatorByCurrencyCode(String currencyCode) {

        CurrencyManipulator result = null;
        if (manipulators.containsKey(currencyCode))
            result = manipulators.get(currencyCode);
        else {
            result = new CurrencyManipulator(currencyCode);
            manipulators.put(currencyCode, result);
        }
        return result;
    }


    public static Collection<CurrencyManipulator> getAllCurrencyManipulators () {
        return manipulators.values();
    }
}

 

package com.javarush.test.level26.lesson15.big01;

/**
 * Created by promoscow on 20.02.17.
 */
public enum Operation {
    LOGIN,
    INFO,
    DEPOSIT,
    WITHDRAW,
    EXIT;

    public static Operation getAllowableOperationByOrdinal(Integer i) {
        switch (i) {
            case 0:
                throw new IllegalArgumentException();
            case 1:
                return INFO;
            case 2:
                return DEPOSIT;
            case 3:
                return WITHDRAW;
            case 4:
                return EXIT;
        }
        throw new IllegalArgumentException();
    }
}