├── .gitignore ├── README.md ├── bin ├── parking_lot ├── parking_lot.sh ├── run_functional_tests ├── setup └── setup.sh ├── file_input.txt ├── pom.xml ├── problem-statment.md └── src ├── main └── java │ └── com │ └── uditagarwal │ ├── Main.java │ ├── OutputPrinter.java │ ├── commands │ ├── ColorToRegNumberCommandExecutor.java │ ├── ColorToSlotNumberCommandExecutor.java │ ├── CommandExecutor.java │ ├── CommandExecutorFactory.java │ ├── CreateParkingLotCommandExecutor.java │ ├── ExitCommandExecutor.java │ ├── LeaveCommandExecutor.java │ ├── ParkCommandExecutor.java │ ├── SlotForRegNumberCommandExecutor.java │ └── StatusCommandExecutor.java │ ├── exception │ ├── InvalidCommandException.java │ ├── InvalidModeException.java │ ├── InvalidSlotException.java │ ├── NoFreeSlotAvailableException.java │ ├── ParkingLotException.java │ └── SlotAlreadyOccupiedException.java │ ├── mode │ ├── FileMode.java │ ├── InteractiveMode.java │ └── Mode.java │ ├── model │ ├── Car.java │ ├── Command.java │ ├── ParkingLot.java │ ├── Slot.java │ └── parking │ │ └── strategy │ │ ├── NaturalOrderingParkingStrategy.java │ │ └── ParkingStrategy.java │ ├── service │ └── ParkingLotService.java │ └── validator │ └── IntegerValidator.java └── test └── java └── com └── uditagarwal ├── MainTest.java ├── commands ├── ColorToRegNumberCommandExecutorTest.java ├── ColorToSlotNumberCommandExecutorTest.java ├── CommandExecutorFactoryTest.java ├── CreateParkingLotCommandExecutorTest.java ├── ExitCommandExecutorTest.java ├── LeaveCommandExecutorTest.java ├── ParkCommandExecutorTest.java ├── SlotForRegNumberCommandExecutorTest.java └── StatusCommandExecutorTest.java ├── model ├── CommandTest.java ├── ParkingLotTest.java └── parking │ └── strategy │ └── NaturalOrderingParkingStrategyTest.java └── service └── ParkingLotServiceTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | target 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Video explanation: 2 | https://youtu.be/7IX84K9g23U 3 | 4 | ## Connect with me and my offerings: 5 | https://enginebogie.com/u/anomaly2104 6 | 7 | ## LLD Cohort: 8 | https://enginebogie.com/u/anomaly2104/offerings/PATH/e6cce7f1-6a56-4fe3-bb82-48e1876e4596 9 | 10 | ## Multi-threading Cohort: 11 | https://enginebogie.com/u/anomaly2104/offerings/PATH/e9522ac1-4e4c-4217-92ba-f691f34c321b 12 | 13 | ---- 14 | # Low Level System Design - Parking lot 15 | 16 | ### Problem Statement 17 | [Check here](problem-statment.md) 18 | 19 | ### Project Requirements 20 | 21 | * JDK 1.8 22 | * Maven 23 | * For Unit Tests: 24 | * Junit 4 25 | * Mockito 26 | 27 | ### Compiling/Building and running the unit tests 28 | Go to the project root folder and then run: ./bin/setup.sh 29 | 30 | ### Runing the project 31 | NOTE: Before running, please make sure you do the above setup step. Otherwise it will not run. 32 | The project can be run as follows in one of the two ways: 33 | 34 | 1) **Using file based input:**: It accepts a filename as a parameter at the command prompt and read the commands from that file. 35 | ./bin/parking_lot.sh 36 | Example: ./bin/parking_lot.sh ./file_input.txt 37 | 2) **Using file based input:**: This will run the program in the interactive shell mode where commands can be typed in. 38 | ./bin/parking_lot.sh 39 | 40 | 41 | ### Further Enhancements: 42 | 43 | * Dependency injection: Currently dependencies are injected manually. We can use some 44 | dependency injection framework like spring. 45 | * Exit command: Exit command is currently coupled with interactive mode only which makes 46 | it non-reusable. 47 | * Parking strategy: Parking strategy is currently associated with `ParkingLotService`. 48 | Instead of that, it makes more sense to associate it with `ParkingLot`. 49 | * Mode: Mode checking is currently done in main function directly. There could be a 50 | factory for that. 51 | -------------------------------------------------------------------------------- /bin/parking_lot: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" 4 | cd "${SCRIPT_DIR}" 5 | cd .. 6 | 7 | JAR_RELATIVE_PATH=target/parking-lot-1.0-SNAPSHOT.jar 8 | 9 | if [ -z "$1" ] ; then 10 | java -jar $JAR_RELATIVE_PATH 11 | exit 1 12 | 13 | else 14 | inputFile=$1 15 | java -jar $JAR_RELATIVE_PATH $inputFile 16 | fi 17 | -------------------------------------------------------------------------------- /bin/parking_lot.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" 4 | cd "${SCRIPT_DIR}" 5 | cd .. 6 | 7 | JAR_RELATIVE_PATH=target/parking-lot-1.0-SNAPSHOT.jar 8 | 9 | if [ -z "$1" ] ; then 10 | java -jar $JAR_RELATIVE_PATH 11 | exit 1 12 | 13 | else 14 | inputFile=$1 15 | java -jar $JAR_RELATIVE_PATH $inputFile 16 | fi 17 | -------------------------------------------------------------------------------- /bin/run_functional_tests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | project_bin_dir = File.join(File.dirname(File.expand_path(__FILE__))) 4 | functional_spec_dir = File.join(project_bin_dir, '..', 'functional_spec') 5 | cmd = "cd #{functional_spec_dir}; bundle install; bundle exec rake spec:functional" 6 | puts cmd 7 | system cmd 8 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" 4 | cd "${SCRIPT_DIR}" 5 | cd .. 6 | mvn clean install 7 | -------------------------------------------------------------------------------- /bin/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" 4 | cd "${SCRIPT_DIR}" 5 | cd .. 6 | mvn clean install 7 | -------------------------------------------------------------------------------- /file_input.txt: -------------------------------------------------------------------------------- 1 | create_parking_lot 6 2 | park KA-01-HH-1234 White 3 | park KA-01-HH-9999 White 4 | park KA-01-BB-0001 Black 5 | park KA-01-HH-7777 Red 6 | park KA-01-HH-2701 Blue 7 | park KA-01-HH-3141 Black 8 | leave 4 9 | status 10 | park KA-01-P-333 White 11 | park DL-12-AA-9999 White 12 | registration_numbers_for_cars_with_colour White 13 | slot_numbers_for_cars_with_colour White 14 | slot_number_for_registration_number KA-01-HH-3141 15 | slot_number_for_registration_number MH-04-AY-1111 -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.uditagarwal 8 | parking-lot 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 16 | 8 17 | 8 18 | 19 | 20 | 21 | org.apache.maven.plugins 22 | maven-jar-plugin 23 | 3.1.0 24 | 25 | 26 | 27 | true 28 | com.uditagarwal.Main 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | junit 38 | junit 39 | 4.13.1 40 | test 41 | 42 | 43 | org.mockito 44 | mockito-all 45 | 1.10.19 46 | test 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /problem-statment.md: -------------------------------------------------------------------------------- 1 | ## Problem Statement 2 | 3 | I own a parking lot that can hold up to 'n' cars at any given point in time. Each slot is 4 | given a number starting at 1 increasing with increasing distance from the entry point 5 | in steps of one. I want to create an automated ticketing system that allows my 6 | customers to use my parking lot without human intervention. 7 | 8 | When a car enters my parking lot, I want to have a ticket issued to the driver. The 9 | ticket issuing process includes us documenting the registration number (number 10 | plate) and the colour of the car and allocating an available parking slot to the car 11 | before actually handing over a ticket to the driver (we assume that our customers are 12 | nice enough to always park in the slots allocated to them). The customer should be 13 | allocated a parking slot which is nearest to the entry. At the exit the customer returns 14 | the ticket which then marks the slot they were using as being available. 15 | 16 | Due to government regulation, the system should provide me with the ability to find out: 17 | * Registration numbers of all cars of a particular colour. 18 | * Slot number in which a car with a given registration number is parked. 19 | * Slot numbers of all slots where a car of a particular colour is parked. 20 | 21 | We interact with the system via a simple set of commands which produce a specific 22 | output. Please take a look at the example below, which includes all the commands 23 | you need to support - they're self explanatory. The system should allow input in two 24 | ways. Just to clarify, the same codebase should support both modes of input - we 25 | don't want two distinct submissions. 26 | 27 | 1. It should provide us with an interactive command prompt based shell where 28 | commands can be typed in 29 | 1. It should accept a filename as a parameter at the command prompt and read the 30 | commands from that file 31 | 32 | ### Example: File 33 | 34 | #### Input (contents of file): 35 | create_parking_lot 6 36 | park KA-01-HH-1234 White 37 | park KA-01-HH-9999 White 38 | park KA-01-BB-0001 Black 39 | park KA-01-HH-7777 Red 40 | park KA-01-HH-2701 Blue 41 | park KA-01-HH-3141 Black 42 | leave 4 43 | status 44 | park KA-01-P-333 White 45 | park DL-12-AA-9999 White 46 | registration_numbers_for_cars_with_colour White 47 | slot_numbers_for_cars_with_colour White 48 | slot_number_for_registration_number KA-01-HH-3141 49 | slot_number_for_registration_number MH-04-AY-1111 50 | 51 | #### Output (to STDOUT) 52 | 53 | Created a parking lot with 6 slots 54 | Allocated slot number: 1 55 | Allocated slot number: 2 56 | Allocated slot number: 3 57 | Allocated slot number: 4 58 | Allocated slot number: 5 59 | Allocated slot number: 6 60 | Slot number 4 is free 61 | Slot No. Registration No Colour 62 | 1 KA-01-HH-1234 White 63 | 2 KA-01-HH-9999 White 64 | 3 KA-01-BB-0001 Black 65 | 5 KA-01-HH-2701 Blue 66 | 6 KA-01-HH-3141 Black 67 | Allocated slot number: 4 68 | Sorry, parking lot is full 69 | KA-01-HH-1234, KA-01-HH-9999, KA-01-P-333 70 | 1, 2, 4 71 | 6 72 | Not found 73 | 74 | ### Example: Interactive 75 | Assuming a parking lot with 6 slots, the following commands should be run in 76 | sequence by typing them in at a prompt and should produce output as described 77 | below the command. Note that `exit` terminates the process and returns control to 78 | the shell. 79 | 80 | $ create_parking_lot 6 81 | Created a parking lot with 6 slots 82 | 83 | $ park KA-01-HH-1234 White 84 | Allocated slot number: 1 85 | 86 | $ park KA-01-HH-9999 White 87 | Allocated slot number: 2 88 | 89 | $ park KA-01-BB-0001 Black 90 | Allocated slot number: 3 91 | 92 | $ park KA-01-HH-7777 Red 93 | Allocated slot number: 4 94 | 95 | $ park KA-01-HH-2701 Blue 96 | Allocated slot number: 5 97 | 98 | $ park KA-01-HH-3141 Black 99 | Allocated slot number: 6 100 | 101 | $ leave 4 102 | Slot number 4 is free 103 | 104 | $ status 105 | Slot No. Registration No Colour 106 | 1 KA-01-HH-1234 White 107 | 2 KA-01-HH-9999 White 108 | 3 KA-01-BB-0001 Black 109 | 5 KA-01-HH-2701 Blue 110 | 6 KA-01-HH-3141 Black 111 | 112 | $ park KA-01-P-333 White 113 | Allocated slot number: 4 114 | 115 | $ park DL-12-AA-9999 White 116 | Sorry, parking lot is full 117 | 118 | $ registration_numbers_for_cars_with_colour White 119 | KA-01-HH-1234, KA-01-HH-9999, KA-01-P-333 120 | 121 | $ slot_numbers_for_cars_with_colour White 122 | 1, 2, 4 123 | 124 | $ slot_number_for_registration_number KA-01-HH-3141 125 | 6 126 | 127 | $ slot_number_for_registration_number MH-04-AY-1111 128 | Not found 129 | 130 | $ exit 131 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/Main.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal; 2 | 3 | import com.uditagarwal.commands.CommandExecutorFactory; 4 | import com.uditagarwal.exception.InvalidModeException; 5 | import com.uditagarwal.mode.FileMode; 6 | import com.uditagarwal.mode.InteractiveMode; 7 | import com.uditagarwal.service.ParkingLotService; 8 | import java.io.IOException; 9 | 10 | public class Main { 11 | public static void main(final String[] args) throws IOException { 12 | final OutputPrinter outputPrinter = new OutputPrinter(); 13 | final ParkingLotService parkingLotService = new ParkingLotService(); 14 | final CommandExecutorFactory commandExecutorFactory = 15 | new CommandExecutorFactory(parkingLotService); 16 | 17 | if (isInteractiveMode(args)) { 18 | new InteractiveMode(commandExecutorFactory, outputPrinter).process(); 19 | } else if (isFileInputMode(args)) { 20 | new FileMode(commandExecutorFactory, outputPrinter, args[0]).process(); 21 | } else { 22 | throw new InvalidModeException(); 23 | } 24 | } 25 | 26 | /** 27 | * Checks whether the program is running using file input mode. 28 | * 29 | * @param args Command line arguments. 30 | * @return Boolean indicating whether in file input mode. 31 | */ 32 | private static boolean isFileInputMode(final String[] args) { 33 | return args.length == 1; 34 | } 35 | 36 | /** 37 | * Checks whether the program is running using interactive shell mode. 38 | * 39 | * @param args Command line arguments. 40 | * @return Boolean indicating whether in interactive shell mode. 41 | */ 42 | private static boolean isInteractiveMode(final String[] args) { 43 | return args.length == 0; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/OutputPrinter.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal; 2 | 3 | /** 4 | * Printer to help in printing the output back to the user. Output medium can be changed here 5 | * anytime without affecting any of the other code. Currently, System.out is used. Tomorrow if file 6 | * has to be used to output, it can be changed here easily. 7 | */ 8 | public class OutputPrinter { 9 | 10 | public void welcome() { 11 | printWithNewLine("Welcome to Go-Jek Parking lot."); 12 | } 13 | 14 | public void end() { 15 | printWithNewLine("Thanks for using Go-Jek Parking lot service."); 16 | } 17 | 18 | public void notFound() { 19 | printWithNewLine("Not found"); 20 | } 21 | 22 | public void statusHeader() { 23 | printWithNewLine("Slot No. Registration No Colour"); 24 | } 25 | 26 | public void parkingLotFull() { 27 | printWithNewLine("Sorry, parking lot is full"); 28 | } 29 | 30 | public void parkingLotEmpty() { 31 | printWithNewLine("Parking lot is empty"); 32 | } 33 | 34 | public void invalidFile() { 35 | printWithNewLine("Invalid file given."); 36 | } 37 | 38 | public void printWithNewLine(final String msg) { 39 | System.out.println(msg); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/commands/ColorToRegNumberCommandExecutor.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import com.uditagarwal.OutputPrinter; 4 | import com.uditagarwal.model.Command; 5 | import com.uditagarwal.model.Slot; 6 | import com.uditagarwal.service.ParkingLotService; 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | /** 11 | * Executor to handle command of fetching all registration number of cars of a particular color. 12 | */ 13 | public class ColorToRegNumberCommandExecutor extends CommandExecutor { 14 | public static String COMMAND_NAME = "registration_numbers_for_cars_with_colour"; 15 | 16 | public ColorToRegNumberCommandExecutor( 17 | final ParkingLotService parkingLotService, final OutputPrinter outputPrinter) { 18 | super(parkingLotService, outputPrinter); 19 | } 20 | 21 | /** 22 | * {@inheritDoc} 23 | */ 24 | @Override 25 | public boolean validate(final Command command) { 26 | return command.getParams().size() == 1; 27 | } 28 | 29 | /** 30 | * {@inheritDoc} 31 | */ 32 | @Override 33 | public void execute(final Command command) { 34 | final List slotsForColor = parkingLotService.getSlotsForColor(command.getParams().get(0)); 35 | if (slotsForColor.isEmpty()) { 36 | outputPrinter.notFound(); 37 | } else { 38 | final String result = 39 | slotsForColor.stream() 40 | .map(slot -> slot.getParkedCar().getRegistrationNumber()) 41 | .collect(Collectors.joining(", ")); 42 | outputPrinter.printWithNewLine(result); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/commands/ColorToSlotNumberCommandExecutor.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import com.uditagarwal.OutputPrinter; 4 | import com.uditagarwal.model.Command; 5 | import com.uditagarwal.model.Slot; 6 | import com.uditagarwal.service.ParkingLotService; 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | /** 11 | * Executor to handle command of fetching all slot numbers in which cars of a particular color are 12 | * parked. 13 | */ 14 | public class ColorToSlotNumberCommandExecutor extends CommandExecutor { 15 | public static String COMMAND_NAME = "slot_numbers_for_cars_with_colour"; 16 | 17 | public ColorToSlotNumberCommandExecutor( 18 | final ParkingLotService parkingLotService, final OutputPrinter outputPrinter) { 19 | super(parkingLotService, outputPrinter); 20 | } 21 | 22 | /** 23 | * {@inheritDoc} 24 | */ 25 | @Override 26 | public boolean validate(final Command command) { 27 | return command.getParams().size() == 1; 28 | } 29 | 30 | /** 31 | * {@inheritDoc} 32 | */ 33 | @Override 34 | public void execute(final Command command) { 35 | final List slotsForColor = parkingLotService.getSlotsForColor(command.getParams().get(0)); 36 | if (slotsForColor.isEmpty()) { 37 | outputPrinter.notFound(); 38 | } else { 39 | final String result = 40 | slotsForColor.stream() 41 | .map(slot -> slot.getSlotNumber().toString()) 42 | .collect(Collectors.joining(", ")); 43 | outputPrinter.printWithNewLine(result); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/commands/CommandExecutor.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import com.uditagarwal.OutputPrinter; 4 | import com.uditagarwal.model.Command; 5 | import com.uditagarwal.service.ParkingLotService; 6 | 7 | /** 8 | * Command executor interface. 9 | */ 10 | public abstract class CommandExecutor { 11 | protected ParkingLotService parkingLotService; 12 | protected OutputPrinter outputPrinter; 13 | 14 | public CommandExecutor(final ParkingLotService parkingLotService, 15 | final OutputPrinter outputPrinter) { 16 | this.parkingLotService = parkingLotService; 17 | this.outputPrinter = outputPrinter; 18 | } 19 | 20 | /** 21 | * Validates that whether a command is valid to be executed or not. 22 | * 23 | * @param command Command to be validated. 24 | * @return Boolean indicating whether command is valid or not. 25 | */ 26 | public abstract boolean validate(Command command); 27 | 28 | /** 29 | * Executes the command. 30 | * 31 | * @param command Command to be executed. 32 | */ 33 | public abstract void execute(Command command); 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/commands/CommandExecutorFactory.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import com.uditagarwal.OutputPrinter; 4 | import com.uditagarwal.exception.InvalidCommandException; 5 | import com.uditagarwal.model.Command; 6 | import com.uditagarwal.service.ParkingLotService; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | /** 11 | * Factory to get correct {@link CommandExecutor} from a given command. 12 | */ 13 | public class CommandExecutorFactory { 14 | private Map commands = new HashMap<>(); 15 | 16 | public CommandExecutorFactory(final ParkingLotService parkingLotService) { 17 | final OutputPrinter outputPrinter = new OutputPrinter(); 18 | commands.put( 19 | CreateParkingLotCommandExecutor.COMMAND_NAME, 20 | new CreateParkingLotCommandExecutor(parkingLotService, outputPrinter)); 21 | commands.put( 22 | ParkCommandExecutor.COMMAND_NAME, 23 | new ParkCommandExecutor(parkingLotService, outputPrinter)); 24 | commands.put( 25 | LeaveCommandExecutor.COMMAND_NAME, 26 | new LeaveCommandExecutor(parkingLotService, outputPrinter)); 27 | commands.put( 28 | StatusCommandExecutor.COMMAND_NAME, 29 | new StatusCommandExecutor(parkingLotService, outputPrinter)); 30 | commands.put( 31 | ColorToRegNumberCommandExecutor.COMMAND_NAME, 32 | new ColorToRegNumberCommandExecutor(parkingLotService, outputPrinter)); 33 | commands.put( 34 | ColorToSlotNumberCommandExecutor.COMMAND_NAME, 35 | new ColorToSlotNumberCommandExecutor(parkingLotService, outputPrinter)); 36 | commands.put( 37 | SlotForRegNumberCommandExecutor.COMMAND_NAME, 38 | new SlotForRegNumberCommandExecutor(parkingLotService, outputPrinter)); 39 | commands.put( 40 | ExitCommandExecutor.COMMAND_NAME, 41 | new ExitCommandExecutor(parkingLotService, outputPrinter)); 42 | } 43 | 44 | /** 45 | * Gets {@link CommandExecutor} for a particular command. It basically uses name of command to 46 | * fetch its corresponding executor. 47 | * 48 | * @param command Command for which executor has to be fetched. 49 | * @return Command executor. 50 | */ 51 | public CommandExecutor getCommandExecutor(final Command command) { 52 | final CommandExecutor commandExecutor = commands.get(command.getCommandName()); 53 | if (commandExecutor == null) { 54 | throw new InvalidCommandException(); 55 | } 56 | return commandExecutor; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/commands/CreateParkingLotCommandExecutor.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import com.uditagarwal.OutputPrinter; 4 | import com.uditagarwal.model.Command; 5 | import com.uditagarwal.model.ParkingLot; 6 | import com.uditagarwal.model.parking.strategy.NaturalOrderingParkingStrategy; 7 | import com.uditagarwal.service.ParkingLotService; 8 | import com.uditagarwal.validator.IntegerValidator; 9 | import java.util.List; 10 | 11 | /** 12 | * Executor to handle command of creating the initial parking lot. 13 | */ 14 | public class CreateParkingLotCommandExecutor extends CommandExecutor { 15 | public static String COMMAND_NAME = "create_parking_lot"; 16 | 17 | public CreateParkingLotCommandExecutor( 18 | final ParkingLotService parkingLotService, final OutputPrinter outputPrinter) { 19 | super(parkingLotService, outputPrinter); 20 | } 21 | 22 | /** 23 | * {@inheritDoc} 24 | */ 25 | @Override 26 | public boolean validate(final Command command) { 27 | final List params = command.getParams(); 28 | if (params.size() != 1) { 29 | return false; 30 | } 31 | return IntegerValidator.isInteger(params.get(0)); 32 | } 33 | 34 | /** 35 | * {@inheritDoc} 36 | */ 37 | @Override 38 | public void execute(final Command command) { 39 | final int parkingLotCapacity = Integer.parseInt(command.getParams().get(0)); 40 | final ParkingLot parkingLot = new ParkingLot(parkingLotCapacity); 41 | parkingLotService.createParkingLot(parkingLot, new NaturalOrderingParkingStrategy()); 42 | outputPrinter.printWithNewLine( 43 | "Created a parking lot with " + parkingLot.getCapacity() + " slots"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/commands/ExitCommandExecutor.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import com.uditagarwal.OutputPrinter; 4 | import com.uditagarwal.model.Command; 5 | import com.uditagarwal.service.ParkingLotService; 6 | 7 | /** 8 | * Executor to handle command of exiting from the parking lot service. This is used in interactive 9 | * mode to exit. 10 | */ 11 | public class ExitCommandExecutor extends CommandExecutor { 12 | public static String COMMAND_NAME = "exit"; 13 | 14 | public ExitCommandExecutor( 15 | final ParkingLotService parkingLotService, final OutputPrinter outputPrinter) { 16 | super(parkingLotService, outputPrinter); 17 | } 18 | 19 | /** 20 | * {@inheritDoc} 21 | */ 22 | @Override 23 | public boolean validate(final Command command) { 24 | return command.getParams().isEmpty(); 25 | } 26 | 27 | /** 28 | * {@inheritDoc} 29 | */ 30 | @Override 31 | public void execute(final Command command) { 32 | outputPrinter.end(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/commands/LeaveCommandExecutor.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import com.uditagarwal.OutputPrinter; 4 | import com.uditagarwal.model.Command; 5 | import com.uditagarwal.service.ParkingLotService; 6 | import com.uditagarwal.validator.IntegerValidator; 7 | import java.util.List; 8 | 9 | /** 10 | * Executor to handle command of freeing of slot from a car. 11 | */ 12 | public class LeaveCommandExecutor extends CommandExecutor { 13 | public static String COMMAND_NAME = "leave"; 14 | 15 | public LeaveCommandExecutor(final ParkingLotService parkingLotService, 16 | final OutputPrinter outputPrinter) { 17 | super(parkingLotService, outputPrinter); 18 | } 19 | 20 | /** 21 | * {@inheritDoc} 22 | */ 23 | @Override 24 | public boolean validate(final Command command) { 25 | final List params = command.getParams(); 26 | if (params.size() != 1) { 27 | return false; 28 | } 29 | return IntegerValidator.isInteger(params.get(0)); 30 | } 31 | 32 | /** 33 | * {@inheritDoc} 34 | */ 35 | @Override 36 | public void execute(final Command command) { 37 | final int slotNum = Integer.parseInt(command.getParams().get(0)); 38 | parkingLotService.makeSlotFree(slotNum); 39 | outputPrinter.printWithNewLine("Slot number " + slotNum + " is free"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/commands/ParkCommandExecutor.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import com.uditagarwal.OutputPrinter; 4 | import com.uditagarwal.exception.NoFreeSlotAvailableException; 5 | import com.uditagarwal.model.Car; 6 | import com.uditagarwal.model.Command; 7 | import com.uditagarwal.service.ParkingLotService; 8 | 9 | /** 10 | * Executor to handle command of parking a car into the parking lot. This outputs the alloted slot 11 | * in which the car is parked. 12 | */ 13 | public class ParkCommandExecutor extends CommandExecutor { 14 | public static String COMMAND_NAME = "park"; 15 | 16 | public ParkCommandExecutor( 17 | final ParkingLotService parkingLotService, final OutputPrinter outputPrinter) { 18 | super(parkingLotService, outputPrinter); 19 | } 20 | 21 | /** 22 | * {@inheritDoc} 23 | */ 24 | @Override 25 | public boolean validate(final Command command) { 26 | return command.getParams().size() == 2; 27 | } 28 | 29 | /** 30 | * {@inheritDoc} 31 | */ 32 | @Override 33 | public void execute(final Command command) { 34 | final Car car = new Car(command.getParams().get(0), command.getParams().get(1)); 35 | try { 36 | final Integer slot = parkingLotService.park(car); 37 | outputPrinter.printWithNewLine("Allocated slot number: " + slot); 38 | } catch (NoFreeSlotAvailableException exception) { 39 | outputPrinter.parkingLotFull(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/commands/SlotForRegNumberCommandExecutor.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import com.uditagarwal.OutputPrinter; 4 | import com.uditagarwal.model.Command; 5 | import com.uditagarwal.model.Slot; 6 | import com.uditagarwal.service.ParkingLotService; 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | /** 11 | * Executor to handle command of fetching slot number of a car with a given registration number. 12 | */ 13 | public class SlotForRegNumberCommandExecutor extends CommandExecutor { 14 | public static String COMMAND_NAME = "slot_number_for_registration_number"; 15 | 16 | public SlotForRegNumberCommandExecutor( 17 | final ParkingLotService parkingLotService, 18 | final OutputPrinter outputPrinter) { 19 | super(parkingLotService, outputPrinter); 20 | } 21 | 22 | /** 23 | * {@inheritDoc} 24 | */ 25 | @Override 26 | public boolean validate(final Command command) { 27 | return command.getParams().size() == 1; 28 | } 29 | 30 | /** 31 | * {@inheritDoc} 32 | */ 33 | @Override 34 | public void execute(final Command command) { 35 | final List occupiedSlots = parkingLotService.getOccupiedSlots(); 36 | final String regNumberToFind = command.getParams().get(0); 37 | final Optional foundSlot = occupiedSlots.stream() 38 | .filter(slot -> slot.getParkedCar().getRegistrationNumber().equals(regNumberToFind)) 39 | .findFirst(); 40 | if (foundSlot.isPresent()) { 41 | outputPrinter.printWithNewLine(foundSlot.get().getSlotNumber().toString()); 42 | } else { 43 | outputPrinter.notFound(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/commands/StatusCommandExecutor.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import com.uditagarwal.OutputPrinter; 4 | import com.uditagarwal.model.Car; 5 | import com.uditagarwal.model.Command; 6 | import com.uditagarwal.model.Slot; 7 | import com.uditagarwal.service.ParkingLotService; 8 | import java.util.List; 9 | 10 | /** 11 | * Executor to handle command of fetching the current status of the parking lot. It gives which 12 | * slot has which car. Car details will have both its registration number and its color. 13 | */ 14 | public class StatusCommandExecutor extends CommandExecutor { 15 | public static String COMMAND_NAME = "status"; 16 | 17 | public StatusCommandExecutor(final ParkingLotService parkingLotService, 18 | final OutputPrinter outputPrinter) { 19 | super(parkingLotService, outputPrinter); 20 | } 21 | 22 | /** 23 | * {@inheritDoc} 24 | */ 25 | @Override 26 | public boolean validate(final Command command) { 27 | return command.getParams().isEmpty(); 28 | } 29 | 30 | /** 31 | * {@inheritDoc} 32 | */ 33 | @Override 34 | public void execute(Command command) { 35 | final List occupiedSlots = parkingLotService.getOccupiedSlots(); 36 | 37 | if (occupiedSlots.isEmpty()) { 38 | outputPrinter.parkingLotEmpty(); 39 | return; 40 | } 41 | 42 | outputPrinter.statusHeader(); 43 | for (Slot slot : occupiedSlots) { 44 | final Car parkedCar = slot.getParkedCar(); 45 | final String slotNumber = slot.getSlotNumber().toString(); 46 | 47 | outputPrinter.printWithNewLine(padString(slotNumber, 12) 48 | + padString(parkedCar.getRegistrationNumber(), 19) + parkedCar.getColor()); 49 | } 50 | } 51 | 52 | private static String padString(final String word, final int length) { 53 | String newWord = word; 54 | for(int count = word.length(); count < length; count++) { 55 | newWord = newWord + " "; 56 | } 57 | return newWord; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/exception/InvalidCommandException.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.exception; 2 | 3 | /** 4 | * Exception given when the command given is invalid or the command params are invalid. 5 | */ 6 | public class InvalidCommandException extends RuntimeException { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/exception/InvalidModeException.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.exception; 2 | 3 | /** 4 | * Exception given when the mode cannot be decided for the parking lot program. 5 | */ 6 | public class InvalidModeException extends RuntimeException { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/exception/InvalidSlotException.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.exception; 2 | 3 | /** 4 | * Exception given when the slot given in input is not a valid slot. 5 | */ 6 | public class InvalidSlotException extends ParkingLotException { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/exception/NoFreeSlotAvailableException.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.exception; 2 | 3 | /** 4 | * Exception given when parking lot is full and we still try to park a car into it. 5 | */ 6 | public class NoFreeSlotAvailableException extends ParkingLotException { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/exception/ParkingLotException.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.exception; 2 | 3 | /** 4 | * Generic exception for general parking lot exceptions. 5 | */ 6 | public class ParkingLotException extends RuntimeException { 7 | 8 | public ParkingLotException() { 9 | } 10 | 11 | public ParkingLotException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/exception/SlotAlreadyOccupiedException.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.exception; 2 | 3 | /** 4 | * Exception given when an already occupied slot is used to park a car into it. 5 | */ 6 | public class SlotAlreadyOccupiedException extends ParkingLotException { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/mode/FileMode.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.mode; 2 | 3 | import com.uditagarwal.OutputPrinter; 4 | import com.uditagarwal.commands.CommandExecutorFactory; 5 | import com.uditagarwal.model.Command; 6 | import java.io.BufferedReader; 7 | import java.io.File; 8 | import java.io.FileNotFoundException; 9 | import java.io.FileReader; 10 | import java.io.IOException; 11 | 12 | /** 13 | * Mode running in which input commands are given from a file. 14 | */ 15 | public class FileMode extends Mode { 16 | private String fileName; 17 | 18 | public FileMode( 19 | final CommandExecutorFactory commandExecutorFactory, 20 | final OutputPrinter outputPrinter, 21 | final String fileName) { 22 | super(commandExecutorFactory, outputPrinter); 23 | this.fileName = fileName; 24 | } 25 | 26 | /** 27 | * {@inheritDoc} 28 | */ 29 | @Override 30 | public void process() throws IOException { 31 | final File file = new File(fileName); 32 | final BufferedReader reader; 33 | try { 34 | reader = new BufferedReader(new FileReader(file)); 35 | } catch (FileNotFoundException e) { 36 | outputPrinter.invalidFile(); 37 | return; 38 | } 39 | 40 | String input = reader.readLine(); 41 | while (input != null) { 42 | final Command command = new Command(input); 43 | processCommand(command); 44 | input = reader.readLine(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/mode/InteractiveMode.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.mode; 2 | 3 | import com.uditagarwal.OutputPrinter; 4 | import com.uditagarwal.commands.CommandExecutorFactory; 5 | import com.uditagarwal.commands.ExitCommandExecutor; 6 | import com.uditagarwal.model.Command; 7 | import java.io.BufferedReader; 8 | import java.io.IOException; 9 | import java.io.InputStreamReader; 10 | 11 | /** 12 | * Mode running in which input commands are given from an interactive shell. 13 | */ 14 | public class InteractiveMode extends Mode { 15 | 16 | public InteractiveMode( 17 | final CommandExecutorFactory commandExecutorFactory, final OutputPrinter outputPrinter) { 18 | super(commandExecutorFactory, outputPrinter); 19 | } 20 | 21 | /** 22 | * {@inheritDoc} 23 | */ 24 | @Override 25 | public void process() throws IOException { 26 | outputPrinter.welcome(); 27 | final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); 28 | while (true) { 29 | final String input = reader.readLine(); 30 | final Command command = new Command(input); 31 | processCommand(command); 32 | if (command.getCommandName().equals(ExitCommandExecutor.COMMAND_NAME)) { 33 | break; 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/mode/Mode.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.mode; 2 | 3 | import com.uditagarwal.OutputPrinter; 4 | import com.uditagarwal.commands.CommandExecutor; 5 | import com.uditagarwal.commands.CommandExecutorFactory; 6 | import com.uditagarwal.exception.InvalidCommandException; 7 | import com.uditagarwal.model.Command; 8 | import java.io.IOException; 9 | 10 | /** 11 | * Interface for mode of the program in which it can be run. 12 | */ 13 | public abstract class Mode { 14 | 15 | private CommandExecutorFactory commandExecutorFactory; 16 | protected OutputPrinter outputPrinter; 17 | 18 | public Mode( 19 | final CommandExecutorFactory commandExecutorFactory, final OutputPrinter outputPrinter) { 20 | this.commandExecutorFactory = commandExecutorFactory; 21 | this.outputPrinter = outputPrinter; 22 | } 23 | 24 | /** 25 | * Helper method to process a command. It basically uses {@link CommandExecutor} to run the given 26 | * command. 27 | * 28 | * @param command Command to be processed. 29 | */ 30 | protected void processCommand(final Command command) { 31 | final CommandExecutor commandExecutor = commandExecutorFactory.getCommandExecutor(command); 32 | if (commandExecutor.validate(command)) { 33 | commandExecutor.execute(command); 34 | } else { 35 | throw new InvalidCommandException(); 36 | } 37 | } 38 | 39 | /** 40 | * Abstract method to process the mode. Each mode will process in its own way. 41 | * 42 | * @throws IOException 43 | */ 44 | public abstract void process() throws IOException; 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/model/Car.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.model; 2 | 3 | /** 4 | * Model object to represent a car. 5 | */ 6 | public class Car { 7 | private String registrationNumber; 8 | private String color; 9 | 10 | public String getRegistrationNumber() { 11 | return registrationNumber; 12 | } 13 | 14 | public String getColor() { 15 | return color; 16 | } 17 | 18 | public Car(final String registrationNumber, final String color) { 19 | this.registrationNumber = registrationNumber; 20 | this.color = color; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/model/Command.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.model; 2 | 3 | import com.uditagarwal.exception.InvalidCommandException; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.stream.Collectors; 7 | 8 | /** 9 | * Model object to represent a input command. 10 | */ 11 | public class Command { 12 | 13 | private static final String SPACE = " "; 14 | private String commandName; 15 | private List params; 16 | 17 | public String getCommandName() { 18 | return commandName; 19 | } 20 | 21 | public List getParams() { 22 | return params; 23 | } 24 | 25 | /** 26 | * Constructor. It takes the input line and parses the command name and param out of it. If the 27 | * command or its given params are not valid, then {@link InvalidCommandException} is thrown. 28 | * 29 | * @param inputLine Given input command line. 30 | */ 31 | public Command(final String inputLine) { 32 | final List tokensList = Arrays.stream(inputLine.trim().split(SPACE)) 33 | .map(String::trim) 34 | .filter(token -> (token.length() > 0)).collect(Collectors.toList()); 35 | 36 | if (tokensList.size() == 0) { 37 | throw new InvalidCommandException(); 38 | } 39 | 40 | commandName = tokensList.get(0).toLowerCase(); 41 | tokensList.remove(0); 42 | params = tokensList; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/model/ParkingLot.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.model; 2 | 3 | import com.uditagarwal.exception.InvalidSlotException; 4 | import com.uditagarwal.exception.ParkingLotException; 5 | import com.uditagarwal.exception.SlotAlreadyOccupiedException; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * Model object to represent the functioning of a parking lot. 11 | */ 12 | public class ParkingLot { 13 | private static int MAX_CAPACITY = 100000; 14 | private int capacity; 15 | private Map slots; 16 | 17 | public int getCapacity() { 18 | return capacity; 19 | } 20 | 21 | public ParkingLot(final int capacity) { 22 | if (capacity > MAX_CAPACITY || capacity <= 0) { 23 | throw new ParkingLotException("Invalid capacity given for parking lot."); 24 | } 25 | this.capacity = capacity; 26 | this.slots = new HashMap<>(); 27 | } 28 | 29 | public Map getSlots() { 30 | return slots; 31 | } 32 | 33 | /** 34 | * Helper method to get a {@link Slot} object for a given slot number. If slot does not exists, 35 | * then new slot will be created before giving it back. 36 | * 37 | * @param slotNumber Slot number. 38 | * @return Slot. 39 | */ 40 | private Slot getSlot(final Integer slotNumber) { 41 | if (slotNumber > getCapacity() || slotNumber <= 0) { 42 | throw new InvalidSlotException(); 43 | } 44 | final Map allSlots = getSlots(); 45 | if (!allSlots.containsKey(slotNumber)) { 46 | allSlots.put(slotNumber, new Slot(slotNumber)); 47 | } 48 | return allSlots.get(slotNumber); 49 | } 50 | 51 | /** 52 | * Parks a car into a given slot number. 53 | * 54 | * @param car Car to be parked. 55 | * @param slotNumber Slot number in which it has to be parked. 56 | * @return {@link Slot} if the parking succeeds. If the slot is already occupied then {@link 57 | * SlotAlreadyOccupiedException} is thrown. 58 | */ 59 | public Slot park(final Car car, final Integer slotNumber) { 60 | final Slot slot = getSlot(slotNumber); 61 | if (!slot.isSlotFree()) { 62 | throw new SlotAlreadyOccupiedException(); 63 | } 64 | slot.assignCar(car); 65 | return slot; 66 | } 67 | 68 | /** 69 | * Makes the slot free from the current parked car. 70 | * 71 | * @param slotNumber Slot number to be freed. 72 | * @return Freed slot. 73 | */ 74 | public Slot makeSlotFree(final Integer slotNumber) { 75 | final Slot slot = getSlot(slotNumber); 76 | slot.unassignCar(); 77 | return slot; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/model/Slot.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.model; 2 | 3 | public class Slot { 4 | private Car parkedCar; 5 | private Integer slotNumber; 6 | 7 | public Slot(final Integer slotNumber) { 8 | this.slotNumber = slotNumber; 9 | } 10 | 11 | public Integer getSlotNumber() { 12 | return slotNumber; 13 | } 14 | 15 | public Car getParkedCar() { 16 | return parkedCar; 17 | } 18 | 19 | public boolean isSlotFree() { 20 | return parkedCar == null; 21 | } 22 | 23 | public void assignCar(Car car) { 24 | this.parkedCar = car; 25 | } 26 | 27 | public void unassignCar() { 28 | this.parkedCar = null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/model/parking/strategy/NaturalOrderingParkingStrategy.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.model.parking.strategy; 2 | 3 | import com.uditagarwal.exception.NoFreeSlotAvailableException; 4 | import java.util.TreeSet; 5 | 6 | /** 7 | * Parking strategy in which the natural ordering numbers are used for deciding the slot numbers. 8 | * For example, 1st car will be parked in slot 1, then next in slot 2, then in slot 3, and so on. 9 | */ 10 | public class NaturalOrderingParkingStrategy implements ParkingStrategy { 11 | TreeSet slotTreeSet; 12 | 13 | public NaturalOrderingParkingStrategy() { 14 | this.slotTreeSet = new TreeSet<>(); 15 | } 16 | 17 | /** 18 | * {@inheritDoc} 19 | */ 20 | @Override 21 | public void addSlot(Integer slotNumber) { 22 | this.slotTreeSet.add(slotNumber); 23 | } 24 | 25 | /** 26 | * {@inheritDoc} 27 | */ 28 | @Override 29 | public void removeSlot(Integer slotNumber) { 30 | this.slotTreeSet.remove(slotNumber); 31 | } 32 | 33 | /** 34 | * {@inheritDoc} 35 | */ 36 | @Override 37 | public Integer getNextSlot() { 38 | if (slotTreeSet.isEmpty()) { 39 | throw new NoFreeSlotAvailableException(); 40 | } 41 | return this.slotTreeSet.first(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/model/parking/strategy/ParkingStrategy.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.model.parking.strategy; 2 | 3 | /** 4 | * Strategy which will be used to decide how slots will be used to park the car. 5 | */ 6 | public interface ParkingStrategy { 7 | 8 | /** 9 | * Add a new slot to parking strategy. After adding, this new slot will become available for 10 | * future parkings. 11 | * 12 | * @param slotNumber Slot number to be added. 13 | */ 14 | public void addSlot(Integer slotNumber); 15 | 16 | /** 17 | * Removes a slot from the parking strategy. After removing, this slot will not be used for future 18 | * parkings. 19 | * 20 | * @param slotNumber Slot number to be removed. 21 | */ 22 | public void removeSlot(Integer slotNumber); 23 | 24 | /** 25 | * Get the next free slot as per the parking strategy. 26 | * 27 | * @return Next free slot number. 28 | */ 29 | public Integer getNextSlot(); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/service/ParkingLotService.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.service; 2 | 3 | import com.uditagarwal.exception.ParkingLotException; 4 | import com.uditagarwal.model.Car; 5 | import com.uditagarwal.model.ParkingLot; 6 | import com.uditagarwal.model.Slot; 7 | import com.uditagarwal.model.parking.strategy.ParkingStrategy; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.stream.Collectors; 12 | 13 | /** 14 | * Service for enable the functioning of a parking lot. This will have all the business logic of 15 | * how the parking service will operate. 16 | */ 17 | public class ParkingLotService { 18 | private ParkingLot parkingLot; 19 | private ParkingStrategy parkingStrategy; 20 | 21 | /** 22 | * Allots a parking lot into the parking service. Throwns {@link ParkingLotException} if there is 23 | * already a parking lot alloted to the service previously. 24 | * 25 | * @param parkingLot Parking lot to be alloted. 26 | * @param parkingStrategy Strategy to be used while parking. 27 | */ 28 | public void createParkingLot(final ParkingLot parkingLot, final ParkingStrategy parkingStrategy) { 29 | if (this.parkingLot != null) { 30 | throw new ParkingLotException("Parking lot already exists."); 31 | } 32 | this.parkingLot = parkingLot; 33 | this.parkingStrategy = parkingStrategy; 34 | for (int i = 1; i <= parkingLot.getCapacity(); i++) { 35 | parkingStrategy.addSlot(i); 36 | } 37 | } 38 | 39 | /** 40 | * Parks a {@link Car} into the parking lot. {@link ParkingStrategy} is used to decide the slot 41 | * number and then the car is parked into the {@link ParkingLot} into that slot number. 42 | * 43 | * @param car Car to be parked. 44 | * @return Slot number in which the car is parked. 45 | */ 46 | public Integer park(final Car car) { 47 | validateParkingLotExists(); 48 | final Integer nextFreeSlot = parkingStrategy.getNextSlot(); 49 | parkingLot.park(car, nextFreeSlot); 50 | parkingStrategy.removeSlot(nextFreeSlot); 51 | return nextFreeSlot; 52 | } 53 | 54 | /** 55 | * Unparks a car from a slot. Freed slot number is given back to the parking strategy so that it 56 | * becomes available for future parkings. 57 | * 58 | * @param slotNumber Slot number to be freed. 59 | */ 60 | public void makeSlotFree(final Integer slotNumber) { 61 | validateParkingLotExists(); 62 | parkingLot.makeSlotFree(slotNumber); 63 | parkingStrategy.addSlot(slotNumber); 64 | } 65 | 66 | /** 67 | * Gets the list of all the slots which are occupied. 68 | */ 69 | public List getOccupiedSlots() { 70 | validateParkingLotExists(); 71 | final List occupiedSlotsList = new ArrayList<>(); 72 | final Map allSlots = parkingLot.getSlots(); 73 | 74 | for (int i = 1; i <= parkingLot.getCapacity(); i++) { 75 | if (allSlots.containsKey(i)) { 76 | final Slot slot = allSlots.get(i); 77 | if (!slot.isSlotFree()) { 78 | occupiedSlotsList.add(slot); 79 | } 80 | } 81 | } 82 | 83 | return occupiedSlotsList; 84 | } 85 | 86 | /** 87 | * Helper method to validate whether the parking lot exists or not. This is used to validate the 88 | * existence of parking lot before doing any operation on it. 89 | */ 90 | private void validateParkingLotExists() { 91 | if (parkingLot == null) { 92 | throw new ParkingLotException("Parking lot does not exists to park."); 93 | } 94 | } 95 | 96 | /** 97 | * Gets all the slots in which a car with given color is parked. 98 | * 99 | * @param color Color to be searched. 100 | * @return All matching slots. 101 | */ 102 | public List getSlotsForColor(final String color) { 103 | final List occupiedSlots = getOccupiedSlots(); 104 | return occupiedSlots.stream() 105 | .filter(slot -> slot.getParkedCar().getColor().equals(color)) 106 | .collect(Collectors.toList()); 107 | } 108 | 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/com/uditagarwal/validator/IntegerValidator.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.validator; 2 | 3 | public class IntegerValidator { 4 | 5 | /** 6 | * Validates that whether a string is a integer or not. Does this by checking if it is 7 | * successfully able to convert the input string to integer or not. 8 | * 9 | * @param input Input string. 10 | * @return Boolean indicating whether a input is integer or not. 11 | */ 12 | public static boolean isInteger(final String input) { 13 | try { 14 | Integer.parseInt(input); 15 | return true; 16 | } catch (NumberFormatException exception) { 17 | return false; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/com/uditagarwal/MainTest.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import com.uditagarwal.exception.InvalidCommandException; 6 | import com.uditagarwal.exception.InvalidModeException; 7 | import java.io.ByteArrayInputStream; 8 | import java.io.ByteArrayOutputStream; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.PrintStream; 12 | import org.junit.After; 13 | import org.junit.Before; 14 | import org.junit.Test; 15 | 16 | public class MainTest { 17 | private InputStream sysInBackup; 18 | private PrintStream sysOutBackup; 19 | private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); 20 | 21 | @Before 22 | public void setUp() throws Exception { 23 | sysInBackup = System.in; // backup System.in to restore it later 24 | sysOutBackup = System.out; // backup System.out to restore it later 25 | System.setOut(new PrintStream(outContent)); 26 | } 27 | 28 | @After 29 | public void tearDown() throws Exception { 30 | System.setIn(sysInBackup); 31 | System.setOut(sysOutBackup); 32 | } 33 | 34 | @Test 35 | public void testInteractiveMode() throws IOException { 36 | final String commands = 37 | "create_parking_lot 6\r\n" 38 | + "park KA-01-HH-1234 White\r\n" 39 | + "park KA-01-HH-9999 White\r\n" 40 | + "park KA-01-BB-0001 Black\r\n" 41 | + "park KA-01-HH-7777 Red\r\n" 42 | + "park KA-01-HH-2701 Blue\r\n" 43 | + "park KA-01-HH-3141 Black\r\n" 44 | + "leave 4\r\n" 45 | + "status\r\n" 46 | + "park KA-01-P-333 White\r\n" 47 | + "park DL-12-AA-9999 White\r\n" 48 | + "registration_numbers_for_cars_with_colour White\r\n" 49 | + "slot_numbers_for_cars_with_colour White\r\n" 50 | + "slot_number_for_registration_number KA-01-HH-3141\r\n" 51 | + "slot_number_for_registration_number MH-04-AY-1111\r\n" 52 | + "exit\r\n"; 53 | 54 | final String expectedOutput = 55 | "Welcome to Go-Jek Parking lot.\n" 56 | + "Created a parking lot with 6 slots\n" 57 | + "Allocated slot number: 1\n" 58 | + "Allocated slot number: 2\n" 59 | + "Allocated slot number: 3\n" 60 | + "Allocated slot number: 4\n" 61 | + "Allocated slot number: 5\n" 62 | + "Allocated slot number: 6\n" 63 | + "Slot number 4 is free\n" 64 | + "Slot No. Registration No Colour\n" 65 | + "1 KA-01-HH-1234 White\n" 66 | + "2 KA-01-HH-9999 White\n" 67 | + "3 KA-01-BB-0001 Black\n" 68 | + "5 KA-01-HH-2701 Blue\n" 69 | + "6 KA-01-HH-3141 Black\n" 70 | + "Allocated slot number: 4\n" 71 | + "Sorry, parking lot is full\n" 72 | + "KA-01-HH-1234, KA-01-HH-9999, KA-01-P-333\n" 73 | + "1, 2, 4\n" 74 | + "6\n" 75 | + "Not found\n" 76 | + "Thanks for using Go-Jek Parking lot service.\n"; 77 | 78 | final ByteArrayInputStream in = new ByteArrayInputStream(commands.getBytes()); 79 | System.setIn(in); 80 | 81 | Main.main(new String[] {}); 82 | assertEquals(expectedOutput, outContent.toString()); 83 | } 84 | 85 | @Test 86 | public void testStatusOfEmptyParkingLot() throws IOException { 87 | final String commands = "create_parking_lot 6\r\n" + "status\r\n" + "exit\r\n"; 88 | final String expectedOutput = 89 | "Welcome to Go-Jek Parking lot.\n" 90 | + "Created a parking lot with 6 slots\n" 91 | + "Parking lot is empty\n" 92 | + "Thanks for using Go-Jek Parking lot service.\n"; 93 | 94 | final ByteArrayInputStream in = new ByteArrayInputStream(commands.getBytes()); 95 | System.setIn(in); 96 | 97 | Main.main(new String[] {}); 98 | assertEquals(expectedOutput, outContent.toString()); 99 | } 100 | 101 | @Test(expected = InvalidCommandException.class) 102 | public void testInvalidCommandParams() throws IOException { 103 | final String commands = "create_parking_lot 6 1\r\n"; 104 | final ByteArrayInputStream in = new ByteArrayInputStream(commands.getBytes()); 105 | System.setIn(in); 106 | 107 | Main.main(new String[] {}); 108 | } 109 | 110 | @Test 111 | public void testFileMode() throws IOException { 112 | final String expectedOutput = 113 | "Created a parking lot with 6 slots\n" 114 | + "Allocated slot number: 1\n" 115 | + "Allocated slot number: 2\n" 116 | + "Allocated slot number: 3\n" 117 | + "Allocated slot number: 4\n" 118 | + "Allocated slot number: 5\n" 119 | + "Allocated slot number: 6\n" 120 | + "Slot number 4 is free\n" 121 | + "Slot No. Registration No Colour\n" 122 | + "1 KA-01-HH-1234 White\n" 123 | + "2 KA-01-HH-9999 White\n" 124 | + "3 KA-01-BB-0001 Black\n" 125 | + "5 KA-01-HH-2701 Blue\n" 126 | + "6 KA-01-HH-3141 Black\n" 127 | + "Allocated slot number: 4\n" 128 | + "Sorry, parking lot is full\n" 129 | + "KA-01-HH-1234, KA-01-HH-9999, KA-01-P-333\n" 130 | + "1, 2, 4\n" 131 | + "6\n" 132 | + "Not found\n"; 133 | Main.main(new String[] {"file_input.txt"}); 134 | assertEquals(expectedOutput, outContent.toString()); 135 | } 136 | 137 | @Test 138 | public void testFileModeWithInvalidFile() throws IOException { 139 | final String expectedOutput = "Invalid file given.\n"; 140 | Main.main(new String[] {"some_random_file.txt"}); 141 | assertEquals(expectedOutput, outContent.toString()); 142 | } 143 | 144 | @Test(expected = InvalidModeException.class) 145 | public void testInvalidMode() throws IOException { 146 | Main.main(new String[] {"file_input.txt", "some-other-input"}); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/test/java/com/uditagarwal/commands/ColorToRegNumberCommandExecutorTest.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertTrue; 5 | import static org.mockito.Mockito.mock; 6 | import static org.mockito.Mockito.verify; 7 | import static org.mockito.Mockito.when; 8 | 9 | import com.uditagarwal.OutputPrinter; 10 | import com.uditagarwal.model.Car; 11 | import com.uditagarwal.model.Command; 12 | import com.uditagarwal.model.Slot; 13 | import com.uditagarwal.service.ParkingLotService; 14 | import java.util.Arrays; 15 | import java.util.Collections; 16 | import org.junit.Before; 17 | import org.junit.Test; 18 | 19 | public class ColorToRegNumberCommandExecutorTest { 20 | private ParkingLotService parkingLotService; 21 | private OutputPrinter outputPrinter; 22 | private ColorToRegNumberCommandExecutor colorToRegNumberCommandExecutor; 23 | 24 | @Before 25 | public void setUp() throws Exception { 26 | parkingLotService = mock(ParkingLotService.class); 27 | outputPrinter = mock(OutputPrinter.class); 28 | colorToRegNumberCommandExecutor = 29 | new ColorToRegNumberCommandExecutor(parkingLotService, outputPrinter); 30 | } 31 | 32 | @Test 33 | public void testValidCommand() { 34 | assertTrue( 35 | colorToRegNumberCommandExecutor.validate( 36 | new Command("registration_numbers_for_cars_with_colour white"))); 37 | } 38 | 39 | @Test 40 | public void testInvalidCommand() { 41 | assertFalse( 42 | colorToRegNumberCommandExecutor.validate( 43 | new Command("registration_numbers_for_cars_with_colour"))); 44 | assertFalse( 45 | colorToRegNumberCommandExecutor.validate( 46 | new Command("registration_numbers_for_cars_with_colour a b"))); 47 | } 48 | 49 | @Test 50 | public void testWhenNoCarsFoundWithAColor() { 51 | when(parkingLotService.getSlotsForColor("white")).thenReturn(Collections.emptyList()); 52 | colorToRegNumberCommandExecutor.execute( 53 | new Command("registration_numbers_for_cars_with_colour white")); 54 | 55 | verify(outputPrinter).notFound(); 56 | } 57 | 58 | @Test 59 | public void testCarsWithAColor() { 60 | final Slot slot1 = new Slot(1); 61 | slot1.assignCar(new Car("num_white1", "white")); 62 | final Slot slot2 = new Slot(2); 63 | slot2.assignCar(new Car("num_white2", "white")); 64 | when(parkingLotService.getSlotsForColor("white")) 65 | .thenReturn(Arrays.asList(slot1, slot2)); 66 | colorToRegNumberCommandExecutor.execute( 67 | new Command("registration_numbers_for_cars_with_colour white")); 68 | 69 | verify(outputPrinter).printWithNewLine("num_white1, num_white2"); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/test/java/com/uditagarwal/commands/ColorToSlotNumberCommandExecutorTest.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertTrue; 5 | import static org.mockito.Mockito.mock; 6 | import static org.mockito.Mockito.verify; 7 | import static org.mockito.Mockito.when; 8 | 9 | import com.uditagarwal.OutputPrinter; 10 | import com.uditagarwal.model.Car; 11 | import com.uditagarwal.model.Command; 12 | import com.uditagarwal.model.Slot; 13 | import com.uditagarwal.service.ParkingLotService; 14 | import java.util.Arrays; 15 | import java.util.Collections; 16 | import org.junit.Before; 17 | import org.junit.Test; 18 | 19 | public class ColorToSlotNumberCommandExecutorTest { 20 | private ParkingLotService parkingLotService; 21 | private OutputPrinter outputPrinter; 22 | private ColorToSlotNumberCommandExecutor colorToSlotNumberCommandExecutor; 23 | 24 | @Before 25 | public void setUp() throws Exception { 26 | parkingLotService = mock(ParkingLotService.class); 27 | outputPrinter = mock(OutputPrinter.class); 28 | colorToSlotNumberCommandExecutor = 29 | new ColorToSlotNumberCommandExecutor(parkingLotService, outputPrinter); 30 | } 31 | 32 | @Test 33 | public void testValidCommand() { 34 | assertTrue( 35 | colorToSlotNumberCommandExecutor.validate( 36 | new Command("slot_numbers_for_cars_with_colour white"))); 37 | } 38 | 39 | @Test 40 | public void testInvalidCommand() { 41 | assertFalse( 42 | colorToSlotNumberCommandExecutor.validate( 43 | new Command("slot_numbers_for_cars_with_colour"))); 44 | assertFalse( 45 | colorToSlotNumberCommandExecutor.validate( 46 | new Command("slot_numbers_for_cars_with_colour a b"))); 47 | } 48 | 49 | @Test 50 | public void testWhenNoSlotsFoundWithAColor() { 51 | when(parkingLotService.getSlotsForColor("white")).thenReturn(Collections.emptyList()); 52 | colorToSlotNumberCommandExecutor.execute( 53 | new Command("slot_numbers_for_cars_with_colour white")); 54 | 55 | verify(outputPrinter).notFound(); 56 | } 57 | 58 | @Test 59 | public void testCarsWithAColor() { 60 | final Slot slot1 = new Slot(1); 61 | slot1.assignCar(new Car("num_white1", "white")); 62 | final Slot slot2 = new Slot(2); 63 | slot2.assignCar(new Car("num_white2", "white")); 64 | when(parkingLotService.getSlotsForColor("white")).thenReturn(Arrays.asList(slot1, slot2)); 65 | colorToSlotNumberCommandExecutor.execute( 66 | new Command("slot_numbers_for_cars_with_colour white")); 67 | 68 | verify(outputPrinter).printWithNewLine("1, 2"); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/com/uditagarwal/commands/CommandExecutorFactoryTest.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import static org.junit.Assert.assertNotNull; 4 | import static org.junit.Assert.assertTrue; 5 | import static org.mockito.Mockito.mock; 6 | 7 | import com.uditagarwal.exception.InvalidCommandException; 8 | import com.uditagarwal.model.Command; 9 | import com.uditagarwal.service.ParkingLotService; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | 13 | public class CommandExecutorFactoryTest { 14 | 15 | private CommandExecutorFactory factory; 16 | 17 | @Before 18 | public void setUp() throws Exception { 19 | final ParkingLotService parkingLotService = mock(ParkingLotService.class); 20 | factory = new CommandExecutorFactory(parkingLotService); 21 | } 22 | 23 | @Test 24 | public void testFetchingExecutorForValidCommand() { 25 | final CommandExecutor commandExecutor = factory.getCommandExecutor(new Command("leave 1")); 26 | assertNotNull(commandExecutor); 27 | assertTrue(commandExecutor instanceof LeaveCommandExecutor); 28 | } 29 | 30 | @Test(expected = InvalidCommandException.class) 31 | public void testFetchingExecutorForInvalidCommand() { 32 | factory.getCommandExecutor(new Command("some-random-command random-param1 random-param2")); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/com/uditagarwal/commands/CreateParkingLotCommandExecutorTest.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertTrue; 6 | import static org.mockito.Matchers.any; 7 | import static org.mockito.Mockito.mock; 8 | import static org.mockito.Mockito.verify; 9 | 10 | import com.uditagarwal.OutputPrinter; 11 | import com.uditagarwal.model.Command; 12 | import com.uditagarwal.model.ParkingLot; 13 | import com.uditagarwal.model.parking.strategy.NaturalOrderingParkingStrategy; 14 | import com.uditagarwal.service.ParkingLotService; 15 | import org.junit.Before; 16 | import org.junit.Test; 17 | import org.mockito.ArgumentCaptor; 18 | 19 | public class CreateParkingLotCommandExecutorTest { 20 | private ParkingLotService parkingLotService; 21 | private OutputPrinter outputPrinter; 22 | private CreateParkingLotCommandExecutor createParkingLotCommandExecutor; 23 | 24 | @Before 25 | public void setUp() throws Exception { 26 | parkingLotService = mock(ParkingLotService.class); 27 | outputPrinter = mock(OutputPrinter.class); 28 | createParkingLotCommandExecutor = 29 | new CreateParkingLotCommandExecutor(parkingLotService, outputPrinter); 30 | } 31 | 32 | @Test 33 | public void testValidCommand() { 34 | assertTrue(createParkingLotCommandExecutor.validate(new Command("create_parking_lot 6"))); 35 | } 36 | 37 | @Test 38 | public void testInvalidCommand() { 39 | assertFalse(createParkingLotCommandExecutor.validate(new Command("create_parking_lot"))); 40 | assertFalse(createParkingLotCommandExecutor.validate(new Command("create_parking_lot abcd"))); 41 | } 42 | 43 | @Test 44 | public void testCommandExecution() { 45 | createParkingLotCommandExecutor.execute(new Command("create_parking_lot 6")); 46 | 47 | final ArgumentCaptor argument = ArgumentCaptor.forClass(ParkingLot.class); 48 | verify(parkingLotService) 49 | .createParkingLot(argument.capture(), any(NaturalOrderingParkingStrategy.class)); 50 | assertEquals(6, argument.getValue().getCapacity()); 51 | verify(outputPrinter).printWithNewLine("Created a parking lot with 6 slots"); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/com/uditagarwal/commands/ExitCommandExecutorTest.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertTrue; 5 | import static org.mockito.Mockito.mock; 6 | import static org.mockito.Mockito.verify; 7 | 8 | import com.uditagarwal.OutputPrinter; 9 | import com.uditagarwal.model.Command; 10 | import com.uditagarwal.service.ParkingLotService; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | 14 | public class ExitCommandExecutorTest { 15 | private ParkingLotService parkingLotService; 16 | private OutputPrinter outputPrinter; 17 | private ExitCommandExecutor exitCommandExecutor; 18 | 19 | @Before 20 | public void setUp() throws Exception { 21 | parkingLotService = mock(ParkingLotService.class); 22 | outputPrinter = mock(OutputPrinter.class); 23 | exitCommandExecutor = new ExitCommandExecutor(parkingLotService, outputPrinter); 24 | } 25 | 26 | @Test 27 | public void testValidCommand() { 28 | assertTrue(exitCommandExecutor.validate(new Command("exit"))); 29 | } 30 | 31 | @Test 32 | public void testInvalidCommand() { 33 | assertFalse(exitCommandExecutor.validate(new Command("exit 1"))); 34 | assertFalse(exitCommandExecutor.validate(new Command("exit 1 2"))); 35 | assertFalse(exitCommandExecutor.validate(new Command("exit a"))); 36 | } 37 | 38 | @Test 39 | public void textCommandExecution() { 40 | exitCommandExecutor.execute(new Command("exit")); 41 | verify(outputPrinter).end(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/com/uditagarwal/commands/LeaveCommandExecutorTest.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertTrue; 5 | import static org.mockito.Mockito.mock; 6 | import static org.mockito.Mockito.verify; 7 | 8 | import com.uditagarwal.OutputPrinter; 9 | import com.uditagarwal.model.Command; 10 | import com.uditagarwal.service.ParkingLotService; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | 14 | public class LeaveCommandExecutorTest { 15 | 16 | private ParkingLotService parkingLotService; 17 | private OutputPrinter outputPrinter; 18 | private LeaveCommandExecutor leaveCommandExecutor; 19 | 20 | @Before 21 | public void setUp() throws Exception { 22 | parkingLotService = mock(ParkingLotService.class); 23 | outputPrinter = mock(OutputPrinter.class); 24 | leaveCommandExecutor = new LeaveCommandExecutor(parkingLotService, outputPrinter); 25 | } 26 | 27 | @Test 28 | public void testValidCommand() { 29 | assertTrue(leaveCommandExecutor.validate(new Command("leave 1"))); 30 | } 31 | 32 | @Test 33 | public void testInvalidCommand() { 34 | assertFalse(leaveCommandExecutor.validate(new Command("leave"))); 35 | assertFalse(leaveCommandExecutor.validate(new Command("leave 1 2"))); 36 | assertFalse(leaveCommandExecutor.validate(new Command("leave 1 a"))); 37 | assertFalse(leaveCommandExecutor.validate(new Command("leave abcd"))); 38 | } 39 | 40 | @Test 41 | public void testLeavingCarFromSlotMakesSlotFreeFromParkingService() { 42 | leaveCommandExecutor.execute(new Command("leave 1")); 43 | verify(parkingLotService).makeSlotFree(1); 44 | verify(outputPrinter).printWithNewLine("Slot number 1 is free"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/com/uditagarwal/commands/ParkCommandExecutorTest.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertTrue; 6 | import static org.mockito.Matchers.any; 7 | import static org.mockito.Mockito.mock; 8 | import static org.mockito.Mockito.verify; 9 | import static org.mockito.Mockito.when; 10 | 11 | import com.uditagarwal.OutputPrinter; 12 | import com.uditagarwal.exception.NoFreeSlotAvailableException; 13 | import com.uditagarwal.model.Car; 14 | import com.uditagarwal.model.Command; 15 | import com.uditagarwal.service.ParkingLotService; 16 | import org.junit.Before; 17 | import org.junit.Test; 18 | import org.mockito.ArgumentCaptor; 19 | 20 | public class ParkCommandExecutorTest { 21 | private ParkingLotService parkingLotService; 22 | private OutputPrinter outputPrinter; 23 | private ParkCommandExecutor parkCommandExecutor; 24 | 25 | @Before 26 | public void setUp() throws Exception { 27 | parkingLotService = mock(ParkingLotService.class); 28 | outputPrinter = mock(OutputPrinter.class); 29 | parkCommandExecutor = new ParkCommandExecutor(parkingLotService, outputPrinter); 30 | } 31 | 32 | @Test 33 | public void testValidCommand() { 34 | assertTrue(parkCommandExecutor.validate(new Command("park test-command-number white"))); 35 | } 36 | 37 | @Test 38 | public void testInvalidCommand() { 39 | assertFalse(parkCommandExecutor.validate(new Command("park"))); 40 | assertFalse(parkCommandExecutor.validate(new Command("park test-car-number"))); 41 | assertFalse(parkCommandExecutor.validate(new Command("park test-car-number white abcd"))); 42 | } 43 | 44 | @Test 45 | public void testCommandExecutionWhenParkingSucceeds() { 46 | when(parkingLotService.park(any())).thenReturn(1); 47 | parkCommandExecutor.execute(new Command("park test-car-number white")); 48 | 49 | final ArgumentCaptor argument = ArgumentCaptor.forClass(Car.class); 50 | verify(parkingLotService).park(argument.capture()); 51 | assertEquals("test-car-number", argument.getValue().getRegistrationNumber()); 52 | assertEquals("white", argument.getValue().getColor()); 53 | 54 | verify(outputPrinter).printWithNewLine("Allocated slot number: 1"); 55 | } 56 | 57 | @Test 58 | public void testCommandExecutionWhenParkingIsFull() { 59 | when(parkingLotService.park(any())).thenThrow(new NoFreeSlotAvailableException()); 60 | parkCommandExecutor.execute(new Command("park test-car-number white")); 61 | 62 | final ArgumentCaptor argument = ArgumentCaptor.forClass(Car.class); 63 | verify(parkingLotService).park(argument.capture()); 64 | assertEquals("test-car-number", argument.getValue().getRegistrationNumber()); 65 | assertEquals("white", argument.getValue().getColor()); 66 | 67 | verify(outputPrinter).parkingLotFull(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/com/uditagarwal/commands/SlotForRegNumberCommandExecutorTest.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertTrue; 5 | import static org.mockito.Mockito.mock; 6 | import static org.mockito.Mockito.verify; 7 | import static org.mockito.Mockito.when; 8 | 9 | import com.uditagarwal.OutputPrinter; 10 | import com.uditagarwal.model.Car; 11 | import com.uditagarwal.model.Command; 12 | import com.uditagarwal.model.Slot; 13 | import com.uditagarwal.service.ParkingLotService; 14 | import java.util.ArrayList; 15 | import java.util.Arrays; 16 | import java.util.List; 17 | import org.junit.Before; 18 | import org.junit.Test; 19 | 20 | public class SlotForRegNumberCommandExecutorTest { 21 | 22 | private ParkingLotService parkingLotService; 23 | private OutputPrinter outputPrinter; 24 | private SlotForRegNumberCommandExecutor slotForRegNumberCommandExecutor; 25 | 26 | @Before 27 | public void setUp() throws Exception { 28 | parkingLotService = mock(ParkingLotService.class); 29 | outputPrinter = mock(OutputPrinter.class); 30 | slotForRegNumberCommandExecutor = 31 | new SlotForRegNumberCommandExecutor(parkingLotService, outputPrinter); 32 | } 33 | 34 | @Test 35 | public void testValidCommand() { 36 | assertTrue( 37 | slotForRegNumberCommandExecutor.validate( 38 | new Command("slot_number_for_registration_number AB-01-CP-1230"))); 39 | } 40 | 41 | @Test 42 | public void testInvalidCommand() { 43 | assertFalse( 44 | slotForRegNumberCommandExecutor.validate( 45 | new Command("slot_number_for_registration_number"))); 46 | assertFalse( 47 | slotForRegNumberCommandExecutor.validate( 48 | new Command("slot_number_for_registration_number AB-01-CP-1230 b"))); 49 | } 50 | 51 | @Test 52 | public void testCorrectSlotNumberForValidRegistrationNumber() { 53 | final Slot slot1 = new Slot(1); 54 | slot1.assignCar(new Car("reg-1", "white")); 55 | 56 | final Slot slot2 = new Slot(2); 57 | slot2.assignCar(new Car("AB-01-CP-1230", "blue")); 58 | 59 | final Slot slot3 = new Slot(3); 60 | slot3.assignCar(new Car("reg-2", "blue")); 61 | 62 | when(parkingLotService.getOccupiedSlots()).thenReturn(Arrays.asList(slot1, slot2, slot3)); 63 | 64 | slotForRegNumberCommandExecutor.execute( 65 | new Command("slot_number_for_registration_number AB-01-CP-1230")); 66 | verify(outputPrinter).printWithNewLine("2"); 67 | } 68 | 69 | @Test 70 | public void testCorrectSlotNumberForNonExistingRegistrationNumber() { 71 | List occupiedSlots = new ArrayList<>(); 72 | when(parkingLotService.getOccupiedSlots()).thenReturn(occupiedSlots); 73 | 74 | slotForRegNumberCommandExecutor.execute( 75 | new Command("slot_number_for_registration_number AB-01-CP-1230")); 76 | verify(outputPrinter).notFound(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/test/java/com/uditagarwal/commands/StatusCommandExecutorTest.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.commands; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertTrue; 5 | import static org.mockito.Mockito.mock; 6 | import static org.mockito.Mockito.verify; 7 | import static org.mockito.Mockito.when; 8 | 9 | import com.uditagarwal.OutputPrinter; 10 | import com.uditagarwal.model.Car; 11 | import com.uditagarwal.model.Command; 12 | import com.uditagarwal.model.Slot; 13 | import com.uditagarwal.service.ParkingLotService; 14 | import java.util.ArrayList; 15 | import java.util.Arrays; 16 | import java.util.List; 17 | import org.junit.Before; 18 | import org.junit.Test; 19 | 20 | public class StatusCommandExecutorTest { 21 | private ParkingLotService parkingLotService; 22 | private OutputPrinter outputPrinter; 23 | private StatusCommandExecutor statusCommandExecutor; 24 | 25 | @Before 26 | public void setUp() throws Exception { 27 | parkingLotService = mock(ParkingLotService.class); 28 | outputPrinter = mock(OutputPrinter.class); 29 | statusCommandExecutor = new StatusCommandExecutor(parkingLotService, outputPrinter); 30 | } 31 | 32 | @Test 33 | public void testValidCommand() { 34 | assertTrue(statusCommandExecutor.validate(new Command("status"))); 35 | } 36 | 37 | @Test 38 | public void testInvalidCommand() { 39 | assertFalse(statusCommandExecutor.validate(new Command("status 1"))); 40 | assertFalse(statusCommandExecutor.validate(new Command("status 2 3"))); 41 | } 42 | 43 | @Test 44 | public void testCommandExecutionWhenParkingLotIsEmpty() { 45 | List occupiedSlots = new ArrayList<>(); 46 | when(parkingLotService.getOccupiedSlots()).thenReturn(occupiedSlots); 47 | statusCommandExecutor.execute(new Command("status")); 48 | verify(parkingLotService).getOccupiedSlots(); 49 | verify(outputPrinter).parkingLotEmpty(); 50 | } 51 | 52 | @Test 53 | public void testCommandExecutionWithOccupiedParkingLot() { 54 | final Slot slot1 = new Slot(1); 55 | slot1.assignCar(new Car("reg-1", "white")); 56 | 57 | final Slot slot2 = new Slot(2); 58 | slot2.assignCar(new Car("reg-2", "blue")); 59 | 60 | when(parkingLotService.getOccupiedSlots()).thenReturn(Arrays.asList(slot1, slot2)); 61 | 62 | statusCommandExecutor.execute(new Command("status")); 63 | 64 | verify(parkingLotService).getOccupiedSlots(); 65 | verify(outputPrinter).statusHeader(); 66 | verify(outputPrinter).printWithNewLine("1 reg-1 white"); 67 | verify(outputPrinter).printWithNewLine("2 reg-2 blue"); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/com/uditagarwal/model/CommandTest.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.model; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNotNull; 5 | 6 | import com.uditagarwal.exception.InvalidCommandException; 7 | import java.util.Arrays; 8 | import java.util.Collections; 9 | import java.util.List; 10 | import org.junit.Test; 11 | 12 | public class CommandTest { 13 | 14 | @Test 15 | public void testCommandParsingFromInput() { 16 | validateCommandParsing("my_command 1 2 3", "my_command", Arrays.asList("1", "2", "3")); 17 | validateCommandParsing("my_command 1 2 ", "my_command", Arrays.asList("1", "2")); 18 | validateCommandParsing("my_command", "my_command", Collections.emptyList()); 19 | validateCommandParsing(" my_command ", "my_command", Collections.emptyList()); 20 | } 21 | 22 | @Test(expected = InvalidCommandException.class) 23 | public void testCommandParsingFromInputHavingOnlySpaces() { 24 | Command command = new Command(" "); 25 | } 26 | 27 | @Test(expected = InvalidCommandException.class) 28 | public void testCommandParsingFromEmptyInput() { 29 | Command command = new Command(""); 30 | } 31 | 32 | /** 33 | * Helper method to validate command parsing. 34 | * 35 | * @param input Input line. 36 | * @param expectedCommandName Expected command name from input. 37 | * @param expectedParams Expected command params from inout. 38 | */ 39 | private void validateCommandParsing( 40 | final String input, final String expectedCommandName, final List expectedParams) { 41 | Command command = new Command(input); 42 | assertNotNull(command); 43 | assertEquals(expectedCommandName, command.getCommandName()); 44 | assertEquals(expectedParams, command.getParams()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/com/uditagarwal/model/ParkingLotTest.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.model; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import com.uditagarwal.exception.InvalidSlotException; 6 | import com.uditagarwal.exception.ParkingLotException; 7 | import com.uditagarwal.exception.SlotAlreadyOccupiedException; 8 | import org.junit.Test; 9 | 10 | public class ParkingLotTest { 11 | 12 | @Test(expected = ParkingLotException.class) 13 | public void testNegativeCapacity() { 14 | new ParkingLot(-1); 15 | } 16 | 17 | @Test(expected = ParkingLotException.class) 18 | public void testZeroCapacity() { 19 | new ParkingLot(0); 20 | } 21 | 22 | @Test 23 | public void testValidCapacity() { 24 | new ParkingLot(100); 25 | } 26 | 27 | @Test(expected = ParkingLotException.class) 28 | public void testMoreThanMaxCapacity() { 29 | new ParkingLot(1000001); 30 | } 31 | 32 | @Test 33 | public void testParkingCar() { 34 | final Car testCar = new Car("test-car-no", "white"); 35 | final ParkingLot parkingLot = new ParkingLot(100); 36 | final Slot slot = parkingLot.park(testCar, 1); 37 | assertEquals(testCar, slot.getParkedCar()); 38 | } 39 | 40 | @Test(expected = SlotAlreadyOccupiedException.class) 41 | public void testParkingOnAlreadyOccupiedSlot() { 42 | final Car testCar1 = new Car("test-car-no1", "white"); 43 | final Car testCar2 = new Car("test-car-no2", "blue"); 44 | final ParkingLot parkingLot = new ParkingLot(100); 45 | parkingLot.park(testCar1, 1); 46 | parkingLot.park(testCar2, 1); 47 | } 48 | 49 | @Test(expected = InvalidSlotException.class) 50 | public void testParkingAtSlotHigherThanCapacity() { 51 | final Car testCar = new Car("test-car-no", "white"); 52 | final ParkingLot parkingLot = new ParkingLot(100); 53 | parkingLot.park(testCar, 101); 54 | } 55 | 56 | @Test(expected = InvalidSlotException.class) 57 | public void testParkingAtSlotInvalidSlot() { 58 | final Car testCar = new Car("test-car-no", "white"); 59 | final ParkingLot parkingLot = new ParkingLot(100); 60 | parkingLot.park(testCar, 0); 61 | } 62 | 63 | @Test 64 | public void testMakingSlotFree() { 65 | final Car testCar = new Car("test-car-no", "white"); 66 | final ParkingLot parkingLot = new ParkingLot(100); 67 | 68 | final Slot parkedSlot = parkingLot.park(testCar, 10); 69 | assertFalse(parkedSlot.isSlotFree()); 70 | 71 | final Slot freedSlot = parkingLot.makeSlotFree(10); 72 | assertTrue(freedSlot.isSlotFree()); 73 | } 74 | 75 | @Test(expected = InvalidSlotException.class) 76 | public void testMakingSlotHigherThanCapacityFree() { 77 | final ParkingLot parkingLot = new ParkingLot(100); 78 | parkingLot.makeSlotFree(101); 79 | } 80 | 81 | @Test(expected = InvalidSlotException.class) 82 | public void testMakingInvalidSlotFree() { 83 | final ParkingLot parkingLot = new ParkingLot(100); 84 | parkingLot.makeSlotFree(-1); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/test/java/com/uditagarwal/model/parking/strategy/NaturalOrderingParkingStrategyTest.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.model.parking.strategy; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import com.uditagarwal.exception.NoFreeSlotAvailableException; 6 | import org.junit.Test; 7 | 8 | public class NaturalOrderingParkingStrategyTest { 9 | private NaturalOrderingParkingStrategy naturalOrderingParkingStrategy = 10 | new NaturalOrderingParkingStrategy(); 11 | 12 | @Test 13 | public void testValidStrategyExecution() { 14 | naturalOrderingParkingStrategy.addSlot(1); 15 | naturalOrderingParkingStrategy.addSlot(2); 16 | naturalOrderingParkingStrategy.addSlot(3); 17 | assertEquals((Integer)1, naturalOrderingParkingStrategy.getNextSlot()); 18 | naturalOrderingParkingStrategy.removeSlot(2); 19 | assertEquals((Integer)1, naturalOrderingParkingStrategy.getNextSlot()); 20 | naturalOrderingParkingStrategy.removeSlot(1); 21 | assertEquals((Integer)3, naturalOrderingParkingStrategy.getNextSlot()); 22 | naturalOrderingParkingStrategy.addSlot(2); 23 | assertEquals((Integer)2, naturalOrderingParkingStrategy.getNextSlot()); 24 | } 25 | 26 | @Test(expected = NoFreeSlotAvailableException.class) 27 | public void testFullParkingStrategy() { 28 | Integer nextSlot = naturalOrderingParkingStrategy.getNextSlot(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/com/uditagarwal/service/ParkingLotServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.uditagarwal.service; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.mockito.Mockito.mock; 5 | import static org.mockito.Mockito.verify; 6 | import static org.mockito.Mockito.when; 7 | 8 | import com.uditagarwal.exception.ParkingLotException; 9 | import com.uditagarwal.model.Car; 10 | import com.uditagarwal.model.ParkingLot; 11 | import com.uditagarwal.model.Slot; 12 | import com.uditagarwal.model.parking.strategy.NaturalOrderingParkingStrategy; 13 | import com.uditagarwal.model.parking.strategy.ParkingStrategy; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.Map; 17 | import org.junit.Before; 18 | import org.junit.Test; 19 | 20 | public class ParkingLotServiceTest { 21 | private ParkingLotService parkingLotService = new ParkingLotService(); 22 | private ParkingStrategy parkingStrategy; 23 | private ParkingLot parkingLot; 24 | 25 | @Before 26 | public void setUp() throws Exception { 27 | parkingStrategy = mock(ParkingStrategy.class); 28 | ; 29 | parkingLot = mock(ParkingLot.class); 30 | parkingLotService.createParkingLot(parkingLot, parkingStrategy); 31 | } 32 | 33 | @Test(expected = ParkingLotException.class) 34 | public void testCreatingParkingLotWhenAlreadyExists() { 35 | final ParkingLotService parkingLotService = new ParkingLotService(); 36 | parkingLotService.createParkingLot(new ParkingLot(10), new NaturalOrderingParkingStrategy()); 37 | parkingLotService.createParkingLot(new ParkingLot(20), new NaturalOrderingParkingStrategy()); 38 | } 39 | 40 | @Test 41 | public void testSlotNumberIsRemovedFromStrategyAfterParking() { 42 | final Car testCar = new Car("test-car-no", "white"); 43 | when(parkingStrategy.getNextSlot()).thenReturn(1); 44 | parkingLotService.park(testCar); 45 | verify(parkingStrategy).removeSlot(1); 46 | } 47 | 48 | @Test 49 | public void testParkingIsDoneInTheParkingLot() { 50 | final Car testCar = new Car("test-car-no", "white"); 51 | when(parkingStrategy.getNextSlot()).thenReturn(1); 52 | parkingLotService.park(testCar); 53 | verify(parkingLot).park(testCar, 1); 54 | } 55 | 56 | @Test(expected = ParkingLotException.class) 57 | public void testParkingCarWithoutCreatingParkingLot() { 58 | final ParkingLotService parkingLotService = new ParkingLotService(); 59 | final Car testCar = new Car("test-car-no", "white"); 60 | parkingLotService.park(testCar); 61 | } 62 | 63 | @Test 64 | public void testFreeingSlot() { 65 | parkingLotService.makeSlotFree(1); 66 | verify(parkingStrategy).addSlot(1); 67 | verify(parkingLot).makeSlotFree(1); 68 | } 69 | 70 | @Test(expected = ParkingLotException.class) 71 | public void testFreeingSlotWithoutCreatingParkingLot() { 72 | final ParkingLotService parkingLotService = new ParkingLotService(); 73 | parkingLotService.makeSlotFree(1); 74 | } 75 | 76 | @Test 77 | public void testOccupiedSlots() { 78 | final Map allSlots = new HashMap<>(); 79 | final Slot slot1 = new Slot(1); 80 | final Slot slot2 = new Slot(2); 81 | slot2.assignCar(new Car("test-car-no1", "white")); 82 | final Slot slot3 = new Slot(3); 83 | final Slot slot4 = new Slot(4); 84 | slot4.assignCar(new Car("test-car-no2", "white")); 85 | 86 | allSlots.put(1, slot1); 87 | allSlots.put(2, slot2); 88 | allSlots.put(3, slot3); 89 | allSlots.put(4, slot4); 90 | 91 | when(parkingLot.getSlots()).thenReturn(allSlots); 92 | when(parkingLot.getCapacity()).thenReturn(10); 93 | 94 | final List occupiedSlots = parkingLotService.getOccupiedSlots(); 95 | assertEquals(2, occupiedSlots.size()); 96 | assertEquals(slot2, occupiedSlots.get(0)); 97 | assertEquals(slot4, occupiedSlots.get(1)); 98 | } 99 | 100 | @Test 101 | public void testGetSlotsForAParticularColor() { 102 | final Map allSlots = new HashMap<>(); 103 | final Slot slot1 = new Slot(1); 104 | slot1.assignCar(new Car("test-car-no1", "blue")); 105 | final Slot slot2 = new Slot(2); 106 | slot2.assignCar(new Car("test-car-no2", "white")); 107 | final Slot slot3 = new Slot(3); 108 | final Slot slot4 = new Slot(4); 109 | slot4.assignCar(new Car("test-car-no3", "white")); 110 | 111 | allSlots.put(1, slot1); 112 | allSlots.put(2, slot2); 113 | allSlots.put(3, slot3); 114 | allSlots.put(4, slot4); 115 | 116 | when(parkingLot.getSlots()).thenReturn(allSlots); 117 | when(parkingLot.getCapacity()).thenReturn(10); 118 | 119 | final List slots = parkingLotService.getSlotsForColor("white"); 120 | assertEquals(2, slots.size()); 121 | assertEquals(slot2, slots.get(0)); 122 | assertEquals(slot4, slots.get(1)); 123 | } 124 | 125 | @Test 126 | public void testGetSlotsForAParticularCarColorWhenNoCarMatches() { 127 | final Map allSlots = new HashMap<>(); 128 | final Slot slot1 = new Slot(1); 129 | slot1.assignCar(new Car("test-car-no1", "blue")); 130 | final Slot slot2 = new Slot(2); 131 | final Slot slot3 = new Slot(3); 132 | slot3.assignCar(new Car("test-car-no2", "red")); 133 | 134 | allSlots.put(1, slot1); 135 | allSlots.put(2, slot2); 136 | allSlots.put(3, slot3); 137 | 138 | when(parkingLot.getSlots()).thenReturn(allSlots); 139 | when(parkingLot.getCapacity()).thenReturn(10); 140 | 141 | final List slots = parkingLotService.getSlotsForColor("white"); 142 | assertEquals(0, slots.size()); 143 | } 144 | } 145 | --------------------------------------------------------------------------------