diff --git a/pom.xml b/pom.xml index 4e600a4..2dc5e53 100644 --- a/pom.xml +++ b/pom.xml @@ -23,24 +23,16 @@ - + + org.apache.maven.plugins + maven-surefire-plugin + 2.18.1 + + + org.apache.maven.plugins + maven-surefire-report-plugin + 2.18.1 + @@ -49,5 +41,11 @@ commons-csv 1.5 + + junit + junit + 4.10 + test + \ No newline at end of file diff --git a/src/main/java/jp/co/training/Book.java b/src/main/java/jp/co/training/Book.java index ad5515a..9ebb507 100644 --- a/src/main/java/jp/co/training/Book.java +++ b/src/main/java/jp/co/training/Book.java @@ -1,5 +1,6 @@ package jp.co.training; +import static jp.co.training.Const.DATE_PATTERN; 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; @@ -15,18 +16,27 @@ public class Book { 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)); + //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; } @@ -65,6 +75,7 @@ private Book(Builder builder) { } public static class Builder { + private String isbn; private String bookName; private String author; diff --git a/src/main/java/jp/co/training/BookUtil.java b/src/main/java/jp/co/training/BookUtil.java index 19e52b7..b44156e 100644 --- a/src/main/java/jp/co/training/BookUtil.java +++ b/src/main/java/jp/co/training/BookUtil.java @@ -1,6 +1,10 @@ package jp.co.training; +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 { @@ -15,15 +19,43 @@ private BookUtil() { * @param target : * @param min * @param max - * @param targetName * @return */ - public static Optional checkRange(String target, int min, int max, String targetName) { + public static Optional checkLength(String target, int min, int max) { if (target.length() < min) { - return Optional.of(targetName + "of size: min is " + min + ". but your input is " + target.length() + "."); + return Optional.of("the length must be " + min + " or more. but actual 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.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(); +// return false; + } + } diff --git a/src/main/java/jp/co/training/Command.java b/src/main/java/jp/co/training/Command.java index 538072e..6c351f0 100644 --- a/src/main/java/jp/co/training/Command.java +++ b/src/main/java/jp/co/training/Command.java @@ -1,12 +1,9 @@ package jp.co.training; -import java.util.List; - public interface Command { - void setArgments(List argments); + void setArgments(String[] argments); - void execute(); + Result execute(); - Result validate(); } diff --git a/src/main/java/jp/co/training/Const.java b/src/main/java/jp/co/training/Const.java index a4a3ab8..24629ce 100644 --- a/src/main/java/jp/co/training/Const.java +++ b/src/main/java/jp/co/training/Const.java @@ -13,22 +13,26 @@ public final class Const { 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 PROMPT; public static final String DELIMITER; public static final String SAVE_FILE; + public static final String END_MESSAGE; //外部ファイルの設定ファイルのキー 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 END_MESSAGE_KEY = "end.message"; 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); + END_MESSAGE = (rb == null || !rb.containsKey(END_MESSAGE_KEY)) ? "bye." : rb.getString(END_MESSAGE_KEY); } private static ResourceBundle loadResource() { diff --git a/src/main/java/jp/co/training/ExitCommand.java b/src/main/java/jp/co/training/ExitCommand.java index 8487f8f..6008b4d 100644 --- a/src/main/java/jp/co/training/ExitCommand.java +++ b/src/main/java/jp/co/training/ExitCommand.java @@ -1,31 +1,17 @@ package jp.co.training; -import java.util.List; - public final class ExitCommand implements Command { - private List argments; - @Override - public void execute() { - System.exit(0); - } + private final Result result = new Result(); @Override - public void setArgments(List argments) { - this.argments = argments; + public Result execute() { + result.setExit(true); + return result; } @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; + public void setArgments(String[] argments) { } } diff --git a/src/main/java/jp/co/training/InsertCommand.java b/src/main/java/jp/co/training/InsertCommand.java index 80e02fe..d534670 100644 --- a/src/main/java/jp/co/training/InsertCommand.java +++ b/src/main/java/jp/co/training/InsertCommand.java @@ -5,53 +5,64 @@ 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; public final class InsertCommand implements Command { - private List argments; - + private final Result result = new Result(); + private String[] argments; private Book book; @Override - public void setArgments(List argments) { + public void setArgments(String[] argments) { this.argments = argments; } @Override - public void execute() { + public Result execute() { + if (!validate()) { + return result; + } + return createBook(); + } + + private Result createBook() { 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.write(book.getIsbn()); + bw.write(book.getBookName()); + bw.write(book.getAuthor()); + bw.write(book.getPublisher()); + bw.write(book.getPublicationDate()); + bw.write(book.getPrice()); bw.newLine(); } catch (IOException ex) { - Logger.getLogger(InsertCommand.class.getName()).log(Level.SEVERE, null, ex); + result.addMessage(SAVE_FILE + ": cannot open."); + result.setExit(true); + return result; } - System.out.println("inserted."); + result.addMessage("inserted."); + return result; } - @Override - public Result validate() { - Result result = new Result(); - //パラメータ数 - if (argments.size() != 6) { - result.addErrMessage("SyntaxError. The number of arguments does not match."); + /* + 検証の結果OKならtrueを返す。それ以外はfalseを返す + */ + private boolean validate() { + //パラメータ数チェック + if (argments.length != 6) { + result.addMessage("SyntaxError. The number of arguments does not match."); + return false; } - 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)) + //書籍情報のチェック + book = new Book.Builder().isbn(argments[0].trim()) + .bookName(argments[1].trim()) + .author(argments[2].trim()) + .publisher(argments[3].trim()) + .publicationDate(argments[4].trim()) + .price(argments[5].trim()) .build(); - result.getErrMesages().addAll(book.validate().getErrMesages()); - - return result; + 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 index f5063ba..585fd0e 100644 --- a/src/main/java/jp/co/training/InvalidCommand.java +++ b/src/main/java/jp/co/training/InvalidCommand.java @@ -1,21 +1,16 @@ package jp.co.training; -import java.util.List; - public final class InvalidCommand implements Command { - @Override - public void setArgments(List argments) { - } + private final Result result = new Result(); @Override - public void execute() { + public void setArgments(String[] argments) { } @Override - public Result validate() { - Result result = new Result(); - result.addErrMessage("Invalid Command."); + public Result execute() { + result.addMessage("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..41f97ac 100644 --- a/src/main/java/jp/co/training/Main.java +++ b/src/main/java/jp/co/training/Main.java @@ -2,8 +2,6 @@ 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; @@ -14,18 +12,23 @@ public static void main(String... args) throws FileNotFoundException, IOExceptio 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)); + Command command = CommandFactory.createCommand(scan.next().toLowerCase()); + command.setArgments(scan.nextLine().split(DELIMITER)); - command.setArgments(argments); - Result result = command.validate(); - if (result != null && result.getErrMesages().size() > 0) { - result.getErrMesages().stream().forEach(message -> { - System.err.println(message); - }); + Result result = command.execute(); + outputMessages(result); + if (result != null && result.isExit()) { + break; } - 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; + } + } diff --git a/src/main/java/jp/co/training/Status.java b/src/main/java/jp/co/training/Status.java new file mode 100644 index 0000000..2379628 --- /dev/null +++ b/src/main/java/jp/co/training/Status.java @@ -0,0 +1,8 @@ +package jp.co.training; + +public enum Status { + + KEEP,//そのまま継続 + BREAK//ループを抜ける + +}