diff --git a/src/main/java/jp/co/training/Book.java b/src/main/java/jp/co/training/Book.java index ad5515a..ad6e2d6 100644 --- a/src/main/java/jp/co/training/Book.java +++ b/src/main/java/jp/co/training/Book.java @@ -1,5 +1,7 @@ package jp.co.training; +import static jp.co.training.Const.DATE_PATTERN; +import static jp.co.training.Const.DELIMITER; import static jp.co.training.Const.MAX_AUTHOR; import static jp.co.training.Const.MAX_BOOK_NAME; import static jp.co.training.Const.MAX_ISBN; @@ -8,104 +10,121 @@ public class Book { - private final String isbn; - private final String bookName; - private final String author; - private final String publisher; - private final String publicationDate; - private final String price; - - - public Result validate() { - Result result = new Result(); - - //最大最小チェック - BookUtil.checkRange(isbn, 1, MAX_ISBN, "ISBM").ifPresent(msg -> result.addErrMessage(msg)); - BookUtil.checkRange(bookName, 1, MAX_BOOK_NAME, "BOOK_NAME").ifPresent(msg -> result.addErrMessage(msg)); - BookUtil.checkRange(author, 1, MAX_AUTHOR, "AUTHOR").ifPresent(msg -> result.addErrMessage(msg)); - BookUtil.checkRange(publisher, 1, MAX_PUBLISHER, "PUBLISHER").ifPresent(msg -> result.addErrMessage(msg)); - BookUtil.checkRange(price, 1, MAX_PRICE, "PRICE").ifPresent(msg -> result.addErrMessage(msg)); - - - - return result; - } - - public String getIsbn() { - return isbn; - } - - public String getBookName() { - return bookName; - } - - public String getAuthor() { - return author; - } - - public String getPublisher() { - return publisher; - } - - public String getPublicationDate() { - return publicationDate; - } - - public String getPrice() { - return price; - } - - private Book(Builder builder) { - this.isbn = builder.isbn; - this.bookName = builder.bookName; - this.author = builder.author; - this.publisher = builder.publisher; - this.publicationDate = builder.publicationDate; - this.price = builder.price; - } - - public static class Builder { - private String isbn; - private String bookName; - private String author; - private String publisher; - private String publicationDate; - private String price; - - public Builder isbn(String isbn) { - this.isbn = isbn; - return this; - } - - public Builder bookName(String bookName) { - this.bookName = bookName; - return this; - } - - public Builder author(String author) { - this.author = author; - return this; - } - - public Builder publisher(String publisher) { - this.publisher = publisher; - return this; - } - - public Builder publicationDate(String publicationDate) { - this.publicationDate = publicationDate; - return this; - } - - public Builder price(String price) { - this.price = price; - return this; - } - - public Book build() { - return new Book(this); - } - - } - + private final String isbn; + private final String bookName; + private final String author; + private final String publisher; + private final String publicationDate; + private final String price; + + public Result validate() { + Result result = new Result(); + + // isbn + BookUtil.checkLength(isbn, 1, MAX_ISBN).ifPresent(msg -> result.addMessage("ISBM:" + msg)); + + // bookName + BookUtil.checkLength(bookName, 1, MAX_BOOK_NAME).ifPresent(msg -> result.addMessage("BOOK_NAME:" + msg)); + + // author + BookUtil.checkLength(author, 1, MAX_AUTHOR).ifPresent(msg -> result.addMessage("AUTHOR" + msg)); + + // publisher + BookUtil.checkLength(publisher, 1, MAX_PUBLISHER).ifPresent(msg -> result.addMessage("PUBLISHER:" + msg)); + + // price + BookUtil.checkLength(price, 1, MAX_PRICE).ifPresent(msg -> result.addMessage("PRICE:" + msg)); + BookUtil.checkNumber(price).ifPresent(msg -> result.addMessage("PRICE:" + msg)); + + // publicationDate + BookUtil.checkDatePattern(publicationDate, DATE_PATTERN) + .ifPresent(msg -> result.addMessage("PUBLICATION_DATE:" + msg)); + + return result; + } + + public String getIsbn() { + return isbn; + } + + public String getBookName() { + return bookName; + } + + public String getAuthor() { + return author; + } + + public String getPublisher() { + return publisher; + } + + public String getPublicationDate() { + return publicationDate; + } + + public String getPrice() { + return price; + } + + private Book(Builder builder) { + this.isbn = builder.isbn; + this.bookName = builder.bookName; + this.author = builder.author; + this.publisher = builder.publisher; + this.publicationDate = builder.publicationDate; + this.price = builder.price; + } + + public static class Builder { + + private String isbn; + private String bookName; + private String author; + private String publisher; + private String publicationDate; + private String price; + + public Builder isbn(String isbn) { + this.isbn = isbn; + return this; + } + + public Builder bookName(String bookName) { + this.bookName = bookName; + return this; + } + + public Builder author(String author) { + this.author = author; + return this; + } + + public Builder publisher(String publisher) { + this.publisher = publisher; + return this; + } + + public Builder publicationDate(String publicationDate) { + this.publicationDate = publicationDate; + return this; + } + + public Builder price(String price) { + this.price = price; + return this; + } + + public Book build() { + return new Book(this); + } + + } + + @Override + public String toString() { + return String.join(this.isbn, this.bookName, this.author, this.publisher, this.publicationDate, this.price, + DELIMITER); + + } } diff --git a/src/main/java/jp/co/training/BookUtil.java b/src/main/java/jp/co/training/BookUtil.java index 19e52b7..79a9f3b 100644 --- a/src/main/java/jp/co/training/BookUtil.java +++ b/src/main/java/jp/co/training/BookUtil.java @@ -1,29 +1,73 @@ package jp.co.training; +import java.security.SecureRandom; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.Optional; +import java.util.regex.Pattern; public class BookUtil { - private BookUtil() { - } - - /** - * 対象文字列が指定された最小文字数と最大文字数の範囲内であるかをチェックします
- * 範囲内である場合はOptionalの中身はnullとなる
- * 範囲外である場合はOptionalの中身は範囲外であることを示すメッセージとなる
- * - * @param target : - * @param min - * @param max - * @param targetName - * @return - */ - public static Optional checkRange(String target, int min, int max, String targetName) { - if (target.length() < min) { - return Optional.of(targetName + "of size: min is " + min + ". but your input is " + target.length() + "."); - } else if (target.length() > max) { - return Optional.of(targetName + "of size: max is " + max + ". but your input is " + target.length() + "."); - } - return Optional.empty(); - } + private BookUtil() { + } + + /** + * 対象文字列が指定された最小文字数と最大文字数の範囲内であるかをチェックします
+ * 範囲内である場合はOptionalの中身はnullとなる
+ * 範囲外である場合はOptionalの中身は範囲外であることを示すメッセージとなる
+ * + * @param target + * : + * @param min + * @param max + * @return + */ + public static Optional checkLength(String target, int min, int max) { + if (target.length() < min) { + return Optional.of("the length must be " + min + " or more. but actual is " + target.length() + "."); + } else if (target.length() > max) { + return Optional.of("the length must be " + max + " or less. but actual is " + target.length() + "."); + } + return Optional.empty(); + } + + public static Optional checkDatePattern(String target, String pattern) { + DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern); + try { + LocalDate.parse(target, df); + } catch (DateTimeParseException e) { + return Optional.of("Invalid Pattern. valid pattern is " + pattern + "."); + } + return Optional.empty(); + } + + public static Optional checkNumber(String target) { + if (!isNumber(target)) { + return Optional.of("It is not a numerical format."); + } + return Optional.empty(); + } + + /** + * valueが数値文字列の場合、または空文字の場合はtrueを返す + * + * @param value + * @return + */ + private static boolean isNumber(String value) { + return Pattern.compile("^[0-9]*$").matcher(value).matches(); + } + + public static String generateID(int digit) { + SecureRandom random = new SecureRandom(); + byte bytes[] = new byte[digit / 2]; + random.nextBytes(bytes); + StringBuilder sb = new StringBuilder(); + for (byte d : bytes) { + sb.append(String.format("%02X", d)); + } + return sb.toString(); + } + } diff --git a/src/main/java/jp/co/training/Command.java b/src/main/java/jp/co/training/Command.java index 538072e..7549704 100644 --- a/src/main/java/jp/co/training/Command.java +++ b/src/main/java/jp/co/training/Command.java @@ -1,12 +1,32 @@ package jp.co.training; -import java.util.List; +public abstract class Command { -public interface Command { + protected String name; - void setArgments(List argments); + protected Command next; - void execute(); + public Command(String name) { + this.name = name; + } + + public Result execute(String command, String[] argments) { + if (command.equals(name)) { + return executeCommand(command, argments); + } else if (next != null) { + return next.execute(command, argments); + } + // どのコマンドにも合致しなかった場合 + Result result = new Result(); + result.addMessage("Invalid Command."); + return result; + }; + + public Command setNext(Command next) { + this.next = next; + return next; + } + + public abstract Result executeCommand(String command, String[] argments); - Result validate(); } diff --git a/src/main/java/jp/co/training/CommandFactory.java b/src/main/java/jp/co/training/CommandFactory.java deleted file mode 100644 index 3010dc0..0000000 --- a/src/main/java/jp/co/training/CommandFactory.java +++ /dev/null @@ -1,19 +0,0 @@ -package jp.co.training; - -import static jp.co.training.Const.EXIT; -import static jp.co.training.Const.INSERT; - -public final class CommandFactory { - - public static Command createCommand(String command) { - - switch (command) { - case INSERT: - return new InsertCommand(); - case EXIT: - return new ExitCommand(); - default: - return new InvalidCommand(); - } - } -} diff --git a/src/main/java/jp/co/training/Const.java b/src/main/java/jp/co/training/Const.java index a4a3ab8..ef7b623 100644 --- a/src/main/java/jp/co/training/Const.java +++ b/src/main/java/jp/co/training/Const.java @@ -5,39 +5,56 @@ public final class Const { - //外部ファイルによる設定不可能な定数 - public static final String INSERT = "insert"; - public static final String EXIT = "exit"; - public static final int MAX_ISBN = 13; - public static final int MAX_BOOK_NAME = 50; - public static final int MAX_AUTHOR = 30; - public static final int MAX_PUBLISHER = 30; - public static final int MAX_PRICE = 9; + // 外部ファイルによる設定不可能な定数 + public static final String INSERT = "insert"; + public static final String EXIT = "exit"; + public static final int MAX_ISBN = 13; + public static final int MAX_BOOK_NAME = 50; + public static final int MAX_AUTHOR = 30; + public static final int MAX_PUBLISHER = 30; + public static final int MAX_PRICE = 9; + public static final String DATE_PATTERN = "yyyyMMdd"; + public static final String DATE_TIME_PATTERN = "yyyyMMdd hhmmss.SSS"; + public static final int ID_LENGTH = 16; - //外部ファイルによる設定が可能な定数 - public static final String PROMPT; - public static final String DELIMITER; - public static final String SAVE_FILE; + // 外部ファイルによる設定が可能な定数 + public static final String PROMPT; + public static final String END_MESSAGE; + public static final String DELIMITER; + public static final String SAVE_FILE; + public static final String USER_NAME; - //外部ファイルの設定ファイルのキー - private static final String PROMPT_KEY = "prompt"; - private static final String DELIMITER_KEY = "delimiter"; - private static final String SAVE_FILE_KEY = "save.file"; + // 外部ファイルの設定ファイルのキー + private static final String PROMPT_KEY = "prompt"; + private static final String END_MESSAGE_KEY = "end.message"; + private static final String DELIMITER_KEY = "delimiter"; + private static final String SAVE_FILE_KEY = "save.file"; + private static final String USER_NAME_KEY = "user.name"; - static { - ResourceBundle rb = loadResource(); - PROMPT = (rb == null || !rb.containsKey(PROMPT_KEY)) ? "books>" : rb.getString(PROMPT_KEY); - DELIMITER = (rb == null || !rb.containsKey(DELIMITER_KEY)) ? "," : rb.getString(DELIMITER_KEY); - SAVE_FILE = (rb == null || !rb.containsKey(SAVE_FILE_KEY)) ? "savefile" : rb.getString(SAVE_FILE_KEY); - } + static { + ResourceBundle rb = loadResource(); + if (rb == null) { + System.out.println("cannnot find configfile."); + System.exit(1); + } + if (!rb.containsKey(USER_NAME_KEY)) { + System.out.println("you must define registrant in configfile."); + System.exit(1); + } + USER_NAME = rb.getString(USER_NAME_KEY); + PROMPT = (rb.containsKey(PROMPT_KEY)) ? rb.getString(PROMPT_KEY) : "books>"; + DELIMITER = (rb.containsKey(DELIMITER_KEY)) ? rb.getString(DELIMITER_KEY) : ","; + SAVE_FILE = (rb.containsKey(SAVE_FILE_KEY)) ? rb.getString(DELIMITER_KEY) : "savefile"; + END_MESSAGE = (rb.containsKey(END_MESSAGE_KEY)) ? rb.getString(END_MESSAGE_KEY) : "bye."; + } - private static ResourceBundle loadResource() { - ResourceBundle rb = null; - try { - rb = ResourceBundle.getBundle("resource"); - } catch (MissingResourceException e) { - System.out.println("WARNNING: cannot find resource.properties"); - } - return rb; - } + private static ResourceBundle loadResource() { + ResourceBundle rb = null; + try { + rb = ResourceBundle.getBundle("resource"); + } catch (MissingResourceException e) { + System.out.println("WARNNING: cannot find resource.properties"); + } + return rb; + } } diff --git a/src/main/java/jp/co/training/ExitCommand.java b/src/main/java/jp/co/training/ExitCommand.java index 8487f8f..79c4866 100644 --- a/src/main/java/jp/co/training/ExitCommand.java +++ b/src/main/java/jp/co/training/ExitCommand.java @@ -1,31 +1,16 @@ package jp.co.training; -import java.util.List; +public final class ExitCommand extends Command { -public final class ExitCommand implements Command { + public ExitCommand(String name) { + super(name); + } - private List argments; - @Override - public void execute() { - System.exit(0); - } - - @Override - public void setArgments(List argments) { - this.argments = argments; - } - - @Override - public Result validate() { - Result result = new Result(); - //パラメータ数 - if (argments != null && !argments.isEmpty()) { - if (argments.size() == 1 && argments.get(0).trim().equals("")) { - return result; - } - result.addErrMessage("SyntaxError. The number of arguments does not match."); - } - return result; - } + @Override + public Result executeCommand(String command, String[] argments) { + Result result = new Result(); + result.setExit(true); + return result; + } } diff --git a/src/main/java/jp/co/training/InsertCommand.java b/src/main/java/jp/co/training/InsertCommand.java index 80e02fe..abbccc6 100644 --- a/src/main/java/jp/co/training/InsertCommand.java +++ b/src/main/java/jp/co/training/InsertCommand.java @@ -1,57 +1,81 @@ package jp.co.training; +import static jp.co.training.Const.DATE_TIME_PATTERN; +import static jp.co.training.Const.DELIMITER; +import static jp.co.training.Const.ID_LENGTH; +import static jp.co.training.Const.SAVE_FILE; +import static jp.co.training.Const.USER_NAME; + import java.io.BufferedWriter; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import static jp.co.training.Const.SAVE_FILE; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public final class InsertCommand extends Command { + + private Result result; + + private Book book; + + public InsertCommand(String name) { + super(name); + } + + @Override + public Result executeCommand(String command, String[] argments) { + result = new Result(); + if (!validate(argments)) { + return result; + } + return createBook(); + } + + private Result createBook() { + + String today = LocalDateTime.now().format(DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)); + + String output = String.join(BookUtil.generateID(ID_LENGTH), + book.toString(), + USER_NAME, today, + USER_NAME, today, + DELIMITER); + + try (BufferedWriter bw = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(SAVE_FILE, true), StandardCharsets.UTF_8))) { + bw.write(output); + bw.newLine(); + } catch (IOException ex) { + result.addMessage(SAVE_FILE + ": cannot open."); + result.setExit(true); + return result; + } + result.addMessage("inserted."); + return result; + } + + /* + * 検証の結果OKならtrueを返す。それ以外はfalseを返す + */ + private boolean validate(String[] argments) { + // パラメータ数チェック + if (argments.length != 6) { + result.addMessage("SyntaxError. The number of arguments does not match."); + return false; + } -public final class InsertCommand implements Command { - - private List argments; - - private Book book; - - @Override - public void setArgments(List argments) { - this.argments = argments; - } - - @Override - public void execute() { - try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(SAVE_FILE, true), StandardCharsets.UTF_8))) { - for (int i = 0, size = argments.size(); i < size; i++) { - bw.write((i == 0 ? "" : ",") + argments.get(i).trim()); - } - bw.newLine(); - } catch (IOException ex) { - Logger.getLogger(InsertCommand.class.getName()).log(Level.SEVERE, null, ex); - } - System.out.println("inserted."); - } - - @Override - public Result validate() { - Result result = new Result(); - //パラメータ数 - if (argments.size() != 6) { - result.addErrMessage("SyntaxError. The number of arguments does not match."); - } - book = new Book.Builder().isbn(argments.get(0)) - .bookName(argments.get(1)) - .author(argments.get(2)) - .publisher(argments.get(3)) - .publicationDate(argments.get(4)) - .author(argments.get(5)) - .price(argments.get(6)) - .build(); - result.getErrMesages().addAll(book.validate().getErrMesages()); - - return result; - } + // 書籍情報のチェック + book = new Book.Builder() + .isbn(argments[0]) + .bookName(argments[1]) + .author(argments[2]) + .publisher(argments[3]) + .publicationDate(argments[4]) + .price(argments[5]).build(); + result.getMesages().addAll(book.validate().getMesages()); + return result.getMesages().isEmpty(); + } } diff --git a/src/main/java/jp/co/training/InvalidCommand.java b/src/main/java/jp/co/training/InvalidCommand.java deleted file mode 100644 index f5063ba..0000000 --- a/src/main/java/jp/co/training/InvalidCommand.java +++ /dev/null @@ -1,22 +0,0 @@ -package jp.co.training; - -import java.util.List; - -public final class InvalidCommand implements Command { - - @Override - public void setArgments(List argments) { - } - - @Override - public void execute() { - } - - @Override - public Result validate() { - Result result = new Result(); - result.addErrMessage("Invalid Command."); - return result; - } - -} diff --git a/src/main/java/jp/co/training/Main.java b/src/main/java/jp/co/training/Main.java index 8bca169..5a9315a 100644 --- a/src/main/java/jp/co/training/Main.java +++ b/src/main/java/jp/co/training/Main.java @@ -1,31 +1,42 @@ package jp.co.training; +import static jp.co.training.Const.DELIMITER; +import static jp.co.training.Const.EXIT; +import static jp.co.training.Const.INSERT; +import static jp.co.training.Const.PROMPT; + import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; -import java.util.List; import java.util.Scanner; -import static jp.co.training.Const.DELIMITER; -import static jp.co.training.Const.PROMPT; public final class Main { - public static void main(String... args) throws FileNotFoundException, IOException { - try (Scanner scan = new Scanner(System.in)) { - while (true) { - System.out.print(PROMPT); - Command command = CommandFactory.createCommand(scan.next()); - List argments = Arrays.asList(scan.nextLine().split(DELIMITER)); + public static void main(String... args) throws FileNotFoundException, IOException { + Command command = new InsertCommand(INSERT); + command.setNext(new ExitCommand(EXIT)); + try (Scanner scan = new Scanner(System.in)) { + while (true) { + System.out.print(PROMPT); + String inputCommand = scan.next().toLowerCase(); + String[] argments = scan.nextLine().split(DELIMITER); + argments = (String[]) Arrays.stream(argments).map(e -> e.trim()).toArray(); + + // コマンド実行 + Result result = command.execute(inputCommand, argments); + outputMessages(result); + if (result != null && result.isExit()) { + break; + } + } + } + } - command.setArgments(argments); - Result result = command.validate(); - if (result != null && result.getErrMesages().size() > 0) { - result.getErrMesages().stream().forEach(message -> { - System.err.println(message); - }); - } - command.execute(); - } - } - } + private static void outputMessages(Result result) { + if (result != null && result.getMesages().size() > 0) { + result.getMesages().stream().forEach(message -> { + System.out.println(message); + }); + } + } } diff --git a/src/main/java/jp/co/training/Result.java b/src/main/java/jp/co/training/Result.java index 6e0a6e2..be2ecff 100644 --- a/src/main/java/jp/co/training/Result.java +++ b/src/main/java/jp/co/training/Result.java @@ -5,13 +5,23 @@ public class Result { - private final List errMesages = new ArrayList<>(); + private boolean exit = false; + private final List mesages = new ArrayList<>(); - public List getErrMesages() { - return errMesages; + public List getMesages() { + return mesages; } - public void addErrMessage(String msg) { - errMesages.add(msg); + public void addMessage(String msg) { + mesages.add(msg); } + + public boolean isExit() { + return exit; + } + + public void setExit(boolean exit) { + this.exit = exit; + } + }