{"id":"loan-desk","title":"A loan desk","summary":"A lending desk for the tool shelf of a community workshop. Tools and members in maps, loans with due days, refusals as an exception class of your own, the whole shelf saved to and loaded from a CSV file, and a text UI over it all.","after":"writing-files","intro":"The workshop on Birch Lane lends its tools to its members: a drill for a weekend, a ladder for a week. In this project you build the program that runs its loan desk. When it's finished, it loads the shelf from a file when it starts, takes commands at the desk, and saves the shelf again when the desk closes.\n\nDays are plain day numbers (day 1, day 2, ...), so no calendar is needed. A loan lasts 7 days: a tool lent on day 14 is due back on day 21.\n\nThe shelf is kept in `shelf.csv`, one line per tool, member and loan, with the parts separated by commas. `loan,T3,M1,11` means that the tool T3 is out with the member whose card is M1, due back on day 11:\n\n```text\ntool,T1,Cordless drill\ntool,T2,Hand saw\ntool,T3,Ladder\nmember,M1,Noor\nmember,M2,Ilse\nloan,T3,M1,11\n```\n\nA day at the desk. The person at the desk types these commands:\n\n```text\nday 14\nloans\nlend T1 M2\nlend T3 M2\nreturn T3\nlate\nquit\n```\n\nand the program answers:\n\n```text\nIt's day 14\nT3 Ladder: Noor (M1), due day 11\nLent: T1 Cordless drill: Ilse (M2), due day 21\nSorry: T3 Ladder is out, due back on day 11\nT3 Ladder is back, 3 days late\nNothing is late\nSaved shelf.csv\n```\n\nThe ladder can't be lent to Ilse while Noor still has it, so the desk refuses with a message instead of crashing. After `quit`, `shelf.csv` has the loan of the drill instead of the ladder's.\n\nThe finished program has these classes (and an exception class of its own, LoanException):\n\n\n<figure class=\"uml\"><div class=\"uml-scroll\" tabindex=\"0\" role=\"group\" aria-label=\"Class diagram of TextUi, ToolShelf, Loan, Tool and Member\">\n<svg class=\"uml-svg\" viewBox=\"0 0 419 368\" width=\"419\" height=\"368\" style=\"width:26.19rem;min-width:24.01rem;aspect-ratio:419/368\" font-size=\"12\" aria-hidden=\"true\" focusable=\"false\"><path class=\"u-edge\" fill=\"none\" d=\"M137.7 78L137.7 100L134 100L134 124\"/><path class=\"u-edge\" fill=\"none\" d=\"M262 124L262 100L262.8 100L262.8 78\"/><path class=\"u-edge\" fill=\"none\" d=\"M134 244L134 266L92 266L92 290\"/><path class=\"u-edge\" fill=\"none\" d=\"M262 244L262 266L267.7 266L267.7 290\"/><path class=\"u-edge\" fill=\"none\" d=\"M340.3 290L340.3 266L404 266L404 100L299.5 100L299.5 78\"/><path class=\"u-edge\" fill=\"none\" d=\"M195 326L147 326\"/><path class=\"u-edge\" fill=\"none\" d=\"M128.5 114L134 124L139.5 114\"/><path class=\"u-edge\" fill=\"none\" d=\"M268.3 88L262.8 78L257.3 88\"/><path class=\"u-edge\" fill=\"none\" d=\"M86.5 280L92 290L97.5 280\"/><path class=\"u-edge\" fill=\"none\" d=\"M262.2 280L267.7 290L273.2 280\"/><path class=\"u-edge\" fill=\"none\" d=\"M305 88L299.5 78L294 88\"/><path class=\"u-edge\" fill=\"none\" d=\"M157 320.5L147 326L157 331.5\"/><rect class=\"u-bg\" fill=\"none\" x=\"79.2\" y=\"6\" width=\"117\" height=\"72\"/><rect class=\"u-head\" fill=\"none\" x=\"79.2\" y=\"6\" width=\"117\" height=\"24\"/><text x=\"137.7\" y=\"22\" text-anchor=\"middle\" class=\"u-name\">TextUi</text><text x=\"87.2\" y=\"46\">-today: int</text><text x=\"87.2\" y=\"70\">+start(): void</text><path class=\"u-sep\" fill=\"none\" d=\"M79.2 30h117M79.2 54h117\"/><rect class=\"u-frame\" fill=\"none\" x=\"79.2\" y=\"6\" width=\"117\" height=\"72\"/><rect class=\"u-bg\" fill=\"none\" x=\"6\" y=\"124\" width=\"384\" height=\"120\"/><rect class=\"u-head\" fill=\"none\" x=\"6\" y=\"124\" width=\"384\" height=\"24\"/><text x=\"198\" y=\"140\" text-anchor=\"middle\" class=\"u-name\">ToolShelf</text><text x=\"14\" y=\"172\">+lend(code: String, card: String, today: int): Loan</text><text x=\"14\" y=\"188\">+giveBack(code: String, today: int): int</text><text x=\"14\" y=\"204\">+save(fileName: String): void</text><text x=\"14\" y=\"220\">+load(fileName: String): void</text><text x=\"14\" y=\"236\">...</text><path class=\"u-sep\" fill=\"none\" d=\"M6 148h384M6 156h384\"/><rect class=\"u-frame\" fill=\"none\" x=\"6\" y=\"124\" width=\"384\" height=\"120\"/><rect class=\"u-bg\" fill=\"none\" x=\"195\" y=\"290\" width=\"218\" height=\"72\"/><rect class=\"u-head\" fill=\"none\" x=\"195\" y=\"290\" width=\"218\" height=\"24\"/><text x=\"304\" y=\"306\" text-anchor=\"middle\" class=\"u-name\">Loan</text><text x=\"203\" y=\"330\">-dueDay: int</text><text x=\"203\" y=\"354\">+isLate(today: int): boolean</text><path class=\"u-sep\" fill=\"none\" d=\"M195 314h218M195 338h218\"/><rect class=\"u-frame\" fill=\"none\" x=\"195\" y=\"290\" width=\"218\" height=\"72\"/><rect class=\"u-bg\" fill=\"none\" x=\"226.2\" y=\"6\" width=\"110\" height=\"72\"/><rect class=\"u-head\" fill=\"none\" x=\"226.2\" y=\"6\" width=\"110\" height=\"24\"/><text x=\"281.2\" y=\"22\" text-anchor=\"middle\" class=\"u-name\">Tool</text><text x=\"234.2\" y=\"46\">-code: String</text><text x=\"234.2\" y=\"62\">-name: String</text><path class=\"u-sep\" fill=\"none\" d=\"M226.2 30h110M226.2 70h110\"/><rect class=\"u-frame\" fill=\"none\" x=\"226.2\" y=\"6\" width=\"110\" height=\"72\"/><rect class=\"u-bg\" fill=\"none\" x=\"37\" y=\"290\" width=\"110\" height=\"72\"/><rect class=\"u-head\" fill=\"none\" x=\"37\" y=\"290\" width=\"110\" height=\"24\"/><text x=\"92\" y=\"306\" text-anchor=\"middle\" class=\"u-name\">Member</text><text x=\"45\" y=\"330\">-card: String</text><text x=\"45\" y=\"346\">-name: String</text><path class=\"u-sep\" fill=\"none\" d=\"M37 314h110M37 354h110\"/><rect class=\"u-frame\" fill=\"none\" x=\"37\" y=\"290\" width=\"110\" height=\"72\"/><text x=\"270.8\" y=\"91\" class=\"u-mult\">*</text><text x=\"100\" y=\"285\" class=\"u-mult\">*</text><text x=\"275.7\" y=\"285\" class=\"u-mult\">*</text></svg>\n</div><details class=\"uml-text\"><summary>The diagram as text</summary><ul><li><strong>TextUi</strong>, a class<ul><li>private field <code>today: int</code></li><li>public method <code>start(): void</code></li></ul></li><li><strong>ToolShelf</strong>, a class<ul><li>public method <code>lend(code: String, card: String, today: int): Loan</code></li><li>public method <code>giveBack(code: String, today: int): int</code></li><li>public method <code>save(fileName: String): void</code></li><li>public method <code>load(fileName: String): void</code></li><li>more members, not shown</li></ul></li><li><strong>Loan</strong>, a class<ul><li>private field <code>dueDay: int</code></li><li>public method <code>isLate(today: int): boolean</code></li></ul></li><li><strong>Tool</strong>, a class<ul><li>private field <code>code: String</code></li><li>private field <code>name: String</code></li></ul></li><li><strong>Member</strong>, a class<ul><li>private field <code>card: String</code></li><li>private field <code>name: String</code></li></ul></li></ul><p>Connections:</p><ul><li>Each TextUi has one ToolShelf object.</li><li>Each ToolShelf has any number of Tool objects.</li><li>Each ToolShelf has any number of Member objects.</li><li>Each ToolShelf has any number of Loan objects.</li><li>Each Loan has one Tool object.</li><li>Each Loan has one Member object.</li></ul></details></figure>\n\n\n### What you need\n\nEverything up to the end of part 11: classes and objects (modules 17 to 19), reading files (module 20), a text UI (module 25), hash maps (module 31), interfaces and Comparable (modules 35 and 38), exceptions (module 42) and writing files (module 43).\n\n### The milestones\n\n1. Tools and members, in two hash maps, printed in order.\n2. Loans with due days, and which ones are late.\n3. Refusals: an exception class of your own for a loan that can't happen.\n4. Saving the shelf to a CSV file.\n5. Loading it back, with a clear message for a damaged line.\n6. The desk: a text UI over the shelf.\n\nThe checks of the first five milestones call your classes' methods directly; `main` becomes the desk only in the last one.\n","milestones":[{"id":"tools-and-members","title":"Tools and members","task":"Every tool on the shelf has a code, such as `T1`, and a name. `Tool.java` is ready. Every member has a card, such as `M1`, and a name.\n\nIn `Member.java`, write the class `Member`:\n\n- a constructor `Member(String card, String name)`\n- `getCard()` and `getName()`, which return them\n- `toString()`, which returns the name and the card in parentheses: `Noor (M1)`\n\nIn `ToolShelf.java`, write the class `ToolShelf`. It keeps the tools in a `HashMap<String, Tool>` called `tools`, with the codes as keys, and the members in a `HashMap<String, Member>` called `members`, with the cards as keys. A map finds a tool by its code at once, which the next milestones need.\n\n- a constructor without parameters, which creates the empty maps\n- `addTool(Tool tool)` and `addMember(Member member)`, which put them in their maps\n- `printTools()`, which prints every tool, one per line, in the alphabetical order of their codes\n- `printMembers()`, which prints every member the same way, in the order of their cards\n\nA HashMap keeps its keys in an order of its own, so copy the keys into a list and sort it before printing. The check runs:\n\n```java\nToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T2\", \"Hand saw\"));\nshelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\nshelf.addMember(new Member(\"M2\", \"Ilse\"));\nshelf.addMember(new Member(\"M1\", \"Noor\"));\nshelf.printTools();\nshelf.printMembers();\n```\n\nand expects:\n\n```\nT1 Cordless drill\nT2 Hand saw\nNoor (M1)\nIlse (M2)\n```\n","kind":"code","seed":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ToolShelf shelf = new ToolShelf();\n        shelf.addTool(new Tool(\"T2\", \"Hand saw\"));\n        shelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\n        shelf.addMember(new Member(\"M1\", \"Noor\"));\n        shelf.printTools();\n        shelf.printMembers();\n    }\n}\n\n// ==== Tool.java ====\npublic class Tool {\n    private String code;\n    private String name;\n\n    public Tool(String code, String name) {\n        this.code = code;\n        this.name = name;\n    }\n\n    public String getCode() {\n        return this.code;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.code + \" \" + this.name;\n    }\n}\n\n// ==== Member.java ====\npublic class Member {\n\n}\n\n// ==== ToolShelf.java ====\npublic class ToolShelf {\n\n}\n","solution":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ToolShelf shelf = new ToolShelf();\n        shelf.addTool(new Tool(\"T2\", \"Hand saw\"));\n        shelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\n        shelf.addMember(new Member(\"M1\", \"Noor\"));\n        shelf.printTools();\n        shelf.printMembers();\n    }\n}\n\n// ==== Tool.java ====\npublic class Tool {\n    private String code;\n    private String name;\n\n    public Tool(String code, String name) {\n        this.code = code;\n        this.name = name;\n    }\n\n    public String getCode() {\n        return this.code;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.code + \" \" + this.name;\n    }\n}\n\n// ==== Member.java ====\npublic class Member {\n    private String card;\n    private String name;\n\n    public Member(String card, String name) {\n        this.card = card;\n        this.name = name;\n    }\n\n    public String getCard() {\n        return this.card;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.name + \" (\" + this.card + \")\";\n    }\n}\n\n// ==== ToolShelf.java ====\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\n\npublic class ToolShelf {\n    private HashMap<String, Tool> tools;\n    private HashMap<String, Member> members;\n\n    public ToolShelf() {\n        this.tools = new HashMap<>();\n        this.members = new HashMap<>();\n    }\n\n    public void addTool(Tool tool) {\n        this.tools.put(tool.getCode(), tool);\n    }\n\n    public void addMember(Member member) {\n        this.members.put(member.getCard(), member);\n    }\n\n    public void printTools() {\n        ArrayList<String> codes = new ArrayList<>(this.tools.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.tools.get(code));\n        }\n    }\n\n    public void printMembers() {\n        ArrayList<String> cards = new ArrayList<>(this.members.keySet());\n        Collections.sort(cards);\n        for (String card : cards) {\n            System.out.println(this.members.get(card));\n        }\n    }\n}\n","hints":["Member looks like Tool: two private String fields, a constructor that sets them, two getters and a `toString` with `@Override` above it.","To print in order: `ArrayList<String> codes = new ArrayList<>(this.tools.keySet());`, then `Collections.sort(codes);`, then a for-each loop over `codes` that prints `this.tools.get(code)`. ToolShelf needs imports for ArrayList, Collections and HashMap."],"tests":[{"name":"Two tools and two members","stdin":"","call":"ToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T2\", \"Hand saw\"));\nshelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\nshelf.addMember(new Member(\"M2\", \"Ilse\"));\nshelf.addMember(new Member(\"M1\", \"Noor\"));\nshelf.printTools();\nshelf.printMembers();\n","expect":"T1 Cordless drill\nT2 Hand saw\nNoor (M1)\nIlse (M2)","hidden":false},{"name":"A member's getters","stdin":"","call":"Member member = new Member(\"M5\", \"Ravi\");\nSystem.out.println(member.getName());\nSystem.out.println(member.getCard());\nSystem.out.println(member);\n","expect":"Ravi\nM5\nRavi (M5)","hidden":false},{"name":"Five tools in order","stdin":"","call":"ToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T5\", \"Wheelbarrow\"));\nshelf.addTool(new Tool(\"T3\", \"Ladder\"));\nshelf.addTool(new Tool(\"T4\", \"Orbital sander\"));\nshelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\nshelf.addTool(new Tool(\"T2\", \"Hand saw\"));\nshelf.printTools();\n","expect":"T1 Cordless drill\nT2 Hand saw\nT3 Ladder\nT4 Orbital sander\nT5 Wheelbarrow","hidden":true},{"name":"Three members in order","stdin":"","call":"ToolShelf shelf = new ToolShelf();\nshelf.addMember(new Member(\"M3\", \"Tomas\"));\nshelf.addMember(new Member(\"M1\", \"Noor\"));\nshelf.addMember(new Member(\"M2\", \"Ilse\"));\nshelf.printMembers();\nshelf.printTools();\nSystem.out.println(\"end\");\n","expect":"Noor (M1)\nIlse (M2)\nTomas (M3)\nend","hidden":true}],"require":[{"pattern":"\\bHashMap\\s*<\\s*String\\s*,\\s*Tool\\s*>","message":"Keep the tools in a `HashMap<String, Tool>`, with the codes as keys."},{"pattern":"\\bHashMap\\s*<\\s*String\\s*,\\s*Member\\s*>","message":"Keep the members in a `HashMap<String, Member>`, with the cards as keys."}],"forbid":[]},{"id":"loans","title":"Loans and due days","task":"A loan lasts 7 days: a tool lent on day 4 is due back on day 11, and it's late from day 12 on. A new file, `Loan.java`, holds the class `Loan`. Write it:\n\n- a constructor `Loan(Tool tool, Member member, int dueDay)`\n- `getTool()`, `getMember()` and `getDueDay()`\n- `isLate(int today)`, which returns `true` when `today` is after the due day\n- `toString()`, which returns the tool, a colon, the member and the due day: `T3 Ladder: Noor (M1), due day 11`\n- Loan implements `Comparable<Loan>`: its natural order is by due day, and loans with the same due day are in the order of their tools' codes.\n\nThen add to ToolShelf a third map, a `HashMap<String, Loan>` called `loans`, with the loans by their tools' codes (a tool can be out only once), and these methods:\n\n- `lend(String toolCode, String card, int today)` creates a loan of that tool to that member, due on `today + 7`, puts it in the map and returns it. For now, it may assume that the tool and the member exist and that the tool is on the shelf.\n- `giveBack(String toolCode, int today)` removes the tool's loan and returns how many days late it was: 0 when it's back on or before its due day.\n- `printLoans()` prints every loan in the order of the tools' codes, or `No tools are out` when there are none.\n- `printLate(int today)` prints the loans that are late on that day, in their natural order (by due day), or `Nothing is late`.\n\nThe check runs:\n\n```java\nToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\nshelf.addTool(new Tool(\"T2\", \"Hand saw\"));\nshelf.addTool(new Tool(\"T3\", \"Ladder\"));\nshelf.addMember(new Member(\"M1\", \"Noor\"));\nshelf.addMember(new Member(\"M2\", \"Ilse\"));\nshelf.printLoans();\nSystem.out.println(shelf.lend(\"T3\", \"M1\", 4));\nshelf.lend(\"T1\", \"M2\", 6);\nshelf.printLoans();\nSystem.out.println(\"Late on day 12:\");\nshelf.printLate(12);\nSystem.out.println(\"Late on day 14:\");\nshelf.printLate(14);\n```\n\nand expects:\n\n```\nNo tools are out\nT3 Ladder: Noor (M1), due day 11\nT1 Cordless drill: Ilse (M2), due day 13\nT3 Ladder: Noor (M1), due day 11\nLate on day 12:\nT3 Ladder: Noor (M1), due day 11\nLate on day 14:\nT3 Ladder: Noor (M1), due day 11\nT1 Cordless drill: Ilse (M2), due day 13\n```\n","files":{"Loan.java":"public class Loan {\n\n}\n"},"kind":"code","seed":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ToolShelf shelf = new ToolShelf();\n        shelf.addTool(new Tool(\"T2\", \"Hand saw\"));\n        shelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\n        shelf.addMember(new Member(\"M1\", \"Noor\"));\n        shelf.printTools();\n        shelf.printMembers();\n    }\n}\n\n// ==== Tool.java ====\npublic class Tool {\n    private String code;\n    private String name;\n\n    public Tool(String code, String name) {\n        this.code = code;\n        this.name = name;\n    }\n\n    public String getCode() {\n        return this.code;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.code + \" \" + this.name;\n    }\n}\n\n// ==== Member.java ====\npublic class Member {\n    private String card;\n    private String name;\n\n    public Member(String card, String name) {\n        this.card = card;\n        this.name = name;\n    }\n\n    public String getCard() {\n        return this.card;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.name + \" (\" + this.card + \")\";\n    }\n}\n\n// ==== ToolShelf.java ====\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\n\npublic class ToolShelf {\n    private HashMap<String, Tool> tools;\n    private HashMap<String, Member> members;\n\n    public ToolShelf() {\n        this.tools = new HashMap<>();\n        this.members = new HashMap<>();\n    }\n\n    public void addTool(Tool tool) {\n        this.tools.put(tool.getCode(), tool);\n    }\n\n    public void addMember(Member member) {\n        this.members.put(member.getCard(), member);\n    }\n\n    public void printTools() {\n        ArrayList<String> codes = new ArrayList<>(this.tools.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.tools.get(code));\n        }\n    }\n\n    public void printMembers() {\n        ArrayList<String> cards = new ArrayList<>(this.members.keySet());\n        Collections.sort(cards);\n        for (String card : cards) {\n            System.out.println(this.members.get(card));\n        }\n    }\n}\n\n// ==== Loan.java ====\npublic class Loan {\n\n}\n","solution":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ToolShelf shelf = new ToolShelf();\n        shelf.addTool(new Tool(\"T2\", \"Hand saw\"));\n        shelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\n        shelf.addMember(new Member(\"M1\", \"Noor\"));\n        shelf.printTools();\n        shelf.printMembers();\n    }\n}\n\n// ==== Tool.java ====\npublic class Tool {\n    private String code;\n    private String name;\n\n    public Tool(String code, String name) {\n        this.code = code;\n        this.name = name;\n    }\n\n    public String getCode() {\n        return this.code;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.code + \" \" + this.name;\n    }\n}\n\n// ==== Member.java ====\npublic class Member {\n    private String card;\n    private String name;\n\n    public Member(String card, String name) {\n        this.card = card;\n        this.name = name;\n    }\n\n    public String getCard() {\n        return this.card;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.name + \" (\" + this.card + \")\";\n    }\n}\n\n// ==== ToolShelf.java ====\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\n\npublic class ToolShelf {\n    private HashMap<String, Tool> tools;\n    private HashMap<String, Member> members;\n    private HashMap<String, Loan> loans;\n\n    public ToolShelf() {\n        this.tools = new HashMap<>();\n        this.members = new HashMap<>();\n        this.loans = new HashMap<>();\n    }\n\n    public void addTool(Tool tool) {\n        this.tools.put(tool.getCode(), tool);\n    }\n\n    public void addMember(Member member) {\n        this.members.put(member.getCard(), member);\n    }\n\n    public void printTools() {\n        ArrayList<String> codes = new ArrayList<>(this.tools.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.tools.get(code));\n        }\n    }\n\n    public void printMembers() {\n        ArrayList<String> cards = new ArrayList<>(this.members.keySet());\n        Collections.sort(cards);\n        for (String card : cards) {\n            System.out.println(this.members.get(card));\n        }\n    }\n\n    public Loan lend(String toolCode, String card, int today) {\n        Loan loan = new Loan(this.tools.get(toolCode), this.members.get(card), today + 7);\n        this.loans.put(toolCode, loan);\n        return loan;\n    }\n\n    public int giveBack(String toolCode, int today) {\n        Loan loan = this.loans.get(toolCode);\n        this.loans.remove(toolCode);\n        if (loan.isLate(today)) {\n            return today - loan.getDueDay();\n        }\n        return 0;\n    }\n\n    public void printLoans() {\n        if (this.loans.isEmpty()) {\n            System.out.println(\"No tools are out\");\n        }\n        ArrayList<String> codes = new ArrayList<>(this.loans.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.loans.get(code));\n        }\n    }\n\n    public void printLate(int today) {\n        ArrayList<Loan> late = new ArrayList<>();\n        for (Loan loan : this.loans.values()) {\n            if (loan.isLate(today)) {\n                late.add(loan);\n            }\n        }\n        if (late.isEmpty()) {\n            System.out.println(\"Nothing is late\");\n        }\n        Collections.sort(late);\n        for (Loan loan : late) {\n            System.out.println(loan);\n        }\n    }\n}\n\n// ==== Loan.java ====\npublic class Loan implements Comparable<Loan> {\n    private Tool tool;\n    private Member member;\n    private int dueDay;\n\n    public Loan(Tool tool, Member member, int dueDay) {\n        this.tool = tool;\n        this.member = member;\n        this.dueDay = dueDay;\n    }\n\n    public Tool getTool() {\n        return this.tool;\n    }\n\n    public Member getMember() {\n        return this.member;\n    }\n\n    public int getDueDay() {\n        return this.dueDay;\n    }\n\n    public boolean isLate(int today) {\n        return today > this.dueDay;\n    }\n\n    @Override\n    public int compareTo(Loan other) {\n        if (this.dueDay != other.dueDay) {\n            return Integer.compare(this.dueDay, other.dueDay);\n        }\n        return this.tool.getCode().compareTo(other.tool.getCode());\n    }\n\n    @Override\n    public String toString() {\n        return this.tool + \": \" + this.member + \", due day \" + this.dueDay;\n    }\n}\n","hints":["`public class Loan implements Comparable<Loan>` and `public int compareTo(Loan other)`: when the due days differ, return `Integer.compare(this.dueDay, other.dueDay)`; when they're the same, compare the tools' codes with the String method `compareTo`.","`printLate` goes through `this.loans.values()`, adds each loan whose `isLate(today)` is true to a new `ArrayList<Loan>`, sorts that list with `Collections.sort` and prints it. `printLoans` sorts the map's keys, as `printTools` does."],"tests":[{"name":"Lend and list","stdin":"","call":"ToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\nshelf.addTool(new Tool(\"T2\", \"Hand saw\"));\nshelf.addTool(new Tool(\"T3\", \"Ladder\"));\nshelf.addMember(new Member(\"M1\", \"Noor\"));\nshelf.addMember(new Member(\"M2\", \"Ilse\"));\nshelf.printLoans();\nSystem.out.println(shelf.lend(\"T3\", \"M1\", 4));\nshelf.lend(\"T1\", \"M2\", 6);\nshelf.printLoans();\nSystem.out.println(\"Late on day 12:\");\nshelf.printLate(12);\nSystem.out.println(\"Late on day 14:\");\nshelf.printLate(14);\n","expect":"No tools are out\nT3 Ladder: Noor (M1), due day 11\nT1 Cordless drill: Ilse (M2), due day 13\nT3 Ladder: Noor (M1), due day 11\nLate on day 12:\nT3 Ladder: Noor (M1), due day 11\nLate on day 14:\nT3 Ladder: Noor (M1), due day 11\nT1 Cordless drill: Ilse (M2), due day 13","hidden":false},{"name":"Give back on time and late","stdin":"","call":"ToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T2\", \"Hand saw\"));\nshelf.addTool(new Tool(\"T3\", \"Ladder\"));\nshelf.addMember(new Member(\"M2\", \"Ilse\"));\nshelf.lend(\"T2\", \"M2\", 1);\nshelf.lend(\"T3\", \"M2\", 1);\nshelf.printLate(8);\nSystem.out.println(shelf.giveBack(\"T2\", 8));\nSystem.out.println(shelf.giveBack(\"T3\", 12));\nshelf.printLoans();\n","expect":"Nothing is late\n0\n4\nNo tools are out","hidden":false},{"name":"A loan's getters","stdin":"","call":"Tool saw = new Tool(\"T2\", \"Hand saw\");\nMember ilse = new Member(\"M2\", \"Ilse\");\nLoan loan = new Loan(saw, ilse, 9);\nSystem.out.println(loan.getTool());\nSystem.out.println(loan.getMember());\nSystem.out.println(loan.getDueDay());\nSystem.out.println(loan.isLate(9));\nSystem.out.println(loan.isLate(10));\n","expect":"T2 Hand saw\nIlse (M2)\n9\nfalse\ntrue","hidden":false},{"name":"Late loans by due day","stdin":"","call":"ToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\nshelf.addTool(new Tool(\"T2\", \"Hand saw\"));\nshelf.addTool(new Tool(\"T4\", \"Orbital sander\"));\nshelf.addTool(new Tool(\"T5\", \"Wheelbarrow\"));\nshelf.addMember(new Member(\"M1\", \"Noor\"));\nshelf.addMember(new Member(\"M3\", \"Tomas\"));\nshelf.lend(\"T1\", \"M1\", 20);\nshelf.lend(\"T5\", \"M3\", 3);\nshelf.lend(\"T2\", \"M3\", 3);\nshelf.lend(\"T4\", \"M1\", 12);\nshelf.printLoans();\nSystem.out.println(\"--\");\nshelf.printLate(30);\nSystem.out.println(\"--\");\nshelf.printLate(19);\nSystem.out.println(\"--\");\nshelf.printLate(10);\n","expect":"T1 Cordless drill: Noor (M1), due day 27\nT2 Hand saw: Tomas (M3), due day 10\nT4 Orbital sander: Noor (M1), due day 19\nT5 Wheelbarrow: Tomas (M3), due day 10\n--\nT2 Hand saw: Tomas (M3), due day 10\nT5 Wheelbarrow: Tomas (M3), due day 10\nT4 Orbital sander: Noor (M1), due day 19\nT1 Cordless drill: Noor (M1), due day 27\n--\nT2 Hand saw: Tomas (M3), due day 10\nT5 Wheelbarrow: Tomas (M3), due day 10\n--\nNothing is late","hidden":true},{"name":"Lent again after a return","stdin":"","call":"ToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T3\", \"Ladder\"));\nshelf.addMember(new Member(\"M1\", \"Noor\"));\nshelf.addMember(new Member(\"M2\", \"Ilse\"));\nshelf.lend(\"T3\", \"M1\", 2);\nSystem.out.println(shelf.giveBack(\"T3\", 5));\nSystem.out.println(shelf.lend(\"T3\", \"M2\", 5));\nSystem.out.println(shelf.giveBack(\"T3\", 13));\nshelf.printLate(40);\n","expect":"0\nT3 Ladder: Ilse (M2), due day 12\n1\nNothing is late","hidden":true}],"require":[{"pattern":"\\bimplements\\s+Comparable\\s*<\\s*Loan\\s*>","message":"Give Loan a natural order: `public class Loan implements Comparable<Loan>`."},{"pattern":"\\bHashMap\\s*<\\s*String\\s*,\\s*Loan\\s*>","message":"Keep the loans in a `HashMap<String, Loan>`, with the tools' codes as keys."}],"forbid":[]},{"id":"refusals","title":"Refusals","task":"So far, `lend` trusts what it's given. At a real desk, people mistype codes and ask for tools that are already out. Now the shelf refuses such loans with an exception of its own kind, so the desk can show the reason and go on.\n\nIn the new file `LoanException.java`, make `LoanException` a checked exception: it extends `Exception`, and its constructor takes the message and hands it on with `super(message)`.\n\nThen add to ToolShelf:\n\n- `getTool(String code)`, which returns the tool with that code, or throws a LoanException with the message `No tool with the code T9` (with the code it was given)\n- `getMember(String card)`, the same for members: `No member with the card M7`\n\nand change `lend` and `giveBack` so that they declare `throws LoanException` and refuse, before anything changes:\n\n- `lend`: a tool or a member that doesn't exist, with the messages above; a tool that is already out: `T1 Cordless drill is out, due back on day 10`; and a member who already has 2 tools out: `Noor (M1) already has 2 tools`. Check in this order and refuse at the first problem: the tool exists, the member exists, the tool is on the shelf, the member has fewer than 2 tools out. So a tool that is out gets the `is out` message even when the member also has 2 tools.\n- `giveBack`: a tool that doesn't exist, and a tool that isn't out: `T3 Ladder is not out`\n\nThe check runs:\n\n```java\nToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\nshelf.addTool(new Tool(\"T2\", \"Hand saw\"));\nshelf.addTool(new Tool(\"T3\", \"Ladder\"));\nshelf.addMember(new Member(\"M1\", \"Noor\"));\nshelf.lend(\"T1\", \"M1\", 3);\nshelf.lend(\"T2\", \"M1\", 3);\ntry {\n    shelf.lend(\"T9\", \"M1\", 5);\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\ntry {\n    shelf.lend(\"T3\", \"M7\", 5);\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\ntry {\n    shelf.lend(\"T1\", \"M1\", 5);\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\ntry {\n    shelf.lend(\"T3\", \"M1\", 5);\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\n```\n\nand expects:\n\n```\nNo tool with the code T9\nNo member with the card M7\nT1 Cordless drill is out, due back on day 10\nNoor (M1) already has 2 tools\n```\n","files":{"LoanException.java":"public class LoanException extends Exception {\n\n}\n"},"kind":"code","seed":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ToolShelf shelf = new ToolShelf();\n        shelf.addTool(new Tool(\"T2\", \"Hand saw\"));\n        shelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\n        shelf.addMember(new Member(\"M1\", \"Noor\"));\n        shelf.printTools();\n        shelf.printMembers();\n    }\n}\n\n// ==== Tool.java ====\npublic class Tool {\n    private String code;\n    private String name;\n\n    public Tool(String code, String name) {\n        this.code = code;\n        this.name = name;\n    }\n\n    public String getCode() {\n        return this.code;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.code + \" \" + this.name;\n    }\n}\n\n// ==== Member.java ====\npublic class Member {\n    private String card;\n    private String name;\n\n    public Member(String card, String name) {\n        this.card = card;\n        this.name = name;\n    }\n\n    public String getCard() {\n        return this.card;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.name + \" (\" + this.card + \")\";\n    }\n}\n\n// ==== ToolShelf.java ====\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\n\npublic class ToolShelf {\n    private HashMap<String, Tool> tools;\n    private HashMap<String, Member> members;\n    private HashMap<String, Loan> loans;\n\n    public ToolShelf() {\n        this.tools = new HashMap<>();\n        this.members = new HashMap<>();\n        this.loans = new HashMap<>();\n    }\n\n    public void addTool(Tool tool) {\n        this.tools.put(tool.getCode(), tool);\n    }\n\n    public void addMember(Member member) {\n        this.members.put(member.getCard(), member);\n    }\n\n    public void printTools() {\n        ArrayList<String> codes = new ArrayList<>(this.tools.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.tools.get(code));\n        }\n    }\n\n    public void printMembers() {\n        ArrayList<String> cards = new ArrayList<>(this.members.keySet());\n        Collections.sort(cards);\n        for (String card : cards) {\n            System.out.println(this.members.get(card));\n        }\n    }\n\n    public Loan lend(String toolCode, String card, int today) {\n        Loan loan = new Loan(this.tools.get(toolCode), this.members.get(card), today + 7);\n        this.loans.put(toolCode, loan);\n        return loan;\n    }\n\n    public int giveBack(String toolCode, int today) {\n        Loan loan = this.loans.get(toolCode);\n        this.loans.remove(toolCode);\n        if (loan.isLate(today)) {\n            return today - loan.getDueDay();\n        }\n        return 0;\n    }\n\n    public void printLoans() {\n        if (this.loans.isEmpty()) {\n            System.out.println(\"No tools are out\");\n        }\n        ArrayList<String> codes = new ArrayList<>(this.loans.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.loans.get(code));\n        }\n    }\n\n    public void printLate(int today) {\n        ArrayList<Loan> late = new ArrayList<>();\n        for (Loan loan : this.loans.values()) {\n            if (loan.isLate(today)) {\n                late.add(loan);\n            }\n        }\n        if (late.isEmpty()) {\n            System.out.println(\"Nothing is late\");\n        }\n        Collections.sort(late);\n        for (Loan loan : late) {\n            System.out.println(loan);\n        }\n    }\n}\n\n// ==== Loan.java ====\npublic class Loan implements Comparable<Loan> {\n    private Tool tool;\n    private Member member;\n    private int dueDay;\n\n    public Loan(Tool tool, Member member, int dueDay) {\n        this.tool = tool;\n        this.member = member;\n        this.dueDay = dueDay;\n    }\n\n    public Tool getTool() {\n        return this.tool;\n    }\n\n    public Member getMember() {\n        return this.member;\n    }\n\n    public int getDueDay() {\n        return this.dueDay;\n    }\n\n    public boolean isLate(int today) {\n        return today > this.dueDay;\n    }\n\n    @Override\n    public int compareTo(Loan other) {\n        if (this.dueDay != other.dueDay) {\n            return Integer.compare(this.dueDay, other.dueDay);\n        }\n        return this.tool.getCode().compareTo(other.tool.getCode());\n    }\n\n    @Override\n    public String toString() {\n        return this.tool + \": \" + this.member + \", due day \" + this.dueDay;\n    }\n}\n\n// ==== LoanException.java ====\npublic class LoanException extends Exception {\n\n}\n","solution":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ToolShelf shelf = new ToolShelf();\n        shelf.addTool(new Tool(\"T2\", \"Hand saw\"));\n        shelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\n        shelf.addMember(new Member(\"M1\", \"Noor\"));\n        shelf.printTools();\n        shelf.printMembers();\n    }\n}\n\n// ==== Tool.java ====\npublic class Tool {\n    private String code;\n    private String name;\n\n    public Tool(String code, String name) {\n        this.code = code;\n        this.name = name;\n    }\n\n    public String getCode() {\n        return this.code;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.code + \" \" + this.name;\n    }\n}\n\n// ==== Member.java ====\npublic class Member {\n    private String card;\n    private String name;\n\n    public Member(String card, String name) {\n        this.card = card;\n        this.name = name;\n    }\n\n    public String getCard() {\n        return this.card;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.name + \" (\" + this.card + \")\";\n    }\n}\n\n// ==== ToolShelf.java ====\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\n\npublic class ToolShelf {\n    private HashMap<String, Tool> tools;\n    private HashMap<String, Member> members;\n    private HashMap<String, Loan> loans;\n\n    public ToolShelf() {\n        this.tools = new HashMap<>();\n        this.members = new HashMap<>();\n        this.loans = new HashMap<>();\n    }\n\n    public void addTool(Tool tool) {\n        this.tools.put(tool.getCode(), tool);\n    }\n\n    public void addMember(Member member) {\n        this.members.put(member.getCard(), member);\n    }\n\n    public Tool getTool(String code) throws LoanException {\n        if (!this.tools.containsKey(code)) {\n            throw new LoanException(\"No tool with the code \" + code);\n        }\n        return this.tools.get(code);\n    }\n\n    public Member getMember(String card) throws LoanException {\n        if (!this.members.containsKey(card)) {\n            throw new LoanException(\"No member with the card \" + card);\n        }\n        return this.members.get(card);\n    }\n\n    public void printTools() {\n        ArrayList<String> codes = new ArrayList<>(this.tools.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.tools.get(code));\n        }\n    }\n\n    public void printMembers() {\n        ArrayList<String> cards = new ArrayList<>(this.members.keySet());\n        Collections.sort(cards);\n        for (String card : cards) {\n            System.out.println(this.members.get(card));\n        }\n    }\n\n    public Loan lend(String toolCode, String card, int today) throws LoanException {\n        Tool tool = this.getTool(toolCode);\n        Member member = this.getMember(card);\n        if (this.loans.containsKey(toolCode)) {\n            int dueDay = this.loans.get(toolCode).getDueDay();\n            throw new LoanException(tool + \" is out, due back on day \" + dueDay);\n        }\n        if (this.countLoans(card) >= 2) {\n            throw new LoanException(member + \" already has 2 tools\");\n        }\n        Loan loan = new Loan(tool, member, today + 7);\n        this.loans.put(toolCode, loan);\n        return loan;\n    }\n\n    private int countLoans(String card) {\n        int count = 0;\n        for (Loan loan : this.loans.values()) {\n            if (loan.getMember().getCard().equals(card)) {\n                count++;\n            }\n        }\n        return count;\n    }\n\n    public int giveBack(String toolCode, int today) throws LoanException {\n        Tool tool = this.getTool(toolCode);\n        if (!this.loans.containsKey(toolCode)) {\n            throw new LoanException(tool + \" is not out\");\n        }\n        Loan loan = this.loans.get(toolCode);\n        this.loans.remove(toolCode);\n        if (loan.isLate(today)) {\n            return today - loan.getDueDay();\n        }\n        return 0;\n    }\n\n    public void printLoans() {\n        if (this.loans.isEmpty()) {\n            System.out.println(\"No tools are out\");\n        }\n        ArrayList<String> codes = new ArrayList<>(this.loans.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.loans.get(code));\n        }\n    }\n\n    public void printLate(int today) {\n        ArrayList<Loan> late = new ArrayList<>();\n        for (Loan loan : this.loans.values()) {\n            if (loan.isLate(today)) {\n                late.add(loan);\n            }\n        }\n        if (late.isEmpty()) {\n            System.out.println(\"Nothing is late\");\n        }\n        Collections.sort(late);\n        for (Loan loan : late) {\n            System.out.println(loan);\n        }\n    }\n}\n\n// ==== Loan.java ====\npublic class Loan implements Comparable<Loan> {\n    private Tool tool;\n    private Member member;\n    private int dueDay;\n\n    public Loan(Tool tool, Member member, int dueDay) {\n        this.tool = tool;\n        this.member = member;\n        this.dueDay = dueDay;\n    }\n\n    public Tool getTool() {\n        return this.tool;\n    }\n\n    public Member getMember() {\n        return this.member;\n    }\n\n    public int getDueDay() {\n        return this.dueDay;\n    }\n\n    public boolean isLate(int today) {\n        return today > this.dueDay;\n    }\n\n    @Override\n    public int compareTo(Loan other) {\n        if (this.dueDay != other.dueDay) {\n            return Integer.compare(this.dueDay, other.dueDay);\n        }\n        return this.tool.getCode().compareTo(other.tool.getCode());\n    }\n\n    @Override\n    public String toString() {\n        return this.tool + \": \" + this.member + \", due day \" + this.dueDay;\n    }\n}\n\n// ==== LoanException.java ====\npublic class LoanException extends Exception {\n    public LoanException(String message) {\n        super(message);\n    }\n}\n","hints":["LoanException needs only a constructor: `public LoanException(String message) { super(message); }`. In `getTool`, check `this.tools.containsKey(code)` and throw before calling `get`.","In `lend`, call `getTool` and `getMember` first: they throw for you. Then check `this.loans.containsKey(toolCode)`, and count the member's loans in `this.loans.values()` by comparing `loan.getMember().getCard()` with the card. Create the loan only after all the checks."],"tests":[{"name":"Refused loans","stdin":"","call":"ToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\nshelf.addTool(new Tool(\"T2\", \"Hand saw\"));\nshelf.addTool(new Tool(\"T3\", \"Ladder\"));\nshelf.addMember(new Member(\"M1\", \"Noor\"));\nshelf.lend(\"T1\", \"M1\", 3);\nshelf.lend(\"T2\", \"M1\", 3);\ntry {\n    shelf.lend(\"T9\", \"M1\", 5);\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\ntry {\n    shelf.lend(\"T3\", \"M7\", 5);\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\ntry {\n    shelf.lend(\"T1\", \"M1\", 5);\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\ntry {\n    shelf.lend(\"T3\", \"M1\", 5);\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\n","expect":"No tool with the code T9\nNo member with the card M7\nT1 Cordless drill is out, due back on day 10\nNoor (M1) already has 2 tools","hidden":false},{"name":"Giving back what isn't out","stdin":"","call":"ToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T3\", \"Ladder\"));\ntry {\n    shelf.giveBack(\"T3\", 5);\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\ntry {\n    shelf.giveBack(\"T8\", 5);\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\n","expect":"T3 Ladder is not out\nNo tool with the code T8","hidden":false},{"name":"Finding tools and members","stdin":"","call":"ToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T4\", \"Orbital sander\"));\nshelf.addMember(new Member(\"M3\", \"Tomas\"));\nSystem.out.println(shelf.getTool(\"T4\"));\nSystem.out.println(shelf.getMember(\"M3\"));\ntry {\n    shelf.getMember(\"M4\");\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\n","expect":"T4 Orbital sander\nTomas (M3)\nNo member with the card M4","hidden":false},{"name":"A return makes room","stdin":"","call":"ToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\nshelf.addTool(new Tool(\"T2\", \"Hand saw\"));\nshelf.addTool(new Tool(\"T5\", \"Wheelbarrow\"));\nshelf.addMember(new Member(\"M2\", \"Ilse\"));\nshelf.addMember(new Member(\"M3\", \"Tomas\"));\nshelf.lend(\"T1\", \"M2\", 1);\nshelf.lend(\"T5\", \"M3\", 1);\nshelf.lend(\"T2\", \"M2\", 2);\ntry {\n    shelf.lend(\"T5\", \"M2\", 4);\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\nSystem.out.println(shelf.giveBack(\"T5\", 4));\ntry {\n    shelf.lend(\"T5\", \"M2\", 4);\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\nSystem.out.println(shelf.giveBack(\"T1\", 10));\nSystem.out.println(shelf.lend(\"T5\", \"M2\", 10));\nshelf.printLoans();\n","expect":"T5 Wheelbarrow is out, due back on day 8\n0\nIlse (M2) already has 2 tools\n2\nT5 Wheelbarrow: Ilse (M2), due day 17\nT2 Hand saw: Ilse (M2), due day 9\nT5 Wheelbarrow: Ilse (M2), due day 17","hidden":true},{"name":"A refusal changes nothing","stdin":"","call":"ToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T3\", \"Ladder\"));\nshelf.addMember(new Member(\"M1\", \"Noor\"));\nshelf.addMember(new Member(\"M2\", \"Ilse\"));\nshelf.lend(\"T3\", \"M1\", 6);\ntry {\n    shelf.lend(\"T3\", \"M2\", 7);\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\ntry {\n    shelf.lend(\"T7\", \"M9\", 7);\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\nshelf.printLoans();\nSystem.out.println(shelf.giveBack(\"T3\", 20));\ntry {\n    shelf.giveBack(\"T3\", 21);\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\n","expect":"T3 Ladder is out, due back on day 13\nNo tool with the code T7\nT3 Ladder: Noor (M1), due day 13\n7\nT3 Ladder is not out","hidden":true}],"require":[{"pattern":"\\bclass\\s+LoanException\\s+extends\\s+Exception\\b","message":"Make LoanException a checked exception: `public class LoanException extends Exception`."},{"pattern":"\\bthrow\\s+new\\s+LoanException\\s*\\(","message":"Refuse with `throw new LoanException(...)`."}],"forbid":[]},{"id":"saving","title":"Saving to a CSV file","task":"The shelf forgets everything when the program ends. Add to ToolShelf a method `save(String fileName)` that writes the whole shelf into that file in CSV form: one line per tool, member and loan, with the parts separated by commas.\n\n- first the tools, in the order of their codes: `tool`, the code and the name\n- then the members, in the order of their cards: `member`, the card and the name\n- last the loans, in the order of the tools' codes: `loan`, the tool's code, the member's card and the due day\n\nWrite the file with a PrintWriter in try-with-resources, so it's closed when the method ends. `save` declares `throws IOException`: the code that calls it decides what to do when the file can't be written. Names have no commas in them, so a comma always separates two parts.\n\nThe check builds a shelf, lends the hand saw to Ilse on day 4, and calls `save(\"shelf.csv\")`:\n\n```java\nToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T2\", \"Hand saw\"));\nshelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\nshelf.addMember(new Member(\"M2\", \"Ilse\"));\nshelf.addMember(new Member(\"M1\", \"Noor\"));\nshelf.lend(\"T2\", \"M2\", 4);\nshelf.save(\"shelf.csv\");\n```\n\nIt prints nothing, and `shelf.csv` holds:\n\n```text\ntool,T1,Cordless drill\ntool,T2,Hand saw\nmember,M1,Noor\nmember,M2,Ilse\nloan,T2,M2,11\n```\n","kind":"code","seed":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ToolShelf shelf = new ToolShelf();\n        shelf.addTool(new Tool(\"T2\", \"Hand saw\"));\n        shelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\n        shelf.addMember(new Member(\"M1\", \"Noor\"));\n        shelf.printTools();\n        shelf.printMembers();\n    }\n}\n\n// ==== Tool.java ====\npublic class Tool {\n    private String code;\n    private String name;\n\n    public Tool(String code, String name) {\n        this.code = code;\n        this.name = name;\n    }\n\n    public String getCode() {\n        return this.code;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.code + \" \" + this.name;\n    }\n}\n\n// ==== Member.java ====\npublic class Member {\n    private String card;\n    private String name;\n\n    public Member(String card, String name) {\n        this.card = card;\n        this.name = name;\n    }\n\n    public String getCard() {\n        return this.card;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.name + \" (\" + this.card + \")\";\n    }\n}\n\n// ==== ToolShelf.java ====\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\n\npublic class ToolShelf {\n    private HashMap<String, Tool> tools;\n    private HashMap<String, Member> members;\n    private HashMap<String, Loan> loans;\n\n    public ToolShelf() {\n        this.tools = new HashMap<>();\n        this.members = new HashMap<>();\n        this.loans = new HashMap<>();\n    }\n\n    public void addTool(Tool tool) {\n        this.tools.put(tool.getCode(), tool);\n    }\n\n    public void addMember(Member member) {\n        this.members.put(member.getCard(), member);\n    }\n\n    public Tool getTool(String code) throws LoanException {\n        if (!this.tools.containsKey(code)) {\n            throw new LoanException(\"No tool with the code \" + code);\n        }\n        return this.tools.get(code);\n    }\n\n    public Member getMember(String card) throws LoanException {\n        if (!this.members.containsKey(card)) {\n            throw new LoanException(\"No member with the card \" + card);\n        }\n        return this.members.get(card);\n    }\n\n    public void printTools() {\n        ArrayList<String> codes = new ArrayList<>(this.tools.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.tools.get(code));\n        }\n    }\n\n    public void printMembers() {\n        ArrayList<String> cards = new ArrayList<>(this.members.keySet());\n        Collections.sort(cards);\n        for (String card : cards) {\n            System.out.println(this.members.get(card));\n        }\n    }\n\n    public Loan lend(String toolCode, String card, int today) throws LoanException {\n        Tool tool = this.getTool(toolCode);\n        Member member = this.getMember(card);\n        if (this.loans.containsKey(toolCode)) {\n            int dueDay = this.loans.get(toolCode).getDueDay();\n            throw new LoanException(tool + \" is out, due back on day \" + dueDay);\n        }\n        if (this.countLoans(card) >= 2) {\n            throw new LoanException(member + \" already has 2 tools\");\n        }\n        Loan loan = new Loan(tool, member, today + 7);\n        this.loans.put(toolCode, loan);\n        return loan;\n    }\n\n    private int countLoans(String card) {\n        int count = 0;\n        for (Loan loan : this.loans.values()) {\n            if (loan.getMember().getCard().equals(card)) {\n                count++;\n            }\n        }\n        return count;\n    }\n\n    public int giveBack(String toolCode, int today) throws LoanException {\n        Tool tool = this.getTool(toolCode);\n        if (!this.loans.containsKey(toolCode)) {\n            throw new LoanException(tool + \" is not out\");\n        }\n        Loan loan = this.loans.get(toolCode);\n        this.loans.remove(toolCode);\n        if (loan.isLate(today)) {\n            return today - loan.getDueDay();\n        }\n        return 0;\n    }\n\n    public void printLoans() {\n        if (this.loans.isEmpty()) {\n            System.out.println(\"No tools are out\");\n        }\n        ArrayList<String> codes = new ArrayList<>(this.loans.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.loans.get(code));\n        }\n    }\n\n    public void printLate(int today) {\n        ArrayList<Loan> late = new ArrayList<>();\n        for (Loan loan : this.loans.values()) {\n            if (loan.isLate(today)) {\n                late.add(loan);\n            }\n        }\n        if (late.isEmpty()) {\n            System.out.println(\"Nothing is late\");\n        }\n        Collections.sort(late);\n        for (Loan loan : late) {\n            System.out.println(loan);\n        }\n    }\n}\n\n// ==== Loan.java ====\npublic class Loan implements Comparable<Loan> {\n    private Tool tool;\n    private Member member;\n    private int dueDay;\n\n    public Loan(Tool tool, Member member, int dueDay) {\n        this.tool = tool;\n        this.member = member;\n        this.dueDay = dueDay;\n    }\n\n    public Tool getTool() {\n        return this.tool;\n    }\n\n    public Member getMember() {\n        return this.member;\n    }\n\n    public int getDueDay() {\n        return this.dueDay;\n    }\n\n    public boolean isLate(int today) {\n        return today > this.dueDay;\n    }\n\n    @Override\n    public int compareTo(Loan other) {\n        if (this.dueDay != other.dueDay) {\n            return Integer.compare(this.dueDay, other.dueDay);\n        }\n        return this.tool.getCode().compareTo(other.tool.getCode());\n    }\n\n    @Override\n    public String toString() {\n        return this.tool + \": \" + this.member + \", due day \" + this.dueDay;\n    }\n}\n\n// ==== LoanException.java ====\npublic class LoanException extends Exception {\n    public LoanException(String message) {\n        super(message);\n    }\n}\n","solution":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ToolShelf shelf = new ToolShelf();\n        shelf.addTool(new Tool(\"T2\", \"Hand saw\"));\n        shelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\n        shelf.addMember(new Member(\"M1\", \"Noor\"));\n        shelf.printTools();\n        shelf.printMembers();\n    }\n}\n\n// ==== Tool.java ====\npublic class Tool {\n    private String code;\n    private String name;\n\n    public Tool(String code, String name) {\n        this.code = code;\n        this.name = name;\n    }\n\n    public String getCode() {\n        return this.code;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.code + \" \" + this.name;\n    }\n}\n\n// ==== Member.java ====\npublic class Member {\n    private String card;\n    private String name;\n\n    public Member(String card, String name) {\n        this.card = card;\n        this.name = name;\n    }\n\n    public String getCard() {\n        return this.card;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.name + \" (\" + this.card + \")\";\n    }\n}\n\n// ==== ToolShelf.java ====\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\n\npublic class ToolShelf {\n    private HashMap<String, Tool> tools;\n    private HashMap<String, Member> members;\n    private HashMap<String, Loan> loans;\n\n    public ToolShelf() {\n        this.tools = new HashMap<>();\n        this.members = new HashMap<>();\n        this.loans = new HashMap<>();\n    }\n\n    public void addTool(Tool tool) {\n        this.tools.put(tool.getCode(), tool);\n    }\n\n    public void addMember(Member member) {\n        this.members.put(member.getCard(), member);\n    }\n\n    public Tool getTool(String code) throws LoanException {\n        if (!this.tools.containsKey(code)) {\n            throw new LoanException(\"No tool with the code \" + code);\n        }\n        return this.tools.get(code);\n    }\n\n    public Member getMember(String card) throws LoanException {\n        if (!this.members.containsKey(card)) {\n            throw new LoanException(\"No member with the card \" + card);\n        }\n        return this.members.get(card);\n    }\n\n    public void printTools() {\n        ArrayList<String> codes = new ArrayList<>(this.tools.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.tools.get(code));\n        }\n    }\n\n    public void printMembers() {\n        ArrayList<String> cards = new ArrayList<>(this.members.keySet());\n        Collections.sort(cards);\n        for (String card : cards) {\n            System.out.println(this.members.get(card));\n        }\n    }\n\n    public Loan lend(String toolCode, String card, int today) throws LoanException {\n        Tool tool = this.getTool(toolCode);\n        Member member = this.getMember(card);\n        if (this.loans.containsKey(toolCode)) {\n            int dueDay = this.loans.get(toolCode).getDueDay();\n            throw new LoanException(tool + \" is out, due back on day \" + dueDay);\n        }\n        if (this.countLoans(card) >= 2) {\n            throw new LoanException(member + \" already has 2 tools\");\n        }\n        Loan loan = new Loan(tool, member, today + 7);\n        this.loans.put(toolCode, loan);\n        return loan;\n    }\n\n    private int countLoans(String card) {\n        int count = 0;\n        for (Loan loan : this.loans.values()) {\n            if (loan.getMember().getCard().equals(card)) {\n                count++;\n            }\n        }\n        return count;\n    }\n\n    public int giveBack(String toolCode, int today) throws LoanException {\n        Tool tool = this.getTool(toolCode);\n        if (!this.loans.containsKey(toolCode)) {\n            throw new LoanException(tool + \" is not out\");\n        }\n        Loan loan = this.loans.get(toolCode);\n        this.loans.remove(toolCode);\n        if (loan.isLate(today)) {\n            return today - loan.getDueDay();\n        }\n        return 0;\n    }\n\n    public void printLoans() {\n        if (this.loans.isEmpty()) {\n            System.out.println(\"No tools are out\");\n        }\n        ArrayList<String> codes = new ArrayList<>(this.loans.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.loans.get(code));\n        }\n    }\n\n    public void printLate(int today) {\n        ArrayList<Loan> late = new ArrayList<>();\n        for (Loan loan : this.loans.values()) {\n            if (loan.isLate(today)) {\n                late.add(loan);\n            }\n        }\n        if (late.isEmpty()) {\n            System.out.println(\"Nothing is late\");\n        }\n        Collections.sort(late);\n        for (Loan loan : late) {\n            System.out.println(loan);\n        }\n    }\n\n    public void save(String fileName) throws IOException {\n        try (PrintWriter writer = new PrintWriter(fileName)) {\n            ArrayList<String> codes = new ArrayList<>(this.tools.keySet());\n            Collections.sort(codes);\n            for (String code : codes) {\n                writer.println(\"tool,\" + code + \",\" + this.tools.get(code).getName());\n            }\n            ArrayList<String> cards = new ArrayList<>(this.members.keySet());\n            Collections.sort(cards);\n            for (String card : cards) {\n                writer.println(\"member,\" + card + \",\" + this.members.get(card).getName());\n            }\n            ArrayList<String> lent = new ArrayList<>(this.loans.keySet());\n            Collections.sort(lent);\n            for (String code : lent) {\n                Loan loan = this.loans.get(code);\n                writer.println(\"loan,\" + code + \",\" + loan.getMember().getCard() + \",\" + loan.getDueDay());\n            }\n        }\n    }\n}\n\n// ==== Loan.java ====\npublic class Loan implements Comparable<Loan> {\n    private Tool tool;\n    private Member member;\n    private int dueDay;\n\n    public Loan(Tool tool, Member member, int dueDay) {\n        this.tool = tool;\n        this.member = member;\n        this.dueDay = dueDay;\n    }\n\n    public Tool getTool() {\n        return this.tool;\n    }\n\n    public Member getMember() {\n        return this.member;\n    }\n\n    public int getDueDay() {\n        return this.dueDay;\n    }\n\n    public boolean isLate(int today) {\n        return today > this.dueDay;\n    }\n\n    @Override\n    public int compareTo(Loan other) {\n        if (this.dueDay != other.dueDay) {\n            return Integer.compare(this.dueDay, other.dueDay);\n        }\n        return this.tool.getCode().compareTo(other.tool.getCode());\n    }\n\n    @Override\n    public String toString() {\n        return this.tool + \": \" + this.member + \", due day \" + this.dueDay;\n    }\n}\n\n// ==== LoanException.java ====\npublic class LoanException extends Exception {\n    public LoanException(String message) {\n        super(message);\n    }\n}\n","hints":["`try (PrintWriter writer = new PrintWriter(fileName)) { ... }`, with imports for `java.io.PrintWriter` and `java.io.IOException`. Inside, three loops over sorted keys, as in `printTools`, `printMembers` and `printLoans`.","A loan's line: `writer.println(\"loan,\" + code + \",\" + loan.getMember().getCard() + \",\" + loan.getDueDay());`. Use the getters, not `toString`: the file needs the parts, not the text the desk shows."],"tests":[{"name":"Save the shelf","stdin":"","call":"ToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T2\", \"Hand saw\"));\nshelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\nshelf.addMember(new Member(\"M2\", \"Ilse\"));\nshelf.addMember(new Member(\"M1\", \"Noor\"));\nshelf.lend(\"T2\", \"M2\", 4);\nshelf.save(\"shelf.csv\");\n","writes":{"shelf.csv":"tool,T1,Cordless drill\ntool,T2,Hand saw\nmember,M1,Noor\nmember,M2,Ilse\nloan,T2,M2,11"},"expect":"","hidden":false},{"name":"Save under another name","stdin":"","call":"ToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T5\", \"Wheelbarrow\"));\nshelf.addTool(new Tool(\"T3\", \"Ladder\"));\nshelf.addTool(new Tool(\"T4\", \"Orbital sander\"));\nshelf.addMember(new Member(\"M3\", \"Tomas\"));\nshelf.addMember(new Member(\"M1\", \"Noor\"));\nshelf.lend(\"T5\", \"M1\", 9);\nshelf.lend(\"T3\", \"M3\", 2);\nshelf.lend(\"T4\", \"M3\", 2);\nshelf.giveBack(\"T3\", 6);\nshelf.save(\"backup.csv\");\nSystem.out.println(\"saved\");\n","writes":{"backup.csv":"tool,T3,Ladder\ntool,T4,Orbital sander\ntool,T5,Wheelbarrow\nmember,M1,Noor\nmember,M3,Tomas\nloan,T4,M3,9\nloan,T5,M1,16"},"expect":"saved","hidden":true},{"name":"Save a shelf without loans","stdin":"","call":"ToolShelf shelf = new ToolShelf();\nshelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\nshelf.addMember(new Member(\"M2\", \"Ilse\"));\nshelf.save(\"quiet.csv\");\n","writes":{"quiet.csv":"tool,T1,Cordless drill\nmember,M2,Ilse"},"expect":"","hidden":true}],"require":[{"pattern":"\\bnew\\s+PrintWriter\\s*\\(","message":"Write the file with a `PrintWriter`."},{"pattern":"\\bvoid\\s+save\\s*\\(\\s*String\\s+\\w+\\s*\\)\\s*throws\\s+IOException\\b","message":"Declare `public void save(String fileName) throws IOException`."}],"forbid":[]},{"id":"loading","title":"Loading, and damaged lines","task":"Now the other direction. Add to ToolShelf a method `load(String fileName)` that reads a file that `save` wrote and adds its tools, members and loans to the shelf. It reads the file with a Scanner, line by line, and splits each line at the commas:\n\n- `tool,T1,Cordless drill` adds a tool, and `member,M1,Noor` a member.\n- `loan,T3,M1,11` puts a loan of the tool T3 to the member M1, due on day 11, straight into the loans map (it's a loan that was already made, so `lend`'s rules don't apply). The tool and the member must already be on the shelf: find them with `getTool` and `getMember`, which throw when they aren't.\n- An empty line is skipped.\n\nA file can be damaged, for example by someone who edited it by hand. Then `load` throws a LoanException whose message starts with `Line `, the number of the line (the first line is 1, and empty lines count) and a colon:\n\n- a line that isn't one of the three kinds, or doesn't have their number of parts: `Line 3: Can't read gadget,X1`, with the whole line\n- a due day that isn't a whole number: `Line 4: Not a day number: soon`\n- a tool or a member that isn't on the shelf: `Line 5: No tool with the code T9`, with `getTool`'s or `getMember`'s message\n\nThe lines before the damaged one stay loaded. `load` declares `throws IOException, LoanException`: a file that can't be read at all throws an IOException, as Scanner does.\n\nThe check gives the program this `shelf.csv`:\n\n```text\ntool,T1,Cordless drill\ntool,T3,Ladder\nmember,M1,Noor\nmember,M2,Ilse\nloan,T3,M1,11\nloan,T1,M2,15\n```\n\nand runs:\n\n```java\nToolShelf shelf = new ToolShelf();\nshelf.load(\"shelf.csv\");\nshelf.printTools();\nshelf.printMembers();\nshelf.printLoans();\nshelf.printLate(14);\n```\n\nIt expects:\n\n```\nT1 Cordless drill\nT3 Ladder\nNoor (M1)\nIlse (M2)\nT1 Cordless drill: Ilse (M2), due day 15\nT3 Ladder: Noor (M1), due day 11\nT3 Ladder: Noor (M1), due day 11\n```\n","kind":"code","seed":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ToolShelf shelf = new ToolShelf();\n        shelf.addTool(new Tool(\"T2\", \"Hand saw\"));\n        shelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\n        shelf.addMember(new Member(\"M1\", \"Noor\"));\n        shelf.printTools();\n        shelf.printMembers();\n    }\n}\n\n// ==== Tool.java ====\npublic class Tool {\n    private String code;\n    private String name;\n\n    public Tool(String code, String name) {\n        this.code = code;\n        this.name = name;\n    }\n\n    public String getCode() {\n        return this.code;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.code + \" \" + this.name;\n    }\n}\n\n// ==== Member.java ====\npublic class Member {\n    private String card;\n    private String name;\n\n    public Member(String card, String name) {\n        this.card = card;\n        this.name = name;\n    }\n\n    public String getCard() {\n        return this.card;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.name + \" (\" + this.card + \")\";\n    }\n}\n\n// ==== ToolShelf.java ====\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\n\npublic class ToolShelf {\n    private HashMap<String, Tool> tools;\n    private HashMap<String, Member> members;\n    private HashMap<String, Loan> loans;\n\n    public ToolShelf() {\n        this.tools = new HashMap<>();\n        this.members = new HashMap<>();\n        this.loans = new HashMap<>();\n    }\n\n    public void addTool(Tool tool) {\n        this.tools.put(tool.getCode(), tool);\n    }\n\n    public void addMember(Member member) {\n        this.members.put(member.getCard(), member);\n    }\n\n    public Tool getTool(String code) throws LoanException {\n        if (!this.tools.containsKey(code)) {\n            throw new LoanException(\"No tool with the code \" + code);\n        }\n        return this.tools.get(code);\n    }\n\n    public Member getMember(String card) throws LoanException {\n        if (!this.members.containsKey(card)) {\n            throw new LoanException(\"No member with the card \" + card);\n        }\n        return this.members.get(card);\n    }\n\n    public void printTools() {\n        ArrayList<String> codes = new ArrayList<>(this.tools.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.tools.get(code));\n        }\n    }\n\n    public void printMembers() {\n        ArrayList<String> cards = new ArrayList<>(this.members.keySet());\n        Collections.sort(cards);\n        for (String card : cards) {\n            System.out.println(this.members.get(card));\n        }\n    }\n\n    public Loan lend(String toolCode, String card, int today) throws LoanException {\n        Tool tool = this.getTool(toolCode);\n        Member member = this.getMember(card);\n        if (this.loans.containsKey(toolCode)) {\n            int dueDay = this.loans.get(toolCode).getDueDay();\n            throw new LoanException(tool + \" is out, due back on day \" + dueDay);\n        }\n        if (this.countLoans(card) >= 2) {\n            throw new LoanException(member + \" already has 2 tools\");\n        }\n        Loan loan = new Loan(tool, member, today + 7);\n        this.loans.put(toolCode, loan);\n        return loan;\n    }\n\n    private int countLoans(String card) {\n        int count = 0;\n        for (Loan loan : this.loans.values()) {\n            if (loan.getMember().getCard().equals(card)) {\n                count++;\n            }\n        }\n        return count;\n    }\n\n    public int giveBack(String toolCode, int today) throws LoanException {\n        Tool tool = this.getTool(toolCode);\n        if (!this.loans.containsKey(toolCode)) {\n            throw new LoanException(tool + \" is not out\");\n        }\n        Loan loan = this.loans.get(toolCode);\n        this.loans.remove(toolCode);\n        if (loan.isLate(today)) {\n            return today - loan.getDueDay();\n        }\n        return 0;\n    }\n\n    public void printLoans() {\n        if (this.loans.isEmpty()) {\n            System.out.println(\"No tools are out\");\n        }\n        ArrayList<String> codes = new ArrayList<>(this.loans.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.loans.get(code));\n        }\n    }\n\n    public void printLate(int today) {\n        ArrayList<Loan> late = new ArrayList<>();\n        for (Loan loan : this.loans.values()) {\n            if (loan.isLate(today)) {\n                late.add(loan);\n            }\n        }\n        if (late.isEmpty()) {\n            System.out.println(\"Nothing is late\");\n        }\n        Collections.sort(late);\n        for (Loan loan : late) {\n            System.out.println(loan);\n        }\n    }\n\n    public void save(String fileName) throws IOException {\n        try (PrintWriter writer = new PrintWriter(fileName)) {\n            ArrayList<String> codes = new ArrayList<>(this.tools.keySet());\n            Collections.sort(codes);\n            for (String code : codes) {\n                writer.println(\"tool,\" + code + \",\" + this.tools.get(code).getName());\n            }\n            ArrayList<String> cards = new ArrayList<>(this.members.keySet());\n            Collections.sort(cards);\n            for (String card : cards) {\n                writer.println(\"member,\" + card + \",\" + this.members.get(card).getName());\n            }\n            ArrayList<String> lent = new ArrayList<>(this.loans.keySet());\n            Collections.sort(lent);\n            for (String code : lent) {\n                Loan loan = this.loans.get(code);\n                writer.println(\"loan,\" + code + \",\" + loan.getMember().getCard() + \",\" + loan.getDueDay());\n            }\n        }\n    }\n}\n\n// ==== Loan.java ====\npublic class Loan implements Comparable<Loan> {\n    private Tool tool;\n    private Member member;\n    private int dueDay;\n\n    public Loan(Tool tool, Member member, int dueDay) {\n        this.tool = tool;\n        this.member = member;\n        this.dueDay = dueDay;\n    }\n\n    public Tool getTool() {\n        return this.tool;\n    }\n\n    public Member getMember() {\n        return this.member;\n    }\n\n    public int getDueDay() {\n        return this.dueDay;\n    }\n\n    public boolean isLate(int today) {\n        return today > this.dueDay;\n    }\n\n    @Override\n    public int compareTo(Loan other) {\n        if (this.dueDay != other.dueDay) {\n            return Integer.compare(this.dueDay, other.dueDay);\n        }\n        return this.tool.getCode().compareTo(other.tool.getCode());\n    }\n\n    @Override\n    public String toString() {\n        return this.tool + \": \" + this.member + \", due day \" + this.dueDay;\n    }\n}\n\n// ==== LoanException.java ====\npublic class LoanException extends Exception {\n    public LoanException(String message) {\n        super(message);\n    }\n}\n","solution":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ToolShelf shelf = new ToolShelf();\n        shelf.addTool(new Tool(\"T2\", \"Hand saw\"));\n        shelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\n        shelf.addMember(new Member(\"M1\", \"Noor\"));\n        shelf.printTools();\n        shelf.printMembers();\n    }\n}\n\n// ==== Tool.java ====\npublic class Tool {\n    private String code;\n    private String name;\n\n    public Tool(String code, String name) {\n        this.code = code;\n        this.name = name;\n    }\n\n    public String getCode() {\n        return this.code;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.code + \" \" + this.name;\n    }\n}\n\n// ==== Member.java ====\npublic class Member {\n    private String card;\n    private String name;\n\n    public Member(String card, String name) {\n        this.card = card;\n        this.name = name;\n    }\n\n    public String getCard() {\n        return this.card;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.name + \" (\" + this.card + \")\";\n    }\n}\n\n// ==== ToolShelf.java ====\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Scanner;\n\npublic class ToolShelf {\n    private HashMap<String, Tool> tools;\n    private HashMap<String, Member> members;\n    private HashMap<String, Loan> loans;\n\n    public ToolShelf() {\n        this.tools = new HashMap<>();\n        this.members = new HashMap<>();\n        this.loans = new HashMap<>();\n    }\n\n    public void addTool(Tool tool) {\n        this.tools.put(tool.getCode(), tool);\n    }\n\n    public void addMember(Member member) {\n        this.members.put(member.getCard(), member);\n    }\n\n    public Tool getTool(String code) throws LoanException {\n        if (!this.tools.containsKey(code)) {\n            throw new LoanException(\"No tool with the code \" + code);\n        }\n        return this.tools.get(code);\n    }\n\n    public Member getMember(String card) throws LoanException {\n        if (!this.members.containsKey(card)) {\n            throw new LoanException(\"No member with the card \" + card);\n        }\n        return this.members.get(card);\n    }\n\n    public void printTools() {\n        ArrayList<String> codes = new ArrayList<>(this.tools.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.tools.get(code));\n        }\n    }\n\n    public void printMembers() {\n        ArrayList<String> cards = new ArrayList<>(this.members.keySet());\n        Collections.sort(cards);\n        for (String card : cards) {\n            System.out.println(this.members.get(card));\n        }\n    }\n\n    public Loan lend(String toolCode, String card, int today) throws LoanException {\n        Tool tool = this.getTool(toolCode);\n        Member member = this.getMember(card);\n        if (this.loans.containsKey(toolCode)) {\n            int dueDay = this.loans.get(toolCode).getDueDay();\n            throw new LoanException(tool + \" is out, due back on day \" + dueDay);\n        }\n        if (this.countLoans(card) >= 2) {\n            throw new LoanException(member + \" already has 2 tools\");\n        }\n        Loan loan = new Loan(tool, member, today + 7);\n        this.loans.put(toolCode, loan);\n        return loan;\n    }\n\n    private int countLoans(String card) {\n        int count = 0;\n        for (Loan loan : this.loans.values()) {\n            if (loan.getMember().getCard().equals(card)) {\n                count++;\n            }\n        }\n        return count;\n    }\n\n    public int giveBack(String toolCode, int today) throws LoanException {\n        Tool tool = this.getTool(toolCode);\n        if (!this.loans.containsKey(toolCode)) {\n            throw new LoanException(tool + \" is not out\");\n        }\n        Loan loan = this.loans.get(toolCode);\n        this.loans.remove(toolCode);\n        if (loan.isLate(today)) {\n            return today - loan.getDueDay();\n        }\n        return 0;\n    }\n\n    public void printLoans() {\n        if (this.loans.isEmpty()) {\n            System.out.println(\"No tools are out\");\n        }\n        ArrayList<String> codes = new ArrayList<>(this.loans.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.loans.get(code));\n        }\n    }\n\n    public void printLate(int today) {\n        ArrayList<Loan> late = new ArrayList<>();\n        for (Loan loan : this.loans.values()) {\n            if (loan.isLate(today)) {\n                late.add(loan);\n            }\n        }\n        if (late.isEmpty()) {\n            System.out.println(\"Nothing is late\");\n        }\n        Collections.sort(late);\n        for (Loan loan : late) {\n            System.out.println(loan);\n        }\n    }\n\n    public void save(String fileName) throws IOException {\n        try (PrintWriter writer = new PrintWriter(fileName)) {\n            ArrayList<String> codes = new ArrayList<>(this.tools.keySet());\n            Collections.sort(codes);\n            for (String code : codes) {\n                writer.println(\"tool,\" + code + \",\" + this.tools.get(code).getName());\n            }\n            ArrayList<String> cards = new ArrayList<>(this.members.keySet());\n            Collections.sort(cards);\n            for (String card : cards) {\n                writer.println(\"member,\" + card + \",\" + this.members.get(card).getName());\n            }\n            ArrayList<String> lent = new ArrayList<>(this.loans.keySet());\n            Collections.sort(lent);\n            for (String code : lent) {\n                Loan loan = this.loans.get(code);\n                writer.println(\"loan,\" + code + \",\" + loan.getMember().getCard() + \",\" + loan.getDueDay());\n            }\n        }\n    }\n\n    public void load(String fileName) throws IOException, LoanException {\n        try (Scanner reader = new Scanner(Paths.get(fileName))) {\n            int number = 0;\n            while (reader.hasNextLine()) {\n                String line = reader.nextLine();\n                number++;\n                if (line.isEmpty()) {\n                    continue;\n                }\n                try {\n                    this.readLine(line);\n                } catch (LoanException e) {\n                    throw new LoanException(\"Line \" + number + \": \" + e.getMessage());\n                }\n            }\n        }\n    }\n\n    private void readLine(String line) throws LoanException {\n        String[] parts = line.split(\",\");\n        if (parts[0].equals(\"tool\") && parts.length == 3) {\n            this.addTool(new Tool(parts[1], parts[2]));\n        } else if (parts[0].equals(\"member\") && parts.length == 3) {\n            this.addMember(new Member(parts[1], parts[2]));\n        } else if (parts[0].equals(\"loan\") && parts.length == 4) {\n            Tool tool = this.getTool(parts[1]);\n            Member member = this.getMember(parts[2]);\n            int dueDay = 0;\n            try {\n                dueDay = Integer.parseInt(parts[3]);\n            } catch (NumberFormatException e) {\n                throw new LoanException(\"Not a day number: \" + parts[3]);\n            }\n            this.loans.put(tool.getCode(), new Loan(tool, member, dueDay));\n        } else {\n            throw new LoanException(\"Can't read \" + line);\n        }\n    }\n}\n\n// ==== Loan.java ====\npublic class Loan implements Comparable<Loan> {\n    private Tool tool;\n    private Member member;\n    private int dueDay;\n\n    public Loan(Tool tool, Member member, int dueDay) {\n        this.tool = tool;\n        this.member = member;\n        this.dueDay = dueDay;\n    }\n\n    public Tool getTool() {\n        return this.tool;\n    }\n\n    public Member getMember() {\n        return this.member;\n    }\n\n    public int getDueDay() {\n        return this.dueDay;\n    }\n\n    public boolean isLate(int today) {\n        return today > this.dueDay;\n    }\n\n    @Override\n    public int compareTo(Loan other) {\n        if (this.dueDay != other.dueDay) {\n            return Integer.compare(this.dueDay, other.dueDay);\n        }\n        return this.tool.getCode().compareTo(other.tool.getCode());\n    }\n\n    @Override\n    public String toString() {\n        return this.tool + \": \" + this.member + \", due day \" + this.dueDay;\n    }\n}\n\n// ==== LoanException.java ====\npublic class LoanException extends Exception {\n    public LoanException(String message) {\n        super(message);\n    }\n}\n","hints":["Count the lines in a variable that grows by one for every line read, before the empty ones are skipped. Read each line in a method of its own, such as `private void readLine(String line) throws LoanException`, and call it in a `try` block: `catch (LoanException e)` throws a new one, `new LoanException(\"Line \" + number + \": \" + e.getMessage())`.","In that method, `String[] parts = line.split(\",\");`, then check the kind and the length together: `parts[0].equals(\"loan\") && parts.length == 4`. Turn the day into a number with `Integer.parseInt` in a `try` block, and catch its NumberFormatException to throw a LoanException instead."],"tests":[{"name":"Load a shelf","stdin":"","call":"ToolShelf shelf = new ToolShelf();\nshelf.load(\"shelf.csv\");\nshelf.printTools();\nshelf.printMembers();\nshelf.printLoans();\nshelf.printLate(14);\n","files":{"shelf.csv":"tool,T1,Cordless drill\ntool,T3,Ladder\nmember,M1,Noor\nmember,M2,Ilse\nloan,T3,M1,11\nloan,T1,M2,15\n"},"expect":"T1 Cordless drill\nT3 Ladder\nNoor (M1)\nIlse (M2)\nT1 Cordless drill: Ilse (M2), due day 15\nT3 Ladder: Noor (M1), due day 11\nT3 Ladder: Noor (M1), due day 11","hidden":false},{"name":"A line that can't be read","stdin":"","call":"ToolShelf shelf = new ToolShelf();\ntry {\n    shelf.load(\"shelf.csv\");\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\nshelf.printTools();\n","files":{"shelf.csv":"tool,T1,Cordless drill\nmember,M1,Noor\ngadget,X1\ntool,T2,Hand saw\n"},"expect":"Line 3: Can't read gadget,X1\nT1 Cordless drill","hidden":false},{"name":"A day that isn't a number","stdin":"","call":"ToolShelf shelf = new ToolShelf();\ntry {\n    shelf.load(\"shelf.csv\");\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\nshelf.printLoans();\n","files":{"shelf.csv":"tool,T1,Cordless drill\ntool,T2,Hand saw\nmember,M1,Noor\nloan,T1,M1,soon\nloan,T2,M1,9\n"},"expect":"Line 4: Not a day number: soon\nNo tools are out","hidden":true},{"name":"A loan of a stranger","stdin":"","call":"ToolShelf shelf = new ToolShelf();\ntry {\n    shelf.load(\"shelf.csv\");\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\nshelf.printMembers();\nshelf.printLoans();\n","files":{"shelf.csv":"tool,T4,Orbital sander\nmember,M1,Noor\n\nloan,T4,M3,20\n"},"expect":"Line 4: No member with the card M3\nNoor (M1)\nNo tools are out","hidden":true},{"name":"Wrong numbers of parts","stdin":"","call":"ToolShelf shelf = new ToolShelf();\ntry {\n    shelf.load(\"one.csv\");\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\ntry {\n    shelf.load(\"two.csv\");\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\ntry {\n    shelf.load(\"three.csv\");\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\nshelf.printMembers();\n","files":{"one.csv":"tool,T1\n","two.csv":"\nmember,M1,Noor\n\nloan,T9,M1,4\n","three.csv":"member,M2,Ilse,extra\n"},"expect":"Line 1: Can't read tool,T1\nLine 4: No tool with the code T9\nLine 1: Can't read member,M2,Ilse,extra\nNoor (M1)","hidden":true},{"name":"Load, then save again","stdin":"","call":"ToolShelf shelf = new ToolShelf();\nshelf.load(\"shelf.csv\");\nshelf.lend(\"T2\", \"M3\", 3);\ntry {\n    shelf.lend(\"T5\", \"M3\", 3);\n} catch (LoanException e) {\n    System.out.println(e.getMessage());\n}\nshelf.save(\"copy.csv\");\n","files":{"shelf.csv":"member,M3,Tomas\ntool,T5,Wheelbarrow\nloan,T5,M3,8\ntool,T2,Hand saw\n"},"writes":{"copy.csv":"tool,T2,Hand saw\ntool,T5,Wheelbarrow\nmember,M3,Tomas\nloan,T2,M3,10\nloan,T5,M3,8"},"expect":"T5 Wheelbarrow is out, due back on day 8","hidden":true}],"require":[{"pattern":"\\bcatch\\s*\\(\\s*NumberFormatException\\b","message":"Catch the NumberFormatException of a due day that isn't a number, and throw a LoanException instead."},{"pattern":"\\bnew\\s+Scanner\\s*\\(\\s*Paths\\.get\\s*\\(","message":"Read the file with `new Scanner(Paths.get(fileName))`."}],"forbid":[]},{"id":"the-desk","title":"The desk","task":"Last, the desk itself. The new file `TextUi.java` holds the class `TextUi`, the program's text UI. Its constructor takes a Scanner and a ToolShelf and keeps them in the fields `scanner` and `shelf`, and the day, kept in the `int` field `today`, starts as day 1. `main` creates the shelf and the TextUi, and calls its `start()`, which:\n\n1. Loads `shelf.csv`. When there's no such file (an IOException), it prints `No shelf.csv yet, so the shelf starts empty` and goes on. When the file is damaged (a LoanException), it prints `Can't load shelf.csv. ` and the exception's message, and ends at once, without saving: saving would overwrite the lines it couldn't read.\n2. Reads commands, one per line, until `quit` or the end of the input. The words of a command are separated by spaces.\n3. Saves the shelf into `shelf.csv` and prints `Saved shelf.csv` (or `Could not save: ` and the message when that fails).\n\nThe commands:\n\n- `day 14` makes it day 14 and prints `It's day 14`. A day before today prints `The day can't go back: it's day 14`, and a day that isn't a whole number `Not a day number: soon`.\n- `tools`, `members` and `loans` print the shelf's tools, members and loans; `late` prints the loans that are late today.\n- `lend T1 M2` lends T1 to M2 today and prints `Lent: ` and the loan.\n- `return T3` gives T3 back and prints `T3 Ladder is back`, followed by `, 3 days late` when it's late (`, 1 day late` for one day).\n- When the shelf refuses a loan or a return, the desk prints `Sorry: ` and the exception's message.\n- Anything else, including a command with the wrong number of words, prints `Unknown command: ` and the line.\n\nInput isn't echoed. The check gives the program the `shelf.csv` of the introduction:\n\n```text\ntool,T1,Cordless drill\ntool,T2,Hand saw\ntool,T3,Ladder\nmember,M1,Noor\nmember,M2,Ilse\nloan,T3,M1,11\n```\n\nFor the input shown, the program prints:\n\n```\nIt's day 14\nT3 Ladder: Noor (M1), due day 11\nLent: T1 Cordless drill: Ilse (M2), due day 21\nSorry: T3 Ladder is out, due back on day 11\nT3 Ladder is back, 3 days late\nNothing is late\nSaved shelf.csv\n```\n","files":{"TextUi.java":"import java.util.Scanner;\n\npublic class TextUi {\n\n}\n"},"kind":"code","seed":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        ToolShelf shelf = new ToolShelf();\n        shelf.addTool(new Tool(\"T2\", \"Hand saw\"));\n        shelf.addTool(new Tool(\"T1\", \"Cordless drill\"));\n        shelf.addMember(new Member(\"M1\", \"Noor\"));\n        shelf.printTools();\n        shelf.printMembers();\n    }\n}\n\n// ==== Tool.java ====\npublic class Tool {\n    private String code;\n    private String name;\n\n    public Tool(String code, String name) {\n        this.code = code;\n        this.name = name;\n    }\n\n    public String getCode() {\n        return this.code;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.code + \" \" + this.name;\n    }\n}\n\n// ==== Member.java ====\npublic class Member {\n    private String card;\n    private String name;\n\n    public Member(String card, String name) {\n        this.card = card;\n        this.name = name;\n    }\n\n    public String getCard() {\n        return this.card;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.name + \" (\" + this.card + \")\";\n    }\n}\n\n// ==== ToolShelf.java ====\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Scanner;\n\npublic class ToolShelf {\n    private HashMap<String, Tool> tools;\n    private HashMap<String, Member> members;\n    private HashMap<String, Loan> loans;\n\n    public ToolShelf() {\n        this.tools = new HashMap<>();\n        this.members = new HashMap<>();\n        this.loans = new HashMap<>();\n    }\n\n    public void addTool(Tool tool) {\n        this.tools.put(tool.getCode(), tool);\n    }\n\n    public void addMember(Member member) {\n        this.members.put(member.getCard(), member);\n    }\n\n    public Tool getTool(String code) throws LoanException {\n        if (!this.tools.containsKey(code)) {\n            throw new LoanException(\"No tool with the code \" + code);\n        }\n        return this.tools.get(code);\n    }\n\n    public Member getMember(String card) throws LoanException {\n        if (!this.members.containsKey(card)) {\n            throw new LoanException(\"No member with the card \" + card);\n        }\n        return this.members.get(card);\n    }\n\n    public void printTools() {\n        ArrayList<String> codes = new ArrayList<>(this.tools.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.tools.get(code));\n        }\n    }\n\n    public void printMembers() {\n        ArrayList<String> cards = new ArrayList<>(this.members.keySet());\n        Collections.sort(cards);\n        for (String card : cards) {\n            System.out.println(this.members.get(card));\n        }\n    }\n\n    public Loan lend(String toolCode, String card, int today) throws LoanException {\n        Tool tool = this.getTool(toolCode);\n        Member member = this.getMember(card);\n        if (this.loans.containsKey(toolCode)) {\n            int dueDay = this.loans.get(toolCode).getDueDay();\n            throw new LoanException(tool + \" is out, due back on day \" + dueDay);\n        }\n        if (this.countLoans(card) >= 2) {\n            throw new LoanException(member + \" already has 2 tools\");\n        }\n        Loan loan = new Loan(tool, member, today + 7);\n        this.loans.put(toolCode, loan);\n        return loan;\n    }\n\n    private int countLoans(String card) {\n        int count = 0;\n        for (Loan loan : this.loans.values()) {\n            if (loan.getMember().getCard().equals(card)) {\n                count++;\n            }\n        }\n        return count;\n    }\n\n    public int giveBack(String toolCode, int today) throws LoanException {\n        Tool tool = this.getTool(toolCode);\n        if (!this.loans.containsKey(toolCode)) {\n            throw new LoanException(tool + \" is not out\");\n        }\n        Loan loan = this.loans.get(toolCode);\n        this.loans.remove(toolCode);\n        if (loan.isLate(today)) {\n            return today - loan.getDueDay();\n        }\n        return 0;\n    }\n\n    public void printLoans() {\n        if (this.loans.isEmpty()) {\n            System.out.println(\"No tools are out\");\n        }\n        ArrayList<String> codes = new ArrayList<>(this.loans.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.loans.get(code));\n        }\n    }\n\n    public void printLate(int today) {\n        ArrayList<Loan> late = new ArrayList<>();\n        for (Loan loan : this.loans.values()) {\n            if (loan.isLate(today)) {\n                late.add(loan);\n            }\n        }\n        if (late.isEmpty()) {\n            System.out.println(\"Nothing is late\");\n        }\n        Collections.sort(late);\n        for (Loan loan : late) {\n            System.out.println(loan);\n        }\n    }\n\n    public void save(String fileName) throws IOException {\n        try (PrintWriter writer = new PrintWriter(fileName)) {\n            ArrayList<String> codes = new ArrayList<>(this.tools.keySet());\n            Collections.sort(codes);\n            for (String code : codes) {\n                writer.println(\"tool,\" + code + \",\" + this.tools.get(code).getName());\n            }\n            ArrayList<String> cards = new ArrayList<>(this.members.keySet());\n            Collections.sort(cards);\n            for (String card : cards) {\n                writer.println(\"member,\" + card + \",\" + this.members.get(card).getName());\n            }\n            ArrayList<String> lent = new ArrayList<>(this.loans.keySet());\n            Collections.sort(lent);\n            for (String code : lent) {\n                Loan loan = this.loans.get(code);\n                writer.println(\"loan,\" + code + \",\" + loan.getMember().getCard() + \",\" + loan.getDueDay());\n            }\n        }\n    }\n\n    public void load(String fileName) throws IOException, LoanException {\n        try (Scanner reader = new Scanner(Paths.get(fileName))) {\n            int number = 0;\n            while (reader.hasNextLine()) {\n                String line = reader.nextLine();\n                number++;\n                if (line.isEmpty()) {\n                    continue;\n                }\n                try {\n                    this.readLine(line);\n                } catch (LoanException e) {\n                    throw new LoanException(\"Line \" + number + \": \" + e.getMessage());\n                }\n            }\n        }\n    }\n\n    private void readLine(String line) throws LoanException {\n        String[] parts = line.split(\",\");\n        if (parts[0].equals(\"tool\") && parts.length == 3) {\n            this.addTool(new Tool(parts[1], parts[2]));\n        } else if (parts[0].equals(\"member\") && parts.length == 3) {\n            this.addMember(new Member(parts[1], parts[2]));\n        } else if (parts[0].equals(\"loan\") && parts.length == 4) {\n            Tool tool = this.getTool(parts[1]);\n            Member member = this.getMember(parts[2]);\n            int dueDay = 0;\n            try {\n                dueDay = Integer.parseInt(parts[3]);\n            } catch (NumberFormatException e) {\n                throw new LoanException(\"Not a day number: \" + parts[3]);\n            }\n            this.loans.put(tool.getCode(), new Loan(tool, member, dueDay));\n        } else {\n            throw new LoanException(\"Can't read \" + line);\n        }\n    }\n}\n\n// ==== Loan.java ====\npublic class Loan implements Comparable<Loan> {\n    private Tool tool;\n    private Member member;\n    private int dueDay;\n\n    public Loan(Tool tool, Member member, int dueDay) {\n        this.tool = tool;\n        this.member = member;\n        this.dueDay = dueDay;\n    }\n\n    public Tool getTool() {\n        return this.tool;\n    }\n\n    public Member getMember() {\n        return this.member;\n    }\n\n    public int getDueDay() {\n        return this.dueDay;\n    }\n\n    public boolean isLate(int today) {\n        return today > this.dueDay;\n    }\n\n    @Override\n    public int compareTo(Loan other) {\n        if (this.dueDay != other.dueDay) {\n            return Integer.compare(this.dueDay, other.dueDay);\n        }\n        return this.tool.getCode().compareTo(other.tool.getCode());\n    }\n\n    @Override\n    public String toString() {\n        return this.tool + \": \" + this.member + \", due day \" + this.dueDay;\n    }\n}\n\n// ==== LoanException.java ====\npublic class LoanException extends Exception {\n    public LoanException(String message) {\n        super(message);\n    }\n}\n\n// ==== TextUi.java ====\nimport java.util.Scanner;\n\npublic class TextUi {\n\n}\n","solution":"// ==== Main.java ====\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        ToolShelf shelf = new ToolShelf();\n        TextUi ui = new TextUi(new Scanner(System.in), shelf);\n        ui.start();\n    }\n}\n\n// ==== Tool.java ====\npublic class Tool {\n    private String code;\n    private String name;\n\n    public Tool(String code, String name) {\n        this.code = code;\n        this.name = name;\n    }\n\n    public String getCode() {\n        return this.code;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.code + \" \" + this.name;\n    }\n}\n\n// ==== Member.java ====\npublic class Member {\n    private String card;\n    private String name;\n\n    public Member(String card, String name) {\n        this.card = card;\n        this.name = name;\n    }\n\n    public String getCard() {\n        return this.card;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    @Override\n    public String toString() {\n        return this.name + \" (\" + this.card + \")\";\n    }\n}\n\n// ==== ToolShelf.java ====\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Scanner;\n\npublic class ToolShelf {\n    private HashMap<String, Tool> tools;\n    private HashMap<String, Member> members;\n    private HashMap<String, Loan> loans;\n\n    public ToolShelf() {\n        this.tools = new HashMap<>();\n        this.members = new HashMap<>();\n        this.loans = new HashMap<>();\n    }\n\n    public void addTool(Tool tool) {\n        this.tools.put(tool.getCode(), tool);\n    }\n\n    public void addMember(Member member) {\n        this.members.put(member.getCard(), member);\n    }\n\n    public Tool getTool(String code) throws LoanException {\n        if (!this.tools.containsKey(code)) {\n            throw new LoanException(\"No tool with the code \" + code);\n        }\n        return this.tools.get(code);\n    }\n\n    public Member getMember(String card) throws LoanException {\n        if (!this.members.containsKey(card)) {\n            throw new LoanException(\"No member with the card \" + card);\n        }\n        return this.members.get(card);\n    }\n\n    public void printTools() {\n        ArrayList<String> codes = new ArrayList<>(this.tools.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.tools.get(code));\n        }\n    }\n\n    public void printMembers() {\n        ArrayList<String> cards = new ArrayList<>(this.members.keySet());\n        Collections.sort(cards);\n        for (String card : cards) {\n            System.out.println(this.members.get(card));\n        }\n    }\n\n    public Loan lend(String toolCode, String card, int today) throws LoanException {\n        Tool tool = this.getTool(toolCode);\n        Member member = this.getMember(card);\n        if (this.loans.containsKey(toolCode)) {\n            int dueDay = this.loans.get(toolCode).getDueDay();\n            throw new LoanException(tool + \" is out, due back on day \" + dueDay);\n        }\n        if (this.countLoans(card) >= 2) {\n            throw new LoanException(member + \" already has 2 tools\");\n        }\n        Loan loan = new Loan(tool, member, today + 7);\n        this.loans.put(toolCode, loan);\n        return loan;\n    }\n\n    private int countLoans(String card) {\n        int count = 0;\n        for (Loan loan : this.loans.values()) {\n            if (loan.getMember().getCard().equals(card)) {\n                count++;\n            }\n        }\n        return count;\n    }\n\n    public int giveBack(String toolCode, int today) throws LoanException {\n        Tool tool = this.getTool(toolCode);\n        if (!this.loans.containsKey(toolCode)) {\n            throw new LoanException(tool + \" is not out\");\n        }\n        Loan loan = this.loans.get(toolCode);\n        this.loans.remove(toolCode);\n        if (loan.isLate(today)) {\n            return today - loan.getDueDay();\n        }\n        return 0;\n    }\n\n    public void printLoans() {\n        if (this.loans.isEmpty()) {\n            System.out.println(\"No tools are out\");\n        }\n        ArrayList<String> codes = new ArrayList<>(this.loans.keySet());\n        Collections.sort(codes);\n        for (String code : codes) {\n            System.out.println(this.loans.get(code));\n        }\n    }\n\n    public void printLate(int today) {\n        ArrayList<Loan> late = new ArrayList<>();\n        for (Loan loan : this.loans.values()) {\n            if (loan.isLate(today)) {\n                late.add(loan);\n            }\n        }\n        if (late.isEmpty()) {\n            System.out.println(\"Nothing is late\");\n        }\n        Collections.sort(late);\n        for (Loan loan : late) {\n            System.out.println(loan);\n        }\n    }\n\n    public void save(String fileName) throws IOException {\n        try (PrintWriter writer = new PrintWriter(fileName)) {\n            ArrayList<String> codes = new ArrayList<>(this.tools.keySet());\n            Collections.sort(codes);\n            for (String code : codes) {\n                writer.println(\"tool,\" + code + \",\" + this.tools.get(code).getName());\n            }\n            ArrayList<String> cards = new ArrayList<>(this.members.keySet());\n            Collections.sort(cards);\n            for (String card : cards) {\n                writer.println(\"member,\" + card + \",\" + this.members.get(card).getName());\n            }\n            ArrayList<String> lent = new ArrayList<>(this.loans.keySet());\n            Collections.sort(lent);\n            for (String code : lent) {\n                Loan loan = this.loans.get(code);\n                writer.println(\"loan,\" + code + \",\" + loan.getMember().getCard() + \",\" + loan.getDueDay());\n            }\n        }\n    }\n\n    public void load(String fileName) throws IOException, LoanException {\n        try (Scanner reader = new Scanner(Paths.get(fileName))) {\n            int number = 0;\n            while (reader.hasNextLine()) {\n                String line = reader.nextLine();\n                number++;\n                if (line.isEmpty()) {\n                    continue;\n                }\n                try {\n                    this.readLine(line);\n                } catch (LoanException e) {\n                    throw new LoanException(\"Line \" + number + \": \" + e.getMessage());\n                }\n            }\n        }\n    }\n\n    private void readLine(String line) throws LoanException {\n        String[] parts = line.split(\",\");\n        if (parts[0].equals(\"tool\") && parts.length == 3) {\n            this.addTool(new Tool(parts[1], parts[2]));\n        } else if (parts[0].equals(\"member\") && parts.length == 3) {\n            this.addMember(new Member(parts[1], parts[2]));\n        } else if (parts[0].equals(\"loan\") && parts.length == 4) {\n            Tool tool = this.getTool(parts[1]);\n            Member member = this.getMember(parts[2]);\n            int dueDay = 0;\n            try {\n                dueDay = Integer.parseInt(parts[3]);\n            } catch (NumberFormatException e) {\n                throw new LoanException(\"Not a day number: \" + parts[3]);\n            }\n            this.loans.put(tool.getCode(), new Loan(tool, member, dueDay));\n        } else {\n            throw new LoanException(\"Can't read \" + line);\n        }\n    }\n}\n\n// ==== Loan.java ====\npublic class Loan implements Comparable<Loan> {\n    private Tool tool;\n    private Member member;\n    private int dueDay;\n\n    public Loan(Tool tool, Member member, int dueDay) {\n        this.tool = tool;\n        this.member = member;\n        this.dueDay = dueDay;\n    }\n\n    public Tool getTool() {\n        return this.tool;\n    }\n\n    public Member getMember() {\n        return this.member;\n    }\n\n    public int getDueDay() {\n        return this.dueDay;\n    }\n\n    public boolean isLate(int today) {\n        return today > this.dueDay;\n    }\n\n    @Override\n    public int compareTo(Loan other) {\n        if (this.dueDay != other.dueDay) {\n            return Integer.compare(this.dueDay, other.dueDay);\n        }\n        return this.tool.getCode().compareTo(other.tool.getCode());\n    }\n\n    @Override\n    public String toString() {\n        return this.tool + \": \" + this.member + \", due day \" + this.dueDay;\n    }\n}\n\n// ==== LoanException.java ====\npublic class LoanException extends Exception {\n    public LoanException(String message) {\n        super(message);\n    }\n}\n\n// ==== TextUi.java ====\nimport java.io.IOException;\nimport java.util.Scanner;\n\npublic class TextUi {\n    private Scanner scanner;\n    private ToolShelf shelf;\n    private int today;\n\n    public TextUi(Scanner scanner, ToolShelf shelf) {\n        this.scanner = scanner;\n        this.shelf = shelf;\n        this.today = 1;\n    }\n\n    public void start() {\n        try {\n            this.shelf.load(\"shelf.csv\");\n        } catch (IOException e) {\n            System.out.println(\"No shelf.csv yet, so the shelf starts empty\");\n        } catch (LoanException e) {\n            System.out.println(\"Can't load shelf.csv. \" + e.getMessage());\n            return;\n        }\n        while (this.scanner.hasNextLine()) {\n            String command = this.scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                break;\n            }\n            try {\n                this.handle(command);\n            } catch (LoanException e) {\n                System.out.println(\"Sorry: \" + e.getMessage());\n            }\n        }\n        try {\n            this.shelf.save(\"shelf.csv\");\n            System.out.println(\"Saved shelf.csv\");\n        } catch (IOException e) {\n            System.out.println(\"Could not save: \" + e.getMessage());\n        }\n    }\n\n    private void handle(String command) throws LoanException {\n        String[] parts = command.split(\" \");\n        if (command.equals(\"tools\")) {\n            this.shelf.printTools();\n        } else if (command.equals(\"members\")) {\n            this.shelf.printMembers();\n        } else if (command.equals(\"loans\")) {\n            this.shelf.printLoans();\n        } else if (command.equals(\"late\")) {\n            this.shelf.printLate(this.today);\n        } else if (parts[0].equals(\"day\") && parts.length == 2) {\n            this.changeDay(parts[1]);\n        } else if (parts[0].equals(\"lend\") && parts.length == 3) {\n            Loan loan = this.shelf.lend(parts[1], parts[2], this.today);\n            System.out.println(\"Lent: \" + loan);\n        } else if (parts[0].equals(\"return\") && parts.length == 2) {\n            this.giveBack(parts[1]);\n        } else {\n            System.out.println(\"Unknown command: \" + command);\n        }\n    }\n\n    private void changeDay(String text) {\n        int day = 0;\n        try {\n            day = Integer.parseInt(text);\n        } catch (NumberFormatException e) {\n            System.out.println(\"Not a day number: \" + text);\n            return;\n        }\n        if (day < this.today) {\n            System.out.println(\"The day can't go back: it's day \" + this.today);\n            return;\n        }\n        this.today = day;\n        System.out.println(\"It's day \" + this.today);\n    }\n\n    private void giveBack(String code) throws LoanException {\n        int late = this.shelf.giveBack(code, this.today);\n        String text = this.shelf.getTool(code) + \" is back\";\n        if (late == 1) {\n            text = text + \", 1 day late\";\n        } else if (late > 1) {\n            text = text + \", \" + late + \" days late\";\n        }\n        System.out.println(text);\n    }\n}\n","hints":["`start()` has three parts, one after the other: a `try` block around `load` with two `catch` blocks (the LoanException one ends with `return;`), a `while (this.scanner.hasNextLine())` loop that breaks on `quit`, and a `try` block around `save`.","Handle one command in a method of its own that declares `throws LoanException`, and call it inside a `try` block in the loop, so one `catch (LoanException e)` prints `Sorry: ` for every command. In it, `String[] parts = command.split(\" \");` and check the first word and `parts.length` together.","For `return`: `int late = this.shelf.giveBack(parts[1], this.today);`, then print `this.shelf.getTool(parts[1])` with `is back`, adding `, 1 day late` or `, ` and the number with `days late` when `late` is more than 0."],"tests":[{"name":"A day at the desk","stdin":"day 14\nloans\nlend T1 M2\nlend T3 M2\nreturn T3\nlate\nquit\n","files":{"shelf.csv":"tool,T1,Cordless drill\ntool,T2,Hand saw\ntool,T3,Ladder\nmember,M1,Noor\nmember,M2,Ilse\nloan,T3,M1,11\n"},"writes":{"shelf.csv":"tool,T1,Cordless drill\ntool,T2,Hand saw\ntool,T3,Ladder\nmember,M1,Noor\nmember,M2,Ilse\nloan,T1,M2,21"},"expect":"It's day 14\nT3 Ladder: Noor (M1), due day 11\nLent: T1 Cordless drill: Ilse (M2), due day 21\nSorry: T3 Ladder is out, due back on day 11\nT3 Ladder is back, 3 days late\nNothing is late\nSaved shelf.csv","hidden":false},{"name":"The first day of the shelf","stdin":"tools\nloans\nlend T1 M1\nquit\n","writes":{"shelf.csv":""},"expect":"No shelf.csv yet, so the shelf starts empty\nNo tools are out\nSorry: No tool with the code T1\nSaved shelf.csv","hidden":false},{"name":"A damaged shelf file","stdin":"tools\nquit\n","files":{"shelf.csv":"tool,T1,Cordless drill\nmember,M1,Noor\nloan,T1,M1,next week\n"},"expect":"Can't load shelf.csv. Line 3: Not a day number: next week","hidden":true},{"name":"Mistakes at the desk","stdin":"day 5\nday 3\nday soon\nday\nborrow T1\nlend T1\nlend T9 M1\nlend T1 M8\nreturn T4\nreturn T2\nday 10\nlend T1 M1\nlend T4 M1\nlend T2 M1\nmembers\ntools\nday 19\nreturn T1\nlate\nday 20\nreturn T4\nquit\ntools\n","files":{"shelf.csv":"tool,T1,Cordless drill\ntool,T2,Hand saw\ntool,T4,Orbital sander\nmember,M1,Noor\nmember,M3,Tomas\nloan,T2,M3,6\n"},"writes":{"shelf.csv":"tool,T1,Cordless drill\ntool,T2,Hand saw\ntool,T4,Orbital sander\nmember,M1,Noor\nmember,M3,Tomas"},"expect":"It's day 5\nThe day can't go back: it's day 5\nNot a day number: soon\nUnknown command: day\nUnknown command: borrow T1\nUnknown command: lend T1\nSorry: No tool with the code T9\nSorry: No member with the card M8\nSorry: T4 Orbital sander is not out\nT2 Hand saw is back\nIt's day 10\nLent: T1 Cordless drill: Noor (M1), due day 17\nLent: T4 Orbital sander: Noor (M1), due day 17\nSorry: Noor (M1) already has 2 tools\nNoor (M1)\nTomas (M3)\nT1 Cordless drill\nT2 Hand saw\nT4 Orbital sander\nIt's day 19\nT1 Cordless drill is back, 2 days late\nT4 Orbital sander: Noor (M1), due day 17\nIt's day 20\nT4 Orbital sander is back, 3 days late\nSaved shelf.csv","hidden":true},{"name":"Input that ends without quit","stdin":"day 3\nlend T5 M2\nlate\nday 11\nlate\nreturn T5\nlend T5 M2\nlate\n","files":{"shelf.csv":"tool,T5,Wheelbarrow\nmember,M2,Ilse\n"},"writes":{"shelf.csv":"tool,T5,Wheelbarrow\nmember,M2,Ilse\nloan,T5,M2,18"},"expect":"It's day 3\nLent: T5 Wheelbarrow: Ilse (M2), due day 10\nNothing is late\nIt's day 11\nT5 Wheelbarrow: Ilse (M2), due day 10\nT5 Wheelbarrow is back, 1 day late\nLent: T5 Wheelbarrow: Ilse (M2), due day 18\nNothing is late\nSaved shelf.csv","hidden":true}],"require":[{"pattern":"\\bcatch\\s*\\(\\s*LoanException\\b","message":"Catch the shelf's LoanException and print `Sorry: ` with its message."},{"pattern":"\\.start\\s*\\(\\s*\\)","message":"In `main`, create a TextUi and call its `start()`."}],"forbid":[]}]}