From 9880a31643017c253aee5cd98b51bd89de7e144c Mon Sep 17 00:00:00 2001 From: Leonid Date: Tue, 28 Jul 2020 23:13:41 +0300 Subject: [PATCH 01/19] model and some other classes created --- .../java/com/ecobike/CommandExecutor.java | 22 + src/main/java/com/ecobike/Communicator.java | 20 + .../java/com/ecobike/ConsoleCommunicator.java | 41 + .../java/com/ecobike/EcoBikeApplication.java | 77 ++ src/main/java/com/ecobike/Operation.java | 11 + .../java/com/ecobike/command/Command.java | 7 + .../java/com/ecobike/command/ExitCommand.java | 12 + .../ecobike/model/AbstractElectroBike.java | 66 ++ src/main/java/com/ecobike/model/Bike.java | 83 ++ src/main/java/com/ecobike/model/EBike.java | 20 + .../java/com/ecobike/model/FoldingBike.java | 63 ++ .../java/com/ecobike/model/SpeedelecBike.java | 20 + src/main/resources/ecobike.txt | 1000 +++++++++++++++++ 13 files changed, 1442 insertions(+) create mode 100644 src/main/java/com/ecobike/CommandExecutor.java create mode 100644 src/main/java/com/ecobike/Communicator.java create mode 100644 src/main/java/com/ecobike/ConsoleCommunicator.java create mode 100644 src/main/java/com/ecobike/EcoBikeApplication.java create mode 100644 src/main/java/com/ecobike/Operation.java create mode 100644 src/main/java/com/ecobike/command/Command.java create mode 100644 src/main/java/com/ecobike/command/ExitCommand.java create mode 100644 src/main/java/com/ecobike/model/AbstractElectroBike.java create mode 100644 src/main/java/com/ecobike/model/Bike.java create mode 100644 src/main/java/com/ecobike/model/EBike.java create mode 100644 src/main/java/com/ecobike/model/FoldingBike.java create mode 100644 src/main/java/com/ecobike/model/SpeedelecBike.java create mode 100644 src/main/resources/ecobike.txt diff --git a/src/main/java/com/ecobike/CommandExecutor.java b/src/main/java/com/ecobike/CommandExecutor.java new file mode 100644 index 0000000..1d20320 --- /dev/null +++ b/src/main/java/com/ecobike/CommandExecutor.java @@ -0,0 +1,22 @@ +package com.ecobike; + +import com.ecobike.command.Command; +import com.ecobike.command.ExitCommand; + +import java.util.HashMap; +import java.util.Map; + +public class CommandExecutor { + private static final Map allKnownCommandsMap = new HashMap<>(); + + static { + allKnownCommandsMap.put(Operation.STOP_PROGRAM, new ExitCommand()); + } + + private CommandExecutor() { + } + + public static void execute(Operation operation) throws Exception { + allKnownCommandsMap.get(operation).execute(); + } +} diff --git a/src/main/java/com/ecobike/Communicator.java b/src/main/java/com/ecobike/Communicator.java new file mode 100644 index 0000000..135efc2 --- /dev/null +++ b/src/main/java/com/ecobike/Communicator.java @@ -0,0 +1,20 @@ +package com.ecobike; + +import java.io.IOException; + +public interface Communicator { + /** + * Method writs message to user. + */ + void writeMessage(String message); + + /** + * Method reads string from user. + */ + String readString(); + + /** + * Method reads integer from user. + */ + int readInt(); +} diff --git a/src/main/java/com/ecobike/ConsoleCommunicator.java b/src/main/java/com/ecobike/ConsoleCommunicator.java new file mode 100644 index 0000000..0a9fe54 --- /dev/null +++ b/src/main/java/com/ecobike/ConsoleCommunicator.java @@ -0,0 +1,41 @@ +package com.ecobike; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +public class ConsoleCommunicator implements Communicator { + + private static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + + @Override + public void writeMessage(String message) { + System.out.println(message); + } + + @Override + public String readString() { + String entry = null; + while (entry == null) { + try { + entry = reader.readLine(); + } catch (IOException e) { + System.out.println("Repeat your entry:"); + } + } + return entry; + } + + @Override + public int readInt() { + int result = -1; + while (result < 0) { + try { + result = Integer.parseInt(readString()); + } catch (NumberFormatException e) { + System.out.println("Repeat your entry (only number):"); + } + } + return result; + } +} diff --git a/src/main/java/com/ecobike/EcoBikeApplication.java b/src/main/java/com/ecobike/EcoBikeApplication.java new file mode 100644 index 0000000..8f8bd90 --- /dev/null +++ b/src/main/java/com/ecobike/EcoBikeApplication.java @@ -0,0 +1,77 @@ +package com.ecobike; + +import com.ecobike.model.Bike; +import com.ecobike.model.EBike; +import com.ecobike.model.FoldingBike; +import com.ecobike.model.SpeedelecBike; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; + +public class EcoBikeApplication { + public static final Communicator COMMUNICATOR = new ConsoleCommunicator(); + private static Path bikeDataFile; + static { + do { + COMMUNICATOR.writeMessage("Enter path to ecobike.txt :"); + bikeDataFile = Paths.get(COMMUNICATOR.readString()); + } while (Files.isRegularFile(bikeDataFile)); + } + private static List bikeList; + + public static void main(String[] args) throws IOException { + bikeList = Files.lines(bikeDataFile) + .map(line -> parseBike(line)) + .collect(Collectors.toList()); + Operation operation = null; + do { + try { + operation = askOperation(); + CommandExecutor.execute(operation); + } catch (Exception e) { + e.printStackTrace(); + COMMUNICATOR.writeMessage("Error. Check entered data."); + } + } while (operation != Operation.STOP_PROGRAM); + } + + private static Operation askOperation() throws IOException { + COMMUNICATOR.writeMessage(""); + COMMUNICATOR.writeMessage("Please make your choice:"); + COMMUNICATOR.writeMessage("\t 1 - Show the entire EcoBike catalog"); + COMMUNICATOR.writeMessage("\t 2 – Add a new folding bike"); + COMMUNICATOR.writeMessage("\t 3 – Add a new speedelec"); + COMMUNICATOR.writeMessage("\t 4 – Add a new e-bike"); + COMMUNICATOR.writeMessage("\t 5 – Find the first item of a particular brand"); + COMMUNICATOR.writeMessage("\t 6 – Write to file"); + COMMUNICATOR.writeMessage("\t 7 – Stop the program"); + + return Operation.values()[COMMUNICATOR.readInt() - 1]; + } + + private static Bike parseBike(String line) { + String[] toConstructor = line.split(" ", 2)[1].split("; "); + switch (line.split(" ")[0]) { + case "SPEEDELEC": + return new SpeedelecBike(toConstructor[0], Integer.parseInt(toConstructor[1]), + Integer.parseInt(toConstructor[2]), Boolean.parseBoolean(toConstructor[3]), + Integer.parseInt(toConstructor[4]), toConstructor[5], + Integer.parseInt(toConstructor[6])); + case "E-BIKE": + return new EBike(toConstructor[0], Integer.parseInt(toConstructor[1]), + Integer.parseInt(toConstructor[2]), Boolean.parseBoolean(toConstructor[3]), + Integer.parseInt(toConstructor[4]), toConstructor[5], + Integer.parseInt(toConstructor[6])); + case "FOLDING BIKE": + return new FoldingBike(toConstructor[0], Integer.parseInt(toConstructor[1]), + Integer.parseInt(toConstructor[2]), Integer.parseInt(toConstructor[3]), + Boolean.parseBoolean(toConstructor[4]), toConstructor[5], + Integer.parseInt(toConstructor[6])); + } + return null; + } +} diff --git a/src/main/java/com/ecobike/Operation.java b/src/main/java/com/ecobike/Operation.java new file mode 100644 index 0000000..3fe4002 --- /dev/null +++ b/src/main/java/com/ecobike/Operation.java @@ -0,0 +1,11 @@ +package com.ecobike; + +public enum Operation { + SHOW_CATALOG, + ADD_FOLDING_BIKE, + ADD_SPEEDELEC_BIKE, + ADD_E_BIKE, + FIND_FIRST_ITEM_BY_BRAND, + WRITE_TO_FILE, + STOP_PROGRAM +} diff --git a/src/main/java/com/ecobike/command/Command.java b/src/main/java/com/ecobike/command/Command.java new file mode 100644 index 0000000..b87d46e --- /dev/null +++ b/src/main/java/com/ecobike/command/Command.java @@ -0,0 +1,7 @@ +package com.ecobike.command; + +import java.io.IOException; + +public interface Command { + void execute() throws IOException; +} diff --git a/src/main/java/com/ecobike/command/ExitCommand.java b/src/main/java/com/ecobike/command/ExitCommand.java new file mode 100644 index 0000000..889a4d6 --- /dev/null +++ b/src/main/java/com/ecobike/command/ExitCommand.java @@ -0,0 +1,12 @@ +package com.ecobike.command; + +import com.ecobike.EcoBikeApplication; + +import java.io.IOException; + +public class ExitCommand implements Command { + @Override + public void execute() throws IOException { + EcoBikeApplication.COMMUNICATOR.writeMessage("Good bay!"); + } +} diff --git a/src/main/java/com/ecobike/model/AbstractElectroBike.java b/src/main/java/com/ecobike/model/AbstractElectroBike.java new file mode 100644 index 0000000..7d5b8e0 --- /dev/null +++ b/src/main/java/com/ecobike/model/AbstractElectroBike.java @@ -0,0 +1,66 @@ +package com.ecobike.model; + +import java.util.Objects; + +public abstract class AbstractElectroBike extends Bike { + + /** + * Max speed in km/h + */ + private int maxSpeed; + + /** + * Battery capacity in mAh + */ + private int batteryCapacity; + + public AbstractElectroBike(String brand, + int maxSpeed, + int weight, + boolean isLightsPresent, + int batteryCapacity, + String color, + int price) { + super(brand, weight, isLightsPresent, color, price); + this.maxSpeed = maxSpeed; + this.batteryCapacity = batteryCapacity; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + AbstractElectroBike that = (AbstractElectroBike) o; + return maxSpeed == that.maxSpeed && + batteryCapacity == that.batteryCapacity; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), maxSpeed, batteryCapacity); + } + + @Override + public String toString() { + return String.format(" %s with %d mAh battery and%s head/tail light." + + "\nPrice: %d euros.", + getBrand(), getBatteryCapacity(), isLightsPresent() ? "" : " no", getPrice()); + } + + public int getMaxSpeed() { + return maxSpeed; + } + + public void setMaxSpeed(int maxSpeed) { + this.maxSpeed = maxSpeed; + } + + public int getBatteryCapacity() { + return batteryCapacity; + } + + public void setBatteryCapacity(int batteryCapacity) { + this.batteryCapacity = batteryCapacity; + } +} diff --git a/src/main/java/com/ecobike/model/Bike.java b/src/main/java/com/ecobike/model/Bike.java new file mode 100644 index 0000000..b8882b7 --- /dev/null +++ b/src/main/java/com/ecobike/model/Bike.java @@ -0,0 +1,83 @@ +package com.ecobike.model; + +import java.util.Objects; + +public abstract class Bike { + + private String brand; + /** + * Bike's weight in grams + */ + private int weight; + private boolean isLightsPresent; + private String color; + /** + * Price in EUR + */ + private int price; + + public Bike(String brand, int weight, boolean isLightsPresent, String color, int price) { + this.brand = brand; + this.weight = weight; + this.isLightsPresent = isLightsPresent; + this.color = color; + this.price = price; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Bike bike = (Bike) o; + return weight == bike.weight && + isLightsPresent == bike.isLightsPresent && + price == bike.price && + Objects.equals(brand, bike.brand) && + Objects.equals(color, bike.color); + } + + @Override + public int hashCode() { + return Objects.hash(brand, weight, isLightsPresent, color, price); + } + + public String getBrand() { + return brand; + } + + public void setBrand(String brand) { + this.brand = brand; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public boolean isLightsPresent() { + return isLightsPresent; + } + + public void setLightsPresent(boolean lightsPresent) { + isLightsPresent = lightsPresent; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + + public int getPrice() { + return price; + } + + public void setPrice(int price) { + this.price = price; + } +} diff --git a/src/main/java/com/ecobike/model/EBike.java b/src/main/java/com/ecobike/model/EBike.java new file mode 100644 index 0000000..e0ee165 --- /dev/null +++ b/src/main/java/com/ecobike/model/EBike.java @@ -0,0 +1,20 @@ +package com.ecobike.model; + +public class EBike extends AbstractElectroBike { + + public EBike(String brand, + int maxSpeed, + int weight, + boolean isLightsPresent, + int batteryCapacity, + String color, + int price) { + super(brand, maxSpeed, weight, isLightsPresent, + batteryCapacity, color, price); + } + + @Override + public String toString() { + return "E-BIKE" + super.toString(); + } +} diff --git a/src/main/java/com/ecobike/model/FoldingBike.java b/src/main/java/com/ecobike/model/FoldingBike.java new file mode 100644 index 0000000..c44ab1c --- /dev/null +++ b/src/main/java/com/ecobike/model/FoldingBike.java @@ -0,0 +1,63 @@ +package com.ecobike.model; + +import java.util.Objects; + +public class FoldingBike extends Bike { + + /** + * Wheel size in inch. + */ + private int wheelSize; + + private int numberOfGears; + + public FoldingBike(String brand, + int wheelSize, + int numberOfGears, + int weight, + boolean isLightsPresent, + String color, + int price) { + super(brand, weight, isLightsPresent, color, price); + this.wheelSize = wheelSize; + this.numberOfGears = numberOfGears; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + FoldingBike that = (FoldingBike) o; + return wheelSize == that.wheelSize && + numberOfGears == that.numberOfGears; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), wheelSize, numberOfGears); + } + + @Override + public String toString() { + return String.format("FOLDING BIKE %s with %d gear(s) and%s head/tail light." + + "\nPrice: %d euros.", + getBrand(), getNumberOfGears(), isLightsPresent() ? "" : " no", getPrice()); + } + + public int getWheelSize() { + return wheelSize; + } + + public void setWheelSize(int wheelSize) { + this.wheelSize = wheelSize; + } + + public int getNumberOfGears() { + return numberOfGears; + } + + public void setNumberOfGears(int numberOfGears) { + this.numberOfGears = numberOfGears; + } +} diff --git a/src/main/java/com/ecobike/model/SpeedelecBike.java b/src/main/java/com/ecobike/model/SpeedelecBike.java new file mode 100644 index 0000000..fc3d62e --- /dev/null +++ b/src/main/java/com/ecobike/model/SpeedelecBike.java @@ -0,0 +1,20 @@ +package com.ecobike.model; + +public class SpeedelecBike extends AbstractElectroBike { + + public SpeedelecBike(String brand, + int maxSpeed, + int weight, + boolean isLightsPresent, + int batteryCapacity, + String color, + int price) { + super(brand, maxSpeed, weight, isLightsPresent, + batteryCapacity, color, price); + } + + @Override + public String toString() { + return "SPEEDELEC" + super.toString(); + } +} diff --git a/src/main/resources/ecobike.txt b/src/main/resources/ecobike.txt new file mode 100644 index 0000000..9a57fdc --- /dev/null +++ b/src/main/resources/ecobike.txt @@ -0,0 +1,1000 @@ +SPEEDELEC Booster; 35; 10900; false; 13200; green; 1279 +SPEEDELEC E-Scooter; 60; 15300; false; 14800; marine; 309 +E-BIKE Lankeleisi; 65; 24200; false; 10000; black; 2399 +E-BIKE Lankeleisi; 50; 21600; false; 30000; flame; 859 +FOLDING BIKE Benetti; 24; 27; 11400; false; rose; 1009 +FOLDING BIKE Benetti; 24; 6; 9400; true; silver; 1195 +FOLDING BIKE Formula; 16; 21; 12200; true; flame; 269 +E-BIKE ElectrO; 20; 19300; true; 14000; beige; 1025 +SPEEDELEC Dualtron; 30; 14400; true; 6500; dark gray; 1019 +FOLDING BIKE Intertool; 24; 21; 12900; true; coral; 1565 +E-BIKE Gazelle; 45; 25200; true; 11000; yellow; 855 +SPEEDELEC EcoRide; 15; 8300; true; 15600; blue; 1055 +SPEEDELEC Smart; 40; 9600; false; 13000; brown; 1065 +E-BIKE Felt; 60; 28300; false; 9000; grey; 2229 +SPEEDELEC Freego; 55; 9000; false; 15800; grenadine; 1505 +E-BIKE Mando Footloose; 20; 26800; false; 12000; beige; 1559 +FOLDING BIKE Brompton; 20; 7; 10800; true; orange; 1469 +FOLDING BIKE Comanche; 24; 6; 13500; true; olive; 609 +E-BIKE SkyBike; 40; 27200; false; 31000; olive; 1089 +FOLDING BIKE Titan; 20; 24; 10400; true; olive; 479 +FOLDING BIKE Titan; 20; 1; 11800; false; khaki; 1115 +SPEEDELEC EcoRide; 50; 8400; false; 8600; brown; 1609 +FOLDING BIKE BMW; 20; 7; 14400; false; lemon; 1085 +E-BIKE Ecofect; 30; 19200; true; 15000; lemon; 2919 +E-BIKE Gazelle; 25; 22200; true; 26000; silver; 1735 +SPEEDELEC Speedway; 25; 6800; false; 15200; blue; 915 +E-BIKE Ferrari; 55; 20900; false; 28000; olive; 3055 +FOLDING BIKE BMW; 16; 9; 11000; true; emerald; 1459 +SPEEDELEC Segway; 50; 11600; true; 7300; marine; 375 +FOLDING BIKE VNV; 20; 27; 10100; true; emerald; 1645 +E-BIKE ION; 25; 20800; true; 30000; coral; 2355 +SPEEDELEC OIO; 10; 10400; false; 8800; pink; 1099 +FOLDING BIKE Author; 20; 9; 11900; true; brown; 209 +SPEEDELEC Segway; 10; 11700; false; 12000; pink; 1365 +SPEEDELEC Peugeot; 60; 19800; true; 12200; yellow; 885 +FOLDING BIKE West Bike; 20; 21; 15300; false; lemon; 1039 +FOLDING BIKE Dahon; 14; 21; 12200; true; golden; 1169 +E-BIKE Porshe Design; 45; 28800; true; 13000; dark gray; 2789 +FOLDING BIKE Trinx; 20; 24; 10700; true; pink; 855 +FOLDING BIKE Cayman; 16; 27; 8500; false; orange; 735 +E-BIKE E-Motion; 50; 26500; false; 8000; yellow; 2145 +E-BIKE Gazelle; 55; 20800; true; 14000; violet; 1805 +SPEEDELEC Speedelec Minirider; 10; 7600; false; 6300; silver; 125 +FOLDING BIKE Polygon; 20; 8; 15600; false; orange; 1285 +FOLDING BIKE SkyBike; 24; 27; 16200; false; orange; 505 +SPEEDELEC Speedelec Minirider; 25; 15900; true; 14200; pink; 299 +SPEEDELEC Smart; 10; 15000; false; 7200; olive; 455 +FOLDING BIKE West Bike; 14; 9; 11500; false; golden; 1075 +SPEEDELEC ITrike; 25; 14500; false; 5600; marine; 259 +SPEEDELEC Maraton; 40; 15000; true; 7500; golden; 269 +SPEEDELEC Speedelec Minirider; 15; 10000; false; 13900; coral; 855 +FOLDING BIKE Intertool; 26; 21; 11700; true; violet; 689 +SPEEDELEC ITrike; 55; 15000; false; 14500; emerald; 945 +FOLDING BIKE Make Bike; 24; 1; 16300; false; grenadine; 1249 +SPEEDELEC Smart; 65; 17800; false; 13300; khaki; 985 +SPEEDELEC ITrike; 60; 18700; false; 7000; blue; 1109 +FOLDING BIKE Comanche; 16; 21; 16500; true; lemon; 1015 +E-BIKE Rover; 45; 27300; false; 12000; grenadine; 2799 +FOLDING BIKE Crosser; 24; 3; 16000; false; marine; 1129 +FOLDING BIKE Titan; 26; 1; 13800; false; blue; 1655 +FOLDING BIKE Polygon; 18; 7; 8200; true; rose; 1005 +FOLDING BIKE Cayman; 26; 1; 16300; false; khaki; 605 +FOLDING BIKE Trinx; 26; 3; 8400; true; flame; 455 +E-BIKE Xiaomi; 65; 26300; false; 29000; yellow; 3489 +SPEEDELEC Ultron; 25; 16000; true; 10400; violet; 909 +SPEEDELEC Peugeot; 20; 9900; true; 2500; orange; 809 +SPEEDELEC Speedway; 45; 7100; false; 9700; red; 615 +SPEEDELEC Maraton; 20; 19800; false; 15900; silver; 1215 +FOLDING BIKE Titan; 14; 9; 13100; false; orange; 1055 +FOLDING BIKE Comanche; 20; 27; 11700; false; yellow; 1349 +FOLDING BIKE Crosser; 24; 6; 9500; false; lemon; 1675 +E-BIKE Farad; 25; 20700; false; 13000; blue; 2355 +E-BIKE Xiaomi; 65; 22100; false; 11000; dark gray; 1905 +SPEEDELEC Ultron; 35; 7600; false; 3900; grenadine; 1179 +SPEEDELEC Dualtron; 35; 12900; true; 9900; grey; 379 +SPEEDELEC AirWheel; 45; 9900; true; 11400; grenadine; 469 +E-BIKE Zaxboard; 30; 20300; false; 30000; yellow; 1975 +E-BIKE Rover; 20; 21400; false; 16000; flame; 1659 +FOLDING BIKE Brompton; 20; 6; 9600; true; orange; 885 +E-BIKE ION; 20; 20200; false; 20000; grey; 1989 +E-BIKE Zaxboard; 30; 22500; false; 28000; pink; 3345 +FOLDING BIKE West Bike; 18; 18; 15700; false; lemon; 339 +SPEEDELEC Speedelec Minirider; 55; 6900; true; 3800; yellow; 685 +E-BIKE Gazelle; 35; 24700; true; 22000; brown; 3219 +SPEEDELEC Xiaomi; 35; 15300; false; 11700; beige; 1269 +FOLDING BIKE Make Bike; 16; 18; 8900; false; orange; 1109 +E-BIKE Zaxboard; 65; 23300; false; 16000; grenadine; 1275 +E-BIKE Ferrari; 25; 21900; false; 15000; green; 1689 +FOLDING BIKE Crosser; 20; 9; 9500; true; rose; 979 +E-BIKE Mercedes; 50; 24200; true; 21000; orange; 3459 +SPEEDELEC Ultron; 40; 11900; true; 7100; white; 1305 +FOLDING BIKE Trinx; 18; 18; 9600; false; yellow; 359 +SPEEDELEC ITrike; 60; 19600; true; 9800; red; 1385 +SPEEDELEC ITrike; 60; 13200; false; 13800; orange; 475 +FOLDING BIKE VNV; 26; 3; 14600; false; beige; 715 +FOLDING BIKE Dahon; 24; 21; 9000; false; dark gray; 485 +E-BIKE Mando Footloose; 40; 21500; false; 28000; white; 1815 +E-BIKE Ferrari; 30; 19200; false; 9000; beige; 3059 +SPEEDELEC Ultron; 30; 20300; true; 14800; green; 299 +FOLDING BIKE Polygon; 16; 24; 11100; false; lemon; 1379 +E-BIKE Farad; 65; 28500; false; 25000; grenadine; 3385 +SPEEDELEC Dualtron; 50; 16100; false; 2600; violet; 1645 +FOLDING BIKE Flex; 24; 21; 12400; true; silver; 1559 +E-BIKE Zaxboard; 45; 24300; true; 23000; marine; 1535 +FOLDING BIKE Kross; 18; 6; 8800; true; golden; 379 +FOLDING BIKE Dahon; 20; 3; 8000; true; lemon; 779 +E-BIKE Porshe Design; 50; 28100; true; 13000; pink; 755 +FOLDING BIKE Author; 24; 27; 13200; false; flame; 325 +FOLDING BIKE BMW; 18; 27; 10600; true; dark gray; 1549 +E-BIKE Rover; 20; 26100; false; 29000; grenadine; 2219 +E-BIKE Felt; 20; 19300; true; 16000; flame; 2745 +SPEEDELEC E-Scooter; 35; 9700; false; 11500; yellow; 559 +SPEEDELEC Maraton; 65; 16300; true; 12100; red; 1499 +SPEEDELEC Xiaomi; 55; 9500; false; 12500; golden; 779 +E-BIKE Haibike; 60; 25700; true; 8000; golden; 1299 +E-BIKE E-Motion; 35; 25700; false; 16000; blue; 3399 +E-BIKE ElectrO; 20; 19100; false; 30000; dark gray; 3259 +SPEEDELEC Aest; 55; 21800; false; 8400; blue; 359 +E-BIKE ElectrO; 50; 22600; false; 22000; emerald; 2205 +FOLDING BIKE Brompton; 20; 7; 8000; false; dark gray; 999 +SPEEDELEC Dualtron; 25; 21900; true; 6500; silver; 1419 +E-BIKE Zaxboard; 25; 26200; true; 22000; silver; 705 +FOLDING BIKE Polygon; 16; 21; 16800; false; khaki; 295 +FOLDING BIKE Author; 14; 9; 9100; false; lemon; 529 +SPEEDELEC Segway; 25; 16800; false; 2200; silver; 929 +SPEEDELEC Speedway; 35; 18400; true; 11900; grey; 339 +FOLDING BIKE Titan; 24; 6; 14400; true; khaki; 359 +E-BIKE E-Motion; 40; 19100; false; 26000; khaki; 1015 +SPEEDELEC Dualtron; 25; 10000; true; 9100; brown; 1279 +SPEEDELEC Booster; 20; 15000; false; 10300; orange; 399 +FOLDING BIKE Cayman; 26; 18; 8600; true; brown; 389 +E-BIKE Rover; 60; 27300; false; 11000; red; 1155 +SPEEDELEC E-Scooter; 25; 9700; false; 14700; beige; 939 +FOLDING BIKE Trinx; 24; 3; 8800; false; rose; 699 +FOLDING BIKE Brompton; 26; 7; 15100; true; golden; 255 +FOLDING BIKE Dahon; 24; 18; 9000; false; red; 1505 +E-BIKE Ferrari; 65; 27300; true; 18000; rose; 1675 +E-BIKE Scott; 20; 27100; false; 20000; black; 2565 +E-BIKE Porshe Design; 65; 28600; true; 11000; yellow; 1085 +FOLDING BIKE Flex; 16; 18; 12500; false; grenadine; 905 +E-BIKE Ecofect; 45; 24500; true; 27000; yellow; 2929 +E-BIKE Xiaomi; 50; 26400; false; 15000; red; 679 +FOLDING BIKE Stern; 26; 24; 11200; true; black; 339 +FOLDING BIKE Intertool; 24; 9; 10600; false; blue; 725 +E-BIKE Felt; 60; 22400; true; 25000; violet; 719 +SPEEDELEC Ultron; 20; 8100; false; 3800; black; 779 +FOLDING BIKE Benetti; 20; 27; 15100; true; lemon; 1095 +SPEEDELEC EcoRide; 50; 19500; false; 11100; green; 449 +FOLDING BIKE Cayman; 26; 18; 12500; true; coral; 1185 +E-BIKE SkyBike; 30; 22800; false; 19000; red; 3305 +SPEEDELEC EcoRide; 15; 7200; false; 11200; green; 1135 +SPEEDELEC ITrike; 35; 18900; false; 7600; flame; 1235 +FOLDING BIKE Dahon; 16; 1; 12100; false; grey; 799 +E-BIKE Porshe Design; 35; 20100; true; 25000; dark gray; 759 +FOLDING BIKE Dahon; 20; 18; 16500; true; olive; 1215 +FOLDING BIKE Formula; 24; 8; 10300; true; silver; 619 +FOLDING BIKE Intertool; 24; 9; 13300; false; silver; 529 +FOLDING BIKE Formula; 16; 6; 16900; false; golden; 445 +FOLDING BIKE Make Bike; 26; 27; 8100; true; red; 305 +E-BIKE Mando Footloose; 20; 22000; true; 16000; blue; 2539 +SPEEDELEC E-Scooter; 30; 10500; false; 9200; emerald; 155 +E-BIKE Xiaomi; 20; 28900; true; 31000; khaki; 1739 +E-BIKE Felt; 35; 22800; false; 13000; beige; 1975 +E-BIKE Farad; 35; 27000; true; 12000; blue; 1579 +SPEEDELEC Smart; 45; 8500; true; 12400; yellow; 545 +SPEEDELEC Ultron; 65; 21600; false; 4800; silver; 599 +SPEEDELEC Smart; 45; 21100; true; 6200; violet; 339 +E-BIKE Ecofect; 30; 21000; true; 10000; orange; 2479 +E-BIKE SkyBike; 65; 23200; false; 31000; khaki; 675 +E-BIKE Scott; 30; 28000; true; 31000; olive; 1125 +SPEEDELEC Dualtron; 15; 16900; false; 7500; black; 225 +FOLDING BIKE SkyBike; 16; 9; 14800; true; yellow; 1049 +FOLDING BIKE Dahon; 20; 1; 13100; false; grenadine; 1329 +FOLDING BIKE Crosser; 20; 1; 14800; true; white; 395 +E-BIKE Ecofect; 25; 26400; false; 17000; silver; 1145 +FOLDING BIKE Dahon; 24; 7; 10900; false; violet; 1515 +E-BIKE Gazelle; 25; 28900; false; 12000; red; 2129 +E-BIKE ION; 45; 23500; false; 24000; pink; 739 +E-BIKE GoCycle; 65; 25200; true; 26000; dark gray; 2829 +E-BIKE Mando Footloose; 55; 20600; false; 21000; coral; 1385 +E-BIKE SkyBike; 55; 25900; false; 9000; silver; 3239 +FOLDING BIKE Crosser; 20; 18; 13800; false; khaki; 249 +E-BIKE Gazelle; 55; 23800; false; 22000; marine; 3375 +SPEEDELEC Dualtron; 65; 6600; false; 6300; grenadine; 1435 +FOLDING BIKE Polygon; 24; 3; 14000; false; khaki; 639 +FOLDING BIKE Dahon; 26; 27; 12600; false; beige; 1025 +SPEEDELEC Speedway; 30; 13200; true; 15600; brown; 1575 +FOLDING BIKE Benetti; 20; 6; 16000; false; dark gray; 289 +FOLDING BIKE Benetti; 20; 21; 13600; false; lemon; 1399 +E-BIKE Haibike; 30; 26200; false; 24000; red; 1979 +E-BIKE SkyBike; 60; 20700; false; 15000; violet; 2119 +E-BIKE SkyBike; 40; 27400; true; 8000; orange; 1699 +FOLDING BIKE Crosser; 20; 18; 14000; true; violet; 369 +E-BIKE Xiaomi; 55; 23600; false; 8000; black; 2555 +FOLDING BIKE West Bike; 18; 27; 11700; false; emerald; 1439 +SPEEDELEC Smart; 55; 9400; true; 12700; white; 535 +E-BIKE Gazelle; 60; 20600; false; 11000; emerald; 2879 +E-BIKE Ferrari; 50; 28500; false; 25000; golden; 1489 +SPEEDELEC Xiaomi; 25; 13500; true; 7300; blue; 1569 +SPEEDELEC Dualtron; 15; 11800; true; 10500; yellow; 1449 +FOLDING BIKE Author; 20; 8; 9300; true; emerald; 1355 +E-BIKE Ferrari; 20; 23800; true; 14000; violet; 2479 +SPEEDELEC Freego; 65; 18400; false; 5000; yellow; 1309 +SPEEDELEC Speedelec Minirider; 15; 10000; false; 15100; orange; 1349 +SPEEDELEC Kugoo; 20; 11600; true; 13500; dark gray; 349 +E-BIKE GoCycle; 55; 25000; false; 23000; flame; 2189 +SPEEDELEC Dualtron; 30; 15500; false; 4900; red; 439 +E-BIKE Felt; 30; 25200; false; 10000; dark gray; 2845 +E-BIKE Rover; 40; 28100; false; 13000; grenadine; 1649 +FOLDING BIKE Polygon; 24; 27; 10600; true; grey; 645 +E-BIKE Zaxboard; 55; 22400; true; 27000; blue; 2249 +E-BIKE GoCycle; 50; 24400; true; 22000; coral; 2195 +E-BIKE Koga; 20; 22900; false; 12000; olive; 2129 +E-BIKE Lankeleisi; 50; 22500; false; 13000; golden; 795 +FOLDING BIKE Intertool; 20; 1; 14300; true; khaki; 859 +SPEEDELEC Xiaomi; 45; 18200; false; 10400; white; 679 +SPEEDELEC OIO; 10; 19300; false; 14500; red; 265 +FOLDING BIKE Stern; 24; 21; 12800; false; blue; 725 +E-BIKE Koga; 65; 20200; true; 19000; grey; 3185 +FOLDING BIKE BMW; 20; 9; 14600; true; marine; 1605 +SPEEDELEC AirWheel; 15; 9900; true; 14000; olive; 1009 +E-BIKE Zaxboard; 55; 20700; false; 27000; marine; 909 +E-BIKE Scott; 25; 21300; true; 15000; pink; 3359 +SPEEDELEC Booster; 65; 6600; false; 11700; pink; 605 +SPEEDELEC Maraton; 55; 17500; false; 2100; dark gray; 765 +SPEEDELEC Ultron; 50; 20500; false; 13000; emerald; 549 +FOLDING BIKE Titan; 24; 3; 10600; false; grenadine; 1075 +SPEEDELEC Booster; 60; 16200; false; 8100; red; 329 +E-BIKE GoCycle; 55; 19600; true; 14000; yellow; 1949 +SPEEDELEC Speedway; 50; 21200; false; 15900; dark gray; 1159 +SPEEDELEC Segway; 50; 12800; true; 4800; green; 1319 +E-BIKE Rover; 35; 22400; false; 11000; rose; 2429 +FOLDING BIKE Dahon; 24; 9; 10600; false; lemon; 389 +SPEEDELEC ITrike; 65; 21800; false; 12900; violet; 1389 +FOLDING BIKE Titan; 20; 6; 13100; true; rose; 365 +FOLDING BIKE Titan; 24; 3; 13100; true; olive; 875 +FOLDING BIKE West Bike; 24; 9; 9600; true; beige; 1315 +FOLDING BIKE Author; 20; 21; 14000; false; grenadine; 1075 +FOLDING BIKE Stern; 18; 3; 14500; false; rose; 1635 +E-BIKE Felt; 60; 21000; false; 26000; dark gray; 2279 +E-BIKE Scott; 45; 21800; true; 28000; dark gray; 2039 +FOLDING BIKE Crosser; 16; 6; 11700; true; black; 1065 +SPEEDELEC Segway; 20; 7600; true; 2200; olive; 669 +SPEEDELEC E-Scooter; 65; 19400; true; 3800; grey; 1115 +FOLDING BIKE Comanche; 20; 18; 9100; true; violet; 1025 +E-BIKE Xiaomi; 30; 22800; true; 25000; coral; 3119 +E-BIKE Zaxboard; 20; 27000; true; 23000; yellow; 1309 +SPEEDELEC Peugeot; 55; 6800; true; 12900; khaki; 515 +FOLDING BIKE Cayman; 24; 7; 15600; false; golden; 999 +SPEEDELEC EcoRide; 45; 8400; false; 8300; beige; 1615 +FOLDING BIKE Dahon; 14; 7; 13900; false; khaki; 1009 +SPEEDELEC Smart; 20; 8700; true; 10500; silver; 1239 +SPEEDELEC Speedway; 45; 21300; false; 4400; green; 1279 +FOLDING BIKE Flex; 26; 3; 14800; false; golden; 205 +SPEEDELEC Speedelec Minirider; 25; 20400; false; 10900; grenadine; 1465 +E-BIKE ION; 25; 19600; false; 8000; green; 2209 +E-BIKE Lankeleisi; 60; 19100; false; 28000; flame; 2989 +FOLDING BIKE Kross; 20; 7; 15800; false; red; 439 +FOLDING BIKE Make Bike; 14; 8; 10000; true; olive; 1149 +SPEEDELEC Tesla; 35; 20300; true; 7700; flame; 135 +FOLDING BIKE Intertool; 24; 27; 9400; false; violet; 1585 +SPEEDELEC ITrike; 10; 20900; true; 5700; golden; 1475 +SPEEDELEC AirWheel; 20; 14300; false; 4000; yellow; 1635 +E-BIKE Scott; 65; 24400; false; 15000; brown; 1215 +E-BIKE Koga; 35; 28100; true; 25000; silver; 2125 +SPEEDELEC Booster; 15; 19000; false; 6600; green; 359 +E-BIKE ElectrO; 20; 22500; false; 31000; rose; 2029 +FOLDING BIKE Comanche; 20; 9; 11100; true; beige; 1639 +E-BIKE Mando Footloose; 40; 24200; false; 16000; golden; 2255 +SPEEDELEC Booster; 55; 19200; true; 2900; pink; 549 +SPEEDELEC OIO; 20; 7600; true; 13500; violet; 849 +SPEEDELEC Ultron; 20; 12900; false; 12300; grenadine; 425 +SPEEDELEC Xiaomi; 10; 9700; true; 8400; silver; 795 +FOLDING BIKE Titan; 24; 9; 15000; true; lemon; 1525 +SPEEDELEC Xiaomi; 10; 20800; true; 10400; brown; 505 +E-BIKE Zaxboard; 60; 28500; false; 27000; silver; 1319 +SPEEDELEC Speedelec Minirider; 35; 9000; true; 4200; black; 1479 +E-BIKE Haibike; 55; 27400; true; 12000; rose; 1955 +FOLDING BIKE BMW; 20; 27; 13200; false; golden; 1289 +E-BIKE Mando Footloose; 35; 24300; false; 26000; yellow; 1369 +E-BIKE ElectrO; 60; 25700; false; 19000; blue; 1725 +E-BIKE Lankeleisi; 35; 27800; true; 26000; coral; 3175 +E-BIKE Felt; 60; 27500; false; 29000; blue; 2825 +E-BIKE Mercedes; 25; 19400; true; 23000; blue; 1719 +SPEEDELEC AirWheel; 65; 12200; false; 13400; rose; 609 +SPEEDELEC Segway; 45; 6800; false; 3700; flame; 259 +SPEEDELEC Aest; 35; 18100; true; 5000; violet; 1049 +E-BIKE ION; 60; 27200; false; 28000; emerald; 1939 +E-BIKE ElectrO; 40; 23600; false; 11000; orange; 2445 +SPEEDELEC Speedelec Minirider; 65; 14500; true; 13600; marine; 1279 +SPEEDELEC Segway; 45; 20700; false; 10700; yellow; 689 +E-BIKE Zaxboard; 35; 24600; true; 17000; khaki; 2015 +E-BIKE Gazelle; 65; 25500; true; 28000; emerald; 3049 +E-BIKE Farad; 60; 21200; false; 16000; green; 1949 +FOLDING BIKE Stern; 20; 8; 15500; false; dark gray; 519 +FOLDING BIKE Flex; 24; 21; 9700; false; dark gray; 1539 +E-BIKE ElectrO; 65; 27100; false; 15000; green; 2615 +E-BIKE Gazelle; 60; 26600; true; 12000; emerald; 2985 +SPEEDELEC EcoRide; 30; 19900; false; 2100; olive; 369 +SPEEDELEC E-Scooter; 60; 14900; true; 2900; white; 129 +E-BIKE Farad; 50; 25900; true; 23000; emerald; 2039 +SPEEDELEC ITrike; 55; 14100; false; 14100; marine; 1459 +FOLDING BIKE Flex; 26; 3; 13700; true; olive; 1129 +FOLDING BIKE VNV; 26; 6; 10800; true; red; 1435 +SPEEDELEC Smart; 30; 19300; false; 12700; marine; 1259 +SPEEDELEC Ultron; 55; 19600; true; 9900; olive; 1515 +FOLDING BIKE Flex; 24; 8; 12600; true; emerald; 825 +E-BIKE Scott; 40; 21600; true; 25000; brown; 1049 +E-BIKE Koga; 20; 22200; false; 27000; pink; 839 +SPEEDELEC Maraton; 45; 9200; false; 7400; marine; 1369 +SPEEDELEC AirWheel; 35; 7100; true; 13600; green; 399 +FOLDING BIKE Stern; 26; 27; 8200; true; olive; 1325 +SPEEDELEC Dualtron; 25; 7000; true; 5100; coral; 1595 +SPEEDELEC Peugeot; 15; 11900; false; 11600; rose; 1589 +E-BIKE Koga; 60; 20400; true; 28000; white; 2829 +SPEEDELEC E-Scooter; 60; 9600; false; 2700; rose; 1415 +SPEEDELEC E-Scooter; 40; 19800; false; 3000; grenadine; 709 +SPEEDELEC EcoRide; 10; 18400; true; 13200; black; 335 +SPEEDELEC Aest; 40; 15100; true; 7700; olive; 159 +SPEEDELEC OIO; 10; 12100; true; 9200; blue; 1169 +SPEEDELEC Segway; 50; 14300; true; 12700; coral; 609 +SPEEDELEC Tesla; 20; 15200; true; 9700; blue; 1015 +FOLDING BIKE BMW; 26; 27; 10800; false; grenadine; 745 +SPEEDELEC Booster; 30; 18500; true; 12700; coral; 885 +FOLDING BIKE Comanche; 20; 21; 11400; false; orange; 985 +FOLDING BIKE Titan; 26; 21; 11200; true; blue; 785 +SPEEDELEC Aest; 40; 14100; true; 6000; silver; 409 +SPEEDELEC Tesla; 35; 16200; true; 6500; coral; 439 +FOLDING BIKE Make Bike; 20; 3; 10300; true; silver; 955 +SPEEDELEC Xiaomi; 55; 6500; false; 3700; blue; 595 +SPEEDELEC Xiaomi; 45; 20900; true; 14900; black; 655 +FOLDING BIKE Brompton; 24; 24; 14500; true; dark gray; 1145 +E-BIKE Zaxboard; 60; 26300; true; 28000; yellow; 1675 +E-BIKE ElectrO; 40; 23400; false; 25000; flame; 1329 +SPEEDELEC Peugeot; 45; 17200; false; 9100; white; 1599 +SPEEDELEC Peugeot; 40; 14800; true; 13200; rose; 339 +E-BIKE Felt; 65; 22000; true; 11000; rose; 1235 +FOLDING BIKE Crosser; 24; 1; 16100; true; orange; 265 +E-BIKE Rover; 60; 27100; true; 20000; beige; 1399 +SPEEDELEC Booster; 50; 11900; false; 5700; pink; 1565 +SPEEDELEC OIO; 45; 10400; true; 11300; golden; 355 +E-BIKE Gazelle; 50; 26900; true; 15000; green; 2359 +E-BIKE Ecofect; 25; 23100; true; 31000; marine; 3419 +SPEEDELEC Speedelec Minirider; 65; 20000; true; 8100; black; 1409 +SPEEDELEC Speedelec Minirider; 55; 12200; true; 12200; rose; 739 +SPEEDELEC Peugeot; 60; 19300; true; 11300; beige; 185 +E-BIKE Farad; 50; 22400; false; 14000; red; 1539 +SPEEDELEC Freego; 65; 18600; false; 14600; white; 209 +SPEEDELEC Aest; 50; 15600; false; 7600; yellow; 379 +FOLDING BIKE Comanche; 20; 7; 15600; false; pink; 459 +E-BIKE Scott; 35; 25300; false; 16000; olive; 3019 +E-BIKE Mercedes; 60; 24300; true; 14000; silver; 1505 +SPEEDELEC AirWheel; 45; 9200; true; 15100; emerald; 815 +FOLDING BIKE VNV; 24; 1; 14400; true; white; 235 +SPEEDELEC Speedelec Minirider; 30; 17600; true; 7900; coral; 1195 +SPEEDELEC Speedelec Minirider; 65; 12300; true; 6200; dark gray; 1399 +E-BIKE Rover; 30; 19000; true; 22000; grey; 3369 +SPEEDELEC ITrike; 45; 13000; true; 8100; grenadine; 1185 +E-BIKE Farad; 20; 20400; true; 20000; beige; 2509 +FOLDING BIKE Trinx; 24; 6; 8200; false; orange; 1559 +SPEEDELEC E-Scooter; 50; 12700; false; 4500; black; 299 +FOLDING BIKE Dahon; 14; 21; 13500; false; coral; 475 +FOLDING BIKE Formula; 20; 24; 16600; true; grenadine; 1585 +FOLDING BIKE Dahon; 20; 9; 13900; true; silver; 1685 +SPEEDELEC EcoRide; 20; 12100; true; 5400; golden; 1199 +FOLDING BIKE Trinx; 26; 24; 14500; true; green; 905 +FOLDING BIKE Intertool; 26; 9; 13400; false; emerald; 435 +E-BIKE Rover; 45; 25700; false; 23000; orange; 3069 +FOLDING BIKE Stern; 16; 1; 13500; true; white; 899 +E-BIKE Rover; 20; 19000; true; 11000; khaki; 1009 +FOLDING BIKE Brompton; 24; 3; 10400; true; olive; 1315 +FOLDING BIKE Crosser; 20; 1; 14700; true; silver; 375 +FOLDING BIKE Dahon; 24; 8; 13100; true; silver; 229 +SPEEDELEC Speedelec Minirider; 10; 7800; false; 8900; grey; 869 +SPEEDELEC AirWheel; 15; 12500; true; 13400; blue; 1675 +E-BIKE Mando Footloose; 30; 20700; true; 23000; dark gray; 2849 +SPEEDELEC Kugoo; 10; 15800; false; 14100; orange; 1575 +E-BIKE ION; 35; 28400; true; 31000; yellow; 3245 +SPEEDELEC Tesla; 65; 12600; true; 6400; brown; 199 +FOLDING BIKE West Bike; 26; 18; 16400; true; green; 335 +FOLDING BIKE West Bike; 20; 18; 11200; false; red; 419 +SPEEDELEC Maraton; 25; 21100; true; 12300; lemon; 909 +FOLDING BIKE Make Bike; 18; 1; 12200; false; lemon; 1305 +FOLDING BIKE West Bike; 18; 21; 15100; false; coral; 1065 +SPEEDELEC E-Scooter; 45; 13500; true; 6100; white; 659 +SPEEDELEC Maraton; 50; 21400; true; 11300; green; 1105 +SPEEDELEC Speedway; 50; 7200; false; 11800; olive; 1325 +SPEEDELEC Smart; 35; 20000; false; 11700; golden; 245 +E-BIKE Mercedes; 55; 25800; true; 27000; olive; 825 +FOLDING BIKE Formula; 20; 8; 16000; false; green; 669 +SPEEDELEC Tesla; 65; 18900; false; 13900; olive; 229 +FOLDING BIKE SkyBike; 26; 9; 8700; false; grey; 1179 +SPEEDELEC Kugoo; 35; 14400; false; 7800; golden; 745 +FOLDING BIKE Titan; 20; 24; 11800; false; flame; 1499 +FOLDING BIKE Benetti; 14; 7; 13400; true; coral; 305 +E-BIKE GoCycle; 35; 27900; false; 10000; brown; 2685 +FOLDING BIKE Trinx; 20; 21; 12300; false; rose; 1425 +E-BIKE E-Motion; 50; 23300; true; 19000; dark gray; 3419 +SPEEDELEC AirWheel; 50; 8400; true; 7900; emerald; 1599 +SPEEDELEC E-Scooter; 10; 15800; false; 3100; yellow; 1375 +E-BIKE Farad; 30; 28700; true; 14000; yellow; 2245 +SPEEDELEC OIO; 45; 10300; true; 13400; pink; 959 +E-BIKE Mando Footloose; 35; 23000; true; 19000; green; 2495 +SPEEDELEC Xiaomi; 40; 6500; false; 9000; silver; 375 +FOLDING BIKE Author; 24; 7; 11900; true; red; 685 +E-BIKE Farad; 45; 21500; true; 12000; silver; 1385 +SPEEDELEC Maraton; 30; 14800; false; 12800; white; 889 +SPEEDELEC Booster; 50; 17900; true; 3100; lemon; 1399 +FOLDING BIKE West Bike; 14; 21; 8300; false; yellow; 189 +E-BIKE Zaxboard; 30; 20300; false; 29000; pink; 2205 +FOLDING BIKE Stern; 20; 1; 8200; false; orange; 615 +SPEEDELEC Speedelec Minirider; 55; 15200; false; 5600; green; 899 +FOLDING BIKE Flex; 24; 8; 9300; true; orange; 779 +E-BIKE Scott; 20; 20900; false; 23000; dark gray; 1465 +SPEEDELEC Xiaomi; 60; 13800; false; 12700; brown; 1475 +FOLDING BIKE Polygon; 24; 6; 15900; true; violet; 405 +E-BIKE ION; 20; 26900; false; 21000; golden; 1359 +E-BIKE Mando Footloose; 40; 27600; false; 16000; flame; 2105 +FOLDING BIKE Flex; 26; 3; 12000; false; pink; 835 +SPEEDELEC AirWheel; 45; 13500; false; 4600; yellow; 1039 +FOLDING BIKE West Bike; 24; 1; 10800; true; red; 525 +SPEEDELEC Speedelec Minirider; 30; 19700; false; 3100; grey; 1649 +FOLDING BIKE Make Bike; 26; 1; 10300; true; lemon; 765 +FOLDING BIKE Make Bike; 14; 24; 13100; false; red; 625 +SPEEDELEC Booster; 10; 7300; false; 7600; flame; 239 +FOLDING BIKE Titan; 24; 7; 16900; true; green; 1339 +SPEEDELEC Kugoo; 15; 10200; true; 12200; khaki; 815 +E-BIKE Mercedes; 45; 24200; false; 28000; beige; 2399 +SPEEDELEC Speedway; 25; 21900; false; 15000; rose; 559 +E-BIKE Porshe Design; 60; 26500; true; 25000; dark gray; 3479 +SPEEDELEC OIO; 30; 18400; false; 10800; golden; 1519 +SPEEDELEC Peugeot; 35; 13900; false; 3100; marine; 1355 +E-BIKE Xiaomi; 65; 28900; true; 14000; golden; 1535 +FOLDING BIKE Intertool; 24; 3; 11500; false; pink; 1579 +E-BIKE ION; 60; 28900; false; 31000; emerald; 3099 +FOLDING BIKE Cayman; 24; 6; 11600; false; orange; 1315 +E-BIKE Felt; 65; 21900; false; 26000; white; 3359 +FOLDING BIKE Stern; 14; 24; 9800; true; white; 465 +SPEEDELEC Booster; 25; 13500; false; 8900; black; 869 +FOLDING BIKE Titan; 20; 9; 13800; false; violet; 1239 +SPEEDELEC E-Scooter; 10; 8600; false; 10200; black; 605 +FOLDING BIKE Flex; 26; 1; 13400; true; white; 595 +SPEEDELEC Segway; 50; 17900; true; 5900; brown; 659 +SPEEDELEC Aest; 45; 6900; false; 5500; brown; 1635 +SPEEDELEC Booster; 40; 9900; true; 2400; black; 1635 +FOLDING BIKE Benetti; 24; 9; 15100; false; dark gray; 519 +SPEEDELEC Xiaomi; 20; 11700; false; 4700; white; 309 +FOLDING BIKE VNV; 20; 9; 9700; false; grenadine; 1325 +E-BIKE ION; 20; 22300; true; 28000; golden; 1179 +SPEEDELEC Segway; 35; 14800; true; 3400; green; 319 +SPEEDELEC Speedelec Minirider; 65; 7800; true; 9700; rose; 665 +E-BIKE Porshe Design; 60; 28300; true; 30000; white; 2869 +E-BIKE Porshe Design; 40; 20800; true; 18000; green; 1119 +FOLDING BIKE Kross; 18; 21; 10200; false; blue; 1269 +E-BIKE Mercedes; 65; 23800; true; 19000; blue; 3259 +SPEEDELEC Speedelec Minirider; 50; 12600; false; 13700; grenadine; 739 +E-BIKE Zaxboard; 55; 23300; false; 21000; coral; 1705 +SPEEDELEC Maraton; 60; 13700; false; 3000; beige; 625 +FOLDING BIKE Titan; 18; 9; 15000; true; blue; 385 +SPEEDELEC Speedelec Minirider; 35; 7000; false; 10500; khaki; 265 +E-BIKE Scott; 45; 25900; false; 21000; pink; 1759 +SPEEDELEC AirWheel; 60; 16900; false; 14100; blue; 295 +SPEEDELEC Freego; 25; 9900; false; 15100; marine; 765 +E-BIKE Rover; 25; 25000; false; 8000; beige; 829 +FOLDING BIKE Crosser; 20; 1; 13600; true; red; 469 +SPEEDELEC ITrike; 65; 9900; false; 9000; rose; 205 +SPEEDELEC OIO; 60; 14100; false; 9100; silver; 415 +SPEEDELEC Ultron; 45; 21900; true; 2300; marine; 1459 +SPEEDELEC Kugoo; 40; 20100; true; 8100; grenadine; 425 +FOLDING BIKE Dahon; 16; 18; 11400; true; beige; 709 +SPEEDELEC ITrike; 50; 17200; true; 5200; violet; 1355 +E-BIKE Porshe Design; 20; 27700; false; 20000; grenadine; 1309 +E-BIKE Koga; 30; 21200; false; 25000; beige; 1019 +SPEEDELEC Booster; 10; 21400; false; 9700; brown; 1019 +FOLDING BIKE Trinx; 24; 3; 13900; true; beige; 1115 +SPEEDELEC OIO; 40; 9900; true; 6100; violet; 535 +FOLDING BIKE Stern; 24; 3; 13400; true; beige; 1209 +E-BIKE ElectrO; 20; 19800; false; 21000; beige; 1875 +FOLDING BIKE Comanche; 26; 1; 9900; true; grey; 199 +E-BIKE Haibike; 25; 24300; true; 25000; beige; 1975 +FOLDING BIKE Polygon; 20; 8; 14100; true; grey; 309 +SPEEDELEC Tesla; 50; 20000; false; 2900; khaki; 325 +SPEEDELEC E-Scooter; 60; 8300; false; 13100; silver; 345 +SPEEDELEC Xiaomi; 60; 17000; true; 12300; violet; 1209 +E-BIKE Gazelle; 55; 25300; true; 13000; beige; 1569 +FOLDING BIKE Author; 26; 24; 10200; true; black; 1599 +E-BIKE Haibike; 40; 25100; false; 13000; beige; 2445 +E-BIKE Rover; 25; 23300; true; 26000; olive; 679 +E-BIKE SkyBike; 45; 27900; false; 30000; violet; 2125 +SPEEDELEC Aest; 20; 11700; true; 12800; emerald; 365 +SPEEDELEC ITrike; 60; 16300; false; 3800; white; 1549 +FOLDING BIKE Comanche; 20; 1; 11600; true; pink; 279 +E-BIKE Zaxboard; 25; 23500; false; 26000; beige; 2785 +FOLDING BIKE Crosser; 16; 7; 12200; false; pink; 695 +E-BIKE Felt; 55; 22700; true; 29000; white; 2835 +FOLDING BIKE Kross; 24; 6; 11600; false; yellow; 759 +SPEEDELEC AirWheel; 45; 7000; true; 13000; golden; 299 +SPEEDELEC ITrike; 55; 13300; true; 10300; khaki; 1019 +FOLDING BIKE Kross; 20; 8; 11500; false; green; 345 +SPEEDELEC EcoRide; 45; 10900; true; 13600; pink; 355 +FOLDING BIKE Kross; 20; 8; 14800; false; yellow; 1675 +FOLDING BIKE Titan; 26; 27; 16900; false; grey; 1299 +SPEEDELEC Maraton; 40; 18900; false; 6800; pink; 869 +SPEEDELEC OIO; 20; 13800; true; 5600; emerald; 135 +FOLDING BIKE Polygon; 20; 1; 10700; true; emerald; 1355 +E-BIKE Mercedes; 50; 23600; true; 15000; emerald; 1505 +E-BIKE Ecofect; 45; 26900; true; 14000; dark gray; 1575 +SPEEDELEC Tesla; 35; 18700; true; 2000; beige; 489 +E-BIKE Felt; 50; 28900; true; 25000; olive; 2599 +FOLDING BIKE Formula; 18; 9; 10100; false; coral; 1625 +FOLDING BIKE Formula; 24; 7; 9600; true; grey; 385 +E-BIKE Felt; 45; 20900; false; 18000; grenadine; 1765 +E-BIKE Haibike; 40; 23800; false; 30000; pink; 1455 +SPEEDELEC Maraton; 30; 18700; false; 2200; golden; 415 +E-BIKE Farad; 35; 21900; false; 11000; coral; 3419 +FOLDING BIKE Make Bike; 20; 24; 14000; false; silver; 1219 +SPEEDELEC AirWheel; 20; 14500; true; 11600; orange; 499 +FOLDING BIKE Dahon; 20; 27; 15900; true; rose; 1589 +E-BIKE Felt; 25; 25200; false; 30000; white; 669 +SPEEDELEC Speedway; 35; 19200; true; 10400; brown; 1019 +FOLDING BIKE SkyBike; 24; 21; 11900; true; pink; 1029 +SPEEDELEC AirWheel; 30; 19600; false; 11500; grenadine; 1555 +FOLDING BIKE Trinx; 18; 8; 13500; true; white; 1465 +SPEEDELEC Freego; 45; 20700; false; 12000; silver; 1445 +FOLDING BIKE West Bike; 16; 21; 14000; true; rose; 1305 +E-BIKE GoCycle; 45; 22700; false; 23000; grey; 2429 +SPEEDELEC Aest; 50; 19500; false; 6300; red; 1385 +FOLDING BIKE Cayman; 20; 1; 12700; true; flame; 1299 +FOLDING BIKE Trinx; 26; 7; 13400; true; grenadine; 1675 +E-BIKE ION; 25; 22600; true; 22000; grenadine; 2995 +SPEEDELEC OIO; 60; 21400; false; 6700; golden; 1455 +FOLDING BIKE Titan; 26; 3; 13500; false; pink; 1685 +SPEEDELEC Dualtron; 35; 18600; true; 4700; grey; 489 +FOLDING BIKE Cayman; 20; 9; 14000; true; silver; 1499 +E-BIKE Mando Footloose; 55; 23200; true; 29000; golden; 2215 +FOLDING BIKE Crosser; 20; 3; 9700; true; brown; 1105 +SPEEDELEC E-Scooter; 25; 16400; false; 7800; rose; 235 +SPEEDELEC E-Scooter; 20; 13500; false; 7500; flame; 435 +SPEEDELEC Speedelec Minirider; 30; 8200; true; 15000; grenadine; 775 +E-BIKE SkyBike; 50; 25800; true; 11000; beige; 3265 +E-BIKE Ecofect; 35; 24500; true; 19000; grenadine; 2865 +FOLDING BIKE Kross; 20; 18; 13300; true; golden; 1445 +SPEEDELEC Speedway; 15; 21900; false; 6600; marine; 1475 +FOLDING BIKE Polygon; 20; 6; 9400; true; black; 975 +FOLDING BIKE Crosser; 24; 18; 9700; true; black; 365 +SPEEDELEC Ultron; 45; 16900; false; 13500; emerald; 1625 +SPEEDELEC Tesla; 20; 9100; true; 3900; violet; 1599 +FOLDING BIKE Author; 18; 24; 13900; false; brown; 1229 +SPEEDELEC Maraton; 50; 14600; true; 8900; emerald; 1005 +SPEEDELEC Speedway; 20; 6500; false; 4800; orange; 365 +E-BIKE Koga; 55; 26000; true; 11000; beige; 905 +FOLDING BIKE Cayman; 18; 3; 12700; true; marine; 1089 +FOLDING BIKE Polygon; 16; 3; 10200; true; coral; 845 +E-BIKE ElectrO; 50; 20300; false; 29000; grey; 1839 +FOLDING BIKE Crosser; 20; 1; 11400; true; grey; 1459 +FOLDING BIKE Brompton; 18; 9; 11900; false; yellow; 845 +E-BIKE Ecofect; 25; 23900; true; 18000; blue; 925 +SPEEDELEC Ultron; 65; 8200; true; 3700; coral; 435 +FOLDING BIKE Stern; 26; 27; 10000; true; green; 355 +FOLDING BIKE BMW; 16; 8; 16800; false; grey; 1165 +SPEEDELEC Tesla; 25; 7800; false; 9500; beige; 1365 +SPEEDELEC Aest; 45; 13600; false; 4100; khaki; 519 +E-BIKE Zaxboard; 50; 22100; true; 20000; beige; 1609 +E-BIKE GoCycle; 35; 19100; false; 20000; golden; 3329 +E-BIKE Felt; 30; 21500; false; 27000; lemon; 1265 +SPEEDELEC Dualtron; 30; 13900; false; 12800; khaki; 1595 +FOLDING BIKE Comanche; 20; 27; 14500; false; silver; 1655 +E-BIKE Gazelle; 50; 21400; true; 30000; green; 1889 +SPEEDELEC Xiaomi; 15; 9000; true; 5800; blue; 1255 +E-BIKE ION; 30; 19100; false; 8000; yellow; 1705 +SPEEDELEC E-Scooter; 15; 21000; false; 6600; beige; 1129 +E-BIKE Gazelle; 25; 25300; false; 19000; silver; 1029 +FOLDING BIKE Brompton; 16; 27; 8300; false; grey; 195 +E-BIKE Porshe Design; 60; 28300; true; 27000; grey; 1005 +SPEEDELEC AirWheel; 10; 7000; false; 2600; blue; 1145 +SPEEDELEC Booster; 15; 6500; false; 3200; green; 699 +E-BIKE SkyBike; 35; 26100; false; 17000; yellow; 3139 +FOLDING BIKE Stern; 14; 8; 9600; true; violet; 1539 +FOLDING BIKE Formula; 26; 9; 9300; false; orange; 915 +SPEEDELEC E-Scooter; 15; 11900; true; 14500; silver; 439 +FOLDING BIKE Kross; 20; 24; 8500; false; brown; 1545 +E-BIKE ElectrO; 45; 24100; false; 23000; silver; 919 +SPEEDELEC Freego; 25; 13900; true; 3800; marine; 849 +E-BIKE Koga; 45; 23400; false; 14000; olive; 3189 +SPEEDELEC Kugoo; 65; 20700; false; 5400; emerald; 1235 +E-BIKE Lankeleisi; 40; 26700; false; 28000; blue; 2865 +E-BIKE ION; 60; 23100; false; 10000; green; 2005 +FOLDING BIKE Flex; 16; 8; 14900; true; violet; 365 +E-BIKE Koga; 45; 27500; true; 11000; golden; 3385 +FOLDING BIKE Stern; 18; 24; 9000; true; emerald; 495 +E-BIKE Koga; 25; 27400; false; 20000; orange; 3345 +E-BIKE Haibike; 55; 26300; false; 18000; red; 1805 +SPEEDELEC Peugeot; 35; 10300; false; 9200; blue; 225 +FOLDING BIKE VNV; 14; 3; 12400; true; khaki; 1169 +SPEEDELEC Ultron; 55; 19800; true; 14400; emerald; 1479 +FOLDING BIKE Author; 18; 9; 11600; false; coral; 1605 +SPEEDELEC Speedelec Minirider; 35; 21900; false; 13400; dark gray; 859 +E-BIKE Scott; 55; 24400; false; 13000; golden; 2719 +FOLDING BIKE Titan; 20; 1; 11800; true; rose; 225 +FOLDING BIKE Comanche; 24; 8; 12800; true; white; 789 +SPEEDELEC Aest; 30; 11600; true; 8500; coral; 805 +E-BIKE GoCycle; 40; 20300; true; 25000; orange; 3269 +E-BIKE Xiaomi; 25; 27600; true; 21000; green; 2229 +FOLDING BIKE Crosser; 14; 27; 15100; true; silver; 1659 +FOLDING BIKE Cayman; 24; 27; 14000; false; rose; 665 +FOLDING BIKE Author; 20; 7; 8800; true; beige; 449 +SPEEDELEC EcoRide; 60; 19400; true; 5100; khaki; 715 +E-BIKE Xiaomi; 45; 27400; true; 16000; silver; 1239 +SPEEDELEC Peugeot; 60; 21100; true; 15800; lemon; 199 +SPEEDELEC Smart; 35; 20300; false; 14500; khaki; 885 +FOLDING BIKE Dahon; 14; 27; 9700; true; white; 1215 +SPEEDELEC Ultron; 60; 10400; false; 7000; emerald; 489 +FOLDING BIKE VNV; 20; 3; 10400; true; silver; 445 +SPEEDELEC EcoRide; 20; 21500; true; 11900; marine; 1375 +FOLDING BIKE VNV; 26; 8; 14900; false; marine; 825 +FOLDING BIKE VNV; 20; 18; 8500; false; green; 339 +SPEEDELEC Tesla; 10; 13400; false; 11300; orange; 1115 +FOLDING BIKE Flex; 26; 9; 12900; true; marine; 349 +FOLDING BIKE Author; 24; 18; 15300; true; red; 1079 +SPEEDELEC Aest; 65; 14200; true; 14200; grey; 1479 +SPEEDELEC Booster; 55; 11700; false; 6000; marine; 1299 +FOLDING BIKE Titan; 20; 8; 10200; false; pink; 185 +SPEEDELEC Tesla; 50; 19500; false; 6000; emerald; 1395 +FOLDING BIKE Make Bike; 20; 7; 8800; true; khaki; 799 +SPEEDELEC Xiaomi; 35; 21600; true; 8300; black; 1565 +FOLDING BIKE Titan; 24; 18; 12200; false; grenadine; 1509 +SPEEDELEC Freego; 45; 10400; false; 2900; green; 985 +E-BIKE Farad; 30; 19200; false; 10000; brown; 1955 +SPEEDELEC Kugoo; 60; 17700; false; 9900; coral; 1175 +FOLDING BIKE Titan; 20; 7; 10300; false; yellow; 1425 +FOLDING BIKE Polygon; 20; 3; 9000; false; coral; 795 +SPEEDELEC Booster; 50; 19600; false; 9100; olive; 779 +SPEEDELEC EcoRide; 60; 20000; true; 2000; green; 699 +SPEEDELEC Kugoo; 10; 14300; true; 3300; olive; 1515 +E-BIKE Rover; 45; 24900; true; 12000; brown; 2665 +E-BIKE Gazelle; 60; 19600; false; 12000; yellow; 835 +SPEEDELEC Booster; 30; 8300; false; 4000; orange; 835 +FOLDING BIKE SkyBike; 24; 21; 15700; false; grenadine; 295 +FOLDING BIKE Polygon; 26; 9; 12800; false; yellow; 1575 +SPEEDELEC Segway; 30; 20600; true; 9000; dark gray; 429 +E-BIKE Zaxboard; 45; 20300; false; 25000; green; 3059 +SPEEDELEC Xiaomi; 65; 20900; true; 3400; beige; 1139 +SPEEDELEC Dualtron; 15; 10600; true; 3400; white; 439 +FOLDING BIKE Author; 24; 7; 11000; true; grenadine; 249 +E-BIKE Rover; 40; 19300; true; 26000; blue; 2295 +E-BIKE Farad; 60; 28300; true; 12000; khaki; 2345 +FOLDING BIKE Polygon; 24; 1; 9100; false; flame; 449 +SPEEDELEC Speedelec Minirider; 45; 10000; false; 6600; white; 1599 +SPEEDELEC ITrike; 45; 13200; false; 2100; green; 715 +SPEEDELEC Tesla; 40; 13900; true; 14400; coral; 725 +E-BIKE Scott; 25; 20200; true; 24000; khaki; 2895 +FOLDING BIKE Benetti; 26; 6; 15000; true; silver; 1315 +SPEEDELEC Booster; 55; 7600; false; 8300; olive; 1339 +SPEEDELEC Segway; 10; 10300; true; 6700; golden; 1055 +FOLDING BIKE Comanche; 24; 18; 8400; true; golden; 599 +SPEEDELEC Maraton; 65; 21700; false; 8700; emerald; 1445 +FOLDING BIKE Comanche; 20; 6; 15100; true; pink; 1259 +FOLDING BIKE VNV; 20; 1; 10700; true; white; 1275 +FOLDING BIKE Titan; 14; 9; 10400; true; golden; 1375 +FOLDING BIKE Trinx; 24; 8; 16100; false; dark gray; 505 +E-BIKE Scott; 40; 19100; false; 27000; marine; 969 +E-BIKE Porshe Design; 45; 28900; true; 29000; khaki; 1399 +SPEEDELEC Xiaomi; 15; 6600; true; 15900; yellow; 1515 +SPEEDELEC Freego; 35; 20100; false; 2100; olive; 295 +FOLDING BIKE Trinx; 20; 6; 11000; true; blue; 1209 +SPEEDELEC EcoRide; 60; 19100; true; 2800; olive; 879 +E-BIKE ION; 50; 25600; false; 24000; blue; 1835 +E-BIKE SkyBike; 20; 23700; true; 22000; golden; 2645 +SPEEDELEC Maraton; 60; 7800; false; 14900; flame; 1239 +FOLDING BIKE Trinx; 14; 6; 10400; true; dark gray; 1535 +SPEEDELEC Xiaomi; 45; 21100; true; 2200; white; 625 +E-BIKE Zaxboard; 65; 25900; true; 9000; rose; 2945 +E-BIKE SkyBike; 45; 22100; false; 13000; silver; 1039 +FOLDING BIKE Flex; 18; 27; 12400; true; yellow; 829 +SPEEDELEC Kugoo; 20; 9000; false; 9900; black; 1225 +E-BIKE GoCycle; 60; 23700; true; 18000; orange; 1219 +E-BIKE ElectrO; 65; 20500; true; 13000; yellow; 1849 +SPEEDELEC Speedelec Minirider; 65; 14100; false; 8000; pink; 1375 +E-BIKE Farad; 45; 26200; true; 14000; black; 2875 +FOLDING BIKE Comanche; 16; 7; 10700; false; pink; 359 +FOLDING BIKE Cayman; 18; 8; 13200; false; pink; 715 +FOLDING BIKE SkyBike; 20; 3; 12900; false; grenadine; 775 +E-BIKE Ecofect; 40; 23900; false; 14000; olive; 2839 +SPEEDELEC Xiaomi; 10; 17400; false; 3000; black; 295 +FOLDING BIKE Brompton; 20; 18; 9700; false; golden; 815 +SPEEDELEC Speedelec Minirider; 30; 14400; false; 7800; rose; 1445 +FOLDING BIKE West Bike; 24; 6; 15200; false; grey; 1119 +FOLDING BIKE BMW; 16; 6; 12500; true; marine; 1559 +E-BIKE Farad; 35; 25900; false; 24000; lemon; 2679 +SPEEDELEC EcoRide; 15; 12300; false; 6200; rose; 1565 +SPEEDELEC AirWheel; 20; 6500; true; 2100; flame; 199 +SPEEDELEC AirWheel; 20; 13900; true; 14700; golden; 549 +FOLDING BIKE Trinx; 20; 1; 10700; true; green; 1599 +E-BIKE Gazelle; 65; 20500; false; 21000; black; 1175 +FOLDING BIKE Dahon; 14; 3; 8100; true; marine; 349 +SPEEDELEC Kugoo; 60; 9000; false; 6400; orange; 1639 +E-BIKE Scott; 60; 19000; false; 23000; beige; 1475 +SPEEDELEC Tesla; 65; 18500; true; 9200; green; 319 +FOLDING BIKE BMW; 20; 1; 16800; true; grenadine; 625 +FOLDING BIKE Titan; 24; 24; 10800; false; lemon; 1485 +E-BIKE Rover; 50; 22600; true; 9000; black; 2369 +E-BIKE Haibike; 45; 27600; false; 17000; flame; 809 +E-BIKE SkyBike; 20; 25600; true; 22000; white; 3329 +E-BIKE E-Motion; 35; 21300; false; 29000; violet; 1799 +FOLDING BIKE Make Bike; 16; 18; 9200; true; brown; 385 +FOLDING BIKE Trinx; 14; 3; 9100; false; coral; 679 +SPEEDELEC ITrike; 25; 14500; true; 2800; white; 915 +FOLDING BIKE Make Bike; 20; 7; 12300; false; orange; 959 +FOLDING BIKE Trinx; 14; 18; 12100; false; black; 435 +E-BIKE Haibike; 45; 24200; false; 16000; khaki; 2765 +SPEEDELEC Speedway; 25; 13800; false; 8000; silver; 1555 +FOLDING BIKE Dahon; 18; 27; 11800; false; brown; 839 +FOLDING BIKE Cayman; 20; 21; 15100; true; pink; 1479 +E-BIKE ION; 40; 28200; false; 11000; brown; 1695 +FOLDING BIKE Intertool; 20; 7; 11200; true; coral; 1129 +SPEEDELEC Peugeot; 25; 19000; true; 9100; brown; 975 +E-BIKE Rover; 30; 19200; false; 21000; rose; 2065 +FOLDING BIKE Cayman; 26; 6; 11800; true; black; 1259 +FOLDING BIKE Make Bike; 16; 7; 9800; false; green; 395 +SPEEDELEC AirWheel; 30; 6900; false; 14400; marine; 1669 +E-BIKE Mando Footloose; 25; 20300; false; 14000; white; 2565 +E-BIKE ElectrO; 30; 20600; false; 31000; red; 2899 +SPEEDELEC Xiaomi; 45; 19900; true; 12800; silver; 575 +FOLDING BIKE Brompton; 20; 7; 15300; false; khaki; 299 +E-BIKE Lankeleisi; 65; 25900; true; 15000; flame; 1315 +E-BIKE Gazelle; 20; 25600; false; 8000; marine; 1405 +FOLDING BIKE Comanche; 16; 1; 15600; true; dark gray; 619 +FOLDING BIKE Polygon; 20; 3; 16600; true; marine; 1419 +E-BIKE GoCycle; 35; 25700; true; 24000; brown; 2605 +SPEEDELEC Peugeot; 20; 14300; false; 7800; violet; 925 +SPEEDELEC Aest; 20; 19900; false; 2600; emerald; 1279 +E-BIKE ION; 35; 25100; true; 12000; marine; 1059 +FOLDING BIKE Benetti; 24; 1; 16100; true; violet; 329 +FOLDING BIKE Make Bike; 20; 24; 8400; true; marine; 1365 +E-BIKE Scott; 45; 28100; true; 15000; rose; 2365 +SPEEDELEC OIO; 25; 16400; true; 7700; khaki; 1469 +E-BIKE Ecofect; 35; 24400; false; 28000; brown; 1789 +E-BIKE Felt; 55; 24200; false; 15000; yellow; 965 +FOLDING BIKE Intertool; 24; 24; 9100; false; orange; 629 +FOLDING BIKE Author; 18; 8; 9000; false; grey; 925 +E-BIKE Scott; 65; 20400; false; 24000; orange; 3209 +E-BIKE Ecofect; 40; 21500; false; 26000; black; 2185 +FOLDING BIKE Cayman; 20; 1; 13600; true; golden; 1045 +SPEEDELEC Tesla; 65; 8300; false; 5100; grey; 475 +FOLDING BIKE Cayman; 26; 7; 13100; false; emerald; 269 +FOLDING BIKE Formula; 26; 6; 10400; false; flame; 825 +FOLDING BIKE Dahon; 26; 6; 16800; false; yellow; 229 +FOLDING BIKE Formula; 20; 6; 8800; false; grey; 729 +E-BIKE ElectrO; 40; 26700; false; 17000; grenadine; 2729 +SPEEDELEC Kugoo; 25; 13600; true; 10800; dark gray; 1125 +SPEEDELEC Tesla; 25; 18800; true; 12800; blue; 1019 +FOLDING BIKE BMW; 14; 1; 13800; true; orange; 1655 +SPEEDELEC OIO; 45; 16900; false; 4700; violet; 945 +FOLDING BIKE Benetti; 24; 3; 9700; true; white; 1349 +E-BIKE Mercedes; 25; 26000; false; 30000; orange; 3445 +SPEEDELEC Kugoo; 30; 9300; false; 7900; dark gray; 559 +SPEEDELEC Speedway; 65; 15900; false; 2100; flame; 1135 +SPEEDELEC Segway; 40; 6700; false; 9900; pink; 969 +FOLDING BIKE Crosser; 18; 8; 8700; true; olive; 1575 +SPEEDELEC Kugoo; 15; 21800; true; 4100; grey; 805 +FOLDING BIKE Kross; 20; 1; 12700; false; grey; 865 +FOLDING BIKE Crosser; 20; 7; 9700; false; black; 609 +E-BIKE Scott; 45; 28900; true; 30000; yellow; 1185 +FOLDING BIKE West Bike; 26; 24; 13300; true; brown; 185 +E-BIKE Ecofect; 60; 21100; false; 21000; flame; 2149 +E-BIKE Gazelle; 35; 26100; false; 14000; marine; 2515 +E-BIKE Mando Footloose; 65; 27700; true; 20000; beige; 1849 +FOLDING BIKE Crosser; 20; 9; 12100; false; grey; 1529 +E-BIKE Zaxboard; 30; 23700; true; 26000; lemon; 2889 +SPEEDELEC E-Scooter; 55; 16500; true; 7200; marine; 659 +FOLDING BIKE Kross; 24; 24; 8500; true; olive; 579 +E-BIKE Farad; 65; 28400; true; 14000; olive; 1149 +FOLDING BIKE BMW; 18; 9; 10300; false; rose; 1065 +E-BIKE Lankeleisi; 50; 27800; true; 26000; orange; 705 +E-BIKE ION; 35; 21700; true; 10000; golden; 2635 +E-BIKE ION; 50; 22100; false; 29000; brown; 945 +SPEEDELEC Kugoo; 50; 16200; true; 10500; silver; 599 +SPEEDELEC Maraton; 55; 8500; true; 2300; pink; 1389 +SPEEDELEC Speedway; 60; 9900; true; 6100; marine; 445 +E-BIKE Mercedes; 60; 20400; false; 24000; grey; 3105 +E-BIKE Mercedes; 65; 27800; false; 12000; violet; 3015 +SPEEDELEC E-Scooter; 10; 16800; true; 3700; brown; 849 +FOLDING BIKE Author; 20; 27; 9200; false; red; 1135 +SPEEDELEC Xiaomi; 15; 6700; false; 11300; emerald; 1225 +FOLDING BIKE Make Bike; 20; 18; 10500; true; lemon; 869 +FOLDING BIKE Titan; 20; 18; 15600; true; green; 665 +SPEEDELEC Tesla; 55; 20800; false; 14500; grenadine; 885 +SPEEDELEC Segway; 55; 16300; false; 6000; brown; 1389 +FOLDING BIKE Intertool; 20; 24; 10900; false; pink; 729 +SPEEDELEC Peugeot; 50; 10200; true; 10600; orange; 1195 +SPEEDELEC E-Scooter; 65; 6600; false; 9700; golden; 1435 +FOLDING BIKE Comanche; 20; 21; 13300; true; dark gray; 405 +E-BIKE Porshe Design; 30; 28300; false; 31000; flame; 689 +E-BIKE Xiaomi; 40; 22900; true; 9000; dark gray; 1465 +E-BIKE Haibike; 30; 22600; false; 21000; grenadine; 1405 +SPEEDELEC Speedelec Minirider; 60; 14500; true; 12400; marine; 1475 +FOLDING BIKE Intertool; 20; 6; 15300; true; rose; 655 +SPEEDELEC Freego; 10; 12900; true; 9400; flame; 759 +SPEEDELEC AirWheel; 65; 6900; false; 6300; green; 1615 +FOLDING BIKE West Bike; 24; 27; 12300; true; black; 1065 +E-BIKE Felt; 35; 24900; false; 22000; grenadine; 1465 +SPEEDELEC Smart; 55; 15500; false; 5900; grey; 315 +FOLDING BIKE Comanche; 20; 6; 16600; true; red; 1535 +FOLDING BIKE Titan; 16; 18; 14600; true; black; 1085 +E-BIKE Zaxboard; 65; 28700; true; 11000; silver; 2975 +SPEEDELEC Speedelec Minirider; 35; 7200; false; 7200; white; 1665 +E-BIKE Koga; 50; 19500; true; 12000; flame; 1725 +FOLDING BIKE Cayman; 24; 1; 8900; false; rose; 1615 +FOLDING BIKE Crosser; 20; 7; 8300; false; olive; 945 +SPEEDELEC AirWheel; 65; 15700; false; 10500; blue; 585 +FOLDING BIKE West Bike; 20; 6; 16300; false; beige; 1025 +FOLDING BIKE VNV; 20; 8; 14000; true; golden; 939 +FOLDING BIKE Comanche; 18; 24; 13700; false; marine; 755 +SPEEDELEC Kugoo; 40; 13600; false; 12000; yellow; 1315 +E-BIKE ION; 35; 22400; false; 17000; khaki; 1005 +E-BIKE Rover; 50; 25200; false; 24000; coral; 2255 +FOLDING BIKE Titan; 14; 27; 11900; false; coral; 1559 +SPEEDELEC Peugeot; 30; 14000; true; 12300; golden; 1175 +E-BIKE Farad; 35; 19700; false; 11000; brown; 3435 +E-BIKE E-Motion; 65; 25900; true; 13000; green; 2919 +E-BIKE Koga; 60; 21200; false; 15000; brown; 1135 +SPEEDELEC Freego; 50; 10600; false; 2300; brown; 125 +E-BIKE ElectrO; 55; 22700; false; 26000; flame; 805 +FOLDING BIKE BMW; 20; 18; 11500; true; green; 469 +E-BIKE E-Motion; 40; 26500; false; 14000; golden; 1715 +FOLDING BIKE Comanche; 20; 7; 14900; false; white; 1369 +E-BIKE Haibike; 30; 24200; true; 19000; pink; 849 +FOLDING BIKE West Bike; 20; 18; 11100; true; rose; 1305 +E-BIKE Rover; 40; 25600; true; 20000; red; 1489 +FOLDING BIKE VNV; 24; 8; 9700; false; coral; 1459 +SPEEDELEC ITrike; 40; 19900; false; 11600; violet; 1285 +FOLDING BIKE Formula; 18; 21; 10900; false; green; 1419 +E-BIKE Scott; 50; 21600; true; 25000; yellow; 739 +E-BIKE Mando Footloose; 50; 19200; true; 28000; yellow; 1385 +SPEEDELEC Xiaomi; 35; 18100; false; 10300; violet; 1499 +E-BIKE ElectrO; 55; 22400; false; 16000; red; 2109 +SPEEDELEC Segway; 55; 11900; false; 2000; marine; 339 +E-BIKE Xiaomi; 25; 22600; false; 14000; flame; 1735 +FOLDING BIKE Flex; 24; 1; 11000; false; dark gray; 389 +E-BIKE Porshe Design; 30; 28700; true; 31000; golden; 1619 +FOLDING BIKE Author; 18; 24; 10300; false; green; 1015 +E-BIKE Mando Footloose; 20; 23300; true; 17000; violet; 1339 +E-BIKE ION; 45; 24600; true; 30000; pink; 1595 +E-BIKE Rover; 65; 24700; true; 27000; green; 2779 +SPEEDELEC Maraton; 25; 9000; true; 9000; dark gray; 1565 +E-BIKE Ferrari; 35; 25600; true; 10000; grenadine; 1555 +E-BIKE GoCycle; 65; 26900; false; 12000; beige; 1945 +FOLDING BIKE Dahon; 20; 1; 8200; true; green; 1435 +E-BIKE GoCycle; 40; 24000; true; 15000; grenadine; 1005 +SPEEDELEC Segway; 40; 11200; true; 11400; brown; 209 +E-BIKE SkyBike; 35; 26800; true; 25000; grey; 899 +E-BIKE Ecofect; 35; 23500; false; 13000; golden; 3389 +SPEEDELEC Xiaomi; 40; 9300; false; 14700; coral; 609 +SPEEDELEC OIO; 65; 12600; false; 8800; coral; 199 +SPEEDELEC Kugoo; 10; 8100; false; 9900; grey; 1299 +SPEEDELEC Freego; 25; 18300; true; 7100; flame; 1059 +E-BIKE Lankeleisi; 60; 24400; false; 13000; white; 3095 +SPEEDELEC OIO; 55; 9000; true; 9500; black; 199 +E-BIKE Scott; 55; 23200; false; 11000; violet; 1625 +E-BIKE Mando Footloose; 65; 19900; false; 8000; coral; 1949 +SPEEDELEC Kugoo; 25; 15100; false; 11300; black; 1429 +FOLDING BIKE Benetti; 14; 27; 10200; false; khaki; 1129 +E-BIKE Mando Footloose; 65; 22500; true; 30000; lemon; 1365 +SPEEDELEC Xiaomi; 20; 17400; true; 4200; emerald; 785 +E-BIKE Ecofect; 20; 27300; false; 16000; golden; 1595 +E-BIKE Lankeleisi; 25; 26600; true; 9000; yellow; 3289 +E-BIKE Rover; 65; 25100; false; 29000; grey; 2939 +SPEEDELEC Tesla; 20; 19700; true; 2200; violet; 799 +E-BIKE SkyBike; 55; 28600; true; 30000; black; 2159 +SPEEDELEC EcoRide; 30; 13400; false; 11500; lemon; 1615 +SPEEDELEC Speedelec Minirider; 15; 18800; false; 13100; white; 729 +E-BIKE ElectrO; 50; 28100; false; 11000; silver; 3139 +SPEEDELEC EcoRide; 65; 21700; false; 12900; flame; 795 +E-BIKE Farad; 30; 22500; true; 10000; rose; 1745 +FOLDING BIKE Benetti; 18; 7; 10900; true; beige; 1165 +FOLDING BIKE Titan; 24; 7; 16500; true; golden; 1295 +SPEEDELEC Booster; 65; 20300; false; 15500; red; 175 +E-BIKE Porshe Design; 60; 27000; false; 13000; marine; 659 +SPEEDELEC AirWheel; 15; 11200; false; 11200; pink; 419 +SPEEDELEC Ultron; 45; 21000; false; 3700; beige; 239 +E-BIKE Zaxboard; 60; 28900; false; 14000; black; 3115 +E-BIKE Lankeleisi; 65; 25100; false; 25000; khaki; 2859 +E-BIKE E-Motion; 45; 19600; false; 24000; silver; 1489 +FOLDING BIKE Flex; 20; 7; 8500; false; flame; 455 +FOLDING BIKE Stern; 20; 1; 9500; false; coral; 1055 +FOLDING BIKE Trinx; 20; 27; 14600; false; pink; 465 +E-BIKE Ferrari; 35; 20600; true; 10000; emerald; 805 +FOLDING BIKE Stern; 24; 24; 14400; true; marine; 705 +FOLDING BIKE BMW; 16; 9; 12600; false; emerald; 1649 +SPEEDELEC Booster; 45; 7500; false; 6500; coral; 1175 +FOLDING BIKE West Bike; 14; 9; 16200; false; dark gray; 209 +FOLDING BIKE Dahon; 20; 8; 16900; true; dark gray; 1595 +E-BIKE Scott; 20; 19800; false; 9000; lemon; 1455 +SPEEDELEC Kugoo; 55; 14000; true; 12400; marine; 1489 +FOLDING BIKE Titan; 20; 8; 8800; true; yellow; 1665 +E-BIKE Scott; 35; 24500; false; 14000; flame; 1775 +E-BIKE ION; 45; 21300; false; 14000; brown; 3149 +FOLDING BIKE Trinx; 26; 7; 11600; true; grey; 1099 +SPEEDELEC Segway; 20; 13300; false; 5900; silver; 335 +SPEEDELEC AirWheel; 40; 7700; true; 11600; orange; 1319 +FOLDING BIKE Cayman; 24; 21; 12700; false; emerald; 279 +SPEEDELEC Peugeot; 20; 19300; true; 14200; red; 1075 +SPEEDELEC AirWheel; 35; 9800; false; 10900; rose; 155 +E-BIKE Xiaomi; 45; 26100; true; 13000; green; 1269 +SPEEDELEC Ultron; 60; 9200; true; 14800; grey; 1549 +SPEEDELEC E-Scooter; 35; 8800; false; 14200; beige; 245 +E-BIKE Mercedes; 55; 26200; true; 12000; orange; 1765 +FOLDING BIKE Brompton; 26; 6; 15400; false; grenadine; 519 +SPEEDELEC Kugoo; 55; 11400; false; 12300; lemon; 1325 +FOLDING BIKE Make Bike; 26; 1; 13900; false; orange; 705 +FOLDING BIKE Formula; 20; 21; 11100; false; rose; 1675 +E-BIKE Gazelle; 30; 20400; true; 29000; grenadine; 2195 +FOLDING BIKE Formula; 24; 27; 8500; true; flame; 1485 +FOLDING BIKE Benetti; 14; 7; 9500; false; grey; 189 +SPEEDELEC EcoRide; 25; 9200; true; 3200; dark gray; 289 +SPEEDELEC Maraton; 40; 14500; false; 12200; lemon; 885 +FOLDING BIKE Comanche; 14; 27; 13400; true; white; 1255 +FOLDING BIKE SkyBike; 20; 24; 15800; false; red; 755 +E-BIKE SkyBike; 45; 23500; true; 30000; rose; 2245 +E-BIKE Scott; 45; 25600; false; 28000; olive; 1215 +FOLDING BIKE VNV; 24; 24; 11000; true; coral; 1505 +FOLDING BIKE Author; 20; 3; 12800; false; silver; 1505 +E-BIKE Haibike; 60; 21400; false; 14000; golden; 2885 +SPEEDELEC Aest; 45; 14300; false; 5800; blue; 1049 +FOLDING BIKE Author; 20; 24; 14500; true; rose; 509 +E-BIKE Lankeleisi; 50; 20500; false; 19000; white; 859 +E-BIKE GoCycle; 60; 25400; false; 29000; green; 1495 +E-BIKE Ecofect; 35; 28200; false; 9000; brown; 2145 +SPEEDELEC Speedelec Minirider; 30; 16900; true; 11000; beige; 985 +FOLDING BIKE Trinx; 24; 1; 11300; false; blue; 975 +FOLDING BIKE Flex; 24; 6; 16100; false; khaki; 569 +E-BIKE Ferrari; 55; 23500; true; 25000; violet; 1649 +E-BIKE ElectrO; 35; 19100; false; 31000; golden; 2179 +SPEEDELEC Speedway; 10; 19200; true; 6400; black; 399 +E-BIKE Koga; 60; 24100; true; 23000; orange; 1485 +FOLDING BIKE VNV; 24; 1; 10100; true; red; 575 +E-BIKE SkyBike; 45; 21700; true; 10000; black; 2119 +SPEEDELEC Kugoo; 15; 21100; false; 9700; flame; 1429 +FOLDING BIKE Formula; 14; 1; 13900; false; khaki; 425 +E-BIKE SkyBike; 25; 19100; true; 11000; golden; 1125 +E-BIKE SkyBike; 30; 28600; false; 20000; green; 2809 +FOLDING BIKE Flex; 20; 27; 16300; true; white; 1409 +SPEEDELEC Ultron; 35; 21700; false; 14600; orange; 1645 +E-BIKE Haibike; 65; 24900; false; 13000; white; 1415 +E-BIKE ION; 40; 24700; true; 27000; dark gray; 1989 +FOLDING BIKE Stern; 26; 21; 11100; false; orange; 465 +FOLDING BIKE Titan; 20; 3; 8200; false; red; 355 +SPEEDELEC Booster; 45; 13600; true; 14100; khaki; 865 +FOLDING BIKE Stern; 26; 3; 14900; true; beige; 819 +E-BIKE Zaxboard; 55; 28800; true; 30000; black; 2395 +E-BIKE Lankeleisi; 45; 28800; false; 13000; pink; 1209 +FOLDING BIKE Trinx; 20; 27; 8000; true; grey; 885 +E-BIKE Rover; 60; 19900; true; 14000; grenadine; 3089 +SPEEDELEC Tesla; 25; 18200; false; 10500; khaki; 559 +E-BIKE Ecofect; 60; 23100; false; 10000; golden; 1729 +SPEEDELEC Segway; 50; 8800; true; 13600; coral; 865 +SPEEDELEC Freego; 30; 10100; true; 2600; khaki; 1679 +E-BIKE ElectrO; 35; 27000; false; 21000; black; 2735 +SPEEDELEC Booster; 50; 10200; false; 2500; green; 1525 +FOLDING BIKE Polygon; 24; 3; 16200; true; blue; 655 +E-BIKE Ecofect; 25; 23800; true; 16000; beige; 2869 +FOLDING BIKE SkyBike; 24; 6; 16800; true; khaki; 445 +SPEEDELEC Speedelec Minirider; 65; 14100; true; 5000; white; 1499 +E-BIKE Farad; 45; 20100; true; 14000; lemon; 2379 +SPEEDELEC Booster; 50; 9300; true; 5600; khaki; 455 +SPEEDELEC Kugoo; 45; 9200; false; 8400; dark gray; 1355 +SPEEDELEC Kugoo; 30; 13000; false; 4400; black; 489 +FOLDING BIKE Cayman; 20; 1; 16800; true; black; 669 +SPEEDELEC Speedway; 35; 9400; false; 2400; green; 1419 +SPEEDELEC Freego; 30; 16400; false; 6000; blue; 1665 +SPEEDELEC Tesla; 45; 20700; false; 14600; olive; 205 +E-BIKE Ecofect; 35; 27000; false; 18000; olive; 1989 +SPEEDELEC ITrike; 50; 12000; true; 11600; marine; 245 +E-BIKE ION; 25; 27700; false; 17000; green; 3235 +E-BIKE Koga; 35; 24900; false; 29000; white; 729 +SPEEDELEC Tesla; 15; 12200; true; 2300; khaki; 615 +FOLDING BIKE BMW; 20; 1; 10500; false; blue; 635 +FOLDING BIKE West Bike; 20; 7; 14700; true; golden; 299 +FOLDING BIKE West Bike; 18; 6; 15600; false; blue; 1219 +E-BIKE Lankeleisi; 60; 28200; false; 15000; coral; 2429 +SPEEDELEC Peugeot; 50; 20800; true; 15700; silver; 1279 +FOLDING BIKE Polygon; 24; 6; 16400; false; coral; 725 +FOLDING BIKE SkyBike; 20; 3; 11900; false; yellow; 1115 +SPEEDELEC Tesla; 15; 9500; false; 13200; lemon; 285 +SPEEDELEC Freego; 35; 21300; false; 4500; yellow; 779 +E-BIKE Haibike; 30; 22700; true; 24000; black; 1085 +SPEEDELEC Kugoo; 50; 20300; true; 4400; flame; 299 +FOLDING BIKE Titan; 14; 18; 13300; true; emerald; 285 +SPEEDELEC OIO; 45; 9700; true; 10500; olive; 785 +SPEEDELEC OIO; 15; 7100; true; 9400; rose; 1065 +SPEEDELEC OIO; 55; 19900; true; 10900; brown; 435 +SPEEDELEC Kugoo; 65; 17700; true; 15100; khaki; 539 +FOLDING BIKE Author; 18; 8; 11600; false; red; 279 +FOLDING BIKE West Bike; 14; 7; 14700; true; black; 1289 +SPEEDELEC Dualtron; 50; 17300; true; 15500; flame; 1549 +SPEEDELEC Booster; 10; 20000; false; 11800; dark gray; 1095 +FOLDING BIKE Stern; 20; 27; 16500; false; white; 439 +SPEEDELEC Dualtron; 60; 20500; false; 15600; golden; 869 +SPEEDELEC Ultron; 60; 7000; false; 11400; lemon; 1055 +E-BIKE Ferrari; 60; 28900; false; 21000; brown; 825 \ No newline at end of file From 1445da0ec2eac08be2f9e185bdb8834b6312a2f2 Mon Sep 17 00:00:00 2001 From: Leonid Date: Wed, 29 Jul 2020 15:42:12 +0300 Subject: [PATCH 02/19] added all command except finding --- .../java/com/ecobike/CommandExecutor.java | 9 +- src/main/java/com/ecobike/Communicator.java | 4 + .../java/com/ecobike/ConsoleCommunicator.java | 17 +- src/main/java/com/ecobike/DataHolder.java | 63 ++ .../java/com/ecobike/EcoBikeApplication.java | 46 +- src/main/java/com/ecobike/FileWriter.java | 29 + .../com/ecobike/command/AddEBikeCommand.java | 27 + .../command/AddFoldingBikeCommand.java | 27 + .../command/AddSpeedelecBikeCommand.java | 27 + .../java/com/ecobike/command/Command.java | 9 +- .../java/com/ecobike/command/ExitCommand.java | 8 +- .../java/com/ecobike/command/ShowCommand.java | 10 + .../ecobike/command/WriteToFileCommand.java | 12 + .../ecobike/model/AbstractElectroBike.java | 13 + src/main/java/com/ecobike/model/Bike.java | 7 + src/main/java/com/ecobike/model/EBike.java | 11 + .../java/com/ecobike/model/FoldingBike.java | 13 + .../java/com/ecobike/model/SpeedelecBike.java | 11 + src/main/resources/backupBikes.txt | 1000 +++++++++++++++++ src/main/resources/ecobike.txt | 2 +- 20 files changed, 1295 insertions(+), 50 deletions(-) create mode 100644 src/main/java/com/ecobike/DataHolder.java create mode 100644 src/main/java/com/ecobike/FileWriter.java create mode 100644 src/main/java/com/ecobike/command/AddEBikeCommand.java create mode 100644 src/main/java/com/ecobike/command/AddFoldingBikeCommand.java create mode 100644 src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java create mode 100644 src/main/java/com/ecobike/command/ShowCommand.java create mode 100644 src/main/java/com/ecobike/command/WriteToFileCommand.java create mode 100644 src/main/resources/backupBikes.txt diff --git a/src/main/java/com/ecobike/CommandExecutor.java b/src/main/java/com/ecobike/CommandExecutor.java index 1d20320..6116828 100644 --- a/src/main/java/com/ecobike/CommandExecutor.java +++ b/src/main/java/com/ecobike/CommandExecutor.java @@ -1,7 +1,6 @@ package com.ecobike; -import com.ecobike.command.Command; -import com.ecobike.command.ExitCommand; +import com.ecobike.command.*; import java.util.HashMap; import java.util.Map; @@ -10,6 +9,12 @@ public class CommandExecutor { private static final Map allKnownCommandsMap = new HashMap<>(); static { + allKnownCommandsMap.put(Operation.SHOW_CATALOG, new ShowCommand()); + allKnownCommandsMap.put(Operation.ADD_FOLDING_BIKE, new AddFoldingBikeCommand()); + allKnownCommandsMap.put(Operation.ADD_SPEEDELEC_BIKE, new AddSpeedelecBikeCommand()); + allKnownCommandsMap.put(Operation.ADD_E_BIKE, new AddEBikeCommand()); + + allKnownCommandsMap.put(Operation.WRITE_TO_FILE, new WriteToFileCommand()); allKnownCommandsMap.put(Operation.STOP_PROGRAM, new ExitCommand()); } diff --git a/src/main/java/com/ecobike/Communicator.java b/src/main/java/com/ecobike/Communicator.java index 135efc2..3c9d51b 100644 --- a/src/main/java/com/ecobike/Communicator.java +++ b/src/main/java/com/ecobike/Communicator.java @@ -17,4 +17,8 @@ public interface Communicator { * Method reads integer from user. */ int readInt(); + /** + * Method reads boolean from user. + */ + boolean readBoolean(); } diff --git a/src/main/java/com/ecobike/ConsoleCommunicator.java b/src/main/java/com/ecobike/ConsoleCommunicator.java index 0a9fe54..fc8c90a 100644 --- a/src/main/java/com/ecobike/ConsoleCommunicator.java +++ b/src/main/java/com/ecobike/ConsoleCommunicator.java @@ -33,9 +33,24 @@ public int readInt() { try { result = Integer.parseInt(readString()); } catch (NumberFormatException e) { - System.out.println("Repeat your entry (only number):"); + System.out.println("Repeat your entry (only positive number):"); } } return result; } + + @Override + public boolean readBoolean() { + System.out.println("Type 1 for TRUE or 0 for FALSE"); + while (true) { + String entry = readString(); + if (entry.equals("0")) { + return false; + } + if (entry.equals("1")) { + return true; + } + System.out.println("Wrong entry. Type 1 for TRUE or 0 for FALSE"); + } + } } diff --git a/src/main/java/com/ecobike/DataHolder.java b/src/main/java/com/ecobike/DataHolder.java new file mode 100644 index 0000000..80f0958 --- /dev/null +++ b/src/main/java/com/ecobike/DataHolder.java @@ -0,0 +1,63 @@ +package com.ecobike; + +import com.ecobike.model.Bike; +import com.ecobike.model.EBike; +import com.ecobike.model.FoldingBike; +import com.ecobike.model.SpeedelecBike; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Collectors; + +public class DataHolder { + private final static DataHolder INSTANCE = new DataHolder(); + private List bikes; + + private DataHolder() { + } + + public static DataHolder getInstance() { + return INSTANCE; + } + + public void loadData(Path file) { + try { + bikes = Files.lines(file) + .map(line -> parseBike(line)) + .collect(Collectors.toList()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public List getBikes() { + return bikes; + } + + private Bike parseBike(String line) { + if (line.startsWith("SPEEDELEC")) { + String[] toConstructor = line.replace("SPEEDELEC ", "").split("; "); + return new SpeedelecBike(toConstructor[0], Integer.parseInt(toConstructor[1]), + Integer.parseInt(toConstructor[2]), Boolean.parseBoolean(toConstructor[3]), + Integer.parseInt(toConstructor[4]), toConstructor[5], + Integer.parseInt(toConstructor[6])); + } + if(line.startsWith("E-BIKE")) { + String[] toConstructor = line.replace("E-BIKE ", "").split("; "); + return new EBike(toConstructor[0], Integer.parseInt(toConstructor[1]), + Integer.parseInt(toConstructor[2]), Boolean.parseBoolean(toConstructor[3]), + Integer.parseInt(toConstructor[4]), toConstructor[5], + Integer.parseInt(toConstructor[6])); + } + if(line.startsWith("FOLDING BIKE")) { + String[] toConstructor = line.replace("FOLDING BIKE ", "").split("; "); + return new FoldingBike(toConstructor[0], Integer.parseInt(toConstructor[1]), + Integer.parseInt(toConstructor[2]), Integer.parseInt(toConstructor[3]), + Boolean.parseBoolean(toConstructor[4]), toConstructor[5], + Integer.parseInt(toConstructor[6])); + } + return null; + } +} diff --git a/src/main/java/com/ecobike/EcoBikeApplication.java b/src/main/java/com/ecobike/EcoBikeApplication.java index 8f8bd90..65e95e8 100644 --- a/src/main/java/com/ecobike/EcoBikeApplication.java +++ b/src/main/java/com/ecobike/EcoBikeApplication.java @@ -1,32 +1,24 @@ package com.ecobike; -import com.ecobike.model.Bike; -import com.ecobike.model.EBike; -import com.ecobike.model.FoldingBike; -import com.ecobike.model.SpeedelecBike; - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.List; -import java.util.stream.Collectors; public class EcoBikeApplication { public static final Communicator COMMUNICATOR = new ConsoleCommunicator(); - private static Path bikeDataFile; - static { + private static final DataHolder DATA_HOLDER = DataHolder.getInstance(); + public static final FileWriter FILE_WRITER = new FileWriter(); + + public static void main(String[] args) throws IOException { + Path bikeDataFile; do { COMMUNICATOR.writeMessage("Enter path to ecobike.txt :"); bikeDataFile = Paths.get(COMMUNICATOR.readString()); - } while (Files.isRegularFile(bikeDataFile)); - } - private static List bikeList; + } while (!Files.isRegularFile(bikeDataFile)); + DATA_HOLDER.loadData(bikeDataFile); + FILE_WRITER.setFile(bikeDataFile); - public static void main(String[] args) throws IOException { - bikeList = Files.lines(bikeDataFile) - .map(line -> parseBike(line)) - .collect(Collectors.toList()); Operation operation = null; do { try { @@ -52,26 +44,4 @@ private static Operation askOperation() throws IOException { return Operation.values()[COMMUNICATOR.readInt() - 1]; } - - private static Bike parseBike(String line) { - String[] toConstructor = line.split(" ", 2)[1].split("; "); - switch (line.split(" ")[0]) { - case "SPEEDELEC": - return new SpeedelecBike(toConstructor[0], Integer.parseInt(toConstructor[1]), - Integer.parseInt(toConstructor[2]), Boolean.parseBoolean(toConstructor[3]), - Integer.parseInt(toConstructor[4]), toConstructor[5], - Integer.parseInt(toConstructor[6])); - case "E-BIKE": - return new EBike(toConstructor[0], Integer.parseInt(toConstructor[1]), - Integer.parseInt(toConstructor[2]), Boolean.parseBoolean(toConstructor[3]), - Integer.parseInt(toConstructor[4]), toConstructor[5], - Integer.parseInt(toConstructor[6])); - case "FOLDING BIKE": - return new FoldingBike(toConstructor[0], Integer.parseInt(toConstructor[1]), - Integer.parseInt(toConstructor[2]), Integer.parseInt(toConstructor[3]), - Boolean.parseBoolean(toConstructor[4]), toConstructor[5], - Integer.parseInt(toConstructor[6])); - } - return null; - } } diff --git a/src/main/java/com/ecobike/FileWriter.java b/src/main/java/com/ecobike/FileWriter.java new file mode 100644 index 0000000..aa5fa97 --- /dev/null +++ b/src/main/java/com/ecobike/FileWriter.java @@ -0,0 +1,29 @@ +package com.ecobike; + +import com.ecobike.model.Bike; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Collectors; + +public class FileWriter { + private Path file; + private DataHolder dataHolder = DataHolder.getInstance(); + + public void setFile(Path file) { + this.file = file; + } + + public void writeData() { + List dataToWrite = dataHolder.getBikes().stream() + .map(Bike::toFileWriterString) + .collect(Collectors.toList()); + try { + Files.write(file, dataToWrite); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/ecobike/command/AddEBikeCommand.java b/src/main/java/com/ecobike/command/AddEBikeCommand.java new file mode 100644 index 0000000..bf76813 --- /dev/null +++ b/src/main/java/com/ecobike/command/AddEBikeCommand.java @@ -0,0 +1,27 @@ +package com.ecobike.command; + +import com.ecobike.model.EBike; + +public class AddEBikeCommand implements Command { + @Override + public void execute() { + COMMUNICATOR.writeMessage("You are adding new E-BIKE"); + COMMUNICATOR.writeMessage("Enter brand:"); + String brand = COMMUNICATOR.readString(); + COMMUNICATOR.writeMessage("Enter max speed:"); + int maxSpeed = COMMUNICATOR.readInt(); + COMMUNICATOR.writeMessage("Enter weight:"); + int weight = COMMUNICATOR.readInt(); + COMMUNICATOR.writeMessage("Enter presence of lights:"); + boolean isLightsPresent = COMMUNICATOR.readBoolean(); + COMMUNICATOR.writeMessage("Enter battery capacity:"); + int batteryCapacity = COMMUNICATOR.readInt(); + COMMUNICATOR.writeMessage("Enter color:"); + String color = COMMUNICATOR.readString(); + COMMUNICATOR.writeMessage("Enter price:"); + int price = COMMUNICATOR.readInt(); + DATA_HOLDER.getBikes().add(new EBike(brand, maxSpeed, weight, + isLightsPresent, batteryCapacity, color, price)); + COMMUNICATOR.writeMessage("New E-BIKE BIKE added."); + } +} diff --git a/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java b/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java new file mode 100644 index 0000000..9bf1018 --- /dev/null +++ b/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java @@ -0,0 +1,27 @@ +package com.ecobike.command; + +import com.ecobike.model.FoldingBike; + +public class AddFoldingBikeCommand implements Command { + @Override + public void execute() { + COMMUNICATOR.writeMessage("You are adding new FOLDING BIKE"); + COMMUNICATOR.writeMessage("Enter brand:"); + String brand = COMMUNICATOR.readString(); + COMMUNICATOR.writeMessage("Enter wheel size:"); + int wheelSize = COMMUNICATOR.readInt(); + COMMUNICATOR.writeMessage("Enter number of gears:"); + int numberOfGears = COMMUNICATOR.readInt(); + COMMUNICATOR.writeMessage("Enter weight:"); + int weight = COMMUNICATOR.readInt(); + COMMUNICATOR.writeMessage("Enter presence of lights:"); + boolean isLightsPresent = COMMUNICATOR.readBoolean(); + COMMUNICATOR.writeMessage("Enter color:"); + String color = COMMUNICATOR.readString(); + COMMUNICATOR.writeMessage("Enter price:"); + int price = COMMUNICATOR.readInt(); + DATA_HOLDER.getBikes().add(new FoldingBike(brand, wheelSize, numberOfGears, + weight, isLightsPresent, color, price)); + COMMUNICATOR.writeMessage("New FOLDING BIKE added."); + } +} diff --git a/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java b/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java new file mode 100644 index 0000000..ccf993e --- /dev/null +++ b/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java @@ -0,0 +1,27 @@ +package com.ecobike.command; + +import com.ecobike.model.SpeedelecBike; + +public class AddSpeedelecBikeCommand implements Command { + @Override + public void execute() { + COMMUNICATOR.writeMessage("You are adding new SPEEDELEC BIKE"); + COMMUNICATOR.writeMessage("Enter brand:"); + String brand = COMMUNICATOR.readString(); + COMMUNICATOR.writeMessage("Enter max speed:"); + int maxSpeed = COMMUNICATOR.readInt(); + COMMUNICATOR.writeMessage("Enter weight:"); + int weight = COMMUNICATOR.readInt(); + COMMUNICATOR.writeMessage("Enter presence of lights:"); + boolean isLightsPresent = COMMUNICATOR.readBoolean(); + COMMUNICATOR.writeMessage("Enter battery capacity:"); + int batteryCapacity = COMMUNICATOR.readInt(); + COMMUNICATOR.writeMessage("Enter color:"); + String color = COMMUNICATOR.readString(); + COMMUNICATOR.writeMessage("Enter price:"); + int price = COMMUNICATOR.readInt(); + DATA_HOLDER.getBikes().add(new SpeedelecBike(brand, maxSpeed, weight, + isLightsPresent, batteryCapacity, color, price)); + COMMUNICATOR.writeMessage("New SPEEDELEC BIKE added."); + } +} diff --git a/src/main/java/com/ecobike/command/Command.java b/src/main/java/com/ecobike/command/Command.java index b87d46e..444469f 100644 --- a/src/main/java/com/ecobike/command/Command.java +++ b/src/main/java/com/ecobike/command/Command.java @@ -1,7 +1,12 @@ package com.ecobike.command; -import java.io.IOException; +import com.ecobike.Communicator; +import com.ecobike.DataHolder; +import com.ecobike.EcoBikeApplication; public interface Command { - void execute() throws IOException; + Communicator COMMUNICATOR = EcoBikeApplication.COMMUNICATOR; + DataHolder DATA_HOLDER = DataHolder.getInstance(); + + void execute(); } diff --git a/src/main/java/com/ecobike/command/ExitCommand.java b/src/main/java/com/ecobike/command/ExitCommand.java index 889a4d6..c2c1c89 100644 --- a/src/main/java/com/ecobike/command/ExitCommand.java +++ b/src/main/java/com/ecobike/command/ExitCommand.java @@ -1,12 +1,8 @@ package com.ecobike.command; -import com.ecobike.EcoBikeApplication; - -import java.io.IOException; - public class ExitCommand implements Command { @Override - public void execute() throws IOException { - EcoBikeApplication.COMMUNICATOR.writeMessage("Good bay!"); + public void execute() { + COMMUNICATOR.writeMessage("Good bay!"); } } diff --git a/src/main/java/com/ecobike/command/ShowCommand.java b/src/main/java/com/ecobike/command/ShowCommand.java new file mode 100644 index 0000000..c5bb28f --- /dev/null +++ b/src/main/java/com/ecobike/command/ShowCommand.java @@ -0,0 +1,10 @@ +package com.ecobike.command; + +public class ShowCommand implements Command { + + @Override + public void execute() { + DATA_HOLDER.getBikes() + .forEach(bike -> COMMUNICATOR.writeMessage(bike.toString())); + } +} diff --git a/src/main/java/com/ecobike/command/WriteToFileCommand.java b/src/main/java/com/ecobike/command/WriteToFileCommand.java new file mode 100644 index 0000000..d339811 --- /dev/null +++ b/src/main/java/com/ecobike/command/WriteToFileCommand.java @@ -0,0 +1,12 @@ +package com.ecobike.command; + +import com.ecobike.EcoBikeApplication; + +public class WriteToFileCommand implements Command { + + @Override + public void execute() { + EcoBikeApplication.FILE_WRITER.writeData(); + COMMUNICATOR.writeMessage("Data has been written successfully."); + } +} diff --git a/src/main/java/com/ecobike/model/AbstractElectroBike.java b/src/main/java/com/ecobike/model/AbstractElectroBike.java index 7d5b8e0..e7eddfa 100644 --- a/src/main/java/com/ecobike/model/AbstractElectroBike.java +++ b/src/main/java/com/ecobike/model/AbstractElectroBike.java @@ -26,6 +26,19 @@ public AbstractElectroBike(String brand, this.batteryCapacity = batteryCapacity; } + /** + * Method converts bike to specific String format + * for writing to file. + * + * @return String representation of the bike. + */ + @Override + public String toFileWriterString() { + return String.format("%s; %d; %d; %s; %d; %s; %d", + getBrand(), maxSpeed, getWeight(), isLightsPresent() ? "true" : "false", + batteryCapacity, getColor(), getPrice()); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/com/ecobike/model/Bike.java b/src/main/java/com/ecobike/model/Bike.java index b8882b7..fb7d3b5 100644 --- a/src/main/java/com/ecobike/model/Bike.java +++ b/src/main/java/com/ecobike/model/Bike.java @@ -24,6 +24,13 @@ public Bike(String brand, int weight, boolean isLightsPresent, String color, int this.price = price; } + /** + * Method converts bike to specific String format + * for writing to file. + * @return String representation of the bike. + */ + public abstract String toFileWriterString(); + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/com/ecobike/model/EBike.java b/src/main/java/com/ecobike/model/EBike.java index e0ee165..f6a8806 100644 --- a/src/main/java/com/ecobike/model/EBike.java +++ b/src/main/java/com/ecobike/model/EBike.java @@ -13,6 +13,17 @@ public EBike(String brand, batteryCapacity, color, price); } + /** + * Method converts bike to specific String format + * for writing to file. + * + * @return String representation of the bike. + */ + @Override + public String toFileWriterString() { + return "E-BIKE " + super.toFileWriterString(); + } + @Override public String toString() { return "E-BIKE" + super.toString(); diff --git a/src/main/java/com/ecobike/model/FoldingBike.java b/src/main/java/com/ecobike/model/FoldingBike.java index c44ab1c..d567886 100644 --- a/src/main/java/com/ecobike/model/FoldingBike.java +++ b/src/main/java/com/ecobike/model/FoldingBike.java @@ -23,6 +23,19 @@ public FoldingBike(String brand, this.numberOfGears = numberOfGears; } + /** + * Method converts bike to specific String format + * for writing to file. + * + * @return String representation of the bike. + */ + @Override + public String toFileWriterString() { + return String.format("FOLDING BIKE %s; %d; %d; %d; %s; %s; %d", + getBrand(), wheelSize, numberOfGears, getWeight(), + isLightsPresent() ? "true" : "false", getColor(), getPrice()); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/com/ecobike/model/SpeedelecBike.java b/src/main/java/com/ecobike/model/SpeedelecBike.java index fc3d62e..8c3d94b 100644 --- a/src/main/java/com/ecobike/model/SpeedelecBike.java +++ b/src/main/java/com/ecobike/model/SpeedelecBike.java @@ -13,6 +13,17 @@ public SpeedelecBike(String brand, batteryCapacity, color, price); } + /** + * Method converts bike to specific String format + * for writing to file. + * + * @return String representation of the bike. + */ + @Override + public String toFileWriterString() { + return "SPEEDELEC " + super.toFileWriterString(); + } + @Override public String toString() { return "SPEEDELEC" + super.toString(); diff --git a/src/main/resources/backupBikes.txt b/src/main/resources/backupBikes.txt new file mode 100644 index 0000000..9a57fdc --- /dev/null +++ b/src/main/resources/backupBikes.txt @@ -0,0 +1,1000 @@ +SPEEDELEC Booster; 35; 10900; false; 13200; green; 1279 +SPEEDELEC E-Scooter; 60; 15300; false; 14800; marine; 309 +E-BIKE Lankeleisi; 65; 24200; false; 10000; black; 2399 +E-BIKE Lankeleisi; 50; 21600; false; 30000; flame; 859 +FOLDING BIKE Benetti; 24; 27; 11400; false; rose; 1009 +FOLDING BIKE Benetti; 24; 6; 9400; true; silver; 1195 +FOLDING BIKE Formula; 16; 21; 12200; true; flame; 269 +E-BIKE ElectrO; 20; 19300; true; 14000; beige; 1025 +SPEEDELEC Dualtron; 30; 14400; true; 6500; dark gray; 1019 +FOLDING BIKE Intertool; 24; 21; 12900; true; coral; 1565 +E-BIKE Gazelle; 45; 25200; true; 11000; yellow; 855 +SPEEDELEC EcoRide; 15; 8300; true; 15600; blue; 1055 +SPEEDELEC Smart; 40; 9600; false; 13000; brown; 1065 +E-BIKE Felt; 60; 28300; false; 9000; grey; 2229 +SPEEDELEC Freego; 55; 9000; false; 15800; grenadine; 1505 +E-BIKE Mando Footloose; 20; 26800; false; 12000; beige; 1559 +FOLDING BIKE Brompton; 20; 7; 10800; true; orange; 1469 +FOLDING BIKE Comanche; 24; 6; 13500; true; olive; 609 +E-BIKE SkyBike; 40; 27200; false; 31000; olive; 1089 +FOLDING BIKE Titan; 20; 24; 10400; true; olive; 479 +FOLDING BIKE Titan; 20; 1; 11800; false; khaki; 1115 +SPEEDELEC EcoRide; 50; 8400; false; 8600; brown; 1609 +FOLDING BIKE BMW; 20; 7; 14400; false; lemon; 1085 +E-BIKE Ecofect; 30; 19200; true; 15000; lemon; 2919 +E-BIKE Gazelle; 25; 22200; true; 26000; silver; 1735 +SPEEDELEC Speedway; 25; 6800; false; 15200; blue; 915 +E-BIKE Ferrari; 55; 20900; false; 28000; olive; 3055 +FOLDING BIKE BMW; 16; 9; 11000; true; emerald; 1459 +SPEEDELEC Segway; 50; 11600; true; 7300; marine; 375 +FOLDING BIKE VNV; 20; 27; 10100; true; emerald; 1645 +E-BIKE ION; 25; 20800; true; 30000; coral; 2355 +SPEEDELEC OIO; 10; 10400; false; 8800; pink; 1099 +FOLDING BIKE Author; 20; 9; 11900; true; brown; 209 +SPEEDELEC Segway; 10; 11700; false; 12000; pink; 1365 +SPEEDELEC Peugeot; 60; 19800; true; 12200; yellow; 885 +FOLDING BIKE West Bike; 20; 21; 15300; false; lemon; 1039 +FOLDING BIKE Dahon; 14; 21; 12200; true; golden; 1169 +E-BIKE Porshe Design; 45; 28800; true; 13000; dark gray; 2789 +FOLDING BIKE Trinx; 20; 24; 10700; true; pink; 855 +FOLDING BIKE Cayman; 16; 27; 8500; false; orange; 735 +E-BIKE E-Motion; 50; 26500; false; 8000; yellow; 2145 +E-BIKE Gazelle; 55; 20800; true; 14000; violet; 1805 +SPEEDELEC Speedelec Minirider; 10; 7600; false; 6300; silver; 125 +FOLDING BIKE Polygon; 20; 8; 15600; false; orange; 1285 +FOLDING BIKE SkyBike; 24; 27; 16200; false; orange; 505 +SPEEDELEC Speedelec Minirider; 25; 15900; true; 14200; pink; 299 +SPEEDELEC Smart; 10; 15000; false; 7200; olive; 455 +FOLDING BIKE West Bike; 14; 9; 11500; false; golden; 1075 +SPEEDELEC ITrike; 25; 14500; false; 5600; marine; 259 +SPEEDELEC Maraton; 40; 15000; true; 7500; golden; 269 +SPEEDELEC Speedelec Minirider; 15; 10000; false; 13900; coral; 855 +FOLDING BIKE Intertool; 26; 21; 11700; true; violet; 689 +SPEEDELEC ITrike; 55; 15000; false; 14500; emerald; 945 +FOLDING BIKE Make Bike; 24; 1; 16300; false; grenadine; 1249 +SPEEDELEC Smart; 65; 17800; false; 13300; khaki; 985 +SPEEDELEC ITrike; 60; 18700; false; 7000; blue; 1109 +FOLDING BIKE Comanche; 16; 21; 16500; true; lemon; 1015 +E-BIKE Rover; 45; 27300; false; 12000; grenadine; 2799 +FOLDING BIKE Crosser; 24; 3; 16000; false; marine; 1129 +FOLDING BIKE Titan; 26; 1; 13800; false; blue; 1655 +FOLDING BIKE Polygon; 18; 7; 8200; true; rose; 1005 +FOLDING BIKE Cayman; 26; 1; 16300; false; khaki; 605 +FOLDING BIKE Trinx; 26; 3; 8400; true; flame; 455 +E-BIKE Xiaomi; 65; 26300; false; 29000; yellow; 3489 +SPEEDELEC Ultron; 25; 16000; true; 10400; violet; 909 +SPEEDELEC Peugeot; 20; 9900; true; 2500; orange; 809 +SPEEDELEC Speedway; 45; 7100; false; 9700; red; 615 +SPEEDELEC Maraton; 20; 19800; false; 15900; silver; 1215 +FOLDING BIKE Titan; 14; 9; 13100; false; orange; 1055 +FOLDING BIKE Comanche; 20; 27; 11700; false; yellow; 1349 +FOLDING BIKE Crosser; 24; 6; 9500; false; lemon; 1675 +E-BIKE Farad; 25; 20700; false; 13000; blue; 2355 +E-BIKE Xiaomi; 65; 22100; false; 11000; dark gray; 1905 +SPEEDELEC Ultron; 35; 7600; false; 3900; grenadine; 1179 +SPEEDELEC Dualtron; 35; 12900; true; 9900; grey; 379 +SPEEDELEC AirWheel; 45; 9900; true; 11400; grenadine; 469 +E-BIKE Zaxboard; 30; 20300; false; 30000; yellow; 1975 +E-BIKE Rover; 20; 21400; false; 16000; flame; 1659 +FOLDING BIKE Brompton; 20; 6; 9600; true; orange; 885 +E-BIKE ION; 20; 20200; false; 20000; grey; 1989 +E-BIKE Zaxboard; 30; 22500; false; 28000; pink; 3345 +FOLDING BIKE West Bike; 18; 18; 15700; false; lemon; 339 +SPEEDELEC Speedelec Minirider; 55; 6900; true; 3800; yellow; 685 +E-BIKE Gazelle; 35; 24700; true; 22000; brown; 3219 +SPEEDELEC Xiaomi; 35; 15300; false; 11700; beige; 1269 +FOLDING BIKE Make Bike; 16; 18; 8900; false; orange; 1109 +E-BIKE Zaxboard; 65; 23300; false; 16000; grenadine; 1275 +E-BIKE Ferrari; 25; 21900; false; 15000; green; 1689 +FOLDING BIKE Crosser; 20; 9; 9500; true; rose; 979 +E-BIKE Mercedes; 50; 24200; true; 21000; orange; 3459 +SPEEDELEC Ultron; 40; 11900; true; 7100; white; 1305 +FOLDING BIKE Trinx; 18; 18; 9600; false; yellow; 359 +SPEEDELEC ITrike; 60; 19600; true; 9800; red; 1385 +SPEEDELEC ITrike; 60; 13200; false; 13800; orange; 475 +FOLDING BIKE VNV; 26; 3; 14600; false; beige; 715 +FOLDING BIKE Dahon; 24; 21; 9000; false; dark gray; 485 +E-BIKE Mando Footloose; 40; 21500; false; 28000; white; 1815 +E-BIKE Ferrari; 30; 19200; false; 9000; beige; 3059 +SPEEDELEC Ultron; 30; 20300; true; 14800; green; 299 +FOLDING BIKE Polygon; 16; 24; 11100; false; lemon; 1379 +E-BIKE Farad; 65; 28500; false; 25000; grenadine; 3385 +SPEEDELEC Dualtron; 50; 16100; false; 2600; violet; 1645 +FOLDING BIKE Flex; 24; 21; 12400; true; silver; 1559 +E-BIKE Zaxboard; 45; 24300; true; 23000; marine; 1535 +FOLDING BIKE Kross; 18; 6; 8800; true; golden; 379 +FOLDING BIKE Dahon; 20; 3; 8000; true; lemon; 779 +E-BIKE Porshe Design; 50; 28100; true; 13000; pink; 755 +FOLDING BIKE Author; 24; 27; 13200; false; flame; 325 +FOLDING BIKE BMW; 18; 27; 10600; true; dark gray; 1549 +E-BIKE Rover; 20; 26100; false; 29000; grenadine; 2219 +E-BIKE Felt; 20; 19300; true; 16000; flame; 2745 +SPEEDELEC E-Scooter; 35; 9700; false; 11500; yellow; 559 +SPEEDELEC Maraton; 65; 16300; true; 12100; red; 1499 +SPEEDELEC Xiaomi; 55; 9500; false; 12500; golden; 779 +E-BIKE Haibike; 60; 25700; true; 8000; golden; 1299 +E-BIKE E-Motion; 35; 25700; false; 16000; blue; 3399 +E-BIKE ElectrO; 20; 19100; false; 30000; dark gray; 3259 +SPEEDELEC Aest; 55; 21800; false; 8400; blue; 359 +E-BIKE ElectrO; 50; 22600; false; 22000; emerald; 2205 +FOLDING BIKE Brompton; 20; 7; 8000; false; dark gray; 999 +SPEEDELEC Dualtron; 25; 21900; true; 6500; silver; 1419 +E-BIKE Zaxboard; 25; 26200; true; 22000; silver; 705 +FOLDING BIKE Polygon; 16; 21; 16800; false; khaki; 295 +FOLDING BIKE Author; 14; 9; 9100; false; lemon; 529 +SPEEDELEC Segway; 25; 16800; false; 2200; silver; 929 +SPEEDELEC Speedway; 35; 18400; true; 11900; grey; 339 +FOLDING BIKE Titan; 24; 6; 14400; true; khaki; 359 +E-BIKE E-Motion; 40; 19100; false; 26000; khaki; 1015 +SPEEDELEC Dualtron; 25; 10000; true; 9100; brown; 1279 +SPEEDELEC Booster; 20; 15000; false; 10300; orange; 399 +FOLDING BIKE Cayman; 26; 18; 8600; true; brown; 389 +E-BIKE Rover; 60; 27300; false; 11000; red; 1155 +SPEEDELEC E-Scooter; 25; 9700; false; 14700; beige; 939 +FOLDING BIKE Trinx; 24; 3; 8800; false; rose; 699 +FOLDING BIKE Brompton; 26; 7; 15100; true; golden; 255 +FOLDING BIKE Dahon; 24; 18; 9000; false; red; 1505 +E-BIKE Ferrari; 65; 27300; true; 18000; rose; 1675 +E-BIKE Scott; 20; 27100; false; 20000; black; 2565 +E-BIKE Porshe Design; 65; 28600; true; 11000; yellow; 1085 +FOLDING BIKE Flex; 16; 18; 12500; false; grenadine; 905 +E-BIKE Ecofect; 45; 24500; true; 27000; yellow; 2929 +E-BIKE Xiaomi; 50; 26400; false; 15000; red; 679 +FOLDING BIKE Stern; 26; 24; 11200; true; black; 339 +FOLDING BIKE Intertool; 24; 9; 10600; false; blue; 725 +E-BIKE Felt; 60; 22400; true; 25000; violet; 719 +SPEEDELEC Ultron; 20; 8100; false; 3800; black; 779 +FOLDING BIKE Benetti; 20; 27; 15100; true; lemon; 1095 +SPEEDELEC EcoRide; 50; 19500; false; 11100; green; 449 +FOLDING BIKE Cayman; 26; 18; 12500; true; coral; 1185 +E-BIKE SkyBike; 30; 22800; false; 19000; red; 3305 +SPEEDELEC EcoRide; 15; 7200; false; 11200; green; 1135 +SPEEDELEC ITrike; 35; 18900; false; 7600; flame; 1235 +FOLDING BIKE Dahon; 16; 1; 12100; false; grey; 799 +E-BIKE Porshe Design; 35; 20100; true; 25000; dark gray; 759 +FOLDING BIKE Dahon; 20; 18; 16500; true; olive; 1215 +FOLDING BIKE Formula; 24; 8; 10300; true; silver; 619 +FOLDING BIKE Intertool; 24; 9; 13300; false; silver; 529 +FOLDING BIKE Formula; 16; 6; 16900; false; golden; 445 +FOLDING BIKE Make Bike; 26; 27; 8100; true; red; 305 +E-BIKE Mando Footloose; 20; 22000; true; 16000; blue; 2539 +SPEEDELEC E-Scooter; 30; 10500; false; 9200; emerald; 155 +E-BIKE Xiaomi; 20; 28900; true; 31000; khaki; 1739 +E-BIKE Felt; 35; 22800; false; 13000; beige; 1975 +E-BIKE Farad; 35; 27000; true; 12000; blue; 1579 +SPEEDELEC Smart; 45; 8500; true; 12400; yellow; 545 +SPEEDELEC Ultron; 65; 21600; false; 4800; silver; 599 +SPEEDELEC Smart; 45; 21100; true; 6200; violet; 339 +E-BIKE Ecofect; 30; 21000; true; 10000; orange; 2479 +E-BIKE SkyBike; 65; 23200; false; 31000; khaki; 675 +E-BIKE Scott; 30; 28000; true; 31000; olive; 1125 +SPEEDELEC Dualtron; 15; 16900; false; 7500; black; 225 +FOLDING BIKE SkyBike; 16; 9; 14800; true; yellow; 1049 +FOLDING BIKE Dahon; 20; 1; 13100; false; grenadine; 1329 +FOLDING BIKE Crosser; 20; 1; 14800; true; white; 395 +E-BIKE Ecofect; 25; 26400; false; 17000; silver; 1145 +FOLDING BIKE Dahon; 24; 7; 10900; false; violet; 1515 +E-BIKE Gazelle; 25; 28900; false; 12000; red; 2129 +E-BIKE ION; 45; 23500; false; 24000; pink; 739 +E-BIKE GoCycle; 65; 25200; true; 26000; dark gray; 2829 +E-BIKE Mando Footloose; 55; 20600; false; 21000; coral; 1385 +E-BIKE SkyBike; 55; 25900; false; 9000; silver; 3239 +FOLDING BIKE Crosser; 20; 18; 13800; false; khaki; 249 +E-BIKE Gazelle; 55; 23800; false; 22000; marine; 3375 +SPEEDELEC Dualtron; 65; 6600; false; 6300; grenadine; 1435 +FOLDING BIKE Polygon; 24; 3; 14000; false; khaki; 639 +FOLDING BIKE Dahon; 26; 27; 12600; false; beige; 1025 +SPEEDELEC Speedway; 30; 13200; true; 15600; brown; 1575 +FOLDING BIKE Benetti; 20; 6; 16000; false; dark gray; 289 +FOLDING BIKE Benetti; 20; 21; 13600; false; lemon; 1399 +E-BIKE Haibike; 30; 26200; false; 24000; red; 1979 +E-BIKE SkyBike; 60; 20700; false; 15000; violet; 2119 +E-BIKE SkyBike; 40; 27400; true; 8000; orange; 1699 +FOLDING BIKE Crosser; 20; 18; 14000; true; violet; 369 +E-BIKE Xiaomi; 55; 23600; false; 8000; black; 2555 +FOLDING BIKE West Bike; 18; 27; 11700; false; emerald; 1439 +SPEEDELEC Smart; 55; 9400; true; 12700; white; 535 +E-BIKE Gazelle; 60; 20600; false; 11000; emerald; 2879 +E-BIKE Ferrari; 50; 28500; false; 25000; golden; 1489 +SPEEDELEC Xiaomi; 25; 13500; true; 7300; blue; 1569 +SPEEDELEC Dualtron; 15; 11800; true; 10500; yellow; 1449 +FOLDING BIKE Author; 20; 8; 9300; true; emerald; 1355 +E-BIKE Ferrari; 20; 23800; true; 14000; violet; 2479 +SPEEDELEC Freego; 65; 18400; false; 5000; yellow; 1309 +SPEEDELEC Speedelec Minirider; 15; 10000; false; 15100; orange; 1349 +SPEEDELEC Kugoo; 20; 11600; true; 13500; dark gray; 349 +E-BIKE GoCycle; 55; 25000; false; 23000; flame; 2189 +SPEEDELEC Dualtron; 30; 15500; false; 4900; red; 439 +E-BIKE Felt; 30; 25200; false; 10000; dark gray; 2845 +E-BIKE Rover; 40; 28100; false; 13000; grenadine; 1649 +FOLDING BIKE Polygon; 24; 27; 10600; true; grey; 645 +E-BIKE Zaxboard; 55; 22400; true; 27000; blue; 2249 +E-BIKE GoCycle; 50; 24400; true; 22000; coral; 2195 +E-BIKE Koga; 20; 22900; false; 12000; olive; 2129 +E-BIKE Lankeleisi; 50; 22500; false; 13000; golden; 795 +FOLDING BIKE Intertool; 20; 1; 14300; true; khaki; 859 +SPEEDELEC Xiaomi; 45; 18200; false; 10400; white; 679 +SPEEDELEC OIO; 10; 19300; false; 14500; red; 265 +FOLDING BIKE Stern; 24; 21; 12800; false; blue; 725 +E-BIKE Koga; 65; 20200; true; 19000; grey; 3185 +FOLDING BIKE BMW; 20; 9; 14600; true; marine; 1605 +SPEEDELEC AirWheel; 15; 9900; true; 14000; olive; 1009 +E-BIKE Zaxboard; 55; 20700; false; 27000; marine; 909 +E-BIKE Scott; 25; 21300; true; 15000; pink; 3359 +SPEEDELEC Booster; 65; 6600; false; 11700; pink; 605 +SPEEDELEC Maraton; 55; 17500; false; 2100; dark gray; 765 +SPEEDELEC Ultron; 50; 20500; false; 13000; emerald; 549 +FOLDING BIKE Titan; 24; 3; 10600; false; grenadine; 1075 +SPEEDELEC Booster; 60; 16200; false; 8100; red; 329 +E-BIKE GoCycle; 55; 19600; true; 14000; yellow; 1949 +SPEEDELEC Speedway; 50; 21200; false; 15900; dark gray; 1159 +SPEEDELEC Segway; 50; 12800; true; 4800; green; 1319 +E-BIKE Rover; 35; 22400; false; 11000; rose; 2429 +FOLDING BIKE Dahon; 24; 9; 10600; false; lemon; 389 +SPEEDELEC ITrike; 65; 21800; false; 12900; violet; 1389 +FOLDING BIKE Titan; 20; 6; 13100; true; rose; 365 +FOLDING BIKE Titan; 24; 3; 13100; true; olive; 875 +FOLDING BIKE West Bike; 24; 9; 9600; true; beige; 1315 +FOLDING BIKE Author; 20; 21; 14000; false; grenadine; 1075 +FOLDING BIKE Stern; 18; 3; 14500; false; rose; 1635 +E-BIKE Felt; 60; 21000; false; 26000; dark gray; 2279 +E-BIKE Scott; 45; 21800; true; 28000; dark gray; 2039 +FOLDING BIKE Crosser; 16; 6; 11700; true; black; 1065 +SPEEDELEC Segway; 20; 7600; true; 2200; olive; 669 +SPEEDELEC E-Scooter; 65; 19400; true; 3800; grey; 1115 +FOLDING BIKE Comanche; 20; 18; 9100; true; violet; 1025 +E-BIKE Xiaomi; 30; 22800; true; 25000; coral; 3119 +E-BIKE Zaxboard; 20; 27000; true; 23000; yellow; 1309 +SPEEDELEC Peugeot; 55; 6800; true; 12900; khaki; 515 +FOLDING BIKE Cayman; 24; 7; 15600; false; golden; 999 +SPEEDELEC EcoRide; 45; 8400; false; 8300; beige; 1615 +FOLDING BIKE Dahon; 14; 7; 13900; false; khaki; 1009 +SPEEDELEC Smart; 20; 8700; true; 10500; silver; 1239 +SPEEDELEC Speedway; 45; 21300; false; 4400; green; 1279 +FOLDING BIKE Flex; 26; 3; 14800; false; golden; 205 +SPEEDELEC Speedelec Minirider; 25; 20400; false; 10900; grenadine; 1465 +E-BIKE ION; 25; 19600; false; 8000; green; 2209 +E-BIKE Lankeleisi; 60; 19100; false; 28000; flame; 2989 +FOLDING BIKE Kross; 20; 7; 15800; false; red; 439 +FOLDING BIKE Make Bike; 14; 8; 10000; true; olive; 1149 +SPEEDELEC Tesla; 35; 20300; true; 7700; flame; 135 +FOLDING BIKE Intertool; 24; 27; 9400; false; violet; 1585 +SPEEDELEC ITrike; 10; 20900; true; 5700; golden; 1475 +SPEEDELEC AirWheel; 20; 14300; false; 4000; yellow; 1635 +E-BIKE Scott; 65; 24400; false; 15000; brown; 1215 +E-BIKE Koga; 35; 28100; true; 25000; silver; 2125 +SPEEDELEC Booster; 15; 19000; false; 6600; green; 359 +E-BIKE ElectrO; 20; 22500; false; 31000; rose; 2029 +FOLDING BIKE Comanche; 20; 9; 11100; true; beige; 1639 +E-BIKE Mando Footloose; 40; 24200; false; 16000; golden; 2255 +SPEEDELEC Booster; 55; 19200; true; 2900; pink; 549 +SPEEDELEC OIO; 20; 7600; true; 13500; violet; 849 +SPEEDELEC Ultron; 20; 12900; false; 12300; grenadine; 425 +SPEEDELEC Xiaomi; 10; 9700; true; 8400; silver; 795 +FOLDING BIKE Titan; 24; 9; 15000; true; lemon; 1525 +SPEEDELEC Xiaomi; 10; 20800; true; 10400; brown; 505 +E-BIKE Zaxboard; 60; 28500; false; 27000; silver; 1319 +SPEEDELEC Speedelec Minirider; 35; 9000; true; 4200; black; 1479 +E-BIKE Haibike; 55; 27400; true; 12000; rose; 1955 +FOLDING BIKE BMW; 20; 27; 13200; false; golden; 1289 +E-BIKE Mando Footloose; 35; 24300; false; 26000; yellow; 1369 +E-BIKE ElectrO; 60; 25700; false; 19000; blue; 1725 +E-BIKE Lankeleisi; 35; 27800; true; 26000; coral; 3175 +E-BIKE Felt; 60; 27500; false; 29000; blue; 2825 +E-BIKE Mercedes; 25; 19400; true; 23000; blue; 1719 +SPEEDELEC AirWheel; 65; 12200; false; 13400; rose; 609 +SPEEDELEC Segway; 45; 6800; false; 3700; flame; 259 +SPEEDELEC Aest; 35; 18100; true; 5000; violet; 1049 +E-BIKE ION; 60; 27200; false; 28000; emerald; 1939 +E-BIKE ElectrO; 40; 23600; false; 11000; orange; 2445 +SPEEDELEC Speedelec Minirider; 65; 14500; true; 13600; marine; 1279 +SPEEDELEC Segway; 45; 20700; false; 10700; yellow; 689 +E-BIKE Zaxboard; 35; 24600; true; 17000; khaki; 2015 +E-BIKE Gazelle; 65; 25500; true; 28000; emerald; 3049 +E-BIKE Farad; 60; 21200; false; 16000; green; 1949 +FOLDING BIKE Stern; 20; 8; 15500; false; dark gray; 519 +FOLDING BIKE Flex; 24; 21; 9700; false; dark gray; 1539 +E-BIKE ElectrO; 65; 27100; false; 15000; green; 2615 +E-BIKE Gazelle; 60; 26600; true; 12000; emerald; 2985 +SPEEDELEC EcoRide; 30; 19900; false; 2100; olive; 369 +SPEEDELEC E-Scooter; 60; 14900; true; 2900; white; 129 +E-BIKE Farad; 50; 25900; true; 23000; emerald; 2039 +SPEEDELEC ITrike; 55; 14100; false; 14100; marine; 1459 +FOLDING BIKE Flex; 26; 3; 13700; true; olive; 1129 +FOLDING BIKE VNV; 26; 6; 10800; true; red; 1435 +SPEEDELEC Smart; 30; 19300; false; 12700; marine; 1259 +SPEEDELEC Ultron; 55; 19600; true; 9900; olive; 1515 +FOLDING BIKE Flex; 24; 8; 12600; true; emerald; 825 +E-BIKE Scott; 40; 21600; true; 25000; brown; 1049 +E-BIKE Koga; 20; 22200; false; 27000; pink; 839 +SPEEDELEC Maraton; 45; 9200; false; 7400; marine; 1369 +SPEEDELEC AirWheel; 35; 7100; true; 13600; green; 399 +FOLDING BIKE Stern; 26; 27; 8200; true; olive; 1325 +SPEEDELEC Dualtron; 25; 7000; true; 5100; coral; 1595 +SPEEDELEC Peugeot; 15; 11900; false; 11600; rose; 1589 +E-BIKE Koga; 60; 20400; true; 28000; white; 2829 +SPEEDELEC E-Scooter; 60; 9600; false; 2700; rose; 1415 +SPEEDELEC E-Scooter; 40; 19800; false; 3000; grenadine; 709 +SPEEDELEC EcoRide; 10; 18400; true; 13200; black; 335 +SPEEDELEC Aest; 40; 15100; true; 7700; olive; 159 +SPEEDELEC OIO; 10; 12100; true; 9200; blue; 1169 +SPEEDELEC Segway; 50; 14300; true; 12700; coral; 609 +SPEEDELEC Tesla; 20; 15200; true; 9700; blue; 1015 +FOLDING BIKE BMW; 26; 27; 10800; false; grenadine; 745 +SPEEDELEC Booster; 30; 18500; true; 12700; coral; 885 +FOLDING BIKE Comanche; 20; 21; 11400; false; orange; 985 +FOLDING BIKE Titan; 26; 21; 11200; true; blue; 785 +SPEEDELEC Aest; 40; 14100; true; 6000; silver; 409 +SPEEDELEC Tesla; 35; 16200; true; 6500; coral; 439 +FOLDING BIKE Make Bike; 20; 3; 10300; true; silver; 955 +SPEEDELEC Xiaomi; 55; 6500; false; 3700; blue; 595 +SPEEDELEC Xiaomi; 45; 20900; true; 14900; black; 655 +FOLDING BIKE Brompton; 24; 24; 14500; true; dark gray; 1145 +E-BIKE Zaxboard; 60; 26300; true; 28000; yellow; 1675 +E-BIKE ElectrO; 40; 23400; false; 25000; flame; 1329 +SPEEDELEC Peugeot; 45; 17200; false; 9100; white; 1599 +SPEEDELEC Peugeot; 40; 14800; true; 13200; rose; 339 +E-BIKE Felt; 65; 22000; true; 11000; rose; 1235 +FOLDING BIKE Crosser; 24; 1; 16100; true; orange; 265 +E-BIKE Rover; 60; 27100; true; 20000; beige; 1399 +SPEEDELEC Booster; 50; 11900; false; 5700; pink; 1565 +SPEEDELEC OIO; 45; 10400; true; 11300; golden; 355 +E-BIKE Gazelle; 50; 26900; true; 15000; green; 2359 +E-BIKE Ecofect; 25; 23100; true; 31000; marine; 3419 +SPEEDELEC Speedelec Minirider; 65; 20000; true; 8100; black; 1409 +SPEEDELEC Speedelec Minirider; 55; 12200; true; 12200; rose; 739 +SPEEDELEC Peugeot; 60; 19300; true; 11300; beige; 185 +E-BIKE Farad; 50; 22400; false; 14000; red; 1539 +SPEEDELEC Freego; 65; 18600; false; 14600; white; 209 +SPEEDELEC Aest; 50; 15600; false; 7600; yellow; 379 +FOLDING BIKE Comanche; 20; 7; 15600; false; pink; 459 +E-BIKE Scott; 35; 25300; false; 16000; olive; 3019 +E-BIKE Mercedes; 60; 24300; true; 14000; silver; 1505 +SPEEDELEC AirWheel; 45; 9200; true; 15100; emerald; 815 +FOLDING BIKE VNV; 24; 1; 14400; true; white; 235 +SPEEDELEC Speedelec Minirider; 30; 17600; true; 7900; coral; 1195 +SPEEDELEC Speedelec Minirider; 65; 12300; true; 6200; dark gray; 1399 +E-BIKE Rover; 30; 19000; true; 22000; grey; 3369 +SPEEDELEC ITrike; 45; 13000; true; 8100; grenadine; 1185 +E-BIKE Farad; 20; 20400; true; 20000; beige; 2509 +FOLDING BIKE Trinx; 24; 6; 8200; false; orange; 1559 +SPEEDELEC E-Scooter; 50; 12700; false; 4500; black; 299 +FOLDING BIKE Dahon; 14; 21; 13500; false; coral; 475 +FOLDING BIKE Formula; 20; 24; 16600; true; grenadine; 1585 +FOLDING BIKE Dahon; 20; 9; 13900; true; silver; 1685 +SPEEDELEC EcoRide; 20; 12100; true; 5400; golden; 1199 +FOLDING BIKE Trinx; 26; 24; 14500; true; green; 905 +FOLDING BIKE Intertool; 26; 9; 13400; false; emerald; 435 +E-BIKE Rover; 45; 25700; false; 23000; orange; 3069 +FOLDING BIKE Stern; 16; 1; 13500; true; white; 899 +E-BIKE Rover; 20; 19000; true; 11000; khaki; 1009 +FOLDING BIKE Brompton; 24; 3; 10400; true; olive; 1315 +FOLDING BIKE Crosser; 20; 1; 14700; true; silver; 375 +FOLDING BIKE Dahon; 24; 8; 13100; true; silver; 229 +SPEEDELEC Speedelec Minirider; 10; 7800; false; 8900; grey; 869 +SPEEDELEC AirWheel; 15; 12500; true; 13400; blue; 1675 +E-BIKE Mando Footloose; 30; 20700; true; 23000; dark gray; 2849 +SPEEDELEC Kugoo; 10; 15800; false; 14100; orange; 1575 +E-BIKE ION; 35; 28400; true; 31000; yellow; 3245 +SPEEDELEC Tesla; 65; 12600; true; 6400; brown; 199 +FOLDING BIKE West Bike; 26; 18; 16400; true; green; 335 +FOLDING BIKE West Bike; 20; 18; 11200; false; red; 419 +SPEEDELEC Maraton; 25; 21100; true; 12300; lemon; 909 +FOLDING BIKE Make Bike; 18; 1; 12200; false; lemon; 1305 +FOLDING BIKE West Bike; 18; 21; 15100; false; coral; 1065 +SPEEDELEC E-Scooter; 45; 13500; true; 6100; white; 659 +SPEEDELEC Maraton; 50; 21400; true; 11300; green; 1105 +SPEEDELEC Speedway; 50; 7200; false; 11800; olive; 1325 +SPEEDELEC Smart; 35; 20000; false; 11700; golden; 245 +E-BIKE Mercedes; 55; 25800; true; 27000; olive; 825 +FOLDING BIKE Formula; 20; 8; 16000; false; green; 669 +SPEEDELEC Tesla; 65; 18900; false; 13900; olive; 229 +FOLDING BIKE SkyBike; 26; 9; 8700; false; grey; 1179 +SPEEDELEC Kugoo; 35; 14400; false; 7800; golden; 745 +FOLDING BIKE Titan; 20; 24; 11800; false; flame; 1499 +FOLDING BIKE Benetti; 14; 7; 13400; true; coral; 305 +E-BIKE GoCycle; 35; 27900; false; 10000; brown; 2685 +FOLDING BIKE Trinx; 20; 21; 12300; false; rose; 1425 +E-BIKE E-Motion; 50; 23300; true; 19000; dark gray; 3419 +SPEEDELEC AirWheel; 50; 8400; true; 7900; emerald; 1599 +SPEEDELEC E-Scooter; 10; 15800; false; 3100; yellow; 1375 +E-BIKE Farad; 30; 28700; true; 14000; yellow; 2245 +SPEEDELEC OIO; 45; 10300; true; 13400; pink; 959 +E-BIKE Mando Footloose; 35; 23000; true; 19000; green; 2495 +SPEEDELEC Xiaomi; 40; 6500; false; 9000; silver; 375 +FOLDING BIKE Author; 24; 7; 11900; true; red; 685 +E-BIKE Farad; 45; 21500; true; 12000; silver; 1385 +SPEEDELEC Maraton; 30; 14800; false; 12800; white; 889 +SPEEDELEC Booster; 50; 17900; true; 3100; lemon; 1399 +FOLDING BIKE West Bike; 14; 21; 8300; false; yellow; 189 +E-BIKE Zaxboard; 30; 20300; false; 29000; pink; 2205 +FOLDING BIKE Stern; 20; 1; 8200; false; orange; 615 +SPEEDELEC Speedelec Minirider; 55; 15200; false; 5600; green; 899 +FOLDING BIKE Flex; 24; 8; 9300; true; orange; 779 +E-BIKE Scott; 20; 20900; false; 23000; dark gray; 1465 +SPEEDELEC Xiaomi; 60; 13800; false; 12700; brown; 1475 +FOLDING BIKE Polygon; 24; 6; 15900; true; violet; 405 +E-BIKE ION; 20; 26900; false; 21000; golden; 1359 +E-BIKE Mando Footloose; 40; 27600; false; 16000; flame; 2105 +FOLDING BIKE Flex; 26; 3; 12000; false; pink; 835 +SPEEDELEC AirWheel; 45; 13500; false; 4600; yellow; 1039 +FOLDING BIKE West Bike; 24; 1; 10800; true; red; 525 +SPEEDELEC Speedelec Minirider; 30; 19700; false; 3100; grey; 1649 +FOLDING BIKE Make Bike; 26; 1; 10300; true; lemon; 765 +FOLDING BIKE Make Bike; 14; 24; 13100; false; red; 625 +SPEEDELEC Booster; 10; 7300; false; 7600; flame; 239 +FOLDING BIKE Titan; 24; 7; 16900; true; green; 1339 +SPEEDELEC Kugoo; 15; 10200; true; 12200; khaki; 815 +E-BIKE Mercedes; 45; 24200; false; 28000; beige; 2399 +SPEEDELEC Speedway; 25; 21900; false; 15000; rose; 559 +E-BIKE Porshe Design; 60; 26500; true; 25000; dark gray; 3479 +SPEEDELEC OIO; 30; 18400; false; 10800; golden; 1519 +SPEEDELEC Peugeot; 35; 13900; false; 3100; marine; 1355 +E-BIKE Xiaomi; 65; 28900; true; 14000; golden; 1535 +FOLDING BIKE Intertool; 24; 3; 11500; false; pink; 1579 +E-BIKE ION; 60; 28900; false; 31000; emerald; 3099 +FOLDING BIKE Cayman; 24; 6; 11600; false; orange; 1315 +E-BIKE Felt; 65; 21900; false; 26000; white; 3359 +FOLDING BIKE Stern; 14; 24; 9800; true; white; 465 +SPEEDELEC Booster; 25; 13500; false; 8900; black; 869 +FOLDING BIKE Titan; 20; 9; 13800; false; violet; 1239 +SPEEDELEC E-Scooter; 10; 8600; false; 10200; black; 605 +FOLDING BIKE Flex; 26; 1; 13400; true; white; 595 +SPEEDELEC Segway; 50; 17900; true; 5900; brown; 659 +SPEEDELEC Aest; 45; 6900; false; 5500; brown; 1635 +SPEEDELEC Booster; 40; 9900; true; 2400; black; 1635 +FOLDING BIKE Benetti; 24; 9; 15100; false; dark gray; 519 +SPEEDELEC Xiaomi; 20; 11700; false; 4700; white; 309 +FOLDING BIKE VNV; 20; 9; 9700; false; grenadine; 1325 +E-BIKE ION; 20; 22300; true; 28000; golden; 1179 +SPEEDELEC Segway; 35; 14800; true; 3400; green; 319 +SPEEDELEC Speedelec Minirider; 65; 7800; true; 9700; rose; 665 +E-BIKE Porshe Design; 60; 28300; true; 30000; white; 2869 +E-BIKE Porshe Design; 40; 20800; true; 18000; green; 1119 +FOLDING BIKE Kross; 18; 21; 10200; false; blue; 1269 +E-BIKE Mercedes; 65; 23800; true; 19000; blue; 3259 +SPEEDELEC Speedelec Minirider; 50; 12600; false; 13700; grenadine; 739 +E-BIKE Zaxboard; 55; 23300; false; 21000; coral; 1705 +SPEEDELEC Maraton; 60; 13700; false; 3000; beige; 625 +FOLDING BIKE Titan; 18; 9; 15000; true; blue; 385 +SPEEDELEC Speedelec Minirider; 35; 7000; false; 10500; khaki; 265 +E-BIKE Scott; 45; 25900; false; 21000; pink; 1759 +SPEEDELEC AirWheel; 60; 16900; false; 14100; blue; 295 +SPEEDELEC Freego; 25; 9900; false; 15100; marine; 765 +E-BIKE Rover; 25; 25000; false; 8000; beige; 829 +FOLDING BIKE Crosser; 20; 1; 13600; true; red; 469 +SPEEDELEC ITrike; 65; 9900; false; 9000; rose; 205 +SPEEDELEC OIO; 60; 14100; false; 9100; silver; 415 +SPEEDELEC Ultron; 45; 21900; true; 2300; marine; 1459 +SPEEDELEC Kugoo; 40; 20100; true; 8100; grenadine; 425 +FOLDING BIKE Dahon; 16; 18; 11400; true; beige; 709 +SPEEDELEC ITrike; 50; 17200; true; 5200; violet; 1355 +E-BIKE Porshe Design; 20; 27700; false; 20000; grenadine; 1309 +E-BIKE Koga; 30; 21200; false; 25000; beige; 1019 +SPEEDELEC Booster; 10; 21400; false; 9700; brown; 1019 +FOLDING BIKE Trinx; 24; 3; 13900; true; beige; 1115 +SPEEDELEC OIO; 40; 9900; true; 6100; violet; 535 +FOLDING BIKE Stern; 24; 3; 13400; true; beige; 1209 +E-BIKE ElectrO; 20; 19800; false; 21000; beige; 1875 +FOLDING BIKE Comanche; 26; 1; 9900; true; grey; 199 +E-BIKE Haibike; 25; 24300; true; 25000; beige; 1975 +FOLDING BIKE Polygon; 20; 8; 14100; true; grey; 309 +SPEEDELEC Tesla; 50; 20000; false; 2900; khaki; 325 +SPEEDELEC E-Scooter; 60; 8300; false; 13100; silver; 345 +SPEEDELEC Xiaomi; 60; 17000; true; 12300; violet; 1209 +E-BIKE Gazelle; 55; 25300; true; 13000; beige; 1569 +FOLDING BIKE Author; 26; 24; 10200; true; black; 1599 +E-BIKE Haibike; 40; 25100; false; 13000; beige; 2445 +E-BIKE Rover; 25; 23300; true; 26000; olive; 679 +E-BIKE SkyBike; 45; 27900; false; 30000; violet; 2125 +SPEEDELEC Aest; 20; 11700; true; 12800; emerald; 365 +SPEEDELEC ITrike; 60; 16300; false; 3800; white; 1549 +FOLDING BIKE Comanche; 20; 1; 11600; true; pink; 279 +E-BIKE Zaxboard; 25; 23500; false; 26000; beige; 2785 +FOLDING BIKE Crosser; 16; 7; 12200; false; pink; 695 +E-BIKE Felt; 55; 22700; true; 29000; white; 2835 +FOLDING BIKE Kross; 24; 6; 11600; false; yellow; 759 +SPEEDELEC AirWheel; 45; 7000; true; 13000; golden; 299 +SPEEDELEC ITrike; 55; 13300; true; 10300; khaki; 1019 +FOLDING BIKE Kross; 20; 8; 11500; false; green; 345 +SPEEDELEC EcoRide; 45; 10900; true; 13600; pink; 355 +FOLDING BIKE Kross; 20; 8; 14800; false; yellow; 1675 +FOLDING BIKE Titan; 26; 27; 16900; false; grey; 1299 +SPEEDELEC Maraton; 40; 18900; false; 6800; pink; 869 +SPEEDELEC OIO; 20; 13800; true; 5600; emerald; 135 +FOLDING BIKE Polygon; 20; 1; 10700; true; emerald; 1355 +E-BIKE Mercedes; 50; 23600; true; 15000; emerald; 1505 +E-BIKE Ecofect; 45; 26900; true; 14000; dark gray; 1575 +SPEEDELEC Tesla; 35; 18700; true; 2000; beige; 489 +E-BIKE Felt; 50; 28900; true; 25000; olive; 2599 +FOLDING BIKE Formula; 18; 9; 10100; false; coral; 1625 +FOLDING BIKE Formula; 24; 7; 9600; true; grey; 385 +E-BIKE Felt; 45; 20900; false; 18000; grenadine; 1765 +E-BIKE Haibike; 40; 23800; false; 30000; pink; 1455 +SPEEDELEC Maraton; 30; 18700; false; 2200; golden; 415 +E-BIKE Farad; 35; 21900; false; 11000; coral; 3419 +FOLDING BIKE Make Bike; 20; 24; 14000; false; silver; 1219 +SPEEDELEC AirWheel; 20; 14500; true; 11600; orange; 499 +FOLDING BIKE Dahon; 20; 27; 15900; true; rose; 1589 +E-BIKE Felt; 25; 25200; false; 30000; white; 669 +SPEEDELEC Speedway; 35; 19200; true; 10400; brown; 1019 +FOLDING BIKE SkyBike; 24; 21; 11900; true; pink; 1029 +SPEEDELEC AirWheel; 30; 19600; false; 11500; grenadine; 1555 +FOLDING BIKE Trinx; 18; 8; 13500; true; white; 1465 +SPEEDELEC Freego; 45; 20700; false; 12000; silver; 1445 +FOLDING BIKE West Bike; 16; 21; 14000; true; rose; 1305 +E-BIKE GoCycle; 45; 22700; false; 23000; grey; 2429 +SPEEDELEC Aest; 50; 19500; false; 6300; red; 1385 +FOLDING BIKE Cayman; 20; 1; 12700; true; flame; 1299 +FOLDING BIKE Trinx; 26; 7; 13400; true; grenadine; 1675 +E-BIKE ION; 25; 22600; true; 22000; grenadine; 2995 +SPEEDELEC OIO; 60; 21400; false; 6700; golden; 1455 +FOLDING BIKE Titan; 26; 3; 13500; false; pink; 1685 +SPEEDELEC Dualtron; 35; 18600; true; 4700; grey; 489 +FOLDING BIKE Cayman; 20; 9; 14000; true; silver; 1499 +E-BIKE Mando Footloose; 55; 23200; true; 29000; golden; 2215 +FOLDING BIKE Crosser; 20; 3; 9700; true; brown; 1105 +SPEEDELEC E-Scooter; 25; 16400; false; 7800; rose; 235 +SPEEDELEC E-Scooter; 20; 13500; false; 7500; flame; 435 +SPEEDELEC Speedelec Minirider; 30; 8200; true; 15000; grenadine; 775 +E-BIKE SkyBike; 50; 25800; true; 11000; beige; 3265 +E-BIKE Ecofect; 35; 24500; true; 19000; grenadine; 2865 +FOLDING BIKE Kross; 20; 18; 13300; true; golden; 1445 +SPEEDELEC Speedway; 15; 21900; false; 6600; marine; 1475 +FOLDING BIKE Polygon; 20; 6; 9400; true; black; 975 +FOLDING BIKE Crosser; 24; 18; 9700; true; black; 365 +SPEEDELEC Ultron; 45; 16900; false; 13500; emerald; 1625 +SPEEDELEC Tesla; 20; 9100; true; 3900; violet; 1599 +FOLDING BIKE Author; 18; 24; 13900; false; brown; 1229 +SPEEDELEC Maraton; 50; 14600; true; 8900; emerald; 1005 +SPEEDELEC Speedway; 20; 6500; false; 4800; orange; 365 +E-BIKE Koga; 55; 26000; true; 11000; beige; 905 +FOLDING BIKE Cayman; 18; 3; 12700; true; marine; 1089 +FOLDING BIKE Polygon; 16; 3; 10200; true; coral; 845 +E-BIKE ElectrO; 50; 20300; false; 29000; grey; 1839 +FOLDING BIKE Crosser; 20; 1; 11400; true; grey; 1459 +FOLDING BIKE Brompton; 18; 9; 11900; false; yellow; 845 +E-BIKE Ecofect; 25; 23900; true; 18000; blue; 925 +SPEEDELEC Ultron; 65; 8200; true; 3700; coral; 435 +FOLDING BIKE Stern; 26; 27; 10000; true; green; 355 +FOLDING BIKE BMW; 16; 8; 16800; false; grey; 1165 +SPEEDELEC Tesla; 25; 7800; false; 9500; beige; 1365 +SPEEDELEC Aest; 45; 13600; false; 4100; khaki; 519 +E-BIKE Zaxboard; 50; 22100; true; 20000; beige; 1609 +E-BIKE GoCycle; 35; 19100; false; 20000; golden; 3329 +E-BIKE Felt; 30; 21500; false; 27000; lemon; 1265 +SPEEDELEC Dualtron; 30; 13900; false; 12800; khaki; 1595 +FOLDING BIKE Comanche; 20; 27; 14500; false; silver; 1655 +E-BIKE Gazelle; 50; 21400; true; 30000; green; 1889 +SPEEDELEC Xiaomi; 15; 9000; true; 5800; blue; 1255 +E-BIKE ION; 30; 19100; false; 8000; yellow; 1705 +SPEEDELEC E-Scooter; 15; 21000; false; 6600; beige; 1129 +E-BIKE Gazelle; 25; 25300; false; 19000; silver; 1029 +FOLDING BIKE Brompton; 16; 27; 8300; false; grey; 195 +E-BIKE Porshe Design; 60; 28300; true; 27000; grey; 1005 +SPEEDELEC AirWheel; 10; 7000; false; 2600; blue; 1145 +SPEEDELEC Booster; 15; 6500; false; 3200; green; 699 +E-BIKE SkyBike; 35; 26100; false; 17000; yellow; 3139 +FOLDING BIKE Stern; 14; 8; 9600; true; violet; 1539 +FOLDING BIKE Formula; 26; 9; 9300; false; orange; 915 +SPEEDELEC E-Scooter; 15; 11900; true; 14500; silver; 439 +FOLDING BIKE Kross; 20; 24; 8500; false; brown; 1545 +E-BIKE ElectrO; 45; 24100; false; 23000; silver; 919 +SPEEDELEC Freego; 25; 13900; true; 3800; marine; 849 +E-BIKE Koga; 45; 23400; false; 14000; olive; 3189 +SPEEDELEC Kugoo; 65; 20700; false; 5400; emerald; 1235 +E-BIKE Lankeleisi; 40; 26700; false; 28000; blue; 2865 +E-BIKE ION; 60; 23100; false; 10000; green; 2005 +FOLDING BIKE Flex; 16; 8; 14900; true; violet; 365 +E-BIKE Koga; 45; 27500; true; 11000; golden; 3385 +FOLDING BIKE Stern; 18; 24; 9000; true; emerald; 495 +E-BIKE Koga; 25; 27400; false; 20000; orange; 3345 +E-BIKE Haibike; 55; 26300; false; 18000; red; 1805 +SPEEDELEC Peugeot; 35; 10300; false; 9200; blue; 225 +FOLDING BIKE VNV; 14; 3; 12400; true; khaki; 1169 +SPEEDELEC Ultron; 55; 19800; true; 14400; emerald; 1479 +FOLDING BIKE Author; 18; 9; 11600; false; coral; 1605 +SPEEDELEC Speedelec Minirider; 35; 21900; false; 13400; dark gray; 859 +E-BIKE Scott; 55; 24400; false; 13000; golden; 2719 +FOLDING BIKE Titan; 20; 1; 11800; true; rose; 225 +FOLDING BIKE Comanche; 24; 8; 12800; true; white; 789 +SPEEDELEC Aest; 30; 11600; true; 8500; coral; 805 +E-BIKE GoCycle; 40; 20300; true; 25000; orange; 3269 +E-BIKE Xiaomi; 25; 27600; true; 21000; green; 2229 +FOLDING BIKE Crosser; 14; 27; 15100; true; silver; 1659 +FOLDING BIKE Cayman; 24; 27; 14000; false; rose; 665 +FOLDING BIKE Author; 20; 7; 8800; true; beige; 449 +SPEEDELEC EcoRide; 60; 19400; true; 5100; khaki; 715 +E-BIKE Xiaomi; 45; 27400; true; 16000; silver; 1239 +SPEEDELEC Peugeot; 60; 21100; true; 15800; lemon; 199 +SPEEDELEC Smart; 35; 20300; false; 14500; khaki; 885 +FOLDING BIKE Dahon; 14; 27; 9700; true; white; 1215 +SPEEDELEC Ultron; 60; 10400; false; 7000; emerald; 489 +FOLDING BIKE VNV; 20; 3; 10400; true; silver; 445 +SPEEDELEC EcoRide; 20; 21500; true; 11900; marine; 1375 +FOLDING BIKE VNV; 26; 8; 14900; false; marine; 825 +FOLDING BIKE VNV; 20; 18; 8500; false; green; 339 +SPEEDELEC Tesla; 10; 13400; false; 11300; orange; 1115 +FOLDING BIKE Flex; 26; 9; 12900; true; marine; 349 +FOLDING BIKE Author; 24; 18; 15300; true; red; 1079 +SPEEDELEC Aest; 65; 14200; true; 14200; grey; 1479 +SPEEDELEC Booster; 55; 11700; false; 6000; marine; 1299 +FOLDING BIKE Titan; 20; 8; 10200; false; pink; 185 +SPEEDELEC Tesla; 50; 19500; false; 6000; emerald; 1395 +FOLDING BIKE Make Bike; 20; 7; 8800; true; khaki; 799 +SPEEDELEC Xiaomi; 35; 21600; true; 8300; black; 1565 +FOLDING BIKE Titan; 24; 18; 12200; false; grenadine; 1509 +SPEEDELEC Freego; 45; 10400; false; 2900; green; 985 +E-BIKE Farad; 30; 19200; false; 10000; brown; 1955 +SPEEDELEC Kugoo; 60; 17700; false; 9900; coral; 1175 +FOLDING BIKE Titan; 20; 7; 10300; false; yellow; 1425 +FOLDING BIKE Polygon; 20; 3; 9000; false; coral; 795 +SPEEDELEC Booster; 50; 19600; false; 9100; olive; 779 +SPEEDELEC EcoRide; 60; 20000; true; 2000; green; 699 +SPEEDELEC Kugoo; 10; 14300; true; 3300; olive; 1515 +E-BIKE Rover; 45; 24900; true; 12000; brown; 2665 +E-BIKE Gazelle; 60; 19600; false; 12000; yellow; 835 +SPEEDELEC Booster; 30; 8300; false; 4000; orange; 835 +FOLDING BIKE SkyBike; 24; 21; 15700; false; grenadine; 295 +FOLDING BIKE Polygon; 26; 9; 12800; false; yellow; 1575 +SPEEDELEC Segway; 30; 20600; true; 9000; dark gray; 429 +E-BIKE Zaxboard; 45; 20300; false; 25000; green; 3059 +SPEEDELEC Xiaomi; 65; 20900; true; 3400; beige; 1139 +SPEEDELEC Dualtron; 15; 10600; true; 3400; white; 439 +FOLDING BIKE Author; 24; 7; 11000; true; grenadine; 249 +E-BIKE Rover; 40; 19300; true; 26000; blue; 2295 +E-BIKE Farad; 60; 28300; true; 12000; khaki; 2345 +FOLDING BIKE Polygon; 24; 1; 9100; false; flame; 449 +SPEEDELEC Speedelec Minirider; 45; 10000; false; 6600; white; 1599 +SPEEDELEC ITrike; 45; 13200; false; 2100; green; 715 +SPEEDELEC Tesla; 40; 13900; true; 14400; coral; 725 +E-BIKE Scott; 25; 20200; true; 24000; khaki; 2895 +FOLDING BIKE Benetti; 26; 6; 15000; true; silver; 1315 +SPEEDELEC Booster; 55; 7600; false; 8300; olive; 1339 +SPEEDELEC Segway; 10; 10300; true; 6700; golden; 1055 +FOLDING BIKE Comanche; 24; 18; 8400; true; golden; 599 +SPEEDELEC Maraton; 65; 21700; false; 8700; emerald; 1445 +FOLDING BIKE Comanche; 20; 6; 15100; true; pink; 1259 +FOLDING BIKE VNV; 20; 1; 10700; true; white; 1275 +FOLDING BIKE Titan; 14; 9; 10400; true; golden; 1375 +FOLDING BIKE Trinx; 24; 8; 16100; false; dark gray; 505 +E-BIKE Scott; 40; 19100; false; 27000; marine; 969 +E-BIKE Porshe Design; 45; 28900; true; 29000; khaki; 1399 +SPEEDELEC Xiaomi; 15; 6600; true; 15900; yellow; 1515 +SPEEDELEC Freego; 35; 20100; false; 2100; olive; 295 +FOLDING BIKE Trinx; 20; 6; 11000; true; blue; 1209 +SPEEDELEC EcoRide; 60; 19100; true; 2800; olive; 879 +E-BIKE ION; 50; 25600; false; 24000; blue; 1835 +E-BIKE SkyBike; 20; 23700; true; 22000; golden; 2645 +SPEEDELEC Maraton; 60; 7800; false; 14900; flame; 1239 +FOLDING BIKE Trinx; 14; 6; 10400; true; dark gray; 1535 +SPEEDELEC Xiaomi; 45; 21100; true; 2200; white; 625 +E-BIKE Zaxboard; 65; 25900; true; 9000; rose; 2945 +E-BIKE SkyBike; 45; 22100; false; 13000; silver; 1039 +FOLDING BIKE Flex; 18; 27; 12400; true; yellow; 829 +SPEEDELEC Kugoo; 20; 9000; false; 9900; black; 1225 +E-BIKE GoCycle; 60; 23700; true; 18000; orange; 1219 +E-BIKE ElectrO; 65; 20500; true; 13000; yellow; 1849 +SPEEDELEC Speedelec Minirider; 65; 14100; false; 8000; pink; 1375 +E-BIKE Farad; 45; 26200; true; 14000; black; 2875 +FOLDING BIKE Comanche; 16; 7; 10700; false; pink; 359 +FOLDING BIKE Cayman; 18; 8; 13200; false; pink; 715 +FOLDING BIKE SkyBike; 20; 3; 12900; false; grenadine; 775 +E-BIKE Ecofect; 40; 23900; false; 14000; olive; 2839 +SPEEDELEC Xiaomi; 10; 17400; false; 3000; black; 295 +FOLDING BIKE Brompton; 20; 18; 9700; false; golden; 815 +SPEEDELEC Speedelec Minirider; 30; 14400; false; 7800; rose; 1445 +FOLDING BIKE West Bike; 24; 6; 15200; false; grey; 1119 +FOLDING BIKE BMW; 16; 6; 12500; true; marine; 1559 +E-BIKE Farad; 35; 25900; false; 24000; lemon; 2679 +SPEEDELEC EcoRide; 15; 12300; false; 6200; rose; 1565 +SPEEDELEC AirWheel; 20; 6500; true; 2100; flame; 199 +SPEEDELEC AirWheel; 20; 13900; true; 14700; golden; 549 +FOLDING BIKE Trinx; 20; 1; 10700; true; green; 1599 +E-BIKE Gazelle; 65; 20500; false; 21000; black; 1175 +FOLDING BIKE Dahon; 14; 3; 8100; true; marine; 349 +SPEEDELEC Kugoo; 60; 9000; false; 6400; orange; 1639 +E-BIKE Scott; 60; 19000; false; 23000; beige; 1475 +SPEEDELEC Tesla; 65; 18500; true; 9200; green; 319 +FOLDING BIKE BMW; 20; 1; 16800; true; grenadine; 625 +FOLDING BIKE Titan; 24; 24; 10800; false; lemon; 1485 +E-BIKE Rover; 50; 22600; true; 9000; black; 2369 +E-BIKE Haibike; 45; 27600; false; 17000; flame; 809 +E-BIKE SkyBike; 20; 25600; true; 22000; white; 3329 +E-BIKE E-Motion; 35; 21300; false; 29000; violet; 1799 +FOLDING BIKE Make Bike; 16; 18; 9200; true; brown; 385 +FOLDING BIKE Trinx; 14; 3; 9100; false; coral; 679 +SPEEDELEC ITrike; 25; 14500; true; 2800; white; 915 +FOLDING BIKE Make Bike; 20; 7; 12300; false; orange; 959 +FOLDING BIKE Trinx; 14; 18; 12100; false; black; 435 +E-BIKE Haibike; 45; 24200; false; 16000; khaki; 2765 +SPEEDELEC Speedway; 25; 13800; false; 8000; silver; 1555 +FOLDING BIKE Dahon; 18; 27; 11800; false; brown; 839 +FOLDING BIKE Cayman; 20; 21; 15100; true; pink; 1479 +E-BIKE ION; 40; 28200; false; 11000; brown; 1695 +FOLDING BIKE Intertool; 20; 7; 11200; true; coral; 1129 +SPEEDELEC Peugeot; 25; 19000; true; 9100; brown; 975 +E-BIKE Rover; 30; 19200; false; 21000; rose; 2065 +FOLDING BIKE Cayman; 26; 6; 11800; true; black; 1259 +FOLDING BIKE Make Bike; 16; 7; 9800; false; green; 395 +SPEEDELEC AirWheel; 30; 6900; false; 14400; marine; 1669 +E-BIKE Mando Footloose; 25; 20300; false; 14000; white; 2565 +E-BIKE ElectrO; 30; 20600; false; 31000; red; 2899 +SPEEDELEC Xiaomi; 45; 19900; true; 12800; silver; 575 +FOLDING BIKE Brompton; 20; 7; 15300; false; khaki; 299 +E-BIKE Lankeleisi; 65; 25900; true; 15000; flame; 1315 +E-BIKE Gazelle; 20; 25600; false; 8000; marine; 1405 +FOLDING BIKE Comanche; 16; 1; 15600; true; dark gray; 619 +FOLDING BIKE Polygon; 20; 3; 16600; true; marine; 1419 +E-BIKE GoCycle; 35; 25700; true; 24000; brown; 2605 +SPEEDELEC Peugeot; 20; 14300; false; 7800; violet; 925 +SPEEDELEC Aest; 20; 19900; false; 2600; emerald; 1279 +E-BIKE ION; 35; 25100; true; 12000; marine; 1059 +FOLDING BIKE Benetti; 24; 1; 16100; true; violet; 329 +FOLDING BIKE Make Bike; 20; 24; 8400; true; marine; 1365 +E-BIKE Scott; 45; 28100; true; 15000; rose; 2365 +SPEEDELEC OIO; 25; 16400; true; 7700; khaki; 1469 +E-BIKE Ecofect; 35; 24400; false; 28000; brown; 1789 +E-BIKE Felt; 55; 24200; false; 15000; yellow; 965 +FOLDING BIKE Intertool; 24; 24; 9100; false; orange; 629 +FOLDING BIKE Author; 18; 8; 9000; false; grey; 925 +E-BIKE Scott; 65; 20400; false; 24000; orange; 3209 +E-BIKE Ecofect; 40; 21500; false; 26000; black; 2185 +FOLDING BIKE Cayman; 20; 1; 13600; true; golden; 1045 +SPEEDELEC Tesla; 65; 8300; false; 5100; grey; 475 +FOLDING BIKE Cayman; 26; 7; 13100; false; emerald; 269 +FOLDING BIKE Formula; 26; 6; 10400; false; flame; 825 +FOLDING BIKE Dahon; 26; 6; 16800; false; yellow; 229 +FOLDING BIKE Formula; 20; 6; 8800; false; grey; 729 +E-BIKE ElectrO; 40; 26700; false; 17000; grenadine; 2729 +SPEEDELEC Kugoo; 25; 13600; true; 10800; dark gray; 1125 +SPEEDELEC Tesla; 25; 18800; true; 12800; blue; 1019 +FOLDING BIKE BMW; 14; 1; 13800; true; orange; 1655 +SPEEDELEC OIO; 45; 16900; false; 4700; violet; 945 +FOLDING BIKE Benetti; 24; 3; 9700; true; white; 1349 +E-BIKE Mercedes; 25; 26000; false; 30000; orange; 3445 +SPEEDELEC Kugoo; 30; 9300; false; 7900; dark gray; 559 +SPEEDELEC Speedway; 65; 15900; false; 2100; flame; 1135 +SPEEDELEC Segway; 40; 6700; false; 9900; pink; 969 +FOLDING BIKE Crosser; 18; 8; 8700; true; olive; 1575 +SPEEDELEC Kugoo; 15; 21800; true; 4100; grey; 805 +FOLDING BIKE Kross; 20; 1; 12700; false; grey; 865 +FOLDING BIKE Crosser; 20; 7; 9700; false; black; 609 +E-BIKE Scott; 45; 28900; true; 30000; yellow; 1185 +FOLDING BIKE West Bike; 26; 24; 13300; true; brown; 185 +E-BIKE Ecofect; 60; 21100; false; 21000; flame; 2149 +E-BIKE Gazelle; 35; 26100; false; 14000; marine; 2515 +E-BIKE Mando Footloose; 65; 27700; true; 20000; beige; 1849 +FOLDING BIKE Crosser; 20; 9; 12100; false; grey; 1529 +E-BIKE Zaxboard; 30; 23700; true; 26000; lemon; 2889 +SPEEDELEC E-Scooter; 55; 16500; true; 7200; marine; 659 +FOLDING BIKE Kross; 24; 24; 8500; true; olive; 579 +E-BIKE Farad; 65; 28400; true; 14000; olive; 1149 +FOLDING BIKE BMW; 18; 9; 10300; false; rose; 1065 +E-BIKE Lankeleisi; 50; 27800; true; 26000; orange; 705 +E-BIKE ION; 35; 21700; true; 10000; golden; 2635 +E-BIKE ION; 50; 22100; false; 29000; brown; 945 +SPEEDELEC Kugoo; 50; 16200; true; 10500; silver; 599 +SPEEDELEC Maraton; 55; 8500; true; 2300; pink; 1389 +SPEEDELEC Speedway; 60; 9900; true; 6100; marine; 445 +E-BIKE Mercedes; 60; 20400; false; 24000; grey; 3105 +E-BIKE Mercedes; 65; 27800; false; 12000; violet; 3015 +SPEEDELEC E-Scooter; 10; 16800; true; 3700; brown; 849 +FOLDING BIKE Author; 20; 27; 9200; false; red; 1135 +SPEEDELEC Xiaomi; 15; 6700; false; 11300; emerald; 1225 +FOLDING BIKE Make Bike; 20; 18; 10500; true; lemon; 869 +FOLDING BIKE Titan; 20; 18; 15600; true; green; 665 +SPEEDELEC Tesla; 55; 20800; false; 14500; grenadine; 885 +SPEEDELEC Segway; 55; 16300; false; 6000; brown; 1389 +FOLDING BIKE Intertool; 20; 24; 10900; false; pink; 729 +SPEEDELEC Peugeot; 50; 10200; true; 10600; orange; 1195 +SPEEDELEC E-Scooter; 65; 6600; false; 9700; golden; 1435 +FOLDING BIKE Comanche; 20; 21; 13300; true; dark gray; 405 +E-BIKE Porshe Design; 30; 28300; false; 31000; flame; 689 +E-BIKE Xiaomi; 40; 22900; true; 9000; dark gray; 1465 +E-BIKE Haibike; 30; 22600; false; 21000; grenadine; 1405 +SPEEDELEC Speedelec Minirider; 60; 14500; true; 12400; marine; 1475 +FOLDING BIKE Intertool; 20; 6; 15300; true; rose; 655 +SPEEDELEC Freego; 10; 12900; true; 9400; flame; 759 +SPEEDELEC AirWheel; 65; 6900; false; 6300; green; 1615 +FOLDING BIKE West Bike; 24; 27; 12300; true; black; 1065 +E-BIKE Felt; 35; 24900; false; 22000; grenadine; 1465 +SPEEDELEC Smart; 55; 15500; false; 5900; grey; 315 +FOLDING BIKE Comanche; 20; 6; 16600; true; red; 1535 +FOLDING BIKE Titan; 16; 18; 14600; true; black; 1085 +E-BIKE Zaxboard; 65; 28700; true; 11000; silver; 2975 +SPEEDELEC Speedelec Minirider; 35; 7200; false; 7200; white; 1665 +E-BIKE Koga; 50; 19500; true; 12000; flame; 1725 +FOLDING BIKE Cayman; 24; 1; 8900; false; rose; 1615 +FOLDING BIKE Crosser; 20; 7; 8300; false; olive; 945 +SPEEDELEC AirWheel; 65; 15700; false; 10500; blue; 585 +FOLDING BIKE West Bike; 20; 6; 16300; false; beige; 1025 +FOLDING BIKE VNV; 20; 8; 14000; true; golden; 939 +FOLDING BIKE Comanche; 18; 24; 13700; false; marine; 755 +SPEEDELEC Kugoo; 40; 13600; false; 12000; yellow; 1315 +E-BIKE ION; 35; 22400; false; 17000; khaki; 1005 +E-BIKE Rover; 50; 25200; false; 24000; coral; 2255 +FOLDING BIKE Titan; 14; 27; 11900; false; coral; 1559 +SPEEDELEC Peugeot; 30; 14000; true; 12300; golden; 1175 +E-BIKE Farad; 35; 19700; false; 11000; brown; 3435 +E-BIKE E-Motion; 65; 25900; true; 13000; green; 2919 +E-BIKE Koga; 60; 21200; false; 15000; brown; 1135 +SPEEDELEC Freego; 50; 10600; false; 2300; brown; 125 +E-BIKE ElectrO; 55; 22700; false; 26000; flame; 805 +FOLDING BIKE BMW; 20; 18; 11500; true; green; 469 +E-BIKE E-Motion; 40; 26500; false; 14000; golden; 1715 +FOLDING BIKE Comanche; 20; 7; 14900; false; white; 1369 +E-BIKE Haibike; 30; 24200; true; 19000; pink; 849 +FOLDING BIKE West Bike; 20; 18; 11100; true; rose; 1305 +E-BIKE Rover; 40; 25600; true; 20000; red; 1489 +FOLDING BIKE VNV; 24; 8; 9700; false; coral; 1459 +SPEEDELEC ITrike; 40; 19900; false; 11600; violet; 1285 +FOLDING BIKE Formula; 18; 21; 10900; false; green; 1419 +E-BIKE Scott; 50; 21600; true; 25000; yellow; 739 +E-BIKE Mando Footloose; 50; 19200; true; 28000; yellow; 1385 +SPEEDELEC Xiaomi; 35; 18100; false; 10300; violet; 1499 +E-BIKE ElectrO; 55; 22400; false; 16000; red; 2109 +SPEEDELEC Segway; 55; 11900; false; 2000; marine; 339 +E-BIKE Xiaomi; 25; 22600; false; 14000; flame; 1735 +FOLDING BIKE Flex; 24; 1; 11000; false; dark gray; 389 +E-BIKE Porshe Design; 30; 28700; true; 31000; golden; 1619 +FOLDING BIKE Author; 18; 24; 10300; false; green; 1015 +E-BIKE Mando Footloose; 20; 23300; true; 17000; violet; 1339 +E-BIKE ION; 45; 24600; true; 30000; pink; 1595 +E-BIKE Rover; 65; 24700; true; 27000; green; 2779 +SPEEDELEC Maraton; 25; 9000; true; 9000; dark gray; 1565 +E-BIKE Ferrari; 35; 25600; true; 10000; grenadine; 1555 +E-BIKE GoCycle; 65; 26900; false; 12000; beige; 1945 +FOLDING BIKE Dahon; 20; 1; 8200; true; green; 1435 +E-BIKE GoCycle; 40; 24000; true; 15000; grenadine; 1005 +SPEEDELEC Segway; 40; 11200; true; 11400; brown; 209 +E-BIKE SkyBike; 35; 26800; true; 25000; grey; 899 +E-BIKE Ecofect; 35; 23500; false; 13000; golden; 3389 +SPEEDELEC Xiaomi; 40; 9300; false; 14700; coral; 609 +SPEEDELEC OIO; 65; 12600; false; 8800; coral; 199 +SPEEDELEC Kugoo; 10; 8100; false; 9900; grey; 1299 +SPEEDELEC Freego; 25; 18300; true; 7100; flame; 1059 +E-BIKE Lankeleisi; 60; 24400; false; 13000; white; 3095 +SPEEDELEC OIO; 55; 9000; true; 9500; black; 199 +E-BIKE Scott; 55; 23200; false; 11000; violet; 1625 +E-BIKE Mando Footloose; 65; 19900; false; 8000; coral; 1949 +SPEEDELEC Kugoo; 25; 15100; false; 11300; black; 1429 +FOLDING BIKE Benetti; 14; 27; 10200; false; khaki; 1129 +E-BIKE Mando Footloose; 65; 22500; true; 30000; lemon; 1365 +SPEEDELEC Xiaomi; 20; 17400; true; 4200; emerald; 785 +E-BIKE Ecofect; 20; 27300; false; 16000; golden; 1595 +E-BIKE Lankeleisi; 25; 26600; true; 9000; yellow; 3289 +E-BIKE Rover; 65; 25100; false; 29000; grey; 2939 +SPEEDELEC Tesla; 20; 19700; true; 2200; violet; 799 +E-BIKE SkyBike; 55; 28600; true; 30000; black; 2159 +SPEEDELEC EcoRide; 30; 13400; false; 11500; lemon; 1615 +SPEEDELEC Speedelec Minirider; 15; 18800; false; 13100; white; 729 +E-BIKE ElectrO; 50; 28100; false; 11000; silver; 3139 +SPEEDELEC EcoRide; 65; 21700; false; 12900; flame; 795 +E-BIKE Farad; 30; 22500; true; 10000; rose; 1745 +FOLDING BIKE Benetti; 18; 7; 10900; true; beige; 1165 +FOLDING BIKE Titan; 24; 7; 16500; true; golden; 1295 +SPEEDELEC Booster; 65; 20300; false; 15500; red; 175 +E-BIKE Porshe Design; 60; 27000; false; 13000; marine; 659 +SPEEDELEC AirWheel; 15; 11200; false; 11200; pink; 419 +SPEEDELEC Ultron; 45; 21000; false; 3700; beige; 239 +E-BIKE Zaxboard; 60; 28900; false; 14000; black; 3115 +E-BIKE Lankeleisi; 65; 25100; false; 25000; khaki; 2859 +E-BIKE E-Motion; 45; 19600; false; 24000; silver; 1489 +FOLDING BIKE Flex; 20; 7; 8500; false; flame; 455 +FOLDING BIKE Stern; 20; 1; 9500; false; coral; 1055 +FOLDING BIKE Trinx; 20; 27; 14600; false; pink; 465 +E-BIKE Ferrari; 35; 20600; true; 10000; emerald; 805 +FOLDING BIKE Stern; 24; 24; 14400; true; marine; 705 +FOLDING BIKE BMW; 16; 9; 12600; false; emerald; 1649 +SPEEDELEC Booster; 45; 7500; false; 6500; coral; 1175 +FOLDING BIKE West Bike; 14; 9; 16200; false; dark gray; 209 +FOLDING BIKE Dahon; 20; 8; 16900; true; dark gray; 1595 +E-BIKE Scott; 20; 19800; false; 9000; lemon; 1455 +SPEEDELEC Kugoo; 55; 14000; true; 12400; marine; 1489 +FOLDING BIKE Titan; 20; 8; 8800; true; yellow; 1665 +E-BIKE Scott; 35; 24500; false; 14000; flame; 1775 +E-BIKE ION; 45; 21300; false; 14000; brown; 3149 +FOLDING BIKE Trinx; 26; 7; 11600; true; grey; 1099 +SPEEDELEC Segway; 20; 13300; false; 5900; silver; 335 +SPEEDELEC AirWheel; 40; 7700; true; 11600; orange; 1319 +FOLDING BIKE Cayman; 24; 21; 12700; false; emerald; 279 +SPEEDELEC Peugeot; 20; 19300; true; 14200; red; 1075 +SPEEDELEC AirWheel; 35; 9800; false; 10900; rose; 155 +E-BIKE Xiaomi; 45; 26100; true; 13000; green; 1269 +SPEEDELEC Ultron; 60; 9200; true; 14800; grey; 1549 +SPEEDELEC E-Scooter; 35; 8800; false; 14200; beige; 245 +E-BIKE Mercedes; 55; 26200; true; 12000; orange; 1765 +FOLDING BIKE Brompton; 26; 6; 15400; false; grenadine; 519 +SPEEDELEC Kugoo; 55; 11400; false; 12300; lemon; 1325 +FOLDING BIKE Make Bike; 26; 1; 13900; false; orange; 705 +FOLDING BIKE Formula; 20; 21; 11100; false; rose; 1675 +E-BIKE Gazelle; 30; 20400; true; 29000; grenadine; 2195 +FOLDING BIKE Formula; 24; 27; 8500; true; flame; 1485 +FOLDING BIKE Benetti; 14; 7; 9500; false; grey; 189 +SPEEDELEC EcoRide; 25; 9200; true; 3200; dark gray; 289 +SPEEDELEC Maraton; 40; 14500; false; 12200; lemon; 885 +FOLDING BIKE Comanche; 14; 27; 13400; true; white; 1255 +FOLDING BIKE SkyBike; 20; 24; 15800; false; red; 755 +E-BIKE SkyBike; 45; 23500; true; 30000; rose; 2245 +E-BIKE Scott; 45; 25600; false; 28000; olive; 1215 +FOLDING BIKE VNV; 24; 24; 11000; true; coral; 1505 +FOLDING BIKE Author; 20; 3; 12800; false; silver; 1505 +E-BIKE Haibike; 60; 21400; false; 14000; golden; 2885 +SPEEDELEC Aest; 45; 14300; false; 5800; blue; 1049 +FOLDING BIKE Author; 20; 24; 14500; true; rose; 509 +E-BIKE Lankeleisi; 50; 20500; false; 19000; white; 859 +E-BIKE GoCycle; 60; 25400; false; 29000; green; 1495 +E-BIKE Ecofect; 35; 28200; false; 9000; brown; 2145 +SPEEDELEC Speedelec Minirider; 30; 16900; true; 11000; beige; 985 +FOLDING BIKE Trinx; 24; 1; 11300; false; blue; 975 +FOLDING BIKE Flex; 24; 6; 16100; false; khaki; 569 +E-BIKE Ferrari; 55; 23500; true; 25000; violet; 1649 +E-BIKE ElectrO; 35; 19100; false; 31000; golden; 2179 +SPEEDELEC Speedway; 10; 19200; true; 6400; black; 399 +E-BIKE Koga; 60; 24100; true; 23000; orange; 1485 +FOLDING BIKE VNV; 24; 1; 10100; true; red; 575 +E-BIKE SkyBike; 45; 21700; true; 10000; black; 2119 +SPEEDELEC Kugoo; 15; 21100; false; 9700; flame; 1429 +FOLDING BIKE Formula; 14; 1; 13900; false; khaki; 425 +E-BIKE SkyBike; 25; 19100; true; 11000; golden; 1125 +E-BIKE SkyBike; 30; 28600; false; 20000; green; 2809 +FOLDING BIKE Flex; 20; 27; 16300; true; white; 1409 +SPEEDELEC Ultron; 35; 21700; false; 14600; orange; 1645 +E-BIKE Haibike; 65; 24900; false; 13000; white; 1415 +E-BIKE ION; 40; 24700; true; 27000; dark gray; 1989 +FOLDING BIKE Stern; 26; 21; 11100; false; orange; 465 +FOLDING BIKE Titan; 20; 3; 8200; false; red; 355 +SPEEDELEC Booster; 45; 13600; true; 14100; khaki; 865 +FOLDING BIKE Stern; 26; 3; 14900; true; beige; 819 +E-BIKE Zaxboard; 55; 28800; true; 30000; black; 2395 +E-BIKE Lankeleisi; 45; 28800; false; 13000; pink; 1209 +FOLDING BIKE Trinx; 20; 27; 8000; true; grey; 885 +E-BIKE Rover; 60; 19900; true; 14000; grenadine; 3089 +SPEEDELEC Tesla; 25; 18200; false; 10500; khaki; 559 +E-BIKE Ecofect; 60; 23100; false; 10000; golden; 1729 +SPEEDELEC Segway; 50; 8800; true; 13600; coral; 865 +SPEEDELEC Freego; 30; 10100; true; 2600; khaki; 1679 +E-BIKE ElectrO; 35; 27000; false; 21000; black; 2735 +SPEEDELEC Booster; 50; 10200; false; 2500; green; 1525 +FOLDING BIKE Polygon; 24; 3; 16200; true; blue; 655 +E-BIKE Ecofect; 25; 23800; true; 16000; beige; 2869 +FOLDING BIKE SkyBike; 24; 6; 16800; true; khaki; 445 +SPEEDELEC Speedelec Minirider; 65; 14100; true; 5000; white; 1499 +E-BIKE Farad; 45; 20100; true; 14000; lemon; 2379 +SPEEDELEC Booster; 50; 9300; true; 5600; khaki; 455 +SPEEDELEC Kugoo; 45; 9200; false; 8400; dark gray; 1355 +SPEEDELEC Kugoo; 30; 13000; false; 4400; black; 489 +FOLDING BIKE Cayman; 20; 1; 16800; true; black; 669 +SPEEDELEC Speedway; 35; 9400; false; 2400; green; 1419 +SPEEDELEC Freego; 30; 16400; false; 6000; blue; 1665 +SPEEDELEC Tesla; 45; 20700; false; 14600; olive; 205 +E-BIKE Ecofect; 35; 27000; false; 18000; olive; 1989 +SPEEDELEC ITrike; 50; 12000; true; 11600; marine; 245 +E-BIKE ION; 25; 27700; false; 17000; green; 3235 +E-BIKE Koga; 35; 24900; false; 29000; white; 729 +SPEEDELEC Tesla; 15; 12200; true; 2300; khaki; 615 +FOLDING BIKE BMW; 20; 1; 10500; false; blue; 635 +FOLDING BIKE West Bike; 20; 7; 14700; true; golden; 299 +FOLDING BIKE West Bike; 18; 6; 15600; false; blue; 1219 +E-BIKE Lankeleisi; 60; 28200; false; 15000; coral; 2429 +SPEEDELEC Peugeot; 50; 20800; true; 15700; silver; 1279 +FOLDING BIKE Polygon; 24; 6; 16400; false; coral; 725 +FOLDING BIKE SkyBike; 20; 3; 11900; false; yellow; 1115 +SPEEDELEC Tesla; 15; 9500; false; 13200; lemon; 285 +SPEEDELEC Freego; 35; 21300; false; 4500; yellow; 779 +E-BIKE Haibike; 30; 22700; true; 24000; black; 1085 +SPEEDELEC Kugoo; 50; 20300; true; 4400; flame; 299 +FOLDING BIKE Titan; 14; 18; 13300; true; emerald; 285 +SPEEDELEC OIO; 45; 9700; true; 10500; olive; 785 +SPEEDELEC OIO; 15; 7100; true; 9400; rose; 1065 +SPEEDELEC OIO; 55; 19900; true; 10900; brown; 435 +SPEEDELEC Kugoo; 65; 17700; true; 15100; khaki; 539 +FOLDING BIKE Author; 18; 8; 11600; false; red; 279 +FOLDING BIKE West Bike; 14; 7; 14700; true; black; 1289 +SPEEDELEC Dualtron; 50; 17300; true; 15500; flame; 1549 +SPEEDELEC Booster; 10; 20000; false; 11800; dark gray; 1095 +FOLDING BIKE Stern; 20; 27; 16500; false; white; 439 +SPEEDELEC Dualtron; 60; 20500; false; 15600; golden; 869 +SPEEDELEC Ultron; 60; 7000; false; 11400; lemon; 1055 +E-BIKE Ferrari; 60; 28900; false; 21000; brown; 825 \ No newline at end of file diff --git a/src/main/resources/ecobike.txt b/src/main/resources/ecobike.txt index 9a57fdc..bef04ec 100644 --- a/src/main/resources/ecobike.txt +++ b/src/main/resources/ecobike.txt @@ -997,4 +997,4 @@ SPEEDELEC Booster; 10; 20000; false; 11800; dark gray; 1095 FOLDING BIKE Stern; 20; 27; 16500; false; white; 439 SPEEDELEC Dualtron; 60; 20500; false; 15600; golden; 869 SPEEDELEC Ultron; 60; 7000; false; 11400; lemon; 1055 -E-BIKE Ferrari; 60; 28900; false; 21000; brown; 825 \ No newline at end of file +E-BIKE Ferrari; 60; 28900; false; 21000; brown; 825 From ced9d15e102e50f4704f2bd24367bd83a311d09e Mon Sep 17 00:00:00 2001 From: Leonid Date: Wed, 29 Jul 2020 19:27:21 +0300 Subject: [PATCH 03/19] find command dialog completed --- .../java/com/ecobike/CommandExecutor.java | 2 +- src/main/java/com/ecobike/Communicator.java | 3 +- .../java/com/ecobike/ConsoleCommunicator.java | 23 +++-- src/main/java/com/ecobike/DataHolder.java | 26 ++--- src/main/java/com/ecobike/FileWriter.java | 2 +- .../com/ecobike/command/AddEBikeCommand.java | 2 +- .../command/AddFoldingBikeCommand.java | 2 +- .../command/AddSpeedelecBikeCommand.java | 2 +- .../java/com/ecobike/command/FindCommand.java | 97 +++++++++++++++++++ .../java/com/ecobike/command/ShowCommand.java | 2 +- .../ecobike/model/AbstractElectroBike.java | 17 +--- src/main/java/com/ecobike/model/Bike.java | 36 +++---- src/main/java/com/ecobike/model/BikeType.java | 18 ++++ src/main/java/com/ecobike/model/EBike.java | 17 +--- .../java/com/ecobike/model/FoldingBike.java | 18 ++-- .../java/com/ecobike/model/SpeedelecBike.java | 17 +--- src/main/resources/ecobike.txt | 1 + 17 files changed, 180 insertions(+), 105 deletions(-) create mode 100644 src/main/java/com/ecobike/command/FindCommand.java create mode 100644 src/main/java/com/ecobike/model/BikeType.java diff --git a/src/main/java/com/ecobike/CommandExecutor.java b/src/main/java/com/ecobike/CommandExecutor.java index 6116828..8cac4e5 100644 --- a/src/main/java/com/ecobike/CommandExecutor.java +++ b/src/main/java/com/ecobike/CommandExecutor.java @@ -13,7 +13,7 @@ public class CommandExecutor { allKnownCommandsMap.put(Operation.ADD_FOLDING_BIKE, new AddFoldingBikeCommand()); allKnownCommandsMap.put(Operation.ADD_SPEEDELEC_BIKE, new AddSpeedelecBikeCommand()); allKnownCommandsMap.put(Operation.ADD_E_BIKE, new AddEBikeCommand()); - + allKnownCommandsMap.put(Operation.FIND_FIRST_ITEM_BY_BRAND, new FindCommand()); allKnownCommandsMap.put(Operation.WRITE_TO_FILE, new WriteToFileCommand()); allKnownCommandsMap.put(Operation.STOP_PROGRAM, new ExitCommand()); } diff --git a/src/main/java/com/ecobike/Communicator.java b/src/main/java/com/ecobike/Communicator.java index 3c9d51b..6763c01 100644 --- a/src/main/java/com/ecobike/Communicator.java +++ b/src/main/java/com/ecobike/Communicator.java @@ -1,7 +1,5 @@ package com.ecobike; -import java.io.IOException; - public interface Communicator { /** * Method writs message to user. @@ -15,6 +13,7 @@ public interface Communicator { /** * Method reads integer from user. + * For empty entry returns 0. */ int readInt(); /** diff --git a/src/main/java/com/ecobike/ConsoleCommunicator.java b/src/main/java/com/ecobike/ConsoleCommunicator.java index fc8c90a..253e69a 100644 --- a/src/main/java/com/ecobike/ConsoleCommunicator.java +++ b/src/main/java/com/ecobike/ConsoleCommunicator.java @@ -28,29 +28,34 @@ public String readString() { @Override public int readInt() { - int result = -1; - while (result < 0) { + while (true) { + String entry = readString(); + if (entry.isEmpty()) { + return 0; + } + int intValue; try { - result = Integer.parseInt(readString()); + if ((intValue = Integer.parseInt(entry)) >= 0) { + return intValue; + } } catch (NumberFormatException e) { System.out.println("Repeat your entry (only positive number):"); } } - return result; } @Override public boolean readBoolean() { - System.out.println("Type 1 for TRUE or 0 for FALSE"); + System.out.println("Type 1 for TRUE or 2 for FALSE"); while (true) { String entry = readString(); - if (entry.equals("0")) { - return false; - } if (entry.equals("1")) { return true; } - System.out.println("Wrong entry. Type 1 for TRUE or 0 for FALSE"); + if (entry.equals("2")) { + return false; + } + System.out.println("Wrong entry. Type 1 for TRUE or 2 for FALSE"); } } } diff --git a/src/main/java/com/ecobike/DataHolder.java b/src/main/java/com/ecobike/DataHolder.java index 80f0958..0c9a42c 100644 --- a/src/main/java/com/ecobike/DataHolder.java +++ b/src/main/java/com/ecobike/DataHolder.java @@ -1,13 +1,11 @@ package com.ecobike; -import com.ecobike.model.Bike; -import com.ecobike.model.EBike; -import com.ecobike.model.FoldingBike; -import com.ecobike.model.SpeedelecBike; +import com.ecobike.model.*; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -32,27 +30,31 @@ public void loadData(Path file) { } } - public List getBikes() { - return bikes; + public void addBike(Bike bike) { + bikes.add(bike); + } + + public List getUnmodifiableBikeList() { + return Collections.unmodifiableList(bikes); } private Bike parseBike(String line) { - if (line.startsWith("SPEEDELEC")) { - String[] toConstructor = line.replace("SPEEDELEC ", "").split("; "); + if (line.startsWith(BikeType.SPEEDELEC.toString())) { + String[] toConstructor = line.replace(BikeType.SPEEDELEC + " ", "").split("; "); return new SpeedelecBike(toConstructor[0], Integer.parseInt(toConstructor[1]), Integer.parseInt(toConstructor[2]), Boolean.parseBoolean(toConstructor[3]), Integer.parseInt(toConstructor[4]), toConstructor[5], Integer.parseInt(toConstructor[6])); } - if(line.startsWith("E-BIKE")) { - String[] toConstructor = line.replace("E-BIKE ", "").split("; "); + if(line.startsWith(BikeType.E_BIKE.toString())) { + String[] toConstructor = line.replace(BikeType.E_BIKE + " ", "").split("; "); return new EBike(toConstructor[0], Integer.parseInt(toConstructor[1]), Integer.parseInt(toConstructor[2]), Boolean.parseBoolean(toConstructor[3]), Integer.parseInt(toConstructor[4]), toConstructor[5], Integer.parseInt(toConstructor[6])); } - if(line.startsWith("FOLDING BIKE")) { - String[] toConstructor = line.replace("FOLDING BIKE ", "").split("; "); + if(line.startsWith(BikeType.FOLDING_BIKE.toString())) { + String[] toConstructor = line.replace(BikeType.FOLDING_BIKE + " ", "").split("; "); return new FoldingBike(toConstructor[0], Integer.parseInt(toConstructor[1]), Integer.parseInt(toConstructor[2]), Integer.parseInt(toConstructor[3]), Boolean.parseBoolean(toConstructor[4]), toConstructor[5], diff --git a/src/main/java/com/ecobike/FileWriter.java b/src/main/java/com/ecobike/FileWriter.java index aa5fa97..cdaecb4 100644 --- a/src/main/java/com/ecobike/FileWriter.java +++ b/src/main/java/com/ecobike/FileWriter.java @@ -17,7 +17,7 @@ public void setFile(Path file) { } public void writeData() { - List dataToWrite = dataHolder.getBikes().stream() + List dataToWrite = dataHolder.getUnmodifiableBikeList().stream() .map(Bike::toFileWriterString) .collect(Collectors.toList()); try { diff --git a/src/main/java/com/ecobike/command/AddEBikeCommand.java b/src/main/java/com/ecobike/command/AddEBikeCommand.java index bf76813..6755bca 100644 --- a/src/main/java/com/ecobike/command/AddEBikeCommand.java +++ b/src/main/java/com/ecobike/command/AddEBikeCommand.java @@ -20,7 +20,7 @@ public void execute() { String color = COMMUNICATOR.readString(); COMMUNICATOR.writeMessage("Enter price:"); int price = COMMUNICATOR.readInt(); - DATA_HOLDER.getBikes().add(new EBike(brand, maxSpeed, weight, + DATA_HOLDER.addBike(new EBike(brand, maxSpeed, weight, isLightsPresent, batteryCapacity, color, price)); COMMUNICATOR.writeMessage("New E-BIKE BIKE added."); } diff --git a/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java b/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java index 9bf1018..58d1bc9 100644 --- a/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java +++ b/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java @@ -20,7 +20,7 @@ public void execute() { String color = COMMUNICATOR.readString(); COMMUNICATOR.writeMessage("Enter price:"); int price = COMMUNICATOR.readInt(); - DATA_HOLDER.getBikes().add(new FoldingBike(brand, wheelSize, numberOfGears, + DATA_HOLDER.addBike(new FoldingBike(brand, wheelSize, numberOfGears, weight, isLightsPresent, color, price)); COMMUNICATOR.writeMessage("New FOLDING BIKE added."); } diff --git a/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java b/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java index ccf993e..52b8916 100644 --- a/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java +++ b/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java @@ -20,7 +20,7 @@ public void execute() { String color = COMMUNICATOR.readString(); COMMUNICATOR.writeMessage("Enter price:"); int price = COMMUNICATOR.readInt(); - DATA_HOLDER.getBikes().add(new SpeedelecBike(brand, maxSpeed, weight, + DATA_HOLDER.addBike(new SpeedelecBike(brand, maxSpeed, weight, isLightsPresent, batteryCapacity, color, price)); COMMUNICATOR.writeMessage("New SPEEDELEC BIKE added."); } diff --git a/src/main/java/com/ecobike/command/FindCommand.java b/src/main/java/com/ecobike/command/FindCommand.java new file mode 100644 index 0000000..bd479d5 --- /dev/null +++ b/src/main/java/com/ecobike/command/FindCommand.java @@ -0,0 +1,97 @@ +package com.ecobike.command; + +import com.ecobike.model.BikeType; + +public class FindCommand implements Command { + + @Override + public void execute() { + BikeType bikeType; + while (true) { + COMMUNICATOR.writeMessage("Select bike type you wont to find"); + COMMUNICATOR.writeMessage("\t 1 - " + BikeType.FOLDING_BIKE); + COMMUNICATOR.writeMessage("\t 2 - " + BikeType.E_BIKE); + COMMUNICATOR.writeMessage("\t 3 - " + BikeType.SPEEDELEC); + int bikeTypeNumber; + if ((bikeTypeNumber = COMMUNICATOR.readInt()) >= 1 && bikeTypeNumber <= 3) { + bikeType = BikeType.values()[bikeTypeNumber - 1]; + break; + } + COMMUNICATOR.writeMessage("=== Wrong entry ==="); + COMMUNICATOR.writeMessage(""); + } + + COMMUNICATOR.writeMessage("Enter bike brand:"); + String brand = ""; + while ((brand = COMMUNICATOR.readString()).isEmpty()) { + COMMUNICATOR.writeMessage("Can't skip. Enter bike brand:"); + } + + COMMUNICATOR.writeMessage("You may choose next parameters (for skipping parameter press \"Enter\"):"); + COMMUNICATOR.writeMessage("Enter min weight:"); + int minWeight = COMMUNICATOR.readInt(); + + COMMUNICATOR.writeMessage("Enter max weight:"); + int maxWeight = COMMUNICATOR.readInt(); + + COMMUNICATOR.writeMessage("Enter lights presence (Type 1 for TRUE or 2 for FALSE):"); + boolean isLightsOptionEntered = false; + boolean isLightsPresent; + while (true) { + String entry = COMMUNICATOR.readString(); + if (entry.isEmpty()) { + break; + } + if (entry.equals("1")) { + isLightsPresent = true; + isLightsOptionEntered = true; + break; + } + if (entry.equals("2")) { + isLightsPresent = false; + isLightsOptionEntered = true; + break; + } + System.out.println("Wrong entry. Type 1 for TRUE, 2 for FALSE or \"Enter\" for skipping"); + } + + COMMUNICATOR.writeMessage("Enter color:"); + String color = COMMUNICATOR.readString(); + + COMMUNICATOR.writeMessage("Enter min price:"); + int minPrice = COMMUNICATOR.readInt(); + + COMMUNICATOR.writeMessage("Enter max price:"); + int maxPrice = COMMUNICATOR.readInt(); + + int minWheelSize; + int maxWheelSize; + int minNumberOfGears; + int maxNumberOfGears; + if (bikeType == BikeType.FOLDING_BIKE) { + COMMUNICATOR.writeMessage("Enter min wheel size:"); + minWheelSize = COMMUNICATOR.readInt(); + COMMUNICATOR.writeMessage("Enter max wheel size:"); + maxWheelSize = COMMUNICATOR.readInt(); + COMMUNICATOR.writeMessage("Enter min number of gears:"); + minNumberOfGears = COMMUNICATOR.readInt(); + COMMUNICATOR.writeMessage("Enter max number of gears:"); + maxNumberOfGears = COMMUNICATOR.readInt(); + } + + int minMaxBikeSpeed; + int maxMaxBikeSpeed; + int minBatteryCapacity; + int maxBatteryCapacity; + if (bikeType == BikeType.E_BIKE || bikeType == BikeType.SPEEDELEC) { + COMMUNICATOR.writeMessage("Enter minimum max bike speed:"); + minMaxBikeSpeed = COMMUNICATOR.readInt(); + COMMUNICATOR.writeMessage("Enter maximum max bike speed:"); + maxMaxBikeSpeed = COMMUNICATOR.readInt(); + COMMUNICATOR.writeMessage("Enter min battery capacity:"); + minBatteryCapacity = COMMUNICATOR.readInt(); + COMMUNICATOR.writeMessage("Enter max battery capacity:"); + maxBatteryCapacity = COMMUNICATOR.readInt(); + } + } +} diff --git a/src/main/java/com/ecobike/command/ShowCommand.java b/src/main/java/com/ecobike/command/ShowCommand.java index c5bb28f..e70b491 100644 --- a/src/main/java/com/ecobike/command/ShowCommand.java +++ b/src/main/java/com/ecobike/command/ShowCommand.java @@ -4,7 +4,7 @@ public class ShowCommand implements Command { @Override public void execute() { - DATA_HOLDER.getBikes() + DATA_HOLDER.getUnmodifiableBikeList() .forEach(bike -> COMMUNICATOR.writeMessage(bike.toString())); } } diff --git a/src/main/java/com/ecobike/model/AbstractElectroBike.java b/src/main/java/com/ecobike/model/AbstractElectroBike.java index e7eddfa..cf91814 100644 --- a/src/main/java/com/ecobike/model/AbstractElectroBike.java +++ b/src/main/java/com/ecobike/model/AbstractElectroBike.java @@ -7,12 +7,12 @@ public abstract class AbstractElectroBike extends Bike { /** * Max speed in km/h */ - private int maxSpeed; + private final int maxSpeed; /** * Battery capacity in mAh */ - private int batteryCapacity; + private final int batteryCapacity; public AbstractElectroBike(String brand, int maxSpeed, @@ -34,7 +34,7 @@ public AbstractElectroBike(String brand, */ @Override public String toFileWriterString() { - return String.format("%s; %d; %d; %s; %d; %s; %d", + return String.format("%s %s; %d; %d; %s; %d; %s; %d", getBikeType(), getBrand(), maxSpeed, getWeight(), isLightsPresent() ? "true" : "false", batteryCapacity, getColor(), getPrice()); } @@ -56,8 +56,8 @@ public int hashCode() { @Override public String toString() { - return String.format(" %s with %d mAh battery and%s head/tail light." + - "\nPrice: %d euros.", + return String.format("%s %s with %d mAh battery and%s head/tail light." + + "\nPrice: %d euros.", getBikeType(), getBrand(), getBatteryCapacity(), isLightsPresent() ? "" : " no", getPrice()); } @@ -65,15 +65,8 @@ public int getMaxSpeed() { return maxSpeed; } - public void setMaxSpeed(int maxSpeed) { - this.maxSpeed = maxSpeed; - } - public int getBatteryCapacity() { return batteryCapacity; } - public void setBatteryCapacity(int batteryCapacity) { - this.batteryCapacity = batteryCapacity; - } } diff --git a/src/main/java/com/ecobike/model/Bike.java b/src/main/java/com/ecobike/model/Bike.java index fb7d3b5..27ef48c 100644 --- a/src/main/java/com/ecobike/model/Bike.java +++ b/src/main/java/com/ecobike/model/Bike.java @@ -3,14 +3,18 @@ import java.util.Objects; public abstract class Bike { + /** + * Type of the bike. + */ + private BikeType bikeType; - private String brand; + private final String brand; /** * Bike's weight in grams */ - private int weight; - private boolean isLightsPresent; - private String color; + private final int weight; + private final boolean isLightsPresent; + private final String color; /** * Price in EUR */ @@ -48,38 +52,30 @@ public int hashCode() { return Objects.hash(brand, weight, isLightsPresent, color, price); } - public String getBrand() { - return brand; + public BikeType getBikeType() { + return bikeType; } - public void setBrand(String brand) { - this.brand = brand; + void setBikeType(BikeType bikeType) { + this.bikeType = bikeType; } - public int getWeight() { - return weight; + public String getBrand() { + return brand; } - public void setWeight(int weight) { - this.weight = weight; + public int getWeight() { + return weight; } public boolean isLightsPresent() { return isLightsPresent; } - public void setLightsPresent(boolean lightsPresent) { - isLightsPresent = lightsPresent; - } - public String getColor() { return color; } - public void setColor(String color) { - this.color = color; - } - public int getPrice() { return price; } diff --git a/src/main/java/com/ecobike/model/BikeType.java b/src/main/java/com/ecobike/model/BikeType.java new file mode 100644 index 0000000..df2c001 --- /dev/null +++ b/src/main/java/com/ecobike/model/BikeType.java @@ -0,0 +1,18 @@ +package com.ecobike.model; + +public enum BikeType { + FOLDING_BIKE("FOLDING BIKE"), + E_BIKE("E-BIKE"), + SPEEDELEC("SPEEDELEC"); + + private String type; + + BikeType(String type) { + this.type = type; + } + + @Override + public String toString() { + return type; + } +} diff --git a/src/main/java/com/ecobike/model/EBike.java b/src/main/java/com/ecobike/model/EBike.java index f6a8806..ebbe890 100644 --- a/src/main/java/com/ecobike/model/EBike.java +++ b/src/main/java/com/ecobike/model/EBike.java @@ -11,21 +11,6 @@ public EBike(String brand, int price) { super(brand, maxSpeed, weight, isLightsPresent, batteryCapacity, color, price); - } - - /** - * Method converts bike to specific String format - * for writing to file. - * - * @return String representation of the bike. - */ - @Override - public String toFileWriterString() { - return "E-BIKE " + super.toFileWriterString(); - } - - @Override - public String toString() { - return "E-BIKE" + super.toString(); + setBikeType(BikeType.E_BIKE); } } diff --git a/src/main/java/com/ecobike/model/FoldingBike.java b/src/main/java/com/ecobike/model/FoldingBike.java index d567886..c060291 100644 --- a/src/main/java/com/ecobike/model/FoldingBike.java +++ b/src/main/java/com/ecobike/model/FoldingBike.java @@ -7,9 +7,9 @@ public class FoldingBike extends Bike { /** * Wheel size in inch. */ - private int wheelSize; + private final int wheelSize; - private int numberOfGears; + private final int numberOfGears; public FoldingBike(String brand, int wheelSize, @@ -21,6 +21,7 @@ public FoldingBike(String brand, super(brand, weight, isLightsPresent, color, price); this.wheelSize = wheelSize; this.numberOfGears = numberOfGears; + setBikeType(BikeType.FOLDING_BIKE); } /** @@ -31,7 +32,7 @@ public FoldingBike(String brand, */ @Override public String toFileWriterString() { - return String.format("FOLDING BIKE %s; %d; %d; %d; %s; %s; %d", + return String.format("%s %s; %d; %d; %d; %s; %s; %d", getBikeType(), getBrand(), wheelSize, numberOfGears, getWeight(), isLightsPresent() ? "true" : "false", getColor(), getPrice()); } @@ -53,8 +54,8 @@ public int hashCode() { @Override public String toString() { - return String.format("FOLDING BIKE %s with %d gear(s) and%s head/tail light." + - "\nPrice: %d euros.", + return String.format("%s %s with %d gear(s) and%s head/tail light." + + "\nPrice: %d euros.", getBikeType(), getBrand(), getNumberOfGears(), isLightsPresent() ? "" : " no", getPrice()); } @@ -62,15 +63,8 @@ public int getWheelSize() { return wheelSize; } - public void setWheelSize(int wheelSize) { - this.wheelSize = wheelSize; - } - public int getNumberOfGears() { return numberOfGears; } - public void setNumberOfGears(int numberOfGears) { - this.numberOfGears = numberOfGears; - } } diff --git a/src/main/java/com/ecobike/model/SpeedelecBike.java b/src/main/java/com/ecobike/model/SpeedelecBike.java index 8c3d94b..380b0b8 100644 --- a/src/main/java/com/ecobike/model/SpeedelecBike.java +++ b/src/main/java/com/ecobike/model/SpeedelecBike.java @@ -11,21 +11,6 @@ public SpeedelecBike(String brand, int price) { super(brand, maxSpeed, weight, isLightsPresent, batteryCapacity, color, price); - } - - /** - * Method converts bike to specific String format - * for writing to file. - * - * @return String representation of the bike. - */ - @Override - public String toFileWriterString() { - return "SPEEDELEC " + super.toFileWriterString(); - } - - @Override - public String toString() { - return "SPEEDELEC" + super.toString(); + setBikeType(BikeType.SPEEDELEC); } } diff --git a/src/main/resources/ecobike.txt b/src/main/resources/ecobike.txt index bef04ec..8f3681f 100644 --- a/src/main/resources/ecobike.txt +++ b/src/main/resources/ecobike.txt @@ -998,3 +998,4 @@ FOLDING BIKE Stern; 20; 27; 16500; false; white; 439 SPEEDELEC Dualtron; 60; 20500; false; 15600; golden; 869 SPEEDELEC Ultron; 60; 7000; false; 11400; lemon; 1055 E-BIKE Ferrari; 60; 28900; false; 21000; brown; 825 +FOLDING BIKE WWWWW; 2; 2; 1; true; 1; 1 From 5774d6a9cb1ba9e8d38b19f023a463220e55a870 Mon Sep 17 00:00:00 2001 From: Leonid Date: Wed, 29 Jul 2020 23:55:01 +0300 Subject: [PATCH 04/19] findBikesByParameter() completed --- src/main/java/com/ecobike/DataHolder.java | 28 +++ .../com/ecobike/SearchParameterContainer.java | 168 ++++++++++++++++++ .../java/com/ecobike/command/FindCommand.java | 60 +++---- 3 files changed, 224 insertions(+), 32 deletions(-) create mode 100644 src/main/java/com/ecobike/SearchParameterContainer.java diff --git a/src/main/java/com/ecobike/DataHolder.java b/src/main/java/com/ecobike/DataHolder.java index 0c9a42c..efc9502 100644 --- a/src/main/java/com/ecobike/DataHolder.java +++ b/src/main/java/com/ecobike/DataHolder.java @@ -8,6 +8,7 @@ import java.util.Collections; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; public class DataHolder { private final static DataHolder INSTANCE = new DataHolder(); @@ -38,6 +39,33 @@ public List getUnmodifiableBikeList() { return Collections.unmodifiableList(bikes); } + public List findBikesByParameter(SearchParameterContainer parCont) { + Stream bikeStream = bikes.parallelStream() + .filter(bike -> bike.getBikeType() == parCont.getBikeType() + && bike.getBrand().equalsIgnoreCase(parCont.getBrand()) + && bike.getWeight() >= parCont.getMinWeight() + && (parCont.getMaxWeight() == 0 || bike.getWeight() <= parCont.getMaxWeight()) + && (!parCont.isLightsOptionEntered() || parCont.isLightsPresent() == bike.isLightsPresent()) + && (parCont.getColor().isEmpty() || parCont.getColor().equalsIgnoreCase(bike.getColor())) + && bike.getPrice() >= parCont.getMinPrice() + && (parCont.getMaxPrice() == 0 || bike.getPrice() <= parCont.getMaxPrice())); + + if (parCont.getBikeType() == BikeType.FOLDING_BIKE) { + return bikeStream.map(bike -> (FoldingBike) bike) + .filter(bike -> bike.getWheelSize() >= parCont.getMinWheelSize() + && (parCont.getMaxWheelSize() == 0 || bike.getWheelSize() <= parCont.getMaxWheelSize()) + && bike.getNumberOfGears() >= parCont.getMinNumberOfGears() + && (parCont.getMaxNumberOfGears() == 0 || bike.getNumberOfGears() <= parCont.getMaxNumberOfGears())) + .collect(Collectors.toList()); + } + return bikeStream.map(bike -> (AbstractElectroBike) bike) + .filter(bike -> bike.getMaxSpeed() >= parCont.getMinMaxBikeSpeed() + && (parCont.getMaxMaxBikeSpeed() == 0 || bike.getMaxSpeed() <= parCont.getMaxMaxBikeSpeed()) + && bike.getBatteryCapacity() >= parCont.getMinBatteryCapacity() + && (parCont.getMaxBatteryCapacity() == 0 || bike.getBatteryCapacity() <= parCont.getMaxBatteryCapacity())) + .collect(Collectors.toList()); + } + private Bike parseBike(String line) { if (line.startsWith(BikeType.SPEEDELEC.toString())) { String[] toConstructor = line.replace(BikeType.SPEEDELEC + " ", "").split("; "); diff --git a/src/main/java/com/ecobike/SearchParameterContainer.java b/src/main/java/com/ecobike/SearchParameterContainer.java new file mode 100644 index 0000000..57d5b80 --- /dev/null +++ b/src/main/java/com/ecobike/SearchParameterContainer.java @@ -0,0 +1,168 @@ +package com.ecobike; + +import com.ecobike.model.BikeType; + +public class SearchParameterContainer { + /** + * Common bike parameters. + */ + private BikeType bikeType; + private String brand; + private int minWeight; + private int maxWeight; + private boolean isLightsOptionEntered; + private boolean isLightsPresent; + private String color; + private int minPrice; + private int maxPrice; + /** + * Specific Folding Bike parameters. + */ + private int minWheelSize; + private int maxWheelSize; + private int minNumberOfGears; + private int maxNumberOfGears; + /** + * Specific Electro Bike parameters + */ + private int minMaxBikeSpeed; + private int maxMaxBikeSpeed; + private int minBatteryCapacity; + private int maxBatteryCapacity; + + public BikeType getBikeType() { + return bikeType; + } + + public void setBikeType(BikeType bikeType) { + this.bikeType = bikeType; + } + + public String getBrand() { + return brand; + } + + public void setBrand(String brand) { + this.brand = brand; + } + + public int getMinWeight() { + return minWeight; + } + + public void setMinWeight(int minWeight) { + this.minWeight = minWeight; + } + + public int getMaxWeight() { + return maxWeight; + } + + public void setMaxWeight(int maxWeight) { + this.maxWeight = maxWeight; + } + + public boolean isLightsOptionEntered() { + return isLightsOptionEntered; + } + + public void setLightsOptionEntered(boolean lightsOptionEntered) { + isLightsOptionEntered = lightsOptionEntered; + } + + public boolean isLightsPresent() { + return isLightsPresent; + } + + public void setLightsPresent(boolean lightsPresent) { + isLightsPresent = lightsPresent; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + + public int getMinPrice() { + return minPrice; + } + + public void setMinPrice(int minPrice) { + this.minPrice = minPrice; + } + + public int getMaxPrice() { + return maxPrice; + } + + public void setMaxPrice(int maxPrice) { + this.maxPrice = maxPrice; + } + + public int getMinWheelSize() { + return minWheelSize; + } + + public void setMinWheelSize(int minWheelSize) { + this.minWheelSize = minWheelSize; + } + + public int getMaxWheelSize() { + return maxWheelSize; + } + + public void setMaxWheelSize(int maxWheelSize) { + this.maxWheelSize = maxWheelSize; + } + + public int getMinNumberOfGears() { + return minNumberOfGears; + } + + public void setMinNumberOfGears(int minNumberOfGears) { + this.minNumberOfGears = minNumberOfGears; + } + + public int getMaxNumberOfGears() { + return maxNumberOfGears; + } + + public void setMaxNumberOfGears(int maxNumberOfGears) { + this.maxNumberOfGears = maxNumberOfGears; + } + + public int getMinMaxBikeSpeed() { + return minMaxBikeSpeed; + } + + public void setMinMaxBikeSpeed(int minMaxBikeSpeed) { + this.minMaxBikeSpeed = minMaxBikeSpeed; + } + + public int getMaxMaxBikeSpeed() { + return maxMaxBikeSpeed; + } + + public void setMaxMaxBikeSpeed(int maxMaxBikeSpeed) { + this.maxMaxBikeSpeed = maxMaxBikeSpeed; + } + + public int getMinBatteryCapacity() { + return minBatteryCapacity; + } + + public void setMinBatteryCapacity(int minBatteryCapacity) { + this.minBatteryCapacity = minBatteryCapacity; + } + + public int getMaxBatteryCapacity() { + return maxBatteryCapacity; + } + + public void setMaxBatteryCapacity(int maxBatteryCapacity) { + this.maxBatteryCapacity = maxBatteryCapacity; + } +} diff --git a/src/main/java/com/ecobike/command/FindCommand.java b/src/main/java/com/ecobike/command/FindCommand.java index bd479d5..09649fd 100644 --- a/src/main/java/com/ecobike/command/FindCommand.java +++ b/src/main/java/com/ecobike/command/FindCommand.java @@ -1,12 +1,13 @@ package com.ecobike.command; +import com.ecobike.SearchParameterContainer; import com.ecobike.model.BikeType; public class FindCommand implements Command { @Override public void execute() { - BikeType bikeType; + SearchParameterContainer paramContainer = new SearchParameterContainer(); while (true) { COMMUNICATOR.writeMessage("Select bike type you wont to find"); COMMUNICATOR.writeMessage("\t 1 - " + BikeType.FOLDING_BIKE); @@ -14,7 +15,7 @@ public void execute() { COMMUNICATOR.writeMessage("\t 3 - " + BikeType.SPEEDELEC); int bikeTypeNumber; if ((bikeTypeNumber = COMMUNICATOR.readInt()) >= 1 && bikeTypeNumber <= 3) { - bikeType = BikeType.values()[bikeTypeNumber - 1]; + paramContainer.setBikeType(BikeType.values()[bikeTypeNumber - 1]); break; } COMMUNICATOR.writeMessage("=== Wrong entry ==="); @@ -22,76 +23,71 @@ public void execute() { } COMMUNICATOR.writeMessage("Enter bike brand:"); - String brand = ""; + String brand; while ((brand = COMMUNICATOR.readString()).isEmpty()) { COMMUNICATOR.writeMessage("Can't skip. Enter bike brand:"); } + paramContainer.setBrand(brand); COMMUNICATOR.writeMessage("You may choose next parameters (for skipping parameter press \"Enter\"):"); COMMUNICATOR.writeMessage("Enter min weight:"); - int minWeight = COMMUNICATOR.readInt(); + paramContainer.setMinWeight(COMMUNICATOR.readInt()); COMMUNICATOR.writeMessage("Enter max weight:"); - int maxWeight = COMMUNICATOR.readInt(); + paramContainer.setMaxWeight(COMMUNICATOR.readInt()); COMMUNICATOR.writeMessage("Enter lights presence (Type 1 for TRUE or 2 for FALSE):"); - boolean isLightsOptionEntered = false; - boolean isLightsPresent; while (true) { String entry = COMMUNICATOR.readString(); if (entry.isEmpty()) { break; } if (entry.equals("1")) { - isLightsPresent = true; - isLightsOptionEntered = true; + paramContainer.setLightsPresent(true); + paramContainer.setLightsOptionEntered(true); break; } if (entry.equals("2")) { - isLightsPresent = false; - isLightsOptionEntered = true; + paramContainer.setLightsPresent(false); + paramContainer.setLightsOptionEntered(true); break; } System.out.println("Wrong entry. Type 1 for TRUE, 2 for FALSE or \"Enter\" for skipping"); } COMMUNICATOR.writeMessage("Enter color:"); - String color = COMMUNICATOR.readString(); + paramContainer.setColor(COMMUNICATOR.readString()); COMMUNICATOR.writeMessage("Enter min price:"); - int minPrice = COMMUNICATOR.readInt(); + paramContainer.setMinPrice(COMMUNICATOR.readInt()); COMMUNICATOR.writeMessage("Enter max price:"); - int maxPrice = COMMUNICATOR.readInt(); + paramContainer.setMaxPrice(COMMUNICATOR.readInt()); - int minWheelSize; - int maxWheelSize; - int minNumberOfGears; - int maxNumberOfGears; - if (bikeType == BikeType.FOLDING_BIKE) { + if (paramContainer.getBikeType() == BikeType.FOLDING_BIKE) { COMMUNICATOR.writeMessage("Enter min wheel size:"); - minWheelSize = COMMUNICATOR.readInt(); + paramContainer.setMinWheelSize(COMMUNICATOR.readInt()); COMMUNICATOR.writeMessage("Enter max wheel size:"); - maxWheelSize = COMMUNICATOR.readInt(); + paramContainer.setMaxWheelSize(COMMUNICATOR.readInt()); COMMUNICATOR.writeMessage("Enter min number of gears:"); - minNumberOfGears = COMMUNICATOR.readInt(); + paramContainer.setMinNumberOfGears(COMMUNICATOR.readInt()); COMMUNICATOR.writeMessage("Enter max number of gears:"); - maxNumberOfGears = COMMUNICATOR.readInt(); + paramContainer.setMaxNumberOfGears(COMMUNICATOR.readInt()); } - int minMaxBikeSpeed; - int maxMaxBikeSpeed; - int minBatteryCapacity; - int maxBatteryCapacity; - if (bikeType == BikeType.E_BIKE || bikeType == BikeType.SPEEDELEC) { + if (paramContainer.getBikeType() == BikeType.E_BIKE + || paramContainer.getBikeType() == BikeType.SPEEDELEC) { COMMUNICATOR.writeMessage("Enter minimum max bike speed:"); - minMaxBikeSpeed = COMMUNICATOR.readInt(); + paramContainer.setMinMaxBikeSpeed(COMMUNICATOR.readInt()); COMMUNICATOR.writeMessage("Enter maximum max bike speed:"); - maxMaxBikeSpeed = COMMUNICATOR.readInt(); + paramContainer.setMaxMaxBikeSpeed(COMMUNICATOR.readInt()); COMMUNICATOR.writeMessage("Enter min battery capacity:"); - minBatteryCapacity = COMMUNICATOR.readInt(); + paramContainer.setMinBatteryCapacity(COMMUNICATOR.readInt()); COMMUNICATOR.writeMessage("Enter max battery capacity:"); - maxBatteryCapacity = COMMUNICATOR.readInt(); + paramContainer.setMaxBatteryCapacity(COMMUNICATOR.readInt()); } + + DATA_HOLDER.findBikesByParameter(paramContainer) + .forEach(bike -> COMMUNICATOR.writeMessage(bike.toString())); } } From dfb3c53d8c36afc5fc4219858187c23bb99c55fb Mon Sep 17 00:00:00 2001 From: Leonid Date: Thu, 30 Jul 2020 19:50:12 +0300 Subject: [PATCH 05/19] everything except tests done --- src/main/java/com/ecobike/BikeFileReader.java | 90 ++++++++++++++ .../{FileWriter.java => BikeFileWriter.java} | 15 ++- .../java/com/ecobike/CommandExecutor.java | 15 ++- src/main/java/com/ecobike/Communicator.java | 43 +++++++ .../java/com/ecobike/ConsoleCommunicator.java | 111 +++++++++++++++++- src/main/java/com/ecobike/DataHolder.java | 104 ++++++++-------- .../java/com/ecobike/EcoBikeApplication.java | 91 ++++++++++---- src/main/java/com/ecobike/Operation.java | 5 +- .../com/ecobike/SearchParameterContainer.java | 4 + .../com/ecobike/command/AddEBikeCommand.java | 36 ++++-- .../command/AddFoldingBikeCommand.java | 36 ++++-- .../command/AddSpeedelecBikeCommand.java | 36 ++++-- .../java/com/ecobike/command/Command.java | 4 + .../java/com/ecobike/command/ExitCommand.java | 8 -- .../java/com/ecobike/command/FindCommand.java | 16 ++- .../java/com/ecobike/command/ShowCommand.java | 6 +- .../ecobike/command/WriteToFileCommand.java | 10 +- .../exception/IllegalDataSourceException.java | 7 ++ .../exception/IllegalFileFormatException.java | 8 ++ .../ecobike/model/AbstractElectroBike.java | 3 + src/main/java/com/ecobike/model/Bike.java | 3 + src/main/java/com/ecobike/model/BikeType.java | 8 +- src/main/java/com/ecobike/model/EBike.java | 3 + .../java/com/ecobike/model/FoldingBike.java | 3 + .../java/com/ecobike/model/SpeedelecBike.java | 3 + 25 files changed, 542 insertions(+), 126 deletions(-) create mode 100644 src/main/java/com/ecobike/BikeFileReader.java rename src/main/java/com/ecobike/{FileWriter.java => BikeFileWriter.java} (61%) delete mode 100644 src/main/java/com/ecobike/command/ExitCommand.java create mode 100644 src/main/java/com/ecobike/exception/IllegalDataSourceException.java create mode 100644 src/main/java/com/ecobike/exception/IllegalFileFormatException.java diff --git a/src/main/java/com/ecobike/BikeFileReader.java b/src/main/java/com/ecobike/BikeFileReader.java new file mode 100644 index 0000000..67df6fe --- /dev/null +++ b/src/main/java/com/ecobike/BikeFileReader.java @@ -0,0 +1,90 @@ +package com.ecobike; + +import com.ecobike.exception.IllegalFileFormatException; +import com.ecobike.model.*; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Class responsible for reading data from file and parsing text to Bike objects. + */ +public class BikeFileReader { + /** + * Path to data file. + */ + private Path file; + private final DataHolder dataHolder = DataHolder.getInstance(); + private final Communicator communicator = EcoBikeApplication.COMMUNICATOR; + + public void setPath(Path file) { + this.file = file; + } + + /** + * Method reads text from file and loads parsed Bike objects to DataHolder. + * + * @throws IllegalFileFormatException if no one Bike object parsed from file. + */ + public void loadData() throws IllegalFileFormatException { + List lines = Collections.emptyList(); + try { + lines = Files.lines(file).collect(Collectors.toList()); + } catch (IOException e) { + e.printStackTrace(); + } + List bikes = new ArrayList<>(lines.size()); + for (int i = 0; i < lines.size(); i++) { + try { + bikes.add(parseBike(lines.get(i))); + } catch (IllegalArgumentException e) { + communicator.writeMessage("Line No. " + (i + 1) + "has wrong format"); + } + } + if (bikes.isEmpty()) { + throw new IllegalFileFormatException(); + } + dataHolder.addBikes(bikes); + communicator.writeMessage(bikes.size() + " bike items has been read from the file"); + } + + /** + * Method parses single String line to Bike object. + * + * @param line string to be parsed. + * @return parsed Bike object + */ + private Bike parseBike(String line) { + try { + if (line.startsWith(BikeType.SPEEDELEC.toString())) { + String[] toConstructor = line.replace(BikeType.SPEEDELEC + " ", "").split("; "); + return new SpeedelecBike(toConstructor[0], Integer.parseInt(toConstructor[1]), + Integer.parseInt(toConstructor[2]), Boolean.parseBoolean(toConstructor[3]), + Integer.parseInt(toConstructor[4]), toConstructor[5], + Integer.parseInt(toConstructor[6])); + } + if (line.startsWith(BikeType.E_BIKE.toString())) { + String[] toConstructor = line.replace(BikeType.E_BIKE + " ", "").split("; "); + return new EBike(toConstructor[0], Integer.parseInt(toConstructor[1]), + Integer.parseInt(toConstructor[2]), Boolean.parseBoolean(toConstructor[3]), + Integer.parseInt(toConstructor[4]), toConstructor[5], + Integer.parseInt(toConstructor[6])); + } + if (line.startsWith(BikeType.FOLDING_BIKE.toString())) { + String[] toConstructor = line.replace(BikeType.FOLDING_BIKE + " ", "").split("; "); + return new FoldingBike(toConstructor[0], Integer.parseInt(toConstructor[1]), + Integer.parseInt(toConstructor[2]), Integer.parseInt(toConstructor[3]), + Boolean.parseBoolean(toConstructor[4]), toConstructor[5], + Integer.parseInt(toConstructor[6])); + } + } catch (RuntimeException e) { + throw new IllegalArgumentException(); + } + throw new IllegalArgumentException(); + } +} diff --git a/src/main/java/com/ecobike/FileWriter.java b/src/main/java/com/ecobike/BikeFileWriter.java similarity index 61% rename from src/main/java/com/ecobike/FileWriter.java rename to src/main/java/com/ecobike/BikeFileWriter.java index cdaecb4..079f509 100644 --- a/src/main/java/com/ecobike/FileWriter.java +++ b/src/main/java/com/ecobike/BikeFileWriter.java @@ -8,14 +8,25 @@ import java.util.List; import java.util.stream.Collectors; -public class FileWriter { +/** + * Class responsible for writing to file Bike objects in text format. + * Old data in file will be replaced by new one. + */ +public class BikeFileWriter { + /** + * Path to data file. + */ private Path file; - private DataHolder dataHolder = DataHolder.getInstance(); + private final DataHolder dataHolder = DataHolder.getInstance(); public void setFile(Path file) { this.file = file; } + /** + * Method writes to file Bike objects in text format. + * Old data in file will be replaced with new one. + */ public void writeData() { List dataToWrite = dataHolder.getUnmodifiableBikeList().stream() .map(Bike::toFileWriterString) diff --git a/src/main/java/com/ecobike/CommandExecutor.java b/src/main/java/com/ecobike/CommandExecutor.java index 8cac4e5..3f59c1c 100644 --- a/src/main/java/com/ecobike/CommandExecutor.java +++ b/src/main/java/com/ecobike/CommandExecutor.java @@ -5,7 +5,13 @@ import java.util.HashMap; import java.util.Map; +/** + * Utility class responsible for execution properly command for operation chosen by user. + */ public class CommandExecutor { + /** + * Map contains properly command for all operations. + */ private static final Map allKnownCommandsMap = new HashMap<>(); static { @@ -13,15 +19,18 @@ public class CommandExecutor { allKnownCommandsMap.put(Operation.ADD_FOLDING_BIKE, new AddFoldingBikeCommand()); allKnownCommandsMap.put(Operation.ADD_SPEEDELEC_BIKE, new AddSpeedelecBikeCommand()); allKnownCommandsMap.put(Operation.ADD_E_BIKE, new AddEBikeCommand()); - allKnownCommandsMap.put(Operation.FIND_FIRST_ITEM_BY_BRAND, new FindCommand()); + allKnownCommandsMap.put(Operation.FIND_ITEMS_BY_BRAND, new FindCommand()); allKnownCommandsMap.put(Operation.WRITE_TO_FILE, new WriteToFileCommand()); - allKnownCommandsMap.put(Operation.STOP_PROGRAM, new ExitCommand()); } private CommandExecutor() { } - public static void execute(Operation operation) throws Exception { + /** + * Method execute command for specified operation. + * @param operation chosen by user. + */ + public static void execute(Operation operation) { allKnownCommandsMap.get(operation).execute(); } } diff --git a/src/main/java/com/ecobike/Communicator.java b/src/main/java/com/ecobike/Communicator.java index 6763c01..6d3f569 100644 --- a/src/main/java/com/ecobike/Communicator.java +++ b/src/main/java/com/ecobike/Communicator.java @@ -1,5 +1,12 @@ package com.ecobike; +import com.ecobike.model.Bike; + +import java.util.List; + +/** + * Interface should be implemented by class responsible for communication with user. + */ public interface Communicator { /** * Method writs message to user. @@ -8,16 +15,52 @@ public interface Communicator { /** * Method reads string from user. + * + * @return entered string. */ String readString(); + /** + * Method reads not empty string from user. + * @return entered string. + */ + String readNotEmptyString(); + /** * Method reads integer from user. * For empty entry returns 0. + * + * @return 0 or positive int value. */ int readInt(); + + /** + * Method reads integer from user. + * Only positive values are allowed. + * + * @return positive int value. + */ + int readPositiveInt(); + /** * Method reads boolean from user. + * @return boolean value from user. */ boolean readBoolean(); + + /** + * Method prints info about bikes from the list + * page by page. + * + * @param bikes list of bikes for printing + */ + void printBikes(List bikes); + + /** + * Ask user to confirm operation. + * + * @param message to be shown for user. + * @return true if confirms or false otherwise. + */ + boolean confirmAction(String message); } diff --git a/src/main/java/com/ecobike/ConsoleCommunicator.java b/src/main/java/com/ecobike/ConsoleCommunicator.java index 253e69a..c4ceaee 100644 --- a/src/main/java/com/ecobike/ConsoleCommunicator.java +++ b/src/main/java/com/ecobike/ConsoleCommunicator.java @@ -1,31 +1,69 @@ package com.ecobike; +import com.ecobike.model.Bike; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.util.List; +/** + * Class responsible for communication with user with console. + */ public class ConsoleCommunicator implements Communicator { - private static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedReader READER = new BufferedReader(new InputStreamReader(System.in)); + /** + * Number of bikes to be shown on one page. + */ + private static final int BIKES_ON_PAGE_QUANTITY = 50; + /** + * Method writs message to user. + */ @Override public void writeMessage(String message) { System.out.println(message); } + /** + * Method reads string from user. + * + * @return entered string + */ @Override public String readString() { String entry = null; while (entry == null) { try { - entry = reader.readLine(); + entry = READER.readLine(); } catch (IOException e) { - System.out.println("Repeat your entry:"); + writeMessage("Repeat your entry:"); } } return entry; } + /** + * Method reads not empty string from user. + * + * @return entered string. + */ + @Override + public String readNotEmptyString() { + String entry; + while ((entry = readString()).isEmpty()) { + writeMessage("Empty entry is not allowed. Repeat, please."); + } + return entry; + } + + /** + * Method reads integer from user. + * For empty entry returns 0. + * + * @return 0 or positive int value. + */ @Override public int readInt() { while (true) { @@ -39,14 +77,33 @@ public int readInt() { return intValue; } } catch (NumberFormatException e) { - System.out.println("Repeat your entry (only positive number):"); + writeMessage("Repeat your entry (only positive number):"); } } } + /** + * Method reads integer from user. + * Only positive values are allowed. + * + * @return positive int value. + */ + @Override + public int readPositiveInt() { + int entry; + while ((entry = readInt()) == 0) { + writeMessage("Empty entry or zero is not allowed. Repeat, please."); + } + return entry; + } + + /** + * Method reads boolean from user. + * @return boolean value from user. + */ @Override public boolean readBoolean() { - System.out.println("Type 1 for TRUE or 2 for FALSE"); + writeMessage("Type 1 for TRUE or 2 for FALSE"); while (true) { String entry = readString(); if (entry.equals("1")) { @@ -55,7 +112,49 @@ public boolean readBoolean() { if (entry.equals("2")) { return false; } - System.out.println("Wrong entry. Type 1 for TRUE or 2 for FALSE"); + writeMessage("Wrong entry. Type 1 for TRUE or 2 for FALSE"); + } + } + + /** + * Method prints info about bikes from the list + * page by page (max 50 on one page by default). + * + * @param bikes list of bikes for printing + */ + @Override + public void printBikes(List bikes) { + for (int i = 0; i < bikes.size(); i++) { + writeMessage(i + 1 + ". " + bikes.get(i).toString()); + if ((i + 1) % BIKES_ON_PAGE_QUANTITY == 0) { + writeMessage(""); + do { + writeMessage("For showing next page press \"Enter\""); + } while (!readString().isEmpty()); + } } } + + /** + * Ask user to confirm operation. + * + * @param message to be shown for user. + * @return true if confirms or false otherwise. + */ + @Override + public boolean confirmAction(String message) { + writeMessage("You are going to:"); + writeMessage(message); + writeMessage(""); + do { + writeMessage("Enter \"y\" for \"yes\" or \"n\" for \"no\""); + String entry = readString(); + if (entry.equalsIgnoreCase("y")) { + return true; + } + if (entry.equalsIgnoreCase("n")) { + return false; + } + } while (true); + } } diff --git a/src/main/java/com/ecobike/DataHolder.java b/src/main/java/com/ecobike/DataHolder.java index efc9502..205a86c 100644 --- a/src/main/java/com/ecobike/DataHolder.java +++ b/src/main/java/com/ecobike/DataHolder.java @@ -1,18 +1,27 @@ package com.ecobike; -import com.ecobike.model.*; +import com.ecobike.model.AbstractElectroBike; +import com.ecobike.model.Bike; +import com.ecobike.model.BikeType; +import com.ecobike.model.FoldingBike; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +/** + * Singleton class responsible for holding and operating with Bikes objects + * loaded from data source. + */ public class DataHolder { private final static DataHolder INSTANCE = new DataHolder(); - private List bikes; + /** + * Bikes container. + */ + private final List bikes = new ArrayList<>(); private DataHolder() { } @@ -21,73 +30,66 @@ public static DataHolder getInstance() { return INSTANCE; } - public void loadData(Path file) { - try { - bikes = Files.lines(file) - .map(line -> parseBike(line)) - .collect(Collectors.toList()); - } catch (IOException e) { - e.printStackTrace(); - } + /** + * Method adds Bikes from collection to Bikes container. + * + * @param bikesToAdd collection with Bikes for adding. + */ + public void addBikes(Collection bikesToAdd) { + bikes.addAll(bikesToAdd); + EcoBikeApplication.isDataChanged = true; } + /** + * Method adds single Bike object to Bikes container. + * + * @param bike to be added. + */ public void addBike(Bike bike) { bikes.add(bike); + EcoBikeApplication.isDataChanged = true; } + /** + * Method returns unmodifiable list represents our Bikes container. + * + * @return unmodifiable list of Bikes. + */ public List getUnmodifiableBikeList() { return Collections.unmodifiableList(bikes); } + /** + * Method finds Bikes in container by parameters from + * SearchParameterContainer object. + * + * @param parCont object with parameters for searching. + * @return list with properly Bikes objects. + */ public List findBikesByParameter(SearchParameterContainer parCont) { Stream bikeStream = bikes.parallelStream() .filter(bike -> bike.getBikeType() == parCont.getBikeType() - && bike.getBrand().equalsIgnoreCase(parCont.getBrand()) - && bike.getWeight() >= parCont.getMinWeight() - && (parCont.getMaxWeight() == 0 || bike.getWeight() <= parCont.getMaxWeight()) - && (!parCont.isLightsOptionEntered() || parCont.isLightsPresent() == bike.isLightsPresent()) - && (parCont.getColor().isEmpty() || parCont.getColor().equalsIgnoreCase(bike.getColor())) - && bike.getPrice() >= parCont.getMinPrice() - && (parCont.getMaxPrice() == 0 || bike.getPrice() <= parCont.getMaxPrice())); + && bike.getBrand().equalsIgnoreCase(parCont.getBrand()) + && bike.getWeight() >= parCont.getMinWeight() + && (parCont.getMaxWeight() == 0 || bike.getWeight() <= parCont.getMaxWeight()) + && (!parCont.isLightsOptionEntered() || parCont.isLightsPresent() == bike.isLightsPresent()) + && (parCont.getColor().isEmpty() || parCont.getColor().equalsIgnoreCase(bike.getColor())) + && bike.getPrice() >= parCont.getMinPrice() + && (parCont.getMaxPrice() == 0 || bike.getPrice() <= parCont.getMaxPrice())); if (parCont.getBikeType() == BikeType.FOLDING_BIKE) { return bikeStream.map(bike -> (FoldingBike) bike) .filter(bike -> bike.getWheelSize() >= parCont.getMinWheelSize() - && (parCont.getMaxWheelSize() == 0 || bike.getWheelSize() <= parCont.getMaxWheelSize()) - && bike.getNumberOfGears() >= parCont.getMinNumberOfGears() - && (parCont.getMaxNumberOfGears() == 0 || bike.getNumberOfGears() <= parCont.getMaxNumberOfGears())) + && (parCont.getMaxWheelSize() == 0 || bike.getWheelSize() <= parCont.getMaxWheelSize()) + && bike.getNumberOfGears() >= parCont.getMinNumberOfGears() + && (parCont.getMaxNumberOfGears() == 0 || bike.getNumberOfGears() <= parCont.getMaxNumberOfGears())) .collect(Collectors.toList()); } return bikeStream.map(bike -> (AbstractElectroBike) bike) .filter(bike -> bike.getMaxSpeed() >= parCont.getMinMaxBikeSpeed() - && (parCont.getMaxMaxBikeSpeed() == 0 || bike.getMaxSpeed() <= parCont.getMaxMaxBikeSpeed()) - && bike.getBatteryCapacity() >= parCont.getMinBatteryCapacity() - && (parCont.getMaxBatteryCapacity() == 0 || bike.getBatteryCapacity() <= parCont.getMaxBatteryCapacity())) + && (parCont.getMaxMaxBikeSpeed() == 0 || bike.getMaxSpeed() <= parCont.getMaxMaxBikeSpeed()) + && bike.getBatteryCapacity() >= parCont.getMinBatteryCapacity() + && (parCont.getMaxBatteryCapacity() == 0 || bike.getBatteryCapacity() <= parCont.getMaxBatteryCapacity())) .collect(Collectors.toList()); } - - private Bike parseBike(String line) { - if (line.startsWith(BikeType.SPEEDELEC.toString())) { - String[] toConstructor = line.replace(BikeType.SPEEDELEC + " ", "").split("; "); - return new SpeedelecBike(toConstructor[0], Integer.parseInt(toConstructor[1]), - Integer.parseInt(toConstructor[2]), Boolean.parseBoolean(toConstructor[3]), - Integer.parseInt(toConstructor[4]), toConstructor[5], - Integer.parseInt(toConstructor[6])); - } - if(line.startsWith(BikeType.E_BIKE.toString())) { - String[] toConstructor = line.replace(BikeType.E_BIKE + " ", "").split("; "); - return new EBike(toConstructor[0], Integer.parseInt(toConstructor[1]), - Integer.parseInt(toConstructor[2]), Boolean.parseBoolean(toConstructor[3]), - Integer.parseInt(toConstructor[4]), toConstructor[5], - Integer.parseInt(toConstructor[6])); - } - if(line.startsWith(BikeType.FOLDING_BIKE.toString())) { - String[] toConstructor = line.replace(BikeType.FOLDING_BIKE + " ", "").split("; "); - return new FoldingBike(toConstructor[0], Integer.parseInt(toConstructor[1]), - Integer.parseInt(toConstructor[2]), Integer.parseInt(toConstructor[3]), - Boolean.parseBoolean(toConstructor[4]), toConstructor[5], - Integer.parseInt(toConstructor[6])); - } - return null; - } } diff --git a/src/main/java/com/ecobike/EcoBikeApplication.java b/src/main/java/com/ecobike/EcoBikeApplication.java index 65e95e8..74a4e58 100644 --- a/src/main/java/com/ecobike/EcoBikeApplication.java +++ b/src/main/java/com/ecobike/EcoBikeApplication.java @@ -1,37 +1,86 @@ package com.ecobike; -import java.io.IOException; +import com.ecobike.exception.IllegalDataSourceException; + import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +/** + * Main class of the EcoBike application. + */ public class EcoBikeApplication { + /** + * Communicator for communication with user. + */ public static final Communicator COMMUNICATOR = new ConsoleCommunicator(); - private static final DataHolder DATA_HOLDER = DataHolder.getInstance(); - public static final FileWriter FILE_WRITER = new FileWriter(); - - public static void main(String[] args) throws IOException { - Path bikeDataFile; - do { - COMMUNICATOR.writeMessage("Enter path to ecobike.txt :"); - bikeDataFile = Paths.get(COMMUNICATOR.readString()); - } while (!Files.isRegularFile(bikeDataFile)); - DATA_HOLDER.loadData(bikeDataFile); - FILE_WRITER.setFile(bikeDataFile); + /** + * Object for loading data from file. + */ + private static final BikeFileReader FILE_READER = new BikeFileReader(); + /** + * Object for writing data to file. + */ + public static final BikeFileWriter FILE_WRITER = new BikeFileWriter(); + /** + * Variable for keeping information was data changed + * since last writing to file or not. + */ + public static boolean isDataChanged = false; - Operation operation = null; - do { + /** + * Main method of the application. + * @param args system arguments. + */ + public static void main(String[] args) { + boolean isFileParsed = false; + while (!isFileParsed) { + Path bikeDataFile; + do { + COMMUNICATOR.writeMessage("Enter path to Bikes data file :"); + bikeDataFile = Paths.get(COMMUNICATOR.readNotEmptyString()); + } while (!Files.isRegularFile(bikeDataFile)); + FILE_READER.setPath(bikeDataFile); + FILE_WRITER.setFile(bikeDataFile); try { - operation = askOperation(); - CommandExecutor.execute(operation); - } catch (Exception e) { - e.printStackTrace(); - COMMUNICATOR.writeMessage("Error. Check entered data."); + FILE_READER.loadData(); + isFileParsed = true; + } catch (IllegalDataSourceException e) { + COMMUNICATOR.writeMessage("File has wrong format or empty"); + COMMUNICATOR.writeMessage(""); + } + } + isDataChanged = false; + while (true){ + Operation operation = askOperation(); + if (isDataChanged && operation == Operation.STOP_PROGRAM) { + COMMUNICATOR.writeMessage("You are going to exit without saving changed data.\n" + + "Do you wont to save data?"); + if (COMMUNICATOR.readBoolean()) { + CommandExecutor.execute(Operation.WRITE_TO_FILE); + } + } + if (!isDataChanged && operation == Operation.WRITE_TO_FILE) { + COMMUNICATOR.writeMessage("Data has not been changed or already has saved"); + continue; + } + if (operation == Operation.STOP_PROGRAM) { + if (COMMUNICATOR.confirmAction("EXIT from program")) { + COMMUNICATOR.writeMessage("Good bay!"); + return; + } + continue; } - } while (operation != Operation.STOP_PROGRAM); + CommandExecutor.execute(operation); + isDataChanged = operation != Operation.WRITE_TO_FILE && isDataChanged; + } } - private static Operation askOperation() throws IOException { + /** + * Method asks user to choose operation. + * @return chosen operation. + */ + private static Operation askOperation() { COMMUNICATOR.writeMessage(""); COMMUNICATOR.writeMessage("Please make your choice:"); COMMUNICATOR.writeMessage("\t 1 - Show the entire EcoBike catalog"); diff --git a/src/main/java/com/ecobike/Operation.java b/src/main/java/com/ecobike/Operation.java index 3fe4002..b60e590 100644 --- a/src/main/java/com/ecobike/Operation.java +++ b/src/main/java/com/ecobike/Operation.java @@ -1,11 +1,14 @@ package com.ecobike; +/** + * Enum contains all possible operations for user. + */ public enum Operation { SHOW_CATALOG, ADD_FOLDING_BIKE, ADD_SPEEDELEC_BIKE, ADD_E_BIKE, - FIND_FIRST_ITEM_BY_BRAND, + FIND_ITEMS_BY_BRAND, WRITE_TO_FILE, STOP_PROGRAM } diff --git a/src/main/java/com/ecobike/SearchParameterContainer.java b/src/main/java/com/ecobike/SearchParameterContainer.java index 57d5b80..3afd63c 100644 --- a/src/main/java/com/ecobike/SearchParameterContainer.java +++ b/src/main/java/com/ecobike/SearchParameterContainer.java @@ -2,6 +2,10 @@ import com.ecobike.model.BikeType; +/** + * Class container for transferring searching parameters from user + * to findBikesByParameter() method in DataHolder. + */ public class SearchParameterContainer { /** * Common bike parameters. diff --git a/src/main/java/com/ecobike/command/AddEBikeCommand.java b/src/main/java/com/ecobike/command/AddEBikeCommand.java index 6755bca..397e70d 100644 --- a/src/main/java/com/ecobike/command/AddEBikeCommand.java +++ b/src/main/java/com/ecobike/command/AddEBikeCommand.java @@ -1,27 +1,45 @@ package com.ecobike.command; +import com.ecobike.model.BikeType; import com.ecobike.model.EBike; +/** + * Class responsible for adding a new E-Bike to DataHolder + */ public class AddEBikeCommand implements Command { @Override public void execute() { COMMUNICATOR.writeMessage("You are adding new E-BIKE"); COMMUNICATOR.writeMessage("Enter brand:"); - String brand = COMMUNICATOR.readString(); + String brand = COMMUNICATOR.readNotEmptyString(); COMMUNICATOR.writeMessage("Enter max speed:"); - int maxSpeed = COMMUNICATOR.readInt(); + int maxSpeed = COMMUNICATOR.readPositiveInt(); COMMUNICATOR.writeMessage("Enter weight:"); - int weight = COMMUNICATOR.readInt(); + int weight = COMMUNICATOR.readPositiveInt(); COMMUNICATOR.writeMessage("Enter presence of lights:"); boolean isLightsPresent = COMMUNICATOR.readBoolean(); COMMUNICATOR.writeMessage("Enter battery capacity:"); - int batteryCapacity = COMMUNICATOR.readInt(); + int batteryCapacity = COMMUNICATOR.readPositiveInt(); COMMUNICATOR.writeMessage("Enter color:"); - String color = COMMUNICATOR.readString(); + String color = COMMUNICATOR.readNotEmptyString(); COMMUNICATOR.writeMessage("Enter price:"); - int price = COMMUNICATOR.readInt(); - DATA_HOLDER.addBike(new EBike(brand, maxSpeed, weight, - isLightsPresent, batteryCapacity, color, price)); - COMMUNICATOR.writeMessage("New E-BIKE BIKE added."); + int price = COMMUNICATOR.readPositiveInt(); + + String confirmMessage = String.format("add new %s with next parametrs:\n" + + "brand: %s\n" + + "maxSpeed: %d\n" + + "weight: %d\n" + + "lights presents: %s\n" + + "battery capacity: %d\n" + + "color: %s\n" + + "price: %d\n", + BikeType.E_BIKE, brand, maxSpeed, weight, + isLightsPresent, batteryCapacity, color, price); + + if (COMMUNICATOR.confirmAction(confirmMessage)) { + DATA_HOLDER.addBike(new EBike(brand, maxSpeed, weight, + isLightsPresent, batteryCapacity, color, price)); + COMMUNICATOR.writeMessage("New E-BIKE BIKE added."); + } } } diff --git a/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java b/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java index 58d1bc9..cc22c3a 100644 --- a/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java +++ b/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java @@ -1,27 +1,45 @@ package com.ecobike.command; +import com.ecobike.model.BikeType; import com.ecobike.model.FoldingBike; +/** + * Class responsible for adding a new Folding Bike to DataHolder + */ public class AddFoldingBikeCommand implements Command { @Override public void execute() { COMMUNICATOR.writeMessage("You are adding new FOLDING BIKE"); COMMUNICATOR.writeMessage("Enter brand:"); - String brand = COMMUNICATOR.readString(); + String brand = COMMUNICATOR.readNotEmptyString(); COMMUNICATOR.writeMessage("Enter wheel size:"); - int wheelSize = COMMUNICATOR.readInt(); + int wheelSize = COMMUNICATOR.readPositiveInt(); COMMUNICATOR.writeMessage("Enter number of gears:"); - int numberOfGears = COMMUNICATOR.readInt(); + int numberOfGears = COMMUNICATOR.readPositiveInt(); COMMUNICATOR.writeMessage("Enter weight:"); - int weight = COMMUNICATOR.readInt(); + int weight = COMMUNICATOR.readPositiveInt(); COMMUNICATOR.writeMessage("Enter presence of lights:"); boolean isLightsPresent = COMMUNICATOR.readBoolean(); COMMUNICATOR.writeMessage("Enter color:"); - String color = COMMUNICATOR.readString(); + String color = COMMUNICATOR.readNotEmptyString(); COMMUNICATOR.writeMessage("Enter price:"); - int price = COMMUNICATOR.readInt(); - DATA_HOLDER.addBike(new FoldingBike(brand, wheelSize, numberOfGears, - weight, isLightsPresent, color, price)); - COMMUNICATOR.writeMessage("New FOLDING BIKE added."); + int price = COMMUNICATOR.readPositiveInt(); + + String confirmMessage = String.format("add new %s with next parametrs:\n" + + "brand: %s\n" + + "wheelSize: %d\n" + + "numberOfGears: %d\n" + + "weight: %d\n" + + "lights presents: %s\n" + + "color: %s\n" + + "price: %d\n", + BikeType.FOLDING_BIKE, brand, wheelSize, numberOfGears, weight, + isLightsPresent, color, price); + + if (COMMUNICATOR.confirmAction(confirmMessage)) { + DATA_HOLDER.addBike(new FoldingBike(brand, wheelSize, numberOfGears, + weight, isLightsPresent, color, price)); + COMMUNICATOR.writeMessage("New FOLDING BIKE added."); + } } } diff --git a/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java b/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java index 52b8916..4918495 100644 --- a/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java +++ b/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java @@ -1,27 +1,45 @@ package com.ecobike.command; +import com.ecobike.model.BikeType; import com.ecobike.model.SpeedelecBike; +/** + * Class responsible for adding a new Speedelec to DataHolder + */ public class AddSpeedelecBikeCommand implements Command { @Override public void execute() { COMMUNICATOR.writeMessage("You are adding new SPEEDELEC BIKE"); COMMUNICATOR.writeMessage("Enter brand:"); - String brand = COMMUNICATOR.readString(); + String brand = COMMUNICATOR.readNotEmptyString(); COMMUNICATOR.writeMessage("Enter max speed:"); - int maxSpeed = COMMUNICATOR.readInt(); + int maxSpeed = COMMUNICATOR.readPositiveInt(); COMMUNICATOR.writeMessage("Enter weight:"); - int weight = COMMUNICATOR.readInt(); + int weight = COMMUNICATOR.readPositiveInt(); COMMUNICATOR.writeMessage("Enter presence of lights:"); boolean isLightsPresent = COMMUNICATOR.readBoolean(); COMMUNICATOR.writeMessage("Enter battery capacity:"); - int batteryCapacity = COMMUNICATOR.readInt(); + int batteryCapacity = COMMUNICATOR.readPositiveInt(); COMMUNICATOR.writeMessage("Enter color:"); - String color = COMMUNICATOR.readString(); + String color = COMMUNICATOR.readNotEmptyString(); COMMUNICATOR.writeMessage("Enter price:"); - int price = COMMUNICATOR.readInt(); - DATA_HOLDER.addBike(new SpeedelecBike(brand, maxSpeed, weight, - isLightsPresent, batteryCapacity, color, price)); - COMMUNICATOR.writeMessage("New SPEEDELEC BIKE added."); + int price = COMMUNICATOR.readPositiveInt(); + + String confirmMessage = String.format("add new %s with next parametrs:\n" + + "brand: %s\n" + + "maxSpeed: %d\n" + + "weight: %d\n" + + "lights presents: %s\n" + + "battery capacity: %d\n" + + "color: %s\n" + + "price: %d\n", + BikeType.SPEEDELEC, brand, maxSpeed, weight, + isLightsPresent, batteryCapacity, color, price); + + if (COMMUNICATOR.confirmAction(confirmMessage)) { + DATA_HOLDER.addBike(new SpeedelecBike(brand, maxSpeed, weight, + isLightsPresent, batteryCapacity, color, price)); + COMMUNICATOR.writeMessage("New SPEEDELEC BIKE added."); + } } } diff --git a/src/main/java/com/ecobike/command/Command.java b/src/main/java/com/ecobike/command/Command.java index 444469f..7fa422f 100644 --- a/src/main/java/com/ecobike/command/Command.java +++ b/src/main/java/com/ecobike/command/Command.java @@ -4,6 +4,10 @@ import com.ecobike.DataHolder; import com.ecobike.EcoBikeApplication; +/** + * Interface should be implemented by class responsible for executing + * operation specified by user. + */ public interface Command { Communicator COMMUNICATOR = EcoBikeApplication.COMMUNICATOR; DataHolder DATA_HOLDER = DataHolder.getInstance(); diff --git a/src/main/java/com/ecobike/command/ExitCommand.java b/src/main/java/com/ecobike/command/ExitCommand.java deleted file mode 100644 index c2c1c89..0000000 --- a/src/main/java/com/ecobike/command/ExitCommand.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.ecobike.command; - -public class ExitCommand implements Command { - @Override - public void execute() { - COMMUNICATOR.writeMessage("Good bay!"); - } -} diff --git a/src/main/java/com/ecobike/command/FindCommand.java b/src/main/java/com/ecobike/command/FindCommand.java index 09649fd..99a96d3 100644 --- a/src/main/java/com/ecobike/command/FindCommand.java +++ b/src/main/java/com/ecobike/command/FindCommand.java @@ -1,8 +1,14 @@ package com.ecobike.command; import com.ecobike.SearchParameterContainer; +import com.ecobike.model.Bike; import com.ecobike.model.BikeType; +import java.util.List; + +/** + * Class responsible for executing Find operation + */ public class FindCommand implements Command { @Override @@ -87,7 +93,13 @@ public void execute() { paramContainer.setMaxBatteryCapacity(COMMUNICATOR.readInt()); } - DATA_HOLDER.findBikesByParameter(paramContainer) - .forEach(bike -> COMMUNICATOR.writeMessage(bike.toString())); + List bikes = DATA_HOLDER.findBikesByParameter(paramContainer); + if (bikes.isEmpty()) { + COMMUNICATOR.writeMessage("No bikes matches your query."); + } else { + COMMUNICATOR.writeMessage(bikes.size() + " bikes matches your query:"); + COMMUNICATOR.writeMessage(""); + COMMUNICATOR.printBikes(bikes); + } } } diff --git a/src/main/java/com/ecobike/command/ShowCommand.java b/src/main/java/com/ecobike/command/ShowCommand.java index e70b491..ebd1f77 100644 --- a/src/main/java/com/ecobike/command/ShowCommand.java +++ b/src/main/java/com/ecobike/command/ShowCommand.java @@ -1,10 +1,12 @@ package com.ecobike.command; +/** + * Class responsible for executing Show operation. + */ public class ShowCommand implements Command { @Override public void execute() { - DATA_HOLDER.getUnmodifiableBikeList() - .forEach(bike -> COMMUNICATOR.writeMessage(bike.toString())); + COMMUNICATOR.printBikes(DATA_HOLDER.getUnmodifiableBikeList()); } } diff --git a/src/main/java/com/ecobike/command/WriteToFileCommand.java b/src/main/java/com/ecobike/command/WriteToFileCommand.java index d339811..f284478 100644 --- a/src/main/java/com/ecobike/command/WriteToFileCommand.java +++ b/src/main/java/com/ecobike/command/WriteToFileCommand.java @@ -2,11 +2,17 @@ import com.ecobike.EcoBikeApplication; +/** + * Class responsible for executing writing data to file operation. + */ public class WriteToFileCommand implements Command { @Override public void execute() { - EcoBikeApplication.FILE_WRITER.writeData(); - COMMUNICATOR.writeMessage("Data has been written successfully."); + String confirmMessage = "write data to file"; + if (COMMUNICATOR.confirmAction(confirmMessage)) { + EcoBikeApplication.FILE_WRITER.writeData(); + COMMUNICATOR.writeMessage("Data has been written successfully."); + } } } diff --git a/src/main/java/com/ecobike/exception/IllegalDataSourceException.java b/src/main/java/com/ecobike/exception/IllegalDataSourceException.java new file mode 100644 index 0000000..6216198 --- /dev/null +++ b/src/main/java/com/ecobike/exception/IllegalDataSourceException.java @@ -0,0 +1,7 @@ +package com.ecobike.exception; +/** + * Exception for throwing if data source specified by user + * has wrong data format and can not be parsed. + */ +public class IllegalDataSourceException extends Exception { +} diff --git a/src/main/java/com/ecobike/exception/IllegalFileFormatException.java b/src/main/java/com/ecobike/exception/IllegalFileFormatException.java new file mode 100644 index 0000000..2799f46 --- /dev/null +++ b/src/main/java/com/ecobike/exception/IllegalFileFormatException.java @@ -0,0 +1,8 @@ +package com.ecobike.exception; + +/** + * Exception for throwing if file with data specified by user + * has wrong data format and can not be parsed. + */ +public class IllegalFileFormatException extends IllegalDataSourceException { +} diff --git a/src/main/java/com/ecobike/model/AbstractElectroBike.java b/src/main/java/com/ecobike/model/AbstractElectroBike.java index cf91814..0d2dab8 100644 --- a/src/main/java/com/ecobike/model/AbstractElectroBike.java +++ b/src/main/java/com/ecobike/model/AbstractElectroBike.java @@ -2,6 +2,9 @@ import java.util.Objects; +/** + * Common class describes electro bike. + */ public abstract class AbstractElectroBike extends Bike { /** diff --git a/src/main/java/com/ecobike/model/Bike.java b/src/main/java/com/ecobike/model/Bike.java index 27ef48c..2324148 100644 --- a/src/main/java/com/ecobike/model/Bike.java +++ b/src/main/java/com/ecobike/model/Bike.java @@ -2,6 +2,9 @@ import java.util.Objects; +/** + * Common class describes abstract bike. + */ public abstract class Bike { /** * Type of the bike. diff --git a/src/main/java/com/ecobike/model/BikeType.java b/src/main/java/com/ecobike/model/BikeType.java index df2c001..d0cfb09 100644 --- a/src/main/java/com/ecobike/model/BikeType.java +++ b/src/main/java/com/ecobike/model/BikeType.java @@ -1,11 +1,17 @@ package com.ecobike.model; +/** + * Enum contains all possible bike types. + */ public enum BikeType { FOLDING_BIKE("FOLDING BIKE"), E_BIKE("E-BIKE"), SPEEDELEC("SPEEDELEC"); - private String type; + /** + * String representation of the type. + */ + private final String type; BikeType(String type) { this.type = type; diff --git a/src/main/java/com/ecobike/model/EBike.java b/src/main/java/com/ecobike/model/EBike.java index ebbe890..f22674c 100644 --- a/src/main/java/com/ecobike/model/EBike.java +++ b/src/main/java/com/ecobike/model/EBike.java @@ -1,5 +1,8 @@ package com.ecobike.model; +/** + * Class describes EBike. + */ public class EBike extends AbstractElectroBike { public EBike(String brand, diff --git a/src/main/java/com/ecobike/model/FoldingBike.java b/src/main/java/com/ecobike/model/FoldingBike.java index c060291..7159b93 100644 --- a/src/main/java/com/ecobike/model/FoldingBike.java +++ b/src/main/java/com/ecobike/model/FoldingBike.java @@ -2,6 +2,9 @@ import java.util.Objects; +/** + * Class describes Folding Bike. + */ public class FoldingBike extends Bike { /** diff --git a/src/main/java/com/ecobike/model/SpeedelecBike.java b/src/main/java/com/ecobike/model/SpeedelecBike.java index 380b0b8..d26880a 100644 --- a/src/main/java/com/ecobike/model/SpeedelecBike.java +++ b/src/main/java/com/ecobike/model/SpeedelecBike.java @@ -1,5 +1,8 @@ package com.ecobike.model; +/** + * Class describes Speedelec bike. + */ public class SpeedelecBike extends AbstractElectroBike { public SpeedelecBike(String brand, From a0747f3571ad472acf72ecf53c1fd3bd7457cac2 Mon Sep 17 00:00:00 2001 From: Leonid Date: Thu, 30 Jul 2020 19:55:02 +0300 Subject: [PATCH 06/19] exception fixed --- src/main/java/com/ecobike/EcoBikeApplication.java | 4 ++-- .../com/ecobike/exception/IllegalDataSourceException.java | 7 ------- .../com/ecobike/exception/IllegalFileFormatException.java | 2 +- 3 files changed, 3 insertions(+), 10 deletions(-) delete mode 100644 src/main/java/com/ecobike/exception/IllegalDataSourceException.java diff --git a/src/main/java/com/ecobike/EcoBikeApplication.java b/src/main/java/com/ecobike/EcoBikeApplication.java index 74a4e58..27692a0 100644 --- a/src/main/java/com/ecobike/EcoBikeApplication.java +++ b/src/main/java/com/ecobike/EcoBikeApplication.java @@ -1,6 +1,6 @@ package com.ecobike; -import com.ecobike.exception.IllegalDataSourceException; +import com.ecobike.exception.IllegalFileFormatException; import java.nio.file.Files; import java.nio.file.Path; @@ -45,7 +45,7 @@ public static void main(String[] args) { try { FILE_READER.loadData(); isFileParsed = true; - } catch (IllegalDataSourceException e) { + } catch (IllegalFileFormatException e) { COMMUNICATOR.writeMessage("File has wrong format or empty"); COMMUNICATOR.writeMessage(""); } diff --git a/src/main/java/com/ecobike/exception/IllegalDataSourceException.java b/src/main/java/com/ecobike/exception/IllegalDataSourceException.java deleted file mode 100644 index 6216198..0000000 --- a/src/main/java/com/ecobike/exception/IllegalDataSourceException.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.ecobike.exception; -/** - * Exception for throwing if data source specified by user - * has wrong data format and can not be parsed. - */ -public class IllegalDataSourceException extends Exception { -} diff --git a/src/main/java/com/ecobike/exception/IllegalFileFormatException.java b/src/main/java/com/ecobike/exception/IllegalFileFormatException.java index 2799f46..a0a002d 100644 --- a/src/main/java/com/ecobike/exception/IllegalFileFormatException.java +++ b/src/main/java/com/ecobike/exception/IllegalFileFormatException.java @@ -4,5 +4,5 @@ * Exception for throwing if file with data specified by user * has wrong data format and can not be parsed. */ -public class IllegalFileFormatException extends IllegalDataSourceException { +public class IllegalFileFormatException extends Exception { } From 40275adc28c02623a140a5c930a8c668650aee2e Mon Sep 17 00:00:00 2001 From: Leonid Date: Thu, 30 Jul 2020 22:16:19 +0300 Subject: [PATCH 07/19] wrong lines in file printing fixed --- src/main/java/com/ecobike/BikeFileReader.java | 4 +++- src/main/resources/ecobike.txt | 7 +++---- src/main/resources/wrongBikeFormat.txt | 2 ++ 3 files changed, 8 insertions(+), 5 deletions(-) create mode 100644 src/main/resources/wrongBikeFormat.txt diff --git a/src/main/java/com/ecobike/BikeFileReader.java b/src/main/java/com/ecobike/BikeFileReader.java index 67df6fe..29ee436 100644 --- a/src/main/java/com/ecobike/BikeFileReader.java +++ b/src/main/java/com/ecobike/BikeFileReader.java @@ -39,16 +39,18 @@ public void loadData() throws IllegalFileFormatException { e.printStackTrace(); } List bikes = new ArrayList<>(lines.size()); + List wrongLinesInfo = new ArrayList<>(); for (int i = 0; i < lines.size(); i++) { try { bikes.add(parseBike(lines.get(i))); } catch (IllegalArgumentException e) { - communicator.writeMessage("Line No. " + (i + 1) + "has wrong format"); + wrongLinesInfo.add("Line No. " + (i + 1) + " has wrong format"); } } if (bikes.isEmpty()) { throw new IllegalFileFormatException(); } + wrongLinesInfo.forEach(communicator::writeMessage); dataHolder.addBikes(bikes); communicator.writeMessage(bikes.size() + " bike items has been read from the file"); } diff --git a/src/main/resources/ecobike.txt b/src/main/resources/ecobike.txt index 8f3681f..7985b6b 100644 --- a/src/main/resources/ecobike.txt +++ b/src/main/resources/ecobike.txt @@ -1,12 +1,12 @@ SPEEDELEC Booster; 35; 10900; false; 13200; green; 1279 -SPEEDELEC E-Scooter; 60; 15300; false; 14800; marine; 309 +SPEELEC E-Scooter; 60; 15300; false; 14800; marine; 309 E-BIKE Lankeleisi; 65; 24200; false; 10000; black; 2399 E-BIKE Lankeleisi; 50; 21600; false; 30000; flame; 859 -FOLDING BIKE Benetti; 24; 27; 11400; false; rose; 1009 +FOING BIKE Benetti; 24; 27; 11400; false; rose; 1009 FOLDING BIKE Benetti; 24; 6; 9400; true; silver; 1195 FOLDING BIKE Formula; 16; 21; 12200; true; flame; 269 E-BIKE ElectrO; 20; 19300; true; 14000; beige; 1025 -SPEEDELEC Dualtron; 30; 14400; true; 6500; dark gray; 1019 +SPEEDELEC Dualtron; но; 14400; true; 6500; dark gray; 1019 FOLDING BIKE Intertool; 24; 21; 12900; true; coral; 1565 E-BIKE Gazelle; 45; 25200; true; 11000; yellow; 855 SPEEDELEC EcoRide; 15; 8300; true; 15600; blue; 1055 @@ -998,4 +998,3 @@ FOLDING BIKE Stern; 20; 27; 16500; false; white; 439 SPEEDELEC Dualtron; 60; 20500; false; 15600; golden; 869 SPEEDELEC Ultron; 60; 7000; false; 11400; lemon; 1055 E-BIKE Ferrari; 60; 28900; false; 21000; brown; 825 -FOLDING BIKE WWWWW; 2; 2; 1; true; 1; 1 diff --git a/src/main/resources/wrongBikeFormat.txt b/src/main/resources/wrongBikeFormat.txt new file mode 100644 index 0000000..f48edce --- /dev/null +++ b/src/main/resources/wrongBikeFormat.txt @@ -0,0 +1,2 @@ +12 +99 \ No newline at end of file From 8d7ee58aca2b21c37748310148f11f43b3277fa2 Mon Sep 17 00:00:00 2001 From: Leonid Date: Fri, 31 Jul 2020 15:38:01 +0300 Subject: [PATCH 08/19] tests created --- pom.xml | 8 + src/main/java/com/ecobike/BikeFileReader.java | 92 ---------- src/main/java/com/ecobike/BikeFileWriter.java | 40 ----- src/main/java/com/ecobike/DataHolder.java | 90 +++++++--- .../java/com/ecobike/EcoBikeApplication.java | 26 ++- .../com/ecobike/SearchParameterContainer.java | 4 +- .../ecobike/command/WriteToFileCommand.java | 2 +- src/main/java/com/ecobike/dao/BikeDAO.java | 32 ++++ .../java/com/ecobike/dao/FileBikeDAO.java | 166 ++++++++++++++++++ .../exception/IllegalDataSourceException.java | 8 + .../exception/IllegalFileFormatException.java | 8 - src/main/java/com/ecobike/model/Bike.java | 18 +- .../resources/test/fileWithFiveTrueBikes.txt | 10 ++ src/main/resources/test/wrongBikeFormat.txt | 2 + src/main/resources/wrongBikeFormat.txt | 2 - src/test/java/com/ecobike/DataHolderTest.java | 85 +++++++++ .../java/com/ecobike/FileBikeDAOTest.java | 59 +++++++ 17 files changed, 466 insertions(+), 186 deletions(-) delete mode 100644 src/main/java/com/ecobike/BikeFileReader.java delete mode 100644 src/main/java/com/ecobike/BikeFileWriter.java create mode 100644 src/main/java/com/ecobike/dao/BikeDAO.java create mode 100644 src/main/java/com/ecobike/dao/FileBikeDAO.java create mode 100644 src/main/java/com/ecobike/exception/IllegalDataSourceException.java delete mode 100644 src/main/java/com/ecobike/exception/IllegalFileFormatException.java create mode 100644 src/main/resources/test/fileWithFiveTrueBikes.txt create mode 100644 src/main/resources/test/wrongBikeFormat.txt delete mode 100644 src/main/resources/wrongBikeFormat.txt create mode 100644 src/test/java/com/ecobike/DataHolderTest.java create mode 100644 src/test/java/com/ecobike/FileBikeDAOTest.java diff --git a/pom.xml b/pom.xml index 1d48358..2af9252 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,14 @@ https://raw.githubusercontent.com/mate-academy/style-guides/master/java/checkstyle.xml + + + junit + junit + 4.12 + test + + diff --git a/src/main/java/com/ecobike/BikeFileReader.java b/src/main/java/com/ecobike/BikeFileReader.java deleted file mode 100644 index 29ee436..0000000 --- a/src/main/java/com/ecobike/BikeFileReader.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.ecobike; - -import com.ecobike.exception.IllegalFileFormatException; -import com.ecobike.model.*; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Class responsible for reading data from file and parsing text to Bike objects. - */ -public class BikeFileReader { - /** - * Path to data file. - */ - private Path file; - private final DataHolder dataHolder = DataHolder.getInstance(); - private final Communicator communicator = EcoBikeApplication.COMMUNICATOR; - - public void setPath(Path file) { - this.file = file; - } - - /** - * Method reads text from file and loads parsed Bike objects to DataHolder. - * - * @throws IllegalFileFormatException if no one Bike object parsed from file. - */ - public void loadData() throws IllegalFileFormatException { - List lines = Collections.emptyList(); - try { - lines = Files.lines(file).collect(Collectors.toList()); - } catch (IOException e) { - e.printStackTrace(); - } - List bikes = new ArrayList<>(lines.size()); - List wrongLinesInfo = new ArrayList<>(); - for (int i = 0; i < lines.size(); i++) { - try { - bikes.add(parseBike(lines.get(i))); - } catch (IllegalArgumentException e) { - wrongLinesInfo.add("Line No. " + (i + 1) + " has wrong format"); - } - } - if (bikes.isEmpty()) { - throw new IllegalFileFormatException(); - } - wrongLinesInfo.forEach(communicator::writeMessage); - dataHolder.addBikes(bikes); - communicator.writeMessage(bikes.size() + " bike items has been read from the file"); - } - - /** - * Method parses single String line to Bike object. - * - * @param line string to be parsed. - * @return parsed Bike object - */ - private Bike parseBike(String line) { - try { - if (line.startsWith(BikeType.SPEEDELEC.toString())) { - String[] toConstructor = line.replace(BikeType.SPEEDELEC + " ", "").split("; "); - return new SpeedelecBike(toConstructor[0], Integer.parseInt(toConstructor[1]), - Integer.parseInt(toConstructor[2]), Boolean.parseBoolean(toConstructor[3]), - Integer.parseInt(toConstructor[4]), toConstructor[5], - Integer.parseInt(toConstructor[6])); - } - if (line.startsWith(BikeType.E_BIKE.toString())) { - String[] toConstructor = line.replace(BikeType.E_BIKE + " ", "").split("; "); - return new EBike(toConstructor[0], Integer.parseInt(toConstructor[1]), - Integer.parseInt(toConstructor[2]), Boolean.parseBoolean(toConstructor[3]), - Integer.parseInt(toConstructor[4]), toConstructor[5], - Integer.parseInt(toConstructor[6])); - } - if (line.startsWith(BikeType.FOLDING_BIKE.toString())) { - String[] toConstructor = line.replace(BikeType.FOLDING_BIKE + " ", "").split("; "); - return new FoldingBike(toConstructor[0], Integer.parseInt(toConstructor[1]), - Integer.parseInt(toConstructor[2]), Integer.parseInt(toConstructor[3]), - Boolean.parseBoolean(toConstructor[4]), toConstructor[5], - Integer.parseInt(toConstructor[6])); - } - } catch (RuntimeException e) { - throw new IllegalArgumentException(); - } - throw new IllegalArgumentException(); - } -} diff --git a/src/main/java/com/ecobike/BikeFileWriter.java b/src/main/java/com/ecobike/BikeFileWriter.java deleted file mode 100644 index 079f509..0000000 --- a/src/main/java/com/ecobike/BikeFileWriter.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.ecobike; - -import com.ecobike.model.Bike; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Class responsible for writing to file Bike objects in text format. - * Old data in file will be replaced by new one. - */ -public class BikeFileWriter { - /** - * Path to data file. - */ - private Path file; - private final DataHolder dataHolder = DataHolder.getInstance(); - - public void setFile(Path file) { - this.file = file; - } - - /** - * Method writes to file Bike objects in text format. - * Old data in file will be replaced with new one. - */ - public void writeData() { - List dataToWrite = dataHolder.getUnmodifiableBikeList().stream() - .map(Bike::toFileWriterString) - .collect(Collectors.toList()); - try { - Files.write(file, dataToWrite); - } catch (IOException e) { - e.printStackTrace(); - } - } -} diff --git a/src/main/java/com/ecobike/DataHolder.java b/src/main/java/com/ecobike/DataHolder.java index 205a86c..d0b7d09 100644 --- a/src/main/java/com/ecobike/DataHolder.java +++ b/src/main/java/com/ecobike/DataHolder.java @@ -9,6 +9,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.concurrent.CountDownLatch; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -23,9 +24,14 @@ public class DataHolder { */ private final List bikes = new ArrayList<>(); + /** + * We need this Latch to be sure that our new Thread in addBikes() method, + * that sorts Bike storage, will take monitor before any other thread. + */ + private CountDownLatch countDownLatch = new CountDownLatch(0); + private DataHolder() { } - public static DataHolder getInstance() { return INSTANCE; } @@ -35,8 +41,13 @@ public static DataHolder getInstance() { * * @param bikesToAdd collection with Bikes for adding. */ - public void addBikes(Collection bikesToAdd) { + public synchronized void addBikes(Collection bikesToAdd) { bikes.addAll(bikesToAdd); + countDownLatch = new CountDownLatch(1); + new Thread(()->{ + sort(); + countDownLatch.countDown(); + }).start(); EcoBikeApplication.isDataChanged = true; } @@ -46,7 +57,15 @@ public void addBikes(Collection bikesToAdd) { * @param bike to be added. */ public void addBike(Bike bike) { - bikes.add(bike); + try { + countDownLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + synchronized (this) { + bikes.add(bike); + sort(); + } EcoBikeApplication.isDataChanged = true; } @@ -56,7 +75,14 @@ public void addBike(Bike bike) { * @return unmodifiable list of Bikes. */ public List getUnmodifiableBikeList() { - return Collections.unmodifiableList(bikes); + try { + countDownLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + synchronized (this) { + return Collections.unmodifiableList(bikes); + } } /** @@ -67,29 +93,43 @@ public List getUnmodifiableBikeList() { * @return list with properly Bikes objects. */ public List findBikesByParameter(SearchParameterContainer parCont) { - Stream bikeStream = bikes.parallelStream() - .filter(bike -> bike.getBikeType() == parCont.getBikeType() - && bike.getBrand().equalsIgnoreCase(parCont.getBrand()) - && bike.getWeight() >= parCont.getMinWeight() - && (parCont.getMaxWeight() == 0 || bike.getWeight() <= parCont.getMaxWeight()) - && (!parCont.isLightsOptionEntered() || parCont.isLightsPresent() == bike.isLightsPresent()) - && (parCont.getColor().isEmpty() || parCont.getColor().equalsIgnoreCase(bike.getColor())) - && bike.getPrice() >= parCont.getMinPrice() - && (parCont.getMaxPrice() == 0 || bike.getPrice() <= parCont.getMaxPrice())); + try { + countDownLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + synchronized (this) { + Stream bikeStream = bikes.parallelStream() + .filter(bike -> bike.getBikeType() == parCont.getBikeType() + && bike.getBrand().equalsIgnoreCase(parCont.getBrand()) + && bike.getWeight() >= parCont.getMinWeight() + && (parCont.getMaxWeight() == 0 || bike.getWeight() <= parCont.getMaxWeight()) + && (!parCont.isLightsOptionEntered() || parCont.isLightsPresent() == bike.isLightsPresent()) + && (parCont.getColor().isEmpty() || parCont.getColor().equalsIgnoreCase(bike.getColor())) + && bike.getPrice() >= parCont.getMinPrice() + && (parCont.getMaxPrice() == 0 || bike.getPrice() <= parCont.getMaxPrice())); - if (parCont.getBikeType() == BikeType.FOLDING_BIKE) { - return bikeStream.map(bike -> (FoldingBike) bike) - .filter(bike -> bike.getWheelSize() >= parCont.getMinWheelSize() - && (parCont.getMaxWheelSize() == 0 || bike.getWheelSize() <= parCont.getMaxWheelSize()) - && bike.getNumberOfGears() >= parCont.getMinNumberOfGears() - && (parCont.getMaxNumberOfGears() == 0 || bike.getNumberOfGears() <= parCont.getMaxNumberOfGears())) + if (parCont.getBikeType() == BikeType.FOLDING_BIKE) { + return bikeStream.map(bike -> (FoldingBike) bike) + .filter(bike -> bike.getWheelSize() >= parCont.getMinWheelSize() + && (parCont.getMaxWheelSize() == 0 || bike.getWheelSize() <= parCont.getMaxWheelSize()) + && bike.getNumberOfGears() >= parCont.getMinNumberOfGears() + && (parCont.getMaxNumberOfGears() == 0 || bike.getNumberOfGears() <= parCont.getMaxNumberOfGears())) + .collect(Collectors.toList()); + } + return bikeStream.map(bike -> (AbstractElectroBike) bike) + .filter(bike -> bike.getMaxSpeed() >= parCont.getMinMaxBikeSpeed() + && (parCont.getMaxMaxBikeSpeed() == 0 || bike.getMaxSpeed() <= parCont.getMaxMaxBikeSpeed()) + && bike.getBatteryCapacity() >= parCont.getMinBatteryCapacity() + && (parCont.getMaxBatteryCapacity() == 0 || bike.getBatteryCapacity() <= parCont.getMaxBatteryCapacity())) .collect(Collectors.toList()); } - return bikeStream.map(bike -> (AbstractElectroBike) bike) - .filter(bike -> bike.getMaxSpeed() >= parCont.getMinMaxBikeSpeed() - && (parCont.getMaxMaxBikeSpeed() == 0 || bike.getMaxSpeed() <= parCont.getMaxMaxBikeSpeed()) - && bike.getBatteryCapacity() >= parCont.getMinBatteryCapacity() - && (parCont.getMaxBatteryCapacity() == 0 || bike.getBatteryCapacity() <= parCont.getMaxBatteryCapacity())) - .collect(Collectors.toList()); + } + + /** + * Method sorts Bikes in container. + */ + private void sort() { + Collections.sort(bikes); } } diff --git a/src/main/java/com/ecobike/EcoBikeApplication.java b/src/main/java/com/ecobike/EcoBikeApplication.java index 27692a0..693ec5d 100644 --- a/src/main/java/com/ecobike/EcoBikeApplication.java +++ b/src/main/java/com/ecobike/EcoBikeApplication.java @@ -1,10 +1,11 @@ package com.ecobike; -import com.ecobike.exception.IllegalFileFormatException; +import com.ecobike.dao.BikeDAO; +import com.ecobike.dao.FileBikeDAO; +import com.ecobike.exception.IllegalDataSourceException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; /** * Main class of the EcoBike application. @@ -15,13 +16,9 @@ public class EcoBikeApplication { */ public static final Communicator COMMUNICATOR = new ConsoleCommunicator(); /** - * Object for loading data from file. + * Object for loading data from and writing data to file. */ - private static final BikeFileReader FILE_READER = new BikeFileReader(); - /** - * Object for writing data to file. - */ - public static final BikeFileWriter FILE_WRITER = new BikeFileWriter(); + public static final BikeDAO BIKE_DAO = FileBikeDAO.getInstance(); /** * Variable for keeping information was data changed * since last writing to file or not. @@ -35,17 +32,16 @@ public class EcoBikeApplication { public static void main(String[] args) { boolean isFileParsed = false; while (!isFileParsed) { - Path bikeDataFile; + String bikeDataFile; do { COMMUNICATOR.writeMessage("Enter path to Bikes data file :"); - bikeDataFile = Paths.get(COMMUNICATOR.readNotEmptyString()); - } while (!Files.isRegularFile(bikeDataFile)); - FILE_READER.setPath(bikeDataFile); - FILE_WRITER.setFile(bikeDataFile); + bikeDataFile = COMMUNICATOR.readNotEmptyString(); + } while (!Files.isRegularFile(Path.of(bikeDataFile))); + BIKE_DAO.setSource(bikeDataFile); try { - FILE_READER.loadData(); + BIKE_DAO.loadBikes(); isFileParsed = true; - } catch (IllegalFileFormatException e) { + } catch (IllegalDataSourceException e) { COMMUNICATOR.writeMessage("File has wrong format or empty"); COMMUNICATOR.writeMessage(""); } diff --git a/src/main/java/com/ecobike/SearchParameterContainer.java b/src/main/java/com/ecobike/SearchParameterContainer.java index 3afd63c..2be3f45 100644 --- a/src/main/java/com/ecobike/SearchParameterContainer.java +++ b/src/main/java/com/ecobike/SearchParameterContainer.java @@ -3,12 +3,12 @@ import com.ecobike.model.BikeType; /** - * Class container for transferring searching parameters from user + * Class-container for transferring searching parameters from user * to findBikesByParameter() method in DataHolder. */ public class SearchParameterContainer { /** - * Common bike parameters. + * Common Bike parameters. */ private BikeType bikeType; private String brand; diff --git a/src/main/java/com/ecobike/command/WriteToFileCommand.java b/src/main/java/com/ecobike/command/WriteToFileCommand.java index f284478..86872b2 100644 --- a/src/main/java/com/ecobike/command/WriteToFileCommand.java +++ b/src/main/java/com/ecobike/command/WriteToFileCommand.java @@ -11,7 +11,7 @@ public class WriteToFileCommand implements Command { public void execute() { String confirmMessage = "write data to file"; if (COMMUNICATOR.confirmAction(confirmMessage)) { - EcoBikeApplication.FILE_WRITER.writeData(); + EcoBikeApplication.BIKE_DAO.writeBikes(); COMMUNICATOR.writeMessage("Data has been written successfully."); } } diff --git a/src/main/java/com/ecobike/dao/BikeDAO.java b/src/main/java/com/ecobike/dao/BikeDAO.java new file mode 100644 index 0000000..e44ec12 --- /dev/null +++ b/src/main/java/com/ecobike/dao/BikeDAO.java @@ -0,0 +1,32 @@ +package com.ecobike.dao; + +import com.ecobike.exception.IllegalDataSourceException; + +/** + * Interface should be implemented by class + * responsible for access to data source with Bike objects. + */ +public interface BikeDAO { + + /** + * Method sets data source. + * + * @param address string with parameters needed + * for connection to data source. + */ + void setSource(String address); + + /** + * Method reads data from data source and + * loads parsed Bike objects to DataHolder. + * + * @throws IllegalDataSourceException if no one Bike object parsed from file. + */ + void loadBikes() throws IllegalDataSourceException; + + /** + * Method writes to data source Bike objects. + * Old data must be replaced with new one. + */ + void writeBikes(); +} diff --git a/src/main/java/com/ecobike/dao/FileBikeDAO.java b/src/main/java/com/ecobike/dao/FileBikeDAO.java new file mode 100644 index 0000000..a56f870 --- /dev/null +++ b/src/main/java/com/ecobike/dao/FileBikeDAO.java @@ -0,0 +1,166 @@ +package com.ecobike.dao; + +import com.ecobike.DataHolder; +import com.ecobike.exception.IllegalDataSourceException; +import com.ecobike.model.*; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.ecobike.EcoBikeApplication.COMMUNICATOR; + +/** + * Singleton class responsible for reading data from file and parsing text to Bike objects, + * and for writing to file Bike objects in text format. + * Old data in file will be replaced by new one. + */ +public class FileBikeDAO implements BikeDAO { + + private final static FileBikeDAO INSTANCE = new FileBikeDAO(); + + private FileBikeDAO() { + } + + public static FileBikeDAO getInstance() { + return INSTANCE; + } + /** + * Path to data file. + */ + private Path file; + + private static final DataHolder DATA_HOLDER = DataHolder.getInstance(); + + /** + * Method sets data file. + * + * @param address string with path to data source. + */ + @Override + public void setSource(String address) { + file = Paths.get(address); + } + + /** + * Method reads text from file and loads parsed Bike objects to DataHolder. + * + * @throws IllegalDataSourceException if no one Bike object parsed from file. + */ + @Override + public void loadBikes() throws IllegalDataSourceException { + List lines = Collections.emptyList(); + try { + lines = Files.readAllLines(file); + } catch (IOException e) { + e.printStackTrace(); + } + List bikes = new ArrayList<>(lines.size()); + List wrongLinesInfo = new ArrayList<>(); + for (int i = 0; i < lines.size(); i++) { + try { + bikes.add(parseBike(lines.get(i))); + } catch (IllegalArgumentException e) { + wrongLinesInfo.add("Line No. " + (i + 1) + " has wrong format"); + } + } + if (bikes.isEmpty()) { + throw new IllegalDataSourceException(); + } + wrongLinesInfo.forEach(COMMUNICATOR::writeMessage); + DATA_HOLDER.addBikes(bikes); + COMMUNICATOR.writeMessage(bikes.size() + " bike items has been read from the file"); + } + + /** + * Method writes to file Bike objects in text format. + * Old data in file will be replaced with new one. + */ + @Override + public void writeBikes() { + List dataToWrite = DATA_HOLDER.getUnmodifiableBikeList().stream() + .map(Bike::toFileWriterString) + .collect(Collectors.toList()); + try { + Files.write(file, dataToWrite); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Method parses single String line to Bike object. + * + * @param line string to be parsed. + * @return parsed Bike object + */ + private Bike parseBike(String line) { + try { + if (line.startsWith(BikeType.SPEEDELEC.toString())) { + String[] parameters = line.replace(BikeType.SPEEDELEC + " ", "").split("; "); + checkParametersForEmpties(parameters); + return new SpeedelecBike(parameters[0], Integer.parseInt(parameters[1]), + Integer.parseInt(parameters[2]), parseBoolean(parameters[3]), + Integer.parseInt(parameters[4]), parameters[5], + Integer.parseInt(parameters[6])); + } + if (line.startsWith(BikeType.E_BIKE.toString())) { + String[] parameters = line.replace(BikeType.E_BIKE + " ", "").split("; "); + checkParametersForEmpties(parameters); + return new EBike(parameters[0], Integer.parseInt(parameters[1]), + Integer.parseInt(parameters[2]), parseBoolean(parameters[3]), + Integer.parseInt(parameters[4]), parameters[5], + Integer.parseInt(parameters[6])); + } + if (line.startsWith(BikeType.FOLDING_BIKE.toString())) { + String[] parameters = line.replace(BikeType.FOLDING_BIKE + " ", "").split("; "); + checkParametersForEmpties(parameters); + return new FoldingBike(parameters[0], Integer.parseInt(parameters[1]), + Integer.parseInt(parameters[2]), Integer.parseInt(parameters[3]), + parseBoolean(parameters[4]), parameters[5], + Integer.parseInt(parameters[6])); + } + } catch (RuntimeException e) { + throw new IllegalArgumentException(); + } + throw new IllegalArgumentException(); + } + + /** + * Method parses string to boolean value. + * + * @param value string to be parsed + * @return TRUE if value equals ignore case "true" + * or FALSE if value equals ignore case "false". + * Otherwise throws IllegalArgumentException. + */ + private boolean parseBoolean(String value) { + if ("true".equalsIgnoreCase(value)) { + return true; + } + if ("false".equalsIgnoreCase(value)) { + return false; + } + throw new IllegalArgumentException(); + } + + /** + * Method checks string array for presence at least one + * empty string. If present - throws IllegalArgumentException. + * + * @param parameters string array. + */ + private void checkParametersForEmpties(String[] parameters) { + boolean isEmptyPresent = Stream.of(parameters) + .anyMatch(String::isEmpty); + if (isEmptyPresent) { + throw new IllegalArgumentException(); + } + } +} diff --git a/src/main/java/com/ecobike/exception/IllegalDataSourceException.java b/src/main/java/com/ecobike/exception/IllegalDataSourceException.java new file mode 100644 index 0000000..e6afc10 --- /dev/null +++ b/src/main/java/com/ecobike/exception/IllegalDataSourceException.java @@ -0,0 +1,8 @@ +package com.ecobike.exception; + +/** + * Exception for throwing if data source with data specified by user + * has wrong data format and/or can not be parsed. + */ +public class IllegalDataSourceException extends Exception { +} diff --git a/src/main/java/com/ecobike/exception/IllegalFileFormatException.java b/src/main/java/com/ecobike/exception/IllegalFileFormatException.java deleted file mode 100644 index a0a002d..0000000 --- a/src/main/java/com/ecobike/exception/IllegalFileFormatException.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.ecobike.exception; - -/** - * Exception for throwing if file with data specified by user - * has wrong data format and can not be parsed. - */ -public class IllegalFileFormatException extends Exception { -} diff --git a/src/main/java/com/ecobike/model/Bike.java b/src/main/java/com/ecobike/model/Bike.java index 2324148..572e984 100644 --- a/src/main/java/com/ecobike/model/Bike.java +++ b/src/main/java/com/ecobike/model/Bike.java @@ -1,11 +1,12 @@ package com.ecobike.model; +import java.util.Comparator; import java.util.Objects; /** * Common class describes abstract bike. */ -public abstract class Bike { +public abstract class Bike implements Comparable { /** * Type of the bike. */ @@ -34,6 +35,7 @@ public Bike(String brand, int weight, boolean isLightsPresent, String color, int /** * Method converts bike to specific String format * for writing to file. + * * @return String representation of the bike. */ public abstract String toFileWriterString(); @@ -55,6 +57,20 @@ public int hashCode() { return Objects.hash(brand, weight, isLightsPresent, color, price); } + /** + * Method compares Bike objects by Type, then by Brand, then by price. + * + * @param o object to be compere with this. + * @return int value. + */ + @Override + public int compareTo(Bike o) { + return Comparator.comparing(Bike::getBikeType, Comparator.comparing(BikeType::toString)) + .thenComparing(Bike::getBrand) + .thenComparing(Bike::getPrice) + .compare(this, o); + } + public BikeType getBikeType() { return bikeType; } diff --git a/src/main/resources/test/fileWithFiveTrueBikes.txt b/src/main/resources/test/fileWithFiveTrueBikes.txt new file mode 100644 index 0000000..f80a3f1 --- /dev/null +++ b/src/main/resources/test/fileWithFiveTrueBikes.txt @@ -0,0 +1,10 @@ +SPEEDELEC EcoRide; 50; 8400; false; 8600; brown; 1609 +FOLDING BIKE BMW; 20; 7; 14400; false; lemon; 1085 +E-BIKE Ecofect; 30; 19200; true; 15000; lemon; 2919 +E-BIKE Gazelle; 25; 22200; true; 26000; silver; 1735 +WRONG Speedway; 25; 6800; false; 15200; blue; 915 +E-BIKE Ferrari; 55; WRONG; false; 28000; olive; 3055 +FOLDING BIKE BMW; 16; 9; 11000; WRONG; emerald; 1459 +FOLDING BIKE BMW; 16; 9; ; true; emerald; 1459 +FOLDING BIKE BMW; 16; 9; 11000; true; ; 1459 +FOLDING BIKE BMW; 16; 9; 11000; true; emerald; 1459 \ No newline at end of file diff --git a/src/main/resources/test/wrongBikeFormat.txt b/src/main/resources/test/wrongBikeFormat.txt new file mode 100644 index 0000000..a2c524c --- /dev/null +++ b/src/main/resources/test/wrongBikeFormat.txt @@ -0,0 +1,2 @@ + 1085 + 1609 diff --git a/src/main/resources/wrongBikeFormat.txt b/src/main/resources/wrongBikeFormat.txt deleted file mode 100644 index f48edce..0000000 --- a/src/main/resources/wrongBikeFormat.txt +++ /dev/null @@ -1,2 +0,0 @@ -12 -99 \ No newline at end of file diff --git a/src/test/java/com/ecobike/DataHolderTest.java b/src/test/java/com/ecobike/DataHolderTest.java new file mode 100644 index 0000000..edce70e --- /dev/null +++ b/src/test/java/com/ecobike/DataHolderTest.java @@ -0,0 +1,85 @@ +package com.ecobike; + +import com.ecobike.dao.BikeDAO; +import com.ecobike.dao.FileBikeDAO; +import com.ecobike.exception.IllegalDataSourceException; +import com.ecobike.model.Bike; +import com.ecobike.model.BikeType; +import com.ecobike.model.EBike; +import com.ecobike.model.FoldingBike; +import com.ecobike.model.SpeedelecBike; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class DataHolderTest { + private static final BikeDAO BIKE_DAO = FileBikeDAO.getInstance(); + private static final DataHolder DATA_HOLDER = DataHolder.getInstance(); + private static final int START_EXPECTED_LIST_SIZE = 5; + private static final int FINISH_EXPECTED_LIST_SIZE = 7; + private static final Bike FIRST_BIKE_TO_ADD = new EBike("AAAA",50, + 8400, false, 8600, "brown", 1609); + private static final Bike LAST_BIKE_TO_ADD = new SpeedelecBike("ZZZZ",50, + 8400, false, 8600, "brown", 1609); + private static final SearchParameterContainer PARAMETER_CONTAINER = + new SearchParameterContainer(); + static { + PARAMETER_CONTAINER.setBikeType(BikeType.FOLDING_BIKE); + PARAMETER_CONTAINER.setBrand("bmW"); + PARAMETER_CONTAINER.setMinWheelSize(16); + PARAMETER_CONTAINER.setMaxWheelSize(25); + PARAMETER_CONTAINER.setColor(""); + } + + private static final Bike FIRST_FOUND_BIKE = new FoldingBike("BMW", 20, 7, + 14400, false, "lemon", 1085); + private static final Bike SECOND_FOUND_BIKE = new FoldingBike("BMW", 16, 9, + 11000, true, "emerald", 1459); + + @Before + public void before() throws IllegalDataSourceException { + BIKE_DAO.setSource("src/main/resources/test/fileWithFiveTrueBikes.txt"); + BIKE_DAO.loadBikes(); + } + + @Test + public void getUnmodifiableBikeList() { + List loadedBikes = DATA_HOLDER.getUnmodifiableBikeList(); + assertEquals(START_EXPECTED_LIST_SIZE, loadedBikes.size()); + } + + @Test + public void addBikes() { + List bikesToAdd = Arrays.asList(FIRST_BIKE_TO_ADD, LAST_BIKE_TO_ADD); + DATA_HOLDER.addBikes(bikesToAdd); + List loadedBikes = DATA_HOLDER.getUnmodifiableBikeList(); + + assertEquals(FINISH_EXPECTED_LIST_SIZE, loadedBikes.size()); + assertEquals(FIRST_BIKE_TO_ADD, loadedBikes.get(0)); + assertEquals(LAST_BIKE_TO_ADD, loadedBikes.get(6)); + } + + @Test + public void addBike() { + DATA_HOLDER.addBike(FIRST_BIKE_TO_ADD); + DATA_HOLDER.addBike(LAST_BIKE_TO_ADD); + List loadedBikes = DATA_HOLDER.getUnmodifiableBikeList(); + + assertEquals(FINISH_EXPECTED_LIST_SIZE, loadedBikes.size()); + assertEquals(FIRST_BIKE_TO_ADD, loadedBikes.get(0)); + assertEquals(LAST_BIKE_TO_ADD, loadedBikes.get(6)); + } + + @Test + public void findBikesByParameter() { + List foundBikes = DATA_HOLDER.findBikesByParameter(PARAMETER_CONTAINER); + assertEquals(2, foundBikes.size()); + assertTrue(foundBikes.contains(FIRST_FOUND_BIKE)); + assertTrue(foundBikes.contains(SECOND_FOUND_BIKE)); + } +} \ No newline at end of file diff --git a/src/test/java/com/ecobike/FileBikeDAOTest.java b/src/test/java/com/ecobike/FileBikeDAOTest.java new file mode 100644 index 0000000..e92d22c --- /dev/null +++ b/src/test/java/com/ecobike/FileBikeDAOTest.java @@ -0,0 +1,59 @@ +package com.ecobike; + +import com.ecobike.dao.BikeDAO; +import com.ecobike.dao.FileBikeDAO; +import com.ecobike.exception.IllegalDataSourceException; +import com.ecobike.model.Bike; +import com.ecobike.model.SpeedelecBike; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +public class FileBikeDAOTest { + private static final BikeDAO BIKE_DAO = FileBikeDAO.getInstance(); + private static final int EXPECTED_LIST_SIZE = 5; + private static final Bike LAST_BIKE = new SpeedelecBike("EcoRide",50, + 8400, false, 8600, "brown", 1609); + private static final Path NEW_FILE = Path.of("src/main/resources/test/newFile.txt"); + + @Rule + public ExpectedException expectedEx = ExpectedException.none(); + + @Test + public void loadBikesTest() throws InterruptedException, IllegalDataSourceException, ClassNotFoundException { + BIKE_DAO.setSource("src/main/resources/test/fileWithFiveTrueBikes.txt"); + BIKE_DAO.loadBikes(); + Thread.sleep(500); + DataHolder dataHolder = DataHolder.getInstance(); + List loadedBikes = dataHolder.getUnmodifiableBikeList(); + + Assert.assertEquals(EXPECTED_LIST_SIZE, loadedBikes.size()); + Assert.assertEquals(loadedBikes.size() - 1, loadedBikes.indexOf(LAST_BIKE)); + + expectedEx.expect(IllegalDataSourceException.class); + BIKE_DAO.setSource("src/main/resources/test/wrongBikeFormat.txt"); + BIKE_DAO.loadBikes(); + } + + @Test + public void writeBikesTest() throws IOException, IllegalDataSourceException { + Files.deleteIfExists(NEW_FILE); + Files.createFile(NEW_FILE); + Files.writeString(NEW_FILE, "FOLDING BIKE BMW; 20; 7; 14400; false; lemon; 1085"); + BIKE_DAO.setSource("src/main/resources/test/newFile.txt"); + BIKE_DAO.loadBikes(); + DataHolder dataHolder = DataHolder.getInstance(); + dataHolder.addBike(LAST_BIKE); + BIKE_DAO.writeBikes(); + List fileLines = Files.readAllLines(NEW_FILE); + Assert.assertEquals(2, fileLines.size()); + Assert.assertEquals("FOLDING BIKE BMW; 20; 7; 14400; false; lemon; 1085", fileLines.get(0)); + Assert.assertEquals("SPEEDELEC EcoRide; 50; 8400; false; 8600; brown; 1609", fileLines.get(1)); + } +} \ No newline at end of file From dd6e6e4224b08ae60b17a85c0574618998aca08c Mon Sep 17 00:00:00 2001 From: Leonid Date: Fri, 31 Jul 2020 16:57:52 +0300 Subject: [PATCH 09/19] checkstyle and mvm build done --- .../java/com/ecobike/CommandExecutor.java | 9 +++- src/main/java/com/ecobike/Communicator.java | 1 - .../java/com/ecobike/ConsoleCommunicator.java | 5 ++- src/main/java/com/ecobike/DataHolder.java | 35 +++++++++------ .../java/com/ecobike/EcoBikeApplication.java | 13 +++--- .../com/ecobike/command/AddEBikeCommand.java | 16 +++---- .../command/AddFoldingBikeCommand.java | 16 +++---- .../command/AddSpeedelecBikeCommand.java | 16 +++---- .../java/com/ecobike/command/FindCommand.java | 7 +-- .../dao/{BikeDAO.java => BikeDao.java} | 2 +- .../{FileBikeDAO.java => FileBikeDao.java} | 30 +++++++------ .../ecobike/model/AbstractElectroBike.java | 20 ++++++--- src/main/java/com/ecobike/model/Bike.java | 18 +++++--- .../java/com/ecobike/model/FoldingBike.java | 20 ++++++--- src/test/java/com/ecobike/DataHolderTest.java | 24 ++++++----- ...eBikeDAOTest.java => FileBikeDaoTest.java} | 43 +++++++++++-------- 16 files changed, 158 insertions(+), 117 deletions(-) rename src/main/java/com/ecobike/dao/{BikeDAO.java => BikeDao.java} (96%) rename src/main/java/com/ecobike/dao/{FileBikeDAO.java => FileBikeDao.java} (93%) rename src/test/java/com/ecobike/{FileBikeDAOTest.java => FileBikeDaoTest.java} (84%) diff --git a/src/main/java/com/ecobike/CommandExecutor.java b/src/main/java/com/ecobike/CommandExecutor.java index 3f59c1c..546e4d0 100644 --- a/src/main/java/com/ecobike/CommandExecutor.java +++ b/src/main/java/com/ecobike/CommandExecutor.java @@ -1,7 +1,12 @@ package com.ecobike; -import com.ecobike.command.*; - +import com.ecobike.command.AddEBikeCommand; +import com.ecobike.command.AddFoldingBikeCommand; +import com.ecobike.command.AddSpeedelecBikeCommand; +import com.ecobike.command.Command; +import com.ecobike.command.FindCommand; +import com.ecobike.command.ShowCommand; +import com.ecobike.command.WriteToFileCommand; import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/com/ecobike/Communicator.java b/src/main/java/com/ecobike/Communicator.java index 6d3f569..e9a49fb 100644 --- a/src/main/java/com/ecobike/Communicator.java +++ b/src/main/java/com/ecobike/Communicator.java @@ -1,7 +1,6 @@ package com.ecobike; import com.ecobike.model.Bike; - import java.util.List; /** diff --git a/src/main/java/com/ecobike/ConsoleCommunicator.java b/src/main/java/com/ecobike/ConsoleCommunicator.java index c4ceaee..2f6356f 100644 --- a/src/main/java/com/ecobike/ConsoleCommunicator.java +++ b/src/main/java/com/ecobike/ConsoleCommunicator.java @@ -1,7 +1,6 @@ package com.ecobike; import com.ecobike.model.Bike; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -12,7 +11,8 @@ */ public class ConsoleCommunicator implements Communicator { - private static final BufferedReader READER = new BufferedReader(new InputStreamReader(System.in)); + private static final BufferedReader READER + = new BufferedReader(new InputStreamReader(System.in)); /** * Number of bikes to be shown on one page. */ @@ -99,6 +99,7 @@ public int readPositiveInt() { /** * Method reads boolean from user. + * * @return boolean value from user. */ @Override diff --git a/src/main/java/com/ecobike/DataHolder.java b/src/main/java/com/ecobike/DataHolder.java index d0b7d09..50df074 100644 --- a/src/main/java/com/ecobike/DataHolder.java +++ b/src/main/java/com/ecobike/DataHolder.java @@ -4,7 +4,6 @@ import com.ecobike.model.Bike; import com.ecobike.model.BikeType; import com.ecobike.model.FoldingBike; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -18,7 +17,7 @@ * loaded from data source. */ public class DataHolder { - private final static DataHolder INSTANCE = new DataHolder(); + private static final DataHolder INSTANCE = new DataHolder(); /** * Bikes container. */ @@ -32,6 +31,7 @@ public class DataHolder { private DataHolder() { } + public static DataHolder getInstance() { return INSTANCE; } @@ -41,10 +41,11 @@ public static DataHolder getInstance() { * * @param bikesToAdd collection with Bikes for adding. */ - public synchronized void addBikes(Collection bikesToAdd) { + public synchronized void init(Collection bikesToAdd) { + bikes.clear(); bikes.addAll(bikesToAdd); countDownLatch = new CountDownLatch(1); - new Thread(()->{ + new Thread(() -> { sort(); countDownLatch.countDown(); }).start(); @@ -103,25 +104,33 @@ public List findBikesByParameter(SearchParameterContainer parCont) { .filter(bike -> bike.getBikeType() == parCont.getBikeType() && bike.getBrand().equalsIgnoreCase(parCont.getBrand()) && bike.getWeight() >= parCont.getMinWeight() - && (parCont.getMaxWeight() == 0 || bike.getWeight() <= parCont.getMaxWeight()) - && (!parCont.isLightsOptionEntered() || parCont.isLightsPresent() == bike.isLightsPresent()) - && (parCont.getColor().isEmpty() || parCont.getColor().equalsIgnoreCase(bike.getColor())) + && (parCont.getMaxWeight() == 0 + || bike.getWeight() <= parCont.getMaxWeight()) + && (!parCont.isLightsOptionEntered() + || parCont.isLightsPresent() == bike.isLightsPresent()) + && (parCont.getColor().isEmpty() + || parCont.getColor().equalsIgnoreCase(bike.getColor())) && bike.getPrice() >= parCont.getMinPrice() - && (parCont.getMaxPrice() == 0 || bike.getPrice() <= parCont.getMaxPrice())); + && (parCont.getMaxPrice() == 0 + || bike.getPrice() <= parCont.getMaxPrice())); if (parCont.getBikeType() == BikeType.FOLDING_BIKE) { return bikeStream.map(bike -> (FoldingBike) bike) .filter(bike -> bike.getWheelSize() >= parCont.getMinWheelSize() - && (parCont.getMaxWheelSize() == 0 || bike.getWheelSize() <= parCont.getMaxWheelSize()) + && (parCont.getMaxWheelSize() == 0 + || bike.getWheelSize() <= parCont.getMaxWheelSize()) && bike.getNumberOfGears() >= parCont.getMinNumberOfGears() - && (parCont.getMaxNumberOfGears() == 0 || bike.getNumberOfGears() <= parCont.getMaxNumberOfGears())) + && (parCont.getMaxNumberOfGears() == 0 + || bike.getNumberOfGears() <= parCont.getMaxNumberOfGears())) .collect(Collectors.toList()); } return bikeStream.map(bike -> (AbstractElectroBike) bike) .filter(bike -> bike.getMaxSpeed() >= parCont.getMinMaxBikeSpeed() - && (parCont.getMaxMaxBikeSpeed() == 0 || bike.getMaxSpeed() <= parCont.getMaxMaxBikeSpeed()) + && (parCont.getMaxMaxBikeSpeed() == 0 + || bike.getMaxSpeed() <= parCont.getMaxMaxBikeSpeed()) && bike.getBatteryCapacity() >= parCont.getMinBatteryCapacity() - && (parCont.getMaxBatteryCapacity() == 0 || bike.getBatteryCapacity() <= parCont.getMaxBatteryCapacity())) + && (parCont.getMaxBatteryCapacity() == 0 + || bike.getBatteryCapacity() <= parCont.getMaxBatteryCapacity())) .collect(Collectors.toList()); } } @@ -129,7 +138,7 @@ public List findBikesByParameter(SearchParameterContainer parCont) { /** * Method sorts Bikes in container. */ - private void sort() { + private synchronized void sort() { Collections.sort(bikes); } } diff --git a/src/main/java/com/ecobike/EcoBikeApplication.java b/src/main/java/com/ecobike/EcoBikeApplication.java index 693ec5d..922611a 100644 --- a/src/main/java/com/ecobike/EcoBikeApplication.java +++ b/src/main/java/com/ecobike/EcoBikeApplication.java @@ -1,9 +1,8 @@ package com.ecobike; -import com.ecobike.dao.BikeDAO; -import com.ecobike.dao.FileBikeDAO; +import com.ecobike.dao.BikeDao; +import com.ecobike.dao.FileBikeDao; import com.ecobike.exception.IllegalDataSourceException; - import java.nio.file.Files; import java.nio.file.Path; @@ -18,7 +17,7 @@ public class EcoBikeApplication { /** * Object for loading data from and writing data to file. */ - public static final BikeDAO BIKE_DAO = FileBikeDAO.getInstance(); + public static final BikeDao BIKE_DAO = FileBikeDao.getInstance(); /** * Variable for keeping information was data changed * since last writing to file or not. @@ -47,11 +46,11 @@ public static void main(String[] args) { } } isDataChanged = false; - while (true){ + while (true) { Operation operation = askOperation(); if (isDataChanged && operation == Operation.STOP_PROGRAM) { - COMMUNICATOR.writeMessage("You are going to exit without saving changed data.\n" + - "Do you wont to save data?"); + COMMUNICATOR.writeMessage("You are going to exit without saving changed data.\n" + + "Do you wont to save data?"); if (COMMUNICATOR.readBoolean()) { CommandExecutor.execute(Operation.WRITE_TO_FILE); } diff --git a/src/main/java/com/ecobike/command/AddEBikeCommand.java b/src/main/java/com/ecobike/command/AddEBikeCommand.java index 397e70d..e3afa12 100644 --- a/src/main/java/com/ecobike/command/AddEBikeCommand.java +++ b/src/main/java/com/ecobike/command/AddEBikeCommand.java @@ -25,14 +25,14 @@ public void execute() { COMMUNICATOR.writeMessage("Enter price:"); int price = COMMUNICATOR.readPositiveInt(); - String confirmMessage = String.format("add new %s with next parametrs:\n" + - "brand: %s\n" + - "maxSpeed: %d\n" + - "weight: %d\n" + - "lights presents: %s\n" + - "battery capacity: %d\n" + - "color: %s\n" + - "price: %d\n", + String confirmMessage = String.format("add new %s with next parametrs:\n" + + "brand: %s\n" + + "maxSpeed: %d\n" + + "weight: %d\n" + + "lights presents: %s\n" + + "battery capacity: %d\n" + + "color: %s\n" + + "price: %d\n", BikeType.E_BIKE, brand, maxSpeed, weight, isLightsPresent, batteryCapacity, color, price); diff --git a/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java b/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java index cc22c3a..902b7e9 100644 --- a/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java +++ b/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java @@ -25,14 +25,14 @@ public void execute() { COMMUNICATOR.writeMessage("Enter price:"); int price = COMMUNICATOR.readPositiveInt(); - String confirmMessage = String.format("add new %s with next parametrs:\n" + - "brand: %s\n" + - "wheelSize: %d\n" + - "numberOfGears: %d\n" + - "weight: %d\n" + - "lights presents: %s\n" + - "color: %s\n" + - "price: %d\n", + String confirmMessage = String.format("add new %s with next parametrs:\n" + + "brand: %s\n" + + "wheelSize: %d\n" + + "numberOfGears: %d\n" + + "weight: %d\n" + + "lights presents: %s\n" + + "color: %s\n" + + "price: %d\n", BikeType.FOLDING_BIKE, brand, wheelSize, numberOfGears, weight, isLightsPresent, color, price); diff --git a/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java b/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java index 4918495..d13b542 100644 --- a/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java +++ b/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java @@ -25,14 +25,14 @@ public void execute() { COMMUNICATOR.writeMessage("Enter price:"); int price = COMMUNICATOR.readPositiveInt(); - String confirmMessage = String.format("add new %s with next parametrs:\n" + - "brand: %s\n" + - "maxSpeed: %d\n" + - "weight: %d\n" + - "lights presents: %s\n" + - "battery capacity: %d\n" + - "color: %s\n" + - "price: %d\n", + String confirmMessage = String.format("add new %s with next parametrs:\n" + + "brand: %s\n" + + "maxSpeed: %d\n" + + "weight: %d\n" + + "lights presents: %s\n" + + "battery capacity: %d\n" + + "color: %s\n" + + "price: %d\n", BikeType.SPEEDELEC, brand, maxSpeed, weight, isLightsPresent, batteryCapacity, color, price); diff --git a/src/main/java/com/ecobike/command/FindCommand.java b/src/main/java/com/ecobike/command/FindCommand.java index 99a96d3..a7d3732 100644 --- a/src/main/java/com/ecobike/command/FindCommand.java +++ b/src/main/java/com/ecobike/command/FindCommand.java @@ -3,7 +3,6 @@ import com.ecobike.SearchParameterContainer; import com.ecobike.model.Bike; import com.ecobike.model.BikeType; - import java.util.List; /** @@ -35,7 +34,8 @@ public void execute() { } paramContainer.setBrand(brand); - COMMUNICATOR.writeMessage("You may choose next parameters (for skipping parameter press \"Enter\"):"); + COMMUNICATOR.writeMessage("You may choose next parameters " + + "(for skipping parameter press \"Enter\"):"); COMMUNICATOR.writeMessage("Enter min weight:"); paramContainer.setMinWeight(COMMUNICATOR.readInt()); @@ -58,7 +58,8 @@ public void execute() { paramContainer.setLightsOptionEntered(true); break; } - System.out.println("Wrong entry. Type 1 for TRUE, 2 for FALSE or \"Enter\" for skipping"); + COMMUNICATOR.writeMessage("Wrong entry. " + + "Type 1 for TRUE, 2 for FALSE or \"Enter\" for skipping"); } COMMUNICATOR.writeMessage("Enter color:"); diff --git a/src/main/java/com/ecobike/dao/BikeDAO.java b/src/main/java/com/ecobike/dao/BikeDao.java similarity index 96% rename from src/main/java/com/ecobike/dao/BikeDAO.java rename to src/main/java/com/ecobike/dao/BikeDao.java index e44ec12..624482b 100644 --- a/src/main/java/com/ecobike/dao/BikeDAO.java +++ b/src/main/java/com/ecobike/dao/BikeDao.java @@ -6,7 +6,7 @@ * Interface should be implemented by class * responsible for access to data source with Bike objects. */ -public interface BikeDAO { +public interface BikeDao { /** * Method sets data source. diff --git a/src/main/java/com/ecobike/dao/FileBikeDAO.java b/src/main/java/com/ecobike/dao/FileBikeDao.java similarity index 93% rename from src/main/java/com/ecobike/dao/FileBikeDAO.java rename to src/main/java/com/ecobike/dao/FileBikeDao.java index a56f870..01a1744 100644 --- a/src/main/java/com/ecobike/dao/FileBikeDAO.java +++ b/src/main/java/com/ecobike/dao/FileBikeDao.java @@ -1,9 +1,14 @@ package com.ecobike.dao; +import static com.ecobike.EcoBikeApplication.COMMUNICATOR; + import com.ecobike.DataHolder; import com.ecobike.exception.IllegalDataSourceException; -import com.ecobike.model.*; - +import com.ecobike.model.Bike; +import com.ecobike.model.BikeType; +import com.ecobike.model.EBike; +import com.ecobike.model.FoldingBike; +import com.ecobike.model.SpeedelecBike; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -14,29 +19,28 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.ecobike.EcoBikeApplication.COMMUNICATOR; - /** * Singleton class responsible for reading data from file and parsing text to Bike objects, * and for writing to file Bike objects in text format. * Old data in file will be replaced by new one. */ -public class FileBikeDAO implements BikeDAO { +public class FileBikeDao implements BikeDao { - private final static FileBikeDAO INSTANCE = new FileBikeDAO(); + private static final FileBikeDao INSTANCE = new FileBikeDao(); - private FileBikeDAO() { - } + private static final DataHolder DATA_HOLDER = DataHolder.getInstance(); - public static FileBikeDAO getInstance() { - return INSTANCE; - } /** * Path to data file. */ private Path file; - private static final DataHolder DATA_HOLDER = DataHolder.getInstance(); + private FileBikeDao() { + } + + public static FileBikeDao getInstance() { + return INSTANCE; + } /** * Method sets data file. @@ -74,7 +78,7 @@ public void loadBikes() throws IllegalDataSourceException { throw new IllegalDataSourceException(); } wrongLinesInfo.forEach(COMMUNICATOR::writeMessage); - DATA_HOLDER.addBikes(bikes); + DATA_HOLDER.init(bikes); COMMUNICATOR.writeMessage(bikes.size() + " bike items has been read from the file"); } diff --git a/src/main/java/com/ecobike/model/AbstractElectroBike.java b/src/main/java/com/ecobike/model/AbstractElectroBike.java index 0d2dab8..6dfff91 100644 --- a/src/main/java/com/ecobike/model/AbstractElectroBike.java +++ b/src/main/java/com/ecobike/model/AbstractElectroBike.java @@ -44,12 +44,18 @@ public String toFileWriterString() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } AbstractElectroBike that = (AbstractElectroBike) o; - return maxSpeed == that.maxSpeed && - batteryCapacity == that.batteryCapacity; + return maxSpeed == that.maxSpeed + && batteryCapacity == that.batteryCapacity; } @Override @@ -59,8 +65,8 @@ public int hashCode() { @Override public String toString() { - return String.format("%s %s with %d mAh battery and%s head/tail light." + - "\nPrice: %d euros.", getBikeType(), + return String.format("%s %s with %d mAh battery and%s head/tail light." + + "\nPrice: %d euros.", getBikeType(), getBrand(), getBatteryCapacity(), isLightsPresent() ? "" : " no", getPrice()); } diff --git a/src/main/java/com/ecobike/model/Bike.java b/src/main/java/com/ecobike/model/Bike.java index 572e984..b14bd08 100644 --- a/src/main/java/com/ecobike/model/Bike.java +++ b/src/main/java/com/ecobike/model/Bike.java @@ -42,14 +42,18 @@ public Bike(String brand, int weight, boolean isLightsPresent, String color, int @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Bike bike = (Bike) o; - return weight == bike.weight && - isLightsPresent == bike.isLightsPresent && - price == bike.price && - Objects.equals(brand, bike.brand) && - Objects.equals(color, bike.color); + return weight == bike.weight + && isLightsPresent == bike.isLightsPresent + && price == bike.price + && Objects.equals(brand, bike.brand) + && Objects.equals(color, bike.color); } @Override diff --git a/src/main/java/com/ecobike/model/FoldingBike.java b/src/main/java/com/ecobike/model/FoldingBike.java index 7159b93..a102ced 100644 --- a/src/main/java/com/ecobike/model/FoldingBike.java +++ b/src/main/java/com/ecobike/model/FoldingBike.java @@ -42,12 +42,18 @@ public String toFileWriterString() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } FoldingBike that = (FoldingBike) o; - return wheelSize == that.wheelSize && - numberOfGears == that.numberOfGears; + return wheelSize == that.wheelSize + && numberOfGears == that.numberOfGears; } @Override @@ -57,8 +63,8 @@ public int hashCode() { @Override public String toString() { - return String.format("%s %s with %d gear(s) and%s head/tail light." + - "\nPrice: %d euros.", getBikeType(), + return String.format("%s %s with %d gear(s) and%s head/tail light." + + "\nPrice: %d euros.", getBikeType(), getBrand(), getNumberOfGears(), isLightsPresent() ? "" : " no", getPrice()); } diff --git a/src/test/java/com/ecobike/DataHolderTest.java b/src/test/java/com/ecobike/DataHolderTest.java index edce70e..7689dea 100644 --- a/src/test/java/com/ecobike/DataHolderTest.java +++ b/src/test/java/com/ecobike/DataHolderTest.java @@ -1,7 +1,7 @@ package com.ecobike; -import com.ecobike.dao.BikeDAO; -import com.ecobike.dao.FileBikeDAO; +import com.ecobike.dao.BikeDao; +import com.ecobike.dao.FileBikeDao; import com.ecobike.exception.IllegalDataSourceException; import com.ecobike.model.Bike; import com.ecobike.model.BikeType; @@ -18,17 +18,20 @@ import static org.junit.Assert.assertTrue; public class DataHolderTest { - private static final BikeDAO BIKE_DAO = FileBikeDAO.getInstance(); + private static final BikeDao BIKE_DAO = FileBikeDao.getInstance(); private static final DataHolder DATA_HOLDER = DataHolder.getInstance(); private static final int START_EXPECTED_LIST_SIZE = 5; - private static final int FINISH_EXPECTED_LIST_SIZE = 7; - private static final Bike FIRST_BIKE_TO_ADD = new EBike("AAAA",50, + private static final int ADDBIKES_EXPECTED_LIST_SIZE = 2; + private static final int ADDBIKE_EXPECTED_LIST_SIZE = 7; + private static final Bike FIRST_BIKE_TO_ADD = new EBike("AAAA", 50, 8400, false, 8600, "brown", 1609); - private static final Bike LAST_BIKE_TO_ADD = new SpeedelecBike("ZZZZ",50, + private static final Bike LAST_BIKE_TO_ADD = new SpeedelecBike("ZZZZ", 50, 8400, false, 8600, "brown", 1609); private static final SearchParameterContainer PARAMETER_CONTAINER = new SearchParameterContainer(); + static { + BIKE_DAO.setSource("src/main/resources/test/fileWithFiveTrueBikes.txt"); PARAMETER_CONTAINER.setBikeType(BikeType.FOLDING_BIKE); PARAMETER_CONTAINER.setBrand("bmW"); PARAMETER_CONTAINER.setMinWheelSize(16); @@ -43,7 +46,6 @@ public class DataHolderTest { @Before public void before() throws IllegalDataSourceException { - BIKE_DAO.setSource("src/main/resources/test/fileWithFiveTrueBikes.txt"); BIKE_DAO.loadBikes(); } @@ -56,12 +58,12 @@ public void getUnmodifiableBikeList() { @Test public void addBikes() { List bikesToAdd = Arrays.asList(FIRST_BIKE_TO_ADD, LAST_BIKE_TO_ADD); - DATA_HOLDER.addBikes(bikesToAdd); + DATA_HOLDER.init(bikesToAdd); List loadedBikes = DATA_HOLDER.getUnmodifiableBikeList(); - assertEquals(FINISH_EXPECTED_LIST_SIZE, loadedBikes.size()); + assertEquals(ADDBIKES_EXPECTED_LIST_SIZE, loadedBikes.size()); assertEquals(FIRST_BIKE_TO_ADD, loadedBikes.get(0)); - assertEquals(LAST_BIKE_TO_ADD, loadedBikes.get(6)); + assertEquals(LAST_BIKE_TO_ADD, loadedBikes.get(1)); } @Test @@ -70,7 +72,7 @@ public void addBike() { DATA_HOLDER.addBike(LAST_BIKE_TO_ADD); List loadedBikes = DATA_HOLDER.getUnmodifiableBikeList(); - assertEquals(FINISH_EXPECTED_LIST_SIZE, loadedBikes.size()); + assertEquals(ADDBIKE_EXPECTED_LIST_SIZE, loadedBikes.size()); assertEquals(FIRST_BIKE_TO_ADD, loadedBikes.get(0)); assertEquals(LAST_BIKE_TO_ADD, loadedBikes.get(6)); } diff --git a/src/test/java/com/ecobike/FileBikeDAOTest.java b/src/test/java/com/ecobike/FileBikeDaoTest.java similarity index 84% rename from src/test/java/com/ecobike/FileBikeDAOTest.java rename to src/test/java/com/ecobike/FileBikeDaoTest.java index e92d22c..dd1b216 100644 --- a/src/test/java/com/ecobike/FileBikeDAOTest.java +++ b/src/test/java/com/ecobike/FileBikeDaoTest.java @@ -1,11 +1,12 @@ package com.ecobike; -import com.ecobike.dao.BikeDAO; -import com.ecobike.dao.FileBikeDAO; +import com.ecobike.dao.BikeDao; +import com.ecobike.dao.FileBikeDao; import com.ecobike.exception.IllegalDataSourceException; import com.ecobike.model.Bike; import com.ecobike.model.SpeedelecBike; import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -15,34 +16,23 @@ import java.nio.file.Path; import java.util.List; -public class FileBikeDAOTest { - private static final BikeDAO BIKE_DAO = FileBikeDAO.getInstance(); +public class FileBikeDaoTest { + private static final BikeDao BIKE_DAO = FileBikeDao.getInstance(); private static final int EXPECTED_LIST_SIZE = 5; - private static final Bike LAST_BIKE = new SpeedelecBike("EcoRide",50, + private static final Bike LAST_BIKE = new SpeedelecBike("EcoRide", 50, 8400, false, 8600, "brown", 1609); private static final Path NEW_FILE = Path.of("src/main/resources/test/newFile.txt"); @Rule public ExpectedException expectedEx = ExpectedException.none(); - @Test - public void loadBikesTest() throws InterruptedException, IllegalDataSourceException, ClassNotFoundException { - BIKE_DAO.setSource("src/main/resources/test/fileWithFiveTrueBikes.txt"); - BIKE_DAO.loadBikes(); - Thread.sleep(500); - DataHolder dataHolder = DataHolder.getInstance(); - List loadedBikes = dataHolder.getUnmodifiableBikeList(); + @BeforeClass + public static void beforeClass() { - Assert.assertEquals(EXPECTED_LIST_SIZE, loadedBikes.size()); - Assert.assertEquals(loadedBikes.size() - 1, loadedBikes.indexOf(LAST_BIKE)); - - expectedEx.expect(IllegalDataSourceException.class); - BIKE_DAO.setSource("src/main/resources/test/wrongBikeFormat.txt"); - BIKE_DAO.loadBikes(); } @Test - public void writeBikesTest() throws IOException, IllegalDataSourceException { + public void writeBikesTest() throws IOException, IllegalDataSourceException, InterruptedException { Files.deleteIfExists(NEW_FILE); Files.createFile(NEW_FILE); Files.writeString(NEW_FILE, "FOLDING BIKE BMW; 20; 7; 14400; false; lemon; 1085"); @@ -56,4 +46,19 @@ public void writeBikesTest() throws IOException, IllegalDataSourceException { Assert.assertEquals("FOLDING BIKE BMW; 20; 7; 14400; false; lemon; 1085", fileLines.get(0)); Assert.assertEquals("SPEEDELEC EcoRide; 50; 8400; false; 8600; brown; 1609", fileLines.get(1)); } + + @Test + public void loadBikesTest() throws IllegalDataSourceException { + BIKE_DAO.setSource("src/main/resources/test/fileWithFiveTrueBikes.txt"); + BIKE_DAO.loadBikes(); + DataHolder dataHolder = DataHolder.getInstance(); + List loadedBikes = dataHolder.getUnmodifiableBikeList(); + + Assert.assertEquals(EXPECTED_LIST_SIZE, loadedBikes.size()); + Assert.assertEquals(loadedBikes.size() - 1, loadedBikes.indexOf(LAST_BIKE)); + + expectedEx.expect(IllegalDataSourceException.class); + BIKE_DAO.setSource("src/main/resources/test/wrongBikeFormat.txt"); + BIKE_DAO.loadBikes(); + } } \ No newline at end of file From 95bd2d1ce7b963387ebf564948488f11da680263 Mon Sep 17 00:00:00 2001 From: Leonid Date: Wed, 5 Aug 2020 17:25:06 +0300 Subject: [PATCH 10/19] isDataChanged moved from main() to DataHolder --- .../java/com/ecobike/CommandExecutor.java | 2 + src/main/java/com/ecobike/DataHolder.java | 85 ++++++++++++------- .../java/com/ecobike/EcoBikeApplication.java | 69 ++++++--------- .../java/com/ecobike/command/Command.java | 2 +- .../ecobike/command/StopProgramCommand.java | 18 ++++ .../ecobike/command/WriteToFileCommand.java | 14 ++- .../java/com/ecobike/dao/FileBikeDao.java | 6 +- 7 files changed, 116 insertions(+), 80 deletions(-) create mode 100644 src/main/java/com/ecobike/command/StopProgramCommand.java diff --git a/src/main/java/com/ecobike/CommandExecutor.java b/src/main/java/com/ecobike/CommandExecutor.java index 546e4d0..0a1a805 100644 --- a/src/main/java/com/ecobike/CommandExecutor.java +++ b/src/main/java/com/ecobike/CommandExecutor.java @@ -6,6 +6,7 @@ import com.ecobike.command.Command; import com.ecobike.command.FindCommand; import com.ecobike.command.ShowCommand; +import com.ecobike.command.StopProgramCommand; import com.ecobike.command.WriteToFileCommand; import java.util.HashMap; import java.util.Map; @@ -26,6 +27,7 @@ public class CommandExecutor { allKnownCommandsMap.put(Operation.ADD_E_BIKE, new AddEBikeCommand()); allKnownCommandsMap.put(Operation.FIND_ITEMS_BY_BRAND, new FindCommand()); allKnownCommandsMap.put(Operation.WRITE_TO_FILE, new WriteToFileCommand()); + allKnownCommandsMap.put(Operation.STOP_PROGRAM, new StopProgramCommand()); } private CommandExecutor() { diff --git a/src/main/java/com/ecobike/DataHolder.java b/src/main/java/com/ecobike/DataHolder.java index 50df074..4c0568a 100644 --- a/src/main/java/com/ecobike/DataHolder.java +++ b/src/main/java/com/ecobike/DataHolder.java @@ -29,6 +29,12 @@ public class DataHolder { */ private CountDownLatch countDownLatch = new CountDownLatch(0); + /** + * Variable for keeping information was data changed + * since last writing to file or not. + */ + private boolean isDataChanged; + private DataHolder() { } @@ -49,7 +55,7 @@ public synchronized void init(Collection bikesToAdd) { sort(); countDownLatch.countDown(); }).start(); - EcoBikeApplication.isDataChanged = true; + isDataChanged = false; } /** @@ -67,7 +73,7 @@ public void addBike(Bike bike) { bikes.add(bike); sort(); } - EcoBikeApplication.isDataChanged = true; + isDataChanged = true; } /** @@ -90,10 +96,10 @@ public List getUnmodifiableBikeList() { * Method finds Bikes in container by parameters from * SearchParameterContainer object. * - * @param parCont object with parameters for searching. + * @param parameters object with parameters for searching. * @return list with properly Bikes objects. */ - public List findBikesByParameter(SearchParameterContainer parCont) { + public List findBikesByParameter(SearchParameterContainer parameters) { try { countDownLatch.await(); } catch (InterruptedException e) { @@ -101,40 +107,61 @@ public List findBikesByParameter(SearchParameterContainer parCont) { } synchronized (this) { Stream bikeStream = bikes.parallelStream() - .filter(bike -> bike.getBikeType() == parCont.getBikeType() - && bike.getBrand().equalsIgnoreCase(parCont.getBrand()) - && bike.getWeight() >= parCont.getMinWeight() - && (parCont.getMaxWeight() == 0 - || bike.getWeight() <= parCont.getMaxWeight()) - && (!parCont.isLightsOptionEntered() - || parCont.isLightsPresent() == bike.isLightsPresent()) - && (parCont.getColor().isEmpty() - || parCont.getColor().equalsIgnoreCase(bike.getColor())) - && bike.getPrice() >= parCont.getMinPrice() - && (parCont.getMaxPrice() == 0 - || bike.getPrice() <= parCont.getMaxPrice())); + .filter(bike -> bike.getBikeType() == parameters.getBikeType() + && bike.getBrand().equalsIgnoreCase(parameters.getBrand()) + && bike.getWeight() >= parameters.getMinWeight() + && (parameters.getMaxWeight() == 0 + || bike.getWeight() <= parameters.getMaxWeight()) + && (!parameters.isLightsOptionEntered() + || parameters.isLightsPresent() == bike.isLightsPresent()) + && (parameters.getColor().isEmpty() + || parameters.getColor().equalsIgnoreCase(bike.getColor())) + && bike.getPrice() >= parameters.getMinPrice() + && (parameters.getMaxPrice() == 0 + || bike.getPrice() <= parameters.getMaxPrice())); - if (parCont.getBikeType() == BikeType.FOLDING_BIKE) { + if (parameters.getBikeType() == BikeType.FOLDING_BIKE) { return bikeStream.map(bike -> (FoldingBike) bike) - .filter(bike -> bike.getWheelSize() >= parCont.getMinWheelSize() - && (parCont.getMaxWheelSize() == 0 - || bike.getWheelSize() <= parCont.getMaxWheelSize()) - && bike.getNumberOfGears() >= parCont.getMinNumberOfGears() - && (parCont.getMaxNumberOfGears() == 0 - || bike.getNumberOfGears() <= parCont.getMaxNumberOfGears())) + .filter(bike -> bike.getWheelSize() >= parameters.getMinWheelSize() + && (parameters.getMaxWheelSize() == 0 + || bike.getWheelSize() <= parameters.getMaxWheelSize()) + && bike.getNumberOfGears() >= parameters.getMinNumberOfGears() + && (parameters.getMaxNumberOfGears() == 0 + || bike.getNumberOfGears() <= parameters.getMaxNumberOfGears())) .collect(Collectors.toList()); } return bikeStream.map(bike -> (AbstractElectroBike) bike) - .filter(bike -> bike.getMaxSpeed() >= parCont.getMinMaxBikeSpeed() - && (parCont.getMaxMaxBikeSpeed() == 0 - || bike.getMaxSpeed() <= parCont.getMaxMaxBikeSpeed()) - && bike.getBatteryCapacity() >= parCont.getMinBatteryCapacity() - && (parCont.getMaxBatteryCapacity() == 0 - || bike.getBatteryCapacity() <= parCont.getMaxBatteryCapacity())) + .filter(bike -> bike.getMaxSpeed() >= parameters.getMinMaxBikeSpeed() + && (parameters.getMaxMaxBikeSpeed() == 0 + || bike.getMaxSpeed() <= parameters.getMaxMaxBikeSpeed()) + && bike.getBatteryCapacity() >= parameters.getMinBatteryCapacity() + && (parameters.getMaxBatteryCapacity() == 0 + || bike.getBatteryCapacity() <= parameters.getMaxBatteryCapacity())) .collect(Collectors.toList()); } } + /** + * Method returns information was data changed + * since last saving or not. + * + * @return was data changed + * since last saving or not. + */ + public boolean isDataChanged() { + return isDataChanged; + } + + /** + * Method sets parameter showing was data changed + * since last saving or not. + * + * @param dataChanged boolean value. + */ + public void setDataChanged(boolean dataChanged) { + isDataChanged = dataChanged; + } + /** * Method sorts Bikes in container. */ diff --git a/src/main/java/com/ecobike/EcoBikeApplication.java b/src/main/java/com/ecobike/EcoBikeApplication.java index 922611a..0b3ae98 100644 --- a/src/main/java/com/ecobike/EcoBikeApplication.java +++ b/src/main/java/com/ecobike/EcoBikeApplication.java @@ -13,19 +13,15 @@ public class EcoBikeApplication { /** * Communicator for communication with user. */ - public static final Communicator COMMUNICATOR = new ConsoleCommunicator(); + public static final Communicator communicator = new ConsoleCommunicator(); /** * Object for loading data from and writing data to file. */ - public static final BikeDao BIKE_DAO = FileBikeDao.getInstance(); - /** - * Variable for keeping information was data changed - * since last writing to file or not. - */ - public static boolean isDataChanged = false; + public static final BikeDao bikeDao = FileBikeDao.getInstance(); /** * Main method of the application. + * * @param args system arguments. */ public static void main(String[] args) { @@ -33,59 +29,46 @@ public static void main(String[] args) { while (!isFileParsed) { String bikeDataFile; do { - COMMUNICATOR.writeMessage("Enter path to Bikes data file :"); - bikeDataFile = COMMUNICATOR.readNotEmptyString(); + communicator.writeMessage("Enter path to Bikes data file :"); + bikeDataFile = communicator.readNotEmptyString(); } while (!Files.isRegularFile(Path.of(bikeDataFile))); - BIKE_DAO.setSource(bikeDataFile); + bikeDao.setSource(bikeDataFile); try { - BIKE_DAO.loadBikes(); + bikeDao.loadBikes(); isFileParsed = true; } catch (IllegalDataSourceException e) { - COMMUNICATOR.writeMessage("File has wrong format or empty"); - COMMUNICATOR.writeMessage(""); + communicator.writeMessage("File has wrong format or empty"); + communicator.writeMessage(""); } } - isDataChanged = false; + while (true) { Operation operation = askOperation(); - if (isDataChanged && operation == Operation.STOP_PROGRAM) { - COMMUNICATOR.writeMessage("You are going to exit without saving changed data.\n" - + "Do you wont to save data?"); - if (COMMUNICATOR.readBoolean()) { - CommandExecutor.execute(Operation.WRITE_TO_FILE); - } - } - if (!isDataChanged && operation == Operation.WRITE_TO_FILE) { - COMMUNICATOR.writeMessage("Data has not been changed or already has saved"); - continue; - } - if (operation == Operation.STOP_PROGRAM) { - if (COMMUNICATOR.confirmAction("EXIT from program")) { - COMMUNICATOR.writeMessage("Good bay!"); - return; - } - continue; - } CommandExecutor.execute(operation); - isDataChanged = operation != Operation.WRITE_TO_FILE && isDataChanged; + if (operation == Operation.STOP_PROGRAM + && communicator.confirmAction("EXIT from program")) { + communicator.writeMessage("Good bay!"); + break; + } } } /** * Method asks user to choose operation. + * * @return chosen operation. */ private static Operation askOperation() { - COMMUNICATOR.writeMessage(""); - COMMUNICATOR.writeMessage("Please make your choice:"); - COMMUNICATOR.writeMessage("\t 1 - Show the entire EcoBike catalog"); - COMMUNICATOR.writeMessage("\t 2 – Add a new folding bike"); - COMMUNICATOR.writeMessage("\t 3 – Add a new speedelec"); - COMMUNICATOR.writeMessage("\t 4 – Add a new e-bike"); - COMMUNICATOR.writeMessage("\t 5 – Find the first item of a particular brand"); - COMMUNICATOR.writeMessage("\t 6 – Write to file"); - COMMUNICATOR.writeMessage("\t 7 – Stop the program"); + communicator.writeMessage(""); + communicator.writeMessage("Please make your choice:"); + communicator.writeMessage("\t 1 - Show the entire EcoBike catalog"); + communicator.writeMessage("\t 2 – Add a new folding bike"); + communicator.writeMessage("\t 3 – Add a new speedelec"); + communicator.writeMessage("\t 4 – Add a new e-bike"); + communicator.writeMessage("\t 5 – Find the first item of a particular brand"); + communicator.writeMessage("\t 6 – Write to file"); + communicator.writeMessage("\t 7 – Stop the program"); - return Operation.values()[COMMUNICATOR.readInt() - 1]; + return Operation.values()[communicator.readInt() - 1]; } } diff --git a/src/main/java/com/ecobike/command/Command.java b/src/main/java/com/ecobike/command/Command.java index 7fa422f..33f0e2d 100644 --- a/src/main/java/com/ecobike/command/Command.java +++ b/src/main/java/com/ecobike/command/Command.java @@ -9,7 +9,7 @@ * operation specified by user. */ public interface Command { - Communicator COMMUNICATOR = EcoBikeApplication.COMMUNICATOR; + Communicator COMMUNICATOR = EcoBikeApplication.communicator; DataHolder DATA_HOLDER = DataHolder.getInstance(); void execute(); diff --git a/src/main/java/com/ecobike/command/StopProgramCommand.java b/src/main/java/com/ecobike/command/StopProgramCommand.java new file mode 100644 index 0000000..b12b5d3 --- /dev/null +++ b/src/main/java/com/ecobike/command/StopProgramCommand.java @@ -0,0 +1,18 @@ +package com.ecobike.command; + +import com.ecobike.CommandExecutor; +import com.ecobike.Operation; + +public class StopProgramCommand implements Command { + @Override + public void execute() { + if (DATA_HOLDER.isDataChanged()) { + COMMUNICATOR.writeMessage("You are going to exit without saving changed data.\n" + + "Do you wont to save data?"); + if (COMMUNICATOR.readBoolean()) { + CommandExecutor.execute(Operation.WRITE_TO_FILE); + } + } + + } +} diff --git a/src/main/java/com/ecobike/command/WriteToFileCommand.java b/src/main/java/com/ecobike/command/WriteToFileCommand.java index 86872b2..41120cb 100644 --- a/src/main/java/com/ecobike/command/WriteToFileCommand.java +++ b/src/main/java/com/ecobike/command/WriteToFileCommand.java @@ -9,10 +9,16 @@ public class WriteToFileCommand implements Command { @Override public void execute() { - String confirmMessage = "write data to file"; - if (COMMUNICATOR.confirmAction(confirmMessage)) { - EcoBikeApplication.BIKE_DAO.writeBikes(); - COMMUNICATOR.writeMessage("Data has been written successfully."); + if (!DATA_HOLDER.isDataChanged()) { + COMMUNICATOR.writeMessage("Data has not been changed or already has saved"); + } else { + String confirmMessage = "write data to file"; + if (COMMUNICATOR.confirmAction(confirmMessage)) { + EcoBikeApplication.bikeDao.writeBikes(); + DATA_HOLDER.setDataChanged(true); + COMMUNICATOR.writeMessage("Data has been written successfully."); + } } + } } diff --git a/src/main/java/com/ecobike/dao/FileBikeDao.java b/src/main/java/com/ecobike/dao/FileBikeDao.java index 01a1744..db07925 100644 --- a/src/main/java/com/ecobike/dao/FileBikeDao.java +++ b/src/main/java/com/ecobike/dao/FileBikeDao.java @@ -1,6 +1,6 @@ package com.ecobike.dao; -import static com.ecobike.EcoBikeApplication.COMMUNICATOR; +import static com.ecobike.EcoBikeApplication.communicator; import com.ecobike.DataHolder; import com.ecobike.exception.IllegalDataSourceException; @@ -77,9 +77,9 @@ public void loadBikes() throws IllegalDataSourceException { if (bikes.isEmpty()) { throw new IllegalDataSourceException(); } - wrongLinesInfo.forEach(COMMUNICATOR::writeMessage); + wrongLinesInfo.forEach(communicator::writeMessage); DATA_HOLDER.init(bikes); - COMMUNICATOR.writeMessage(bikes.size() + " bike items has been read from the file"); + communicator.writeMessage(bikes.size() + " bike items has been read from the file"); } /** From 3a2b8e51d35deb4e2302f07bace02d0461e9af75 Mon Sep 17 00:00:00 2001 From: Leonid Date: Wed, 5 Aug 2020 17:45:20 +0300 Subject: [PATCH 11/19] naming changed --- src/main/java/com/ecobike/ConsoleCommunicator.java | 2 +- .../java/com/ecobike/command/WriteToFileCommand.java | 2 +- src/main/java/com/ecobike/dao/BikeDao.java | 2 +- src/main/java/com/ecobike/dao/FileBikeDao.java | 2 +- .../java/com/ecobike/model/AbstractElectroBike.java | 12 ++++-------- src/main/java/com/ecobike/model/Bike.java | 8 ++++++++ src/main/java/com/ecobike/model/FoldingBike.java | 2 +- src/test/java/com/ecobike/FileBikeDaoTest.java | 2 +- 8 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/ecobike/ConsoleCommunicator.java b/src/main/java/com/ecobike/ConsoleCommunicator.java index 2f6356f..bb7d524 100644 --- a/src/main/java/com/ecobike/ConsoleCommunicator.java +++ b/src/main/java/com/ecobike/ConsoleCommunicator.java @@ -126,7 +126,7 @@ public boolean readBoolean() { @Override public void printBikes(List bikes) { for (int i = 0; i < bikes.size(); i++) { - writeMessage(i + 1 + ". " + bikes.get(i).toString()); + writeMessage(i + 1 + ". " + bikes.get(i).toOutputString()); if ((i + 1) % BIKES_ON_PAGE_QUANTITY == 0) { writeMessage(""); do { diff --git a/src/main/java/com/ecobike/command/WriteToFileCommand.java b/src/main/java/com/ecobike/command/WriteToFileCommand.java index 41120cb..6b8604e 100644 --- a/src/main/java/com/ecobike/command/WriteToFileCommand.java +++ b/src/main/java/com/ecobike/command/WriteToFileCommand.java @@ -14,7 +14,7 @@ public void execute() { } else { String confirmMessage = "write data to file"; if (COMMUNICATOR.confirmAction(confirmMessage)) { - EcoBikeApplication.bikeDao.writeBikes(); + EcoBikeApplication.bikeDao.saveBikes(); DATA_HOLDER.setDataChanged(true); COMMUNICATOR.writeMessage("Data has been written successfully."); } diff --git a/src/main/java/com/ecobike/dao/BikeDao.java b/src/main/java/com/ecobike/dao/BikeDao.java index 624482b..011d18a 100644 --- a/src/main/java/com/ecobike/dao/BikeDao.java +++ b/src/main/java/com/ecobike/dao/BikeDao.java @@ -28,5 +28,5 @@ public interface BikeDao { * Method writes to data source Bike objects. * Old data must be replaced with new one. */ - void writeBikes(); + void saveBikes(); } diff --git a/src/main/java/com/ecobike/dao/FileBikeDao.java b/src/main/java/com/ecobike/dao/FileBikeDao.java index db07925..1333bf0 100644 --- a/src/main/java/com/ecobike/dao/FileBikeDao.java +++ b/src/main/java/com/ecobike/dao/FileBikeDao.java @@ -87,7 +87,7 @@ public void loadBikes() throws IllegalDataSourceException { * Old data in file will be replaced with new one. */ @Override - public void writeBikes() { + public void saveBikes() { List dataToWrite = DATA_HOLDER.getUnmodifiableBikeList().stream() .map(Bike::toFileWriterString) .collect(Collectors.toList()); diff --git a/src/main/java/com/ecobike/model/AbstractElectroBike.java b/src/main/java/com/ecobike/model/AbstractElectroBike.java index 6dfff91..85d8f3b 100644 --- a/src/main/java/com/ecobike/model/AbstractElectroBike.java +++ b/src/main/java/com/ecobike/model/AbstractElectroBike.java @@ -17,13 +17,9 @@ public abstract class AbstractElectroBike extends Bike { */ private final int batteryCapacity; - public AbstractElectroBike(String brand, - int maxSpeed, - int weight, - boolean isLightsPresent, - int batteryCapacity, - String color, - int price) { + public AbstractElectroBike(String brand, int maxSpeed, int weight, + boolean isLightsPresent, int batteryCapacity, + String color, int price) { super(brand, weight, isLightsPresent, color, price); this.maxSpeed = maxSpeed; this.batteryCapacity = batteryCapacity; @@ -64,7 +60,7 @@ public int hashCode() { } @Override - public String toString() { + public String toOutputString() { return String.format("%s %s with %d mAh battery and%s head/tail light." + "\nPrice: %d euros.", getBikeType(), getBrand(), getBatteryCapacity(), isLightsPresent() ? "" : " no", getPrice()); diff --git a/src/main/java/com/ecobike/model/Bike.java b/src/main/java/com/ecobike/model/Bike.java index b14bd08..57b7999 100644 --- a/src/main/java/com/ecobike/model/Bike.java +++ b/src/main/java/com/ecobike/model/Bike.java @@ -40,6 +40,14 @@ public Bike(String brand, int weight, boolean isLightsPresent, String color, int */ public abstract String toFileWriterString(); + /** + * Method converts bike to specific String format + * for showing to user. + * + * @return String representation of the bike. + */ + public abstract String toOutputString(); + @Override public boolean equals(Object o) { if (this == o) { diff --git a/src/main/java/com/ecobike/model/FoldingBike.java b/src/main/java/com/ecobike/model/FoldingBike.java index a102ced..f13338c 100644 --- a/src/main/java/com/ecobike/model/FoldingBike.java +++ b/src/main/java/com/ecobike/model/FoldingBike.java @@ -62,7 +62,7 @@ public int hashCode() { } @Override - public String toString() { + public String toOutputString() { return String.format("%s %s with %d gear(s) and%s head/tail light." + "\nPrice: %d euros.", getBikeType(), getBrand(), getNumberOfGears(), isLightsPresent() ? "" : " no", getPrice()); diff --git a/src/test/java/com/ecobike/FileBikeDaoTest.java b/src/test/java/com/ecobike/FileBikeDaoTest.java index dd1b216..5ecd95e 100644 --- a/src/test/java/com/ecobike/FileBikeDaoTest.java +++ b/src/test/java/com/ecobike/FileBikeDaoTest.java @@ -40,7 +40,7 @@ public void writeBikesTest() throws IOException, IllegalDataSourceException, Int BIKE_DAO.loadBikes(); DataHolder dataHolder = DataHolder.getInstance(); dataHolder.addBike(LAST_BIKE); - BIKE_DAO.writeBikes(); + BIKE_DAO.saveBikes(); List fileLines = Files.readAllLines(NEW_FILE); Assert.assertEquals(2, fileLines.size()); Assert.assertEquals("FOLDING BIKE BMW; 20; 7; 14400; false; lemon; 1085", fileLines.get(0)); From b30d1199e4e4fafda4c81962ec3dc46d4a28c90e Mon Sep 17 00:00:00 2001 From: Leonid Date: Fri, 7 Aug 2020 12:38:24 +0300 Subject: [PATCH 12/19] FileBikeDao DI changed --- .../java/com/ecobike/ConsoleCommunicator.java | 4 +- src/main/java/com/ecobike/DataHolder.java | 6 +- .../java/com/ecobike/EcoBikeApplication.java | 37 +++++--- .../com/ecobike/command/AddEBikeCommand.java | 36 +++---- .../command/AddFoldingBikeCommand.java | 36 +++---- .../command/AddSpeedelecBikeCommand.java | 36 +++---- .../java/com/ecobike/command/Command.java | 4 +- .../java/com/ecobike/command/FindCommand.java | 94 +++++++++---------- .../java/com/ecobike/command/ShowCommand.java | 2 +- .../ecobike/command/StopProgramCommand.java | 6 +- .../ecobike/command/WriteToFileCommand.java | 10 +- src/main/java/com/ecobike/dao/BikeDao.java | 8 -- .../java/com/ecobike/dao/FileBikeDao.java | 24 ++--- src/test/java/com/ecobike/DataHolderTest.java | 5 +- .../java/com/ecobike/FileBikeDaoTest.java | 16 ++-- 15 files changed, 160 insertions(+), 164 deletions(-) diff --git a/src/main/java/com/ecobike/ConsoleCommunicator.java b/src/main/java/com/ecobike/ConsoleCommunicator.java index bb7d524..488d246 100644 --- a/src/main/java/com/ecobike/ConsoleCommunicator.java +++ b/src/main/java/com/ecobike/ConsoleCommunicator.java @@ -11,7 +11,7 @@ */ public class ConsoleCommunicator implements Communicator { - private static final BufferedReader READER + private static final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); /** * Number of bikes to be shown on one page. @@ -36,7 +36,7 @@ public String readString() { String entry = null; while (entry == null) { try { - entry = READER.readLine(); + entry = reader.readLine(); } catch (IOException e) { writeMessage("Repeat your entry:"); } diff --git a/src/main/java/com/ecobike/DataHolder.java b/src/main/java/com/ecobike/DataHolder.java index 4c0568a..821ceb1 100644 --- a/src/main/java/com/ecobike/DataHolder.java +++ b/src/main/java/com/ecobike/DataHolder.java @@ -17,7 +17,7 @@ * loaded from data source. */ public class DataHolder { - private static final DataHolder INSTANCE = new DataHolder(); + private static final DataHolder instance = new DataHolder(); /** * Bikes container. */ @@ -39,7 +39,7 @@ private DataHolder() { } public static DataHolder getInstance() { - return INSTANCE; + return instance; } /** @@ -103,7 +103,7 @@ public List findBikesByParameter(SearchParameterContainer parameters) { try { countDownLatch.await(); } catch (InterruptedException e) { - e.printStackTrace(); + throw new RuntimeException(e); } synchronized (this) { Stream bikeStream = bikes.parallelStream() diff --git a/src/main/java/com/ecobike/EcoBikeApplication.java b/src/main/java/com/ecobike/EcoBikeApplication.java index 0b3ae98..f94b41a 100644 --- a/src/main/java/com/ecobike/EcoBikeApplication.java +++ b/src/main/java/com/ecobike/EcoBikeApplication.java @@ -17,7 +17,7 @@ public class EcoBikeApplication { /** * Object for loading data from and writing data to file. */ - public static final BikeDao bikeDao = FileBikeDao.getInstance(); + public static BikeDao bikeDao; /** * Main method of the application. @@ -25,14 +25,31 @@ public class EcoBikeApplication { * @param args system arguments. */ public static void main(String[] args) { + initFileBikeDao(); + while (true) { + try { + Operation operation = askOperation(); + CommandExecutor.execute(operation); + if (operation == Operation.STOP_PROGRAM + && communicator.confirmAction("EXIT from program")) { + communicator.writeMessage("Good bay!"); + break; + } + } catch (Exception e) { + communicator.writeMessage("Error occurred. Repeat action."); + } + } + } + + private static void initFileBikeDao() { boolean isFileParsed = false; while (!isFileParsed) { - String bikeDataFile; + Path bikeDataFile; do { communicator.writeMessage("Enter path to Bikes data file :"); - bikeDataFile = communicator.readNotEmptyString(); - } while (!Files.isRegularFile(Path.of(bikeDataFile))); - bikeDao.setSource(bikeDataFile); + bikeDataFile = Path.of(communicator.readNotEmptyString()); + } while (!Files.isRegularFile(bikeDataFile)); + bikeDao = new FileBikeDao(bikeDataFile); try { bikeDao.loadBikes(); isFileParsed = true; @@ -41,16 +58,6 @@ public static void main(String[] args) { communicator.writeMessage(""); } } - - while (true) { - Operation operation = askOperation(); - CommandExecutor.execute(operation); - if (operation == Operation.STOP_PROGRAM - && communicator.confirmAction("EXIT from program")) { - communicator.writeMessage("Good bay!"); - break; - } - } } /** diff --git a/src/main/java/com/ecobike/command/AddEBikeCommand.java b/src/main/java/com/ecobike/command/AddEBikeCommand.java index e3afa12..765e86d 100644 --- a/src/main/java/com/ecobike/command/AddEBikeCommand.java +++ b/src/main/java/com/ecobike/command/AddEBikeCommand.java @@ -9,21 +9,21 @@ public class AddEBikeCommand implements Command { @Override public void execute() { - COMMUNICATOR.writeMessage("You are adding new E-BIKE"); - COMMUNICATOR.writeMessage("Enter brand:"); - String brand = COMMUNICATOR.readNotEmptyString(); - COMMUNICATOR.writeMessage("Enter max speed:"); - int maxSpeed = COMMUNICATOR.readPositiveInt(); - COMMUNICATOR.writeMessage("Enter weight:"); - int weight = COMMUNICATOR.readPositiveInt(); - COMMUNICATOR.writeMessage("Enter presence of lights:"); - boolean isLightsPresent = COMMUNICATOR.readBoolean(); - COMMUNICATOR.writeMessage("Enter battery capacity:"); - int batteryCapacity = COMMUNICATOR.readPositiveInt(); - COMMUNICATOR.writeMessage("Enter color:"); - String color = COMMUNICATOR.readNotEmptyString(); - COMMUNICATOR.writeMessage("Enter price:"); - int price = COMMUNICATOR.readPositiveInt(); + communicator.writeMessage("You are adding new E-BIKE"); + communicator.writeMessage("Enter brand:"); + String brand = communicator.readNotEmptyString(); + communicator.writeMessage("Enter max speed:"); + int maxSpeed = communicator.readPositiveInt(); + communicator.writeMessage("Enter weight:"); + int weight = communicator.readPositiveInt(); + communicator.writeMessage("Enter presence of lights:"); + boolean isLightsPresent = communicator.readBoolean(); + communicator.writeMessage("Enter battery capacity:"); + int batteryCapacity = communicator.readPositiveInt(); + communicator.writeMessage("Enter color:"); + String color = communicator.readNotEmptyString(); + communicator.writeMessage("Enter price:"); + int price = communicator.readPositiveInt(); String confirmMessage = String.format("add new %s with next parametrs:\n" + "brand: %s\n" @@ -36,10 +36,10 @@ public void execute() { BikeType.E_BIKE, brand, maxSpeed, weight, isLightsPresent, batteryCapacity, color, price); - if (COMMUNICATOR.confirmAction(confirmMessage)) { - DATA_HOLDER.addBike(new EBike(brand, maxSpeed, weight, + if (communicator.confirmAction(confirmMessage)) { + dataHolder.addBike(new EBike(brand, maxSpeed, weight, isLightsPresent, batteryCapacity, color, price)); - COMMUNICATOR.writeMessage("New E-BIKE BIKE added."); + communicator.writeMessage("New E-BIKE BIKE added."); } } } diff --git a/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java b/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java index 902b7e9..9fc6302 100644 --- a/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java +++ b/src/main/java/com/ecobike/command/AddFoldingBikeCommand.java @@ -9,21 +9,21 @@ public class AddFoldingBikeCommand implements Command { @Override public void execute() { - COMMUNICATOR.writeMessage("You are adding new FOLDING BIKE"); - COMMUNICATOR.writeMessage("Enter brand:"); - String brand = COMMUNICATOR.readNotEmptyString(); - COMMUNICATOR.writeMessage("Enter wheel size:"); - int wheelSize = COMMUNICATOR.readPositiveInt(); - COMMUNICATOR.writeMessage("Enter number of gears:"); - int numberOfGears = COMMUNICATOR.readPositiveInt(); - COMMUNICATOR.writeMessage("Enter weight:"); - int weight = COMMUNICATOR.readPositiveInt(); - COMMUNICATOR.writeMessage("Enter presence of lights:"); - boolean isLightsPresent = COMMUNICATOR.readBoolean(); - COMMUNICATOR.writeMessage("Enter color:"); - String color = COMMUNICATOR.readNotEmptyString(); - COMMUNICATOR.writeMessage("Enter price:"); - int price = COMMUNICATOR.readPositiveInt(); + communicator.writeMessage("You are adding new FOLDING BIKE"); + communicator.writeMessage("Enter brand:"); + String brand = communicator.readNotEmptyString(); + communicator.writeMessage("Enter wheel size:"); + int wheelSize = communicator.readPositiveInt(); + communicator.writeMessage("Enter number of gears:"); + int numberOfGears = communicator.readPositiveInt(); + communicator.writeMessage("Enter weight:"); + int weight = communicator.readPositiveInt(); + communicator.writeMessage("Enter presence of lights:"); + boolean isLightsPresent = communicator.readBoolean(); + communicator.writeMessage("Enter color:"); + String color = communicator.readNotEmptyString(); + communicator.writeMessage("Enter price:"); + int price = communicator.readPositiveInt(); String confirmMessage = String.format("add new %s with next parametrs:\n" + "brand: %s\n" @@ -36,10 +36,10 @@ public void execute() { BikeType.FOLDING_BIKE, brand, wheelSize, numberOfGears, weight, isLightsPresent, color, price); - if (COMMUNICATOR.confirmAction(confirmMessage)) { - DATA_HOLDER.addBike(new FoldingBike(brand, wheelSize, numberOfGears, + if (communicator.confirmAction(confirmMessage)) { + dataHolder.addBike(new FoldingBike(brand, wheelSize, numberOfGears, weight, isLightsPresent, color, price)); - COMMUNICATOR.writeMessage("New FOLDING BIKE added."); + communicator.writeMessage("New FOLDING BIKE added."); } } } diff --git a/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java b/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java index d13b542..6ab747a 100644 --- a/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java +++ b/src/main/java/com/ecobike/command/AddSpeedelecBikeCommand.java @@ -9,21 +9,21 @@ public class AddSpeedelecBikeCommand implements Command { @Override public void execute() { - COMMUNICATOR.writeMessage("You are adding new SPEEDELEC BIKE"); - COMMUNICATOR.writeMessage("Enter brand:"); - String brand = COMMUNICATOR.readNotEmptyString(); - COMMUNICATOR.writeMessage("Enter max speed:"); - int maxSpeed = COMMUNICATOR.readPositiveInt(); - COMMUNICATOR.writeMessage("Enter weight:"); - int weight = COMMUNICATOR.readPositiveInt(); - COMMUNICATOR.writeMessage("Enter presence of lights:"); - boolean isLightsPresent = COMMUNICATOR.readBoolean(); - COMMUNICATOR.writeMessage("Enter battery capacity:"); - int batteryCapacity = COMMUNICATOR.readPositiveInt(); - COMMUNICATOR.writeMessage("Enter color:"); - String color = COMMUNICATOR.readNotEmptyString(); - COMMUNICATOR.writeMessage("Enter price:"); - int price = COMMUNICATOR.readPositiveInt(); + communicator.writeMessage("You are adding new SPEEDELEC BIKE"); + communicator.writeMessage("Enter brand:"); + String brand = communicator.readNotEmptyString(); + communicator.writeMessage("Enter max speed:"); + int maxSpeed = communicator.readPositiveInt(); + communicator.writeMessage("Enter weight:"); + int weight = communicator.readPositiveInt(); + communicator.writeMessage("Enter presence of lights:"); + boolean isLightsPresent = communicator.readBoolean(); + communicator.writeMessage("Enter battery capacity:"); + int batteryCapacity = communicator.readPositiveInt(); + communicator.writeMessage("Enter color:"); + String color = communicator.readNotEmptyString(); + communicator.writeMessage("Enter price:"); + int price = communicator.readPositiveInt(); String confirmMessage = String.format("add new %s with next parametrs:\n" + "brand: %s\n" @@ -36,10 +36,10 @@ public void execute() { BikeType.SPEEDELEC, brand, maxSpeed, weight, isLightsPresent, batteryCapacity, color, price); - if (COMMUNICATOR.confirmAction(confirmMessage)) { - DATA_HOLDER.addBike(new SpeedelecBike(brand, maxSpeed, weight, + if (communicator.confirmAction(confirmMessage)) { + dataHolder.addBike(new SpeedelecBike(brand, maxSpeed, weight, isLightsPresent, batteryCapacity, color, price)); - COMMUNICATOR.writeMessage("New SPEEDELEC BIKE added."); + communicator.writeMessage("New SPEEDELEC BIKE added."); } } } diff --git a/src/main/java/com/ecobike/command/Command.java b/src/main/java/com/ecobike/command/Command.java index 33f0e2d..b1b6ae7 100644 --- a/src/main/java/com/ecobike/command/Command.java +++ b/src/main/java/com/ecobike/command/Command.java @@ -9,8 +9,8 @@ * operation specified by user. */ public interface Command { - Communicator COMMUNICATOR = EcoBikeApplication.communicator; - DataHolder DATA_HOLDER = DataHolder.getInstance(); + Communicator communicator = EcoBikeApplication.communicator; + DataHolder dataHolder = DataHolder.getInstance(); void execute(); } diff --git a/src/main/java/com/ecobike/command/FindCommand.java b/src/main/java/com/ecobike/command/FindCommand.java index a7d3732..9a5b9e1 100644 --- a/src/main/java/com/ecobike/command/FindCommand.java +++ b/src/main/java/com/ecobike/command/FindCommand.java @@ -14,37 +14,37 @@ public class FindCommand implements Command { public void execute() { SearchParameterContainer paramContainer = new SearchParameterContainer(); while (true) { - COMMUNICATOR.writeMessage("Select bike type you wont to find"); - COMMUNICATOR.writeMessage("\t 1 - " + BikeType.FOLDING_BIKE); - COMMUNICATOR.writeMessage("\t 2 - " + BikeType.E_BIKE); - COMMUNICATOR.writeMessage("\t 3 - " + BikeType.SPEEDELEC); + communicator.writeMessage("Select bike type you wont to find"); + communicator.writeMessage("\t 1 - " + BikeType.FOLDING_BIKE); + communicator.writeMessage("\t 2 - " + BikeType.E_BIKE); + communicator.writeMessage("\t 3 - " + BikeType.SPEEDELEC); int bikeTypeNumber; - if ((bikeTypeNumber = COMMUNICATOR.readInt()) >= 1 && bikeTypeNumber <= 3) { + if ((bikeTypeNumber = communicator.readInt()) >= 1 && bikeTypeNumber <= 3) { paramContainer.setBikeType(BikeType.values()[bikeTypeNumber - 1]); break; } - COMMUNICATOR.writeMessage("=== Wrong entry ==="); - COMMUNICATOR.writeMessage(""); + communicator.writeMessage("=== Wrong entry ==="); + communicator.writeMessage(""); } - COMMUNICATOR.writeMessage("Enter bike brand:"); + communicator.writeMessage("Enter bike brand:"); String brand; - while ((brand = COMMUNICATOR.readString()).isEmpty()) { - COMMUNICATOR.writeMessage("Can't skip. Enter bike brand:"); + while ((brand = communicator.readString()).isEmpty()) { + communicator.writeMessage("Can't skip. Enter bike brand:"); } paramContainer.setBrand(brand); - COMMUNICATOR.writeMessage("You may choose next parameters " - + "(for skipping parameter press \"Enter\"):"); - COMMUNICATOR.writeMessage("Enter min weight:"); - paramContainer.setMinWeight(COMMUNICATOR.readInt()); + communicator.writeMessage("You may choose next parameters " + + "(for skipping parameter press \"Enter\"):"); + communicator.writeMessage("Enter min weight:"); + paramContainer.setMinWeight(communicator.readInt()); - COMMUNICATOR.writeMessage("Enter max weight:"); - paramContainer.setMaxWeight(COMMUNICATOR.readInt()); + communicator.writeMessage("Enter max weight:"); + paramContainer.setMaxWeight(communicator.readInt()); - COMMUNICATOR.writeMessage("Enter lights presence (Type 1 for TRUE or 2 for FALSE):"); + communicator.writeMessage("Enter lights presence (Type 1 for TRUE or 2 for FALSE):"); while (true) { - String entry = COMMUNICATOR.readString(); + String entry = communicator.readString(); if (entry.isEmpty()) { break; } @@ -58,49 +58,49 @@ public void execute() { paramContainer.setLightsOptionEntered(true); break; } - COMMUNICATOR.writeMessage("Wrong entry. " - + "Type 1 for TRUE, 2 for FALSE or \"Enter\" for skipping"); + communicator.writeMessage("Wrong entry. " + + "Type 1 for TRUE, 2 for FALSE or \"Enter\" for skipping"); } - COMMUNICATOR.writeMessage("Enter color:"); - paramContainer.setColor(COMMUNICATOR.readString()); + communicator.writeMessage("Enter color:"); + paramContainer.setColor(communicator.readString()); - COMMUNICATOR.writeMessage("Enter min price:"); - paramContainer.setMinPrice(COMMUNICATOR.readInt()); + communicator.writeMessage("Enter min price:"); + paramContainer.setMinPrice(communicator.readInt()); - COMMUNICATOR.writeMessage("Enter max price:"); - paramContainer.setMaxPrice(COMMUNICATOR.readInt()); + communicator.writeMessage("Enter max price:"); + paramContainer.setMaxPrice(communicator.readInt()); if (paramContainer.getBikeType() == BikeType.FOLDING_BIKE) { - COMMUNICATOR.writeMessage("Enter min wheel size:"); - paramContainer.setMinWheelSize(COMMUNICATOR.readInt()); - COMMUNICATOR.writeMessage("Enter max wheel size:"); - paramContainer.setMaxWheelSize(COMMUNICATOR.readInt()); - COMMUNICATOR.writeMessage("Enter min number of gears:"); - paramContainer.setMinNumberOfGears(COMMUNICATOR.readInt()); - COMMUNICATOR.writeMessage("Enter max number of gears:"); - paramContainer.setMaxNumberOfGears(COMMUNICATOR.readInt()); + communicator.writeMessage("Enter min wheel size:"); + paramContainer.setMinWheelSize(communicator.readInt()); + communicator.writeMessage("Enter max wheel size:"); + paramContainer.setMaxWheelSize(communicator.readInt()); + communicator.writeMessage("Enter min number of gears:"); + paramContainer.setMinNumberOfGears(communicator.readInt()); + communicator.writeMessage("Enter max number of gears:"); + paramContainer.setMaxNumberOfGears(communicator.readInt()); } if (paramContainer.getBikeType() == BikeType.E_BIKE || paramContainer.getBikeType() == BikeType.SPEEDELEC) { - COMMUNICATOR.writeMessage("Enter minimum max bike speed:"); - paramContainer.setMinMaxBikeSpeed(COMMUNICATOR.readInt()); - COMMUNICATOR.writeMessage("Enter maximum max bike speed:"); - paramContainer.setMaxMaxBikeSpeed(COMMUNICATOR.readInt()); - COMMUNICATOR.writeMessage("Enter min battery capacity:"); - paramContainer.setMinBatteryCapacity(COMMUNICATOR.readInt()); - COMMUNICATOR.writeMessage("Enter max battery capacity:"); - paramContainer.setMaxBatteryCapacity(COMMUNICATOR.readInt()); + communicator.writeMessage("Enter minimum max bike speed:"); + paramContainer.setMinMaxBikeSpeed(communicator.readInt()); + communicator.writeMessage("Enter maximum max bike speed:"); + paramContainer.setMaxMaxBikeSpeed(communicator.readInt()); + communicator.writeMessage("Enter min battery capacity:"); + paramContainer.setMinBatteryCapacity(communicator.readInt()); + communicator.writeMessage("Enter max battery capacity:"); + paramContainer.setMaxBatteryCapacity(communicator.readInt()); } - List bikes = DATA_HOLDER.findBikesByParameter(paramContainer); + List bikes = dataHolder.findBikesByParameter(paramContainer); if (bikes.isEmpty()) { - COMMUNICATOR.writeMessage("No bikes matches your query."); + communicator.writeMessage("No bikes matches your query."); } else { - COMMUNICATOR.writeMessage(bikes.size() + " bikes matches your query:"); - COMMUNICATOR.writeMessage(""); - COMMUNICATOR.printBikes(bikes); + communicator.writeMessage(bikes.size() + " bikes matches your query:"); + communicator.writeMessage(""); + communicator.printBikes(bikes); } } } diff --git a/src/main/java/com/ecobike/command/ShowCommand.java b/src/main/java/com/ecobike/command/ShowCommand.java index ebd1f77..e5eb4a2 100644 --- a/src/main/java/com/ecobike/command/ShowCommand.java +++ b/src/main/java/com/ecobike/command/ShowCommand.java @@ -7,6 +7,6 @@ public class ShowCommand implements Command { @Override public void execute() { - COMMUNICATOR.printBikes(DATA_HOLDER.getUnmodifiableBikeList()); + communicator.printBikes(dataHolder.getUnmodifiableBikeList()); } } diff --git a/src/main/java/com/ecobike/command/StopProgramCommand.java b/src/main/java/com/ecobike/command/StopProgramCommand.java index b12b5d3..1645fea 100644 --- a/src/main/java/com/ecobike/command/StopProgramCommand.java +++ b/src/main/java/com/ecobike/command/StopProgramCommand.java @@ -6,10 +6,10 @@ public class StopProgramCommand implements Command { @Override public void execute() { - if (DATA_HOLDER.isDataChanged()) { - COMMUNICATOR.writeMessage("You are going to exit without saving changed data.\n" + if (dataHolder.isDataChanged()) { + communicator.writeMessage("You are going to exit without saving changed data.\n" + "Do you wont to save data?"); - if (COMMUNICATOR.readBoolean()) { + if (communicator.readBoolean()) { CommandExecutor.execute(Operation.WRITE_TO_FILE); } } diff --git a/src/main/java/com/ecobike/command/WriteToFileCommand.java b/src/main/java/com/ecobike/command/WriteToFileCommand.java index 6b8604e..9067b45 100644 --- a/src/main/java/com/ecobike/command/WriteToFileCommand.java +++ b/src/main/java/com/ecobike/command/WriteToFileCommand.java @@ -9,14 +9,14 @@ public class WriteToFileCommand implements Command { @Override public void execute() { - if (!DATA_HOLDER.isDataChanged()) { - COMMUNICATOR.writeMessage("Data has not been changed or already has saved"); + if (!dataHolder.isDataChanged()) { + communicator.writeMessage("Data has not been changed or already has saved"); } else { String confirmMessage = "write data to file"; - if (COMMUNICATOR.confirmAction(confirmMessage)) { + if (communicator.confirmAction(confirmMessage)) { EcoBikeApplication.bikeDao.saveBikes(); - DATA_HOLDER.setDataChanged(true); - COMMUNICATOR.writeMessage("Data has been written successfully."); + dataHolder.setDataChanged(true); + communicator.writeMessage("Data has been written successfully."); } } diff --git a/src/main/java/com/ecobike/dao/BikeDao.java b/src/main/java/com/ecobike/dao/BikeDao.java index 011d18a..054d3ea 100644 --- a/src/main/java/com/ecobike/dao/BikeDao.java +++ b/src/main/java/com/ecobike/dao/BikeDao.java @@ -8,14 +8,6 @@ */ public interface BikeDao { - /** - * Method sets data source. - * - * @param address string with parameters needed - * for connection to data source. - */ - void setSource(String address); - /** * Method reads data from data source and * loads parsed Bike objects to DataHolder. diff --git a/src/main/java/com/ecobike/dao/FileBikeDao.java b/src/main/java/com/ecobike/dao/FileBikeDao.java index 1333bf0..e6332aa 100644 --- a/src/main/java/com/ecobike/dao/FileBikeDao.java +++ b/src/main/java/com/ecobike/dao/FileBikeDao.java @@ -1,8 +1,8 @@ package com.ecobike.dao; -import static com.ecobike.EcoBikeApplication.communicator; - +import com.ecobike.Communicator; import com.ecobike.DataHolder; +import com.ecobike.EcoBikeApplication; import com.ecobike.exception.IllegalDataSourceException; import com.ecobike.model.Bike; import com.ecobike.model.BikeType; @@ -20,26 +20,23 @@ import java.util.stream.Stream; /** - * Singleton class responsible for reading data from file and parsing text to Bike objects, + * Class responsible for reading data from file and parsing text to Bike objects, * and for writing to file Bike objects in text format. * Old data in file will be replaced by new one. */ public class FileBikeDao implements BikeDao { - private static final FileBikeDao INSTANCE = new FileBikeDao(); - - private static final DataHolder DATA_HOLDER = DataHolder.getInstance(); + private static final DataHolder dataHolder = DataHolder.getInstance(); + private static final Communicator communicator = EcoBikeApplication.communicator; /** * Path to data file. */ private Path file; - private FileBikeDao() { - } - public static FileBikeDao getInstance() { - return INSTANCE; + public FileBikeDao(Path file) { + this.file = file; } /** @@ -47,8 +44,7 @@ public static FileBikeDao getInstance() { * * @param address string with path to data source. */ - @Override - public void setSource(String address) { + public void setFile(String address) { file = Paths.get(address); } @@ -78,7 +74,7 @@ public void loadBikes() throws IllegalDataSourceException { throw new IllegalDataSourceException(); } wrongLinesInfo.forEach(communicator::writeMessage); - DATA_HOLDER.init(bikes); + dataHolder.init(bikes); communicator.writeMessage(bikes.size() + " bike items has been read from the file"); } @@ -88,7 +84,7 @@ public void loadBikes() throws IllegalDataSourceException { */ @Override public void saveBikes() { - List dataToWrite = DATA_HOLDER.getUnmodifiableBikeList().stream() + List dataToWrite = dataHolder.getUnmodifiableBikeList().stream() .map(Bike::toFileWriterString) .collect(Collectors.toList()); try { diff --git a/src/test/java/com/ecobike/DataHolderTest.java b/src/test/java/com/ecobike/DataHolderTest.java index 7689dea..f47984a 100644 --- a/src/test/java/com/ecobike/DataHolderTest.java +++ b/src/test/java/com/ecobike/DataHolderTest.java @@ -11,6 +11,7 @@ import org.junit.Before; import org.junit.Test; +import java.nio.file.Path; import java.util.Arrays; import java.util.List; @@ -18,7 +19,8 @@ import static org.junit.Assert.assertTrue; public class DataHolderTest { - private static final BikeDao BIKE_DAO = FileBikeDao.getInstance(); + private static final BikeDao BIKE_DAO + = new FileBikeDao(Path.of("src/main/resources/test/fileWithFiveTrueBikes.txt")); private static final DataHolder DATA_HOLDER = DataHolder.getInstance(); private static final int START_EXPECTED_LIST_SIZE = 5; private static final int ADDBIKES_EXPECTED_LIST_SIZE = 2; @@ -31,7 +33,6 @@ public class DataHolderTest { new SearchParameterContainer(); static { - BIKE_DAO.setSource("src/main/resources/test/fileWithFiveTrueBikes.txt"); PARAMETER_CONTAINER.setBikeType(BikeType.FOLDING_BIKE); PARAMETER_CONTAINER.setBrand("bmW"); PARAMETER_CONTAINER.setMinWheelSize(16); diff --git a/src/test/java/com/ecobike/FileBikeDaoTest.java b/src/test/java/com/ecobike/FileBikeDaoTest.java index 5ecd95e..b65a9cd 100644 --- a/src/test/java/com/ecobike/FileBikeDaoTest.java +++ b/src/test/java/com/ecobike/FileBikeDaoTest.java @@ -17,11 +17,11 @@ import java.util.List; public class FileBikeDaoTest { - private static final BikeDao BIKE_DAO = FileBikeDao.getInstance(); private static final int EXPECTED_LIST_SIZE = 5; private static final Bike LAST_BIKE = new SpeedelecBike("EcoRide", 50, 8400, false, 8600, "brown", 1609); private static final Path NEW_FILE = Path.of("src/main/resources/test/newFile.txt"); + private BikeDao bikeDao; @Rule public ExpectedException expectedEx = ExpectedException.none(); @@ -36,11 +36,11 @@ public void writeBikesTest() throws IOException, IllegalDataSourceException, Int Files.deleteIfExists(NEW_FILE); Files.createFile(NEW_FILE); Files.writeString(NEW_FILE, "FOLDING BIKE BMW; 20; 7; 14400; false; lemon; 1085"); - BIKE_DAO.setSource("src/main/resources/test/newFile.txt"); - BIKE_DAO.loadBikes(); + bikeDao = new FileBikeDao(Path.of("src/main/resources/test/newFile.txt")); + bikeDao.loadBikes(); DataHolder dataHolder = DataHolder.getInstance(); dataHolder.addBike(LAST_BIKE); - BIKE_DAO.saveBikes(); + bikeDao.saveBikes(); List fileLines = Files.readAllLines(NEW_FILE); Assert.assertEquals(2, fileLines.size()); Assert.assertEquals("FOLDING BIKE BMW; 20; 7; 14400; false; lemon; 1085", fileLines.get(0)); @@ -49,8 +49,8 @@ public void writeBikesTest() throws IOException, IllegalDataSourceException, Int @Test public void loadBikesTest() throws IllegalDataSourceException { - BIKE_DAO.setSource("src/main/resources/test/fileWithFiveTrueBikes.txt"); - BIKE_DAO.loadBikes(); + bikeDao = new FileBikeDao(Path.of("src/main/resources/test/fileWithFiveTrueBikes.txt")); + bikeDao.loadBikes(); DataHolder dataHolder = DataHolder.getInstance(); List loadedBikes = dataHolder.getUnmodifiableBikeList(); @@ -58,7 +58,7 @@ public void loadBikesTest() throws IllegalDataSourceException { Assert.assertEquals(loadedBikes.size() - 1, loadedBikes.indexOf(LAST_BIKE)); expectedEx.expect(IllegalDataSourceException.class); - BIKE_DAO.setSource("src/main/resources/test/wrongBikeFormat.txt"); - BIKE_DAO.loadBikes(); + bikeDao = new FileBikeDao(Path.of("src/main/resources/test/wrongBikeFormat.txt")); + bikeDao.loadBikes(); } } \ No newline at end of file From e590b83e1962b577e3c80ef05848ecbbd69813f1 Mon Sep 17 00:00:00 2001 From: Leonid Date: Fri, 7 Aug 2020 13:21:09 +0300 Subject: [PATCH 13/19] FileBikeParser added --- .../java/com/ecobike/dao/FileBikeDao.java | 92 +------------------ .../java/com/ecobike/dao/FileBikeParser.java | 83 +++++++++++++++++ .../exception/IllegalDataSourceException.java | 6 ++ ...aoTest.java => FileBikeParserDaoTest.java} | 2 +- 4 files changed, 93 insertions(+), 90 deletions(-) create mode 100644 src/main/java/com/ecobike/dao/FileBikeParser.java rename src/test/java/com/ecobike/{FileBikeDaoTest.java => FileBikeParserDaoTest.java} (98%) diff --git a/src/main/java/com/ecobike/dao/FileBikeDao.java b/src/main/java/com/ecobike/dao/FileBikeDao.java index e6332aa..aa738ae 100644 --- a/src/main/java/com/ecobike/dao/FileBikeDao.java +++ b/src/main/java/com/ecobike/dao/FileBikeDao.java @@ -5,19 +5,13 @@ import com.ecobike.EcoBikeApplication; import com.ecobike.exception.IllegalDataSourceException; import com.ecobike.model.Bike; -import com.ecobike.model.BikeType; -import com.ecobike.model.EBike; -import com.ecobike.model.FoldingBike; -import com.ecobike.model.SpeedelecBike; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -import java.util.stream.Stream; /** * Class responsible for reading data from file and parsing text to Bike objects, @@ -27,27 +21,17 @@ public class FileBikeDao implements BikeDao { private static final DataHolder dataHolder = DataHolder.getInstance(); - private static final Communicator communicator = EcoBikeApplication.communicator; + private static final FileBikeParser parser = new FileBikeParser(); /** * Path to data file. */ private Path file; - public FileBikeDao(Path file) { this.file = file; } - /** - * Method sets data file. - * - * @param address string with path to data source. - */ - public void setFile(String address) { - file = Paths.get(address); - } - /** * Method reads text from file and loads parsed Bike objects to DataHolder. * @@ -59,13 +43,13 @@ public void loadBikes() throws IllegalDataSourceException { try { lines = Files.readAllLines(file); } catch (IOException e) { - e.printStackTrace(); + throw new IllegalDataSourceException(e); } List bikes = new ArrayList<>(lines.size()); List wrongLinesInfo = new ArrayList<>(); for (int i = 0; i < lines.size(); i++) { try { - bikes.add(parseBike(lines.get(i))); + bikes.add(parser.parseBike(lines.get(i))); } catch (IllegalArgumentException e) { wrongLinesInfo.add("Line No. " + (i + 1) + " has wrong format"); } @@ -93,74 +77,4 @@ public void saveBikes() { e.printStackTrace(); } } - - /** - * Method parses single String line to Bike object. - * - * @param line string to be parsed. - * @return parsed Bike object - */ - private Bike parseBike(String line) { - try { - if (line.startsWith(BikeType.SPEEDELEC.toString())) { - String[] parameters = line.replace(BikeType.SPEEDELEC + " ", "").split("; "); - checkParametersForEmpties(parameters); - return new SpeedelecBike(parameters[0], Integer.parseInt(parameters[1]), - Integer.parseInt(parameters[2]), parseBoolean(parameters[3]), - Integer.parseInt(parameters[4]), parameters[5], - Integer.parseInt(parameters[6])); - } - if (line.startsWith(BikeType.E_BIKE.toString())) { - String[] parameters = line.replace(BikeType.E_BIKE + " ", "").split("; "); - checkParametersForEmpties(parameters); - return new EBike(parameters[0], Integer.parseInt(parameters[1]), - Integer.parseInt(parameters[2]), parseBoolean(parameters[3]), - Integer.parseInt(parameters[4]), parameters[5], - Integer.parseInt(parameters[6])); - } - if (line.startsWith(BikeType.FOLDING_BIKE.toString())) { - String[] parameters = line.replace(BikeType.FOLDING_BIKE + " ", "").split("; "); - checkParametersForEmpties(parameters); - return new FoldingBike(parameters[0], Integer.parseInt(parameters[1]), - Integer.parseInt(parameters[2]), Integer.parseInt(parameters[3]), - parseBoolean(parameters[4]), parameters[5], - Integer.parseInt(parameters[6])); - } - } catch (RuntimeException e) { - throw new IllegalArgumentException(); - } - throw new IllegalArgumentException(); - } - - /** - * Method parses string to boolean value. - * - * @param value string to be parsed - * @return TRUE if value equals ignore case "true" - * or FALSE if value equals ignore case "false". - * Otherwise throws IllegalArgumentException. - */ - private boolean parseBoolean(String value) { - if ("true".equalsIgnoreCase(value)) { - return true; - } - if ("false".equalsIgnoreCase(value)) { - return false; - } - throw new IllegalArgumentException(); - } - - /** - * Method checks string array for presence at least one - * empty string. If present - throws IllegalArgumentException. - * - * @param parameters string array. - */ - private void checkParametersForEmpties(String[] parameters) { - boolean isEmptyPresent = Stream.of(parameters) - .anyMatch(String::isEmpty); - if (isEmptyPresent) { - throw new IllegalArgumentException(); - } - } } diff --git a/src/main/java/com/ecobike/dao/FileBikeParser.java b/src/main/java/com/ecobike/dao/FileBikeParser.java new file mode 100644 index 0000000..55b3345 --- /dev/null +++ b/src/main/java/com/ecobike/dao/FileBikeParser.java @@ -0,0 +1,83 @@ +package com.ecobike.dao; + +import com.ecobike.model.Bike; +import com.ecobike.model.BikeType; +import com.ecobike.model.EBike; +import com.ecobike.model.FoldingBike; +import com.ecobike.model.SpeedelecBike; +import java.util.stream.Stream; + +public class FileBikeParser { + public FileBikeParser() { + } + + /** + * Method parses single String line to Bike object. + * + * @param line string to be parsed. + * @return parsed Bike object + */ + public Bike parseBike(String line) { + try { + if (line.startsWith(BikeType.SPEEDELEC.toString())) { + String[] parameters = line.replace(BikeType.SPEEDELEC + " ", "").split("; "); + checkParametersForEmpties(parameters); + return new SpeedelecBike(parameters[0], Integer.parseInt(parameters[1]), + Integer.parseInt(parameters[2]), parseBoolean(parameters[3]), + Integer.parseInt(parameters[4]), parameters[5], + Integer.parseInt(parameters[6])); + } + if (line.startsWith(BikeType.E_BIKE.toString())) { + String[] parameters = line.replace(BikeType.E_BIKE + " ", "").split("; "); + checkParametersForEmpties(parameters); + return new EBike(parameters[0], Integer.parseInt(parameters[1]), + Integer.parseInt(parameters[2]), parseBoolean(parameters[3]), + Integer.parseInt(parameters[4]), parameters[5], + Integer.parseInt(parameters[6])); + } + if (line.startsWith(BikeType.FOLDING_BIKE.toString())) { + String[] parameters = line.replace(BikeType.FOLDING_BIKE + " ", "").split("; "); + checkParametersForEmpties(parameters); + return new FoldingBike(parameters[0], Integer.parseInt(parameters[1]), + Integer.parseInt(parameters[2]), Integer.parseInt(parameters[3]), + parseBoolean(parameters[4]), parameters[5], + Integer.parseInt(parameters[6])); + } + } catch (RuntimeException e) { + throw new IllegalArgumentException(); + } + throw new IllegalArgumentException(); + } + + /** + * Method parses string to boolean value. + * + * @param value string to be parsed + * @return TRUE if value equals ignore case "true" + * or FALSE if value equals ignore case "false". + * Otherwise throws IllegalArgumentException. + */ + public boolean parseBoolean(String value) { + if ("true".equalsIgnoreCase(value)) { + return true; + } + if ("false".equalsIgnoreCase(value)) { + return false; + } + throw new IllegalArgumentException(); + } + + /** + * Method checks string array for presence at least one + * empty string. If present - throws IllegalArgumentException. + * + * @param parameters string array. + */ + public void checkParametersForEmpties(String[] parameters) { + boolean isEmptyPresent = Stream.of(parameters) + .anyMatch(String::isEmpty); + if (isEmptyPresent) { + throw new IllegalArgumentException(); + } + } +} diff --git a/src/main/java/com/ecobike/exception/IllegalDataSourceException.java b/src/main/java/com/ecobike/exception/IllegalDataSourceException.java index e6afc10..9c0ea3d 100644 --- a/src/main/java/com/ecobike/exception/IllegalDataSourceException.java +++ b/src/main/java/com/ecobike/exception/IllegalDataSourceException.java @@ -5,4 +5,10 @@ * has wrong data format and/or can not be parsed. */ public class IllegalDataSourceException extends Exception { + public IllegalDataSourceException(Throwable cause) { + super(cause); + } + + public IllegalDataSourceException() { + } } diff --git a/src/test/java/com/ecobike/FileBikeDaoTest.java b/src/test/java/com/ecobike/FileBikeParserDaoTest.java similarity index 98% rename from src/test/java/com/ecobike/FileBikeDaoTest.java rename to src/test/java/com/ecobike/FileBikeParserDaoTest.java index b65a9cd..e8dc7cd 100644 --- a/src/test/java/com/ecobike/FileBikeDaoTest.java +++ b/src/test/java/com/ecobike/FileBikeParserDaoTest.java @@ -16,7 +16,7 @@ import java.nio.file.Path; import java.util.List; -public class FileBikeDaoTest { +public class FileBikeParserDaoTest { private static final int EXPECTED_LIST_SIZE = 5; private static final Bike LAST_BIKE = new SpeedelecBike("EcoRide", 50, 8400, false, 8600, "brown", 1609); From b82857bfb98f61e50828a53e7824db992c5f2c5b Mon Sep 17 00:00:00 2001 From: Leonid Date: Sat, 8 Aug 2020 09:03:33 +0300 Subject: [PATCH 14/19] parser package added --- .../ecobike/command/WriteToFileCommand.java | 1 - .../java/com/ecobike/dao/FileBikeDao.java | 10 ++- .../java/com/ecobike/dao/FileBikeParser.java | 83 ------------------- .../java/com/ecobike/parser/BikeParser.java | 13 +++ .../com/ecobike/parser/EbikeFileParser.java | 18 ++++ .../com/ecobike/parser/FileBikeParser.java | 52 ++++++++++++ .../ecobike/parser/FoldingBikeFileParser.java | 18 ++++ .../ecobike/parser/SpedelecFileParser.java | 18 ++++ 8 files changed, 126 insertions(+), 87 deletions(-) delete mode 100644 src/main/java/com/ecobike/dao/FileBikeParser.java create mode 100644 src/main/java/com/ecobike/parser/BikeParser.java create mode 100644 src/main/java/com/ecobike/parser/EbikeFileParser.java create mode 100644 src/main/java/com/ecobike/parser/FileBikeParser.java create mode 100644 src/main/java/com/ecobike/parser/FoldingBikeFileParser.java create mode 100644 src/main/java/com/ecobike/parser/SpedelecFileParser.java diff --git a/src/main/java/com/ecobike/command/WriteToFileCommand.java b/src/main/java/com/ecobike/command/WriteToFileCommand.java index 9067b45..62433af 100644 --- a/src/main/java/com/ecobike/command/WriteToFileCommand.java +++ b/src/main/java/com/ecobike/command/WriteToFileCommand.java @@ -19,6 +19,5 @@ public void execute() { communicator.writeMessage("Data has been written successfully."); } } - } } diff --git a/src/main/java/com/ecobike/dao/FileBikeDao.java b/src/main/java/com/ecobike/dao/FileBikeDao.java index aa738ae..c2f1274 100644 --- a/src/main/java/com/ecobike/dao/FileBikeDao.java +++ b/src/main/java/com/ecobike/dao/FileBikeDao.java @@ -5,6 +5,9 @@ import com.ecobike.EcoBikeApplication; import com.ecobike.exception.IllegalDataSourceException; import com.ecobike.model.Bike; +import com.ecobike.model.BikeType; +import com.ecobike.parser.BikeParser; +import com.ecobike.parser.FileBikeParser; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -22,7 +25,6 @@ public class FileBikeDao implements BikeDao { private static final DataHolder dataHolder = DataHolder.getInstance(); private static final Communicator communicator = EcoBikeApplication.communicator; - private static final FileBikeParser parser = new FileBikeParser(); /** * Path to data file. */ @@ -49,8 +51,10 @@ public void loadBikes() throws IllegalDataSourceException { List wrongLinesInfo = new ArrayList<>(); for (int i = 0; i < lines.size(); i++) { try { - bikes.add(parser.parseBike(lines.get(i))); - } catch (IllegalArgumentException e) { + String line = lines.get(i); + BikeParser parser = FileBikeParser.getBikeParser(line); + bikes.add(parser.parseBike(line)); + } catch (RuntimeException e) { wrongLinesInfo.add("Line No. " + (i + 1) + " has wrong format"); } } diff --git a/src/main/java/com/ecobike/dao/FileBikeParser.java b/src/main/java/com/ecobike/dao/FileBikeParser.java deleted file mode 100644 index 55b3345..0000000 --- a/src/main/java/com/ecobike/dao/FileBikeParser.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.ecobike.dao; - -import com.ecobike.model.Bike; -import com.ecobike.model.BikeType; -import com.ecobike.model.EBike; -import com.ecobike.model.FoldingBike; -import com.ecobike.model.SpeedelecBike; -import java.util.stream.Stream; - -public class FileBikeParser { - public FileBikeParser() { - } - - /** - * Method parses single String line to Bike object. - * - * @param line string to be parsed. - * @return parsed Bike object - */ - public Bike parseBike(String line) { - try { - if (line.startsWith(BikeType.SPEEDELEC.toString())) { - String[] parameters = line.replace(BikeType.SPEEDELEC + " ", "").split("; "); - checkParametersForEmpties(parameters); - return new SpeedelecBike(parameters[0], Integer.parseInt(parameters[1]), - Integer.parseInt(parameters[2]), parseBoolean(parameters[3]), - Integer.parseInt(parameters[4]), parameters[5], - Integer.parseInt(parameters[6])); - } - if (line.startsWith(BikeType.E_BIKE.toString())) { - String[] parameters = line.replace(BikeType.E_BIKE + " ", "").split("; "); - checkParametersForEmpties(parameters); - return new EBike(parameters[0], Integer.parseInt(parameters[1]), - Integer.parseInt(parameters[2]), parseBoolean(parameters[3]), - Integer.parseInt(parameters[4]), parameters[5], - Integer.parseInt(parameters[6])); - } - if (line.startsWith(BikeType.FOLDING_BIKE.toString())) { - String[] parameters = line.replace(BikeType.FOLDING_BIKE + " ", "").split("; "); - checkParametersForEmpties(parameters); - return new FoldingBike(parameters[0], Integer.parseInt(parameters[1]), - Integer.parseInt(parameters[2]), Integer.parseInt(parameters[3]), - parseBoolean(parameters[4]), parameters[5], - Integer.parseInt(parameters[6])); - } - } catch (RuntimeException e) { - throw new IllegalArgumentException(); - } - throw new IllegalArgumentException(); - } - - /** - * Method parses string to boolean value. - * - * @param value string to be parsed - * @return TRUE if value equals ignore case "true" - * or FALSE if value equals ignore case "false". - * Otherwise throws IllegalArgumentException. - */ - public boolean parseBoolean(String value) { - if ("true".equalsIgnoreCase(value)) { - return true; - } - if ("false".equalsIgnoreCase(value)) { - return false; - } - throw new IllegalArgumentException(); - } - - /** - * Method checks string array for presence at least one - * empty string. If present - throws IllegalArgumentException. - * - * @param parameters string array. - */ - public void checkParametersForEmpties(String[] parameters) { - boolean isEmptyPresent = Stream.of(parameters) - .anyMatch(String::isEmpty); - if (isEmptyPresent) { - throw new IllegalArgumentException(); - } - } -} diff --git a/src/main/java/com/ecobike/parser/BikeParser.java b/src/main/java/com/ecobike/parser/BikeParser.java new file mode 100644 index 0000000..e59032f --- /dev/null +++ b/src/main/java/com/ecobike/parser/BikeParser.java @@ -0,0 +1,13 @@ +package com.ecobike.parser; + +import com.ecobike.model.Bike; + +public interface BikeParser { + /** + * Method parses single String line to Bike object. + * + * @param line string to be parsed. + * @return parsed Bike object + */ + Bike parseBike(String line); +} diff --git a/src/main/java/com/ecobike/parser/EbikeFileParser.java b/src/main/java/com/ecobike/parser/EbikeFileParser.java new file mode 100644 index 0000000..f1456fb --- /dev/null +++ b/src/main/java/com/ecobike/parser/EbikeFileParser.java @@ -0,0 +1,18 @@ +package com.ecobike.parser; + +import com.ecobike.model.Bike; +import com.ecobike.model.BikeType; +import com.ecobike.model.EBike; + +public class EbikeFileParser extends FileBikeParser { + + @Override + public Bike parseBike(String line) { + String[] parameters = line.replace(BikeType.E_BIKE + " ", "").split("; "); + checkParametersForEmpties(parameters); + return new EBike(parameters[0], Integer.parseInt(parameters[1]), + Integer.parseInt(parameters[2]), parseBoolean(parameters[3]), + Integer.parseInt(parameters[4]), parameters[5], + Integer.parseInt(parameters[6])); + } +} diff --git a/src/main/java/com/ecobike/parser/FileBikeParser.java b/src/main/java/com/ecobike/parser/FileBikeParser.java new file mode 100644 index 0000000..dab5842 --- /dev/null +++ b/src/main/java/com/ecobike/parser/FileBikeParser.java @@ -0,0 +1,52 @@ +package com.ecobike.parser; + +import com.ecobike.model.BikeType; +import java.util.stream.Stream; + +public abstract class FileBikeParser implements BikeParser { + + public static BikeParser getBikeParser(String line) { + if (line.startsWith(BikeType.E_BIKE.toString())) { + return new EbikeFileParser(); + } + if (line.startsWith(BikeType.FOLDING_BIKE.toString())) { + return new FoldingBikeFileParser(); + } + if (line.startsWith(BikeType.SPEEDELEC.toString())) { + return new SpedelecFileParser(); + } + throw new IllegalArgumentException("Don't such parser exist"); + } + + /** + * Method parses string to boolean value. + * + * @param value string to be parsed + * @return TRUE if value equals ignore case "true" + * or FALSE if value equals ignore case "false". + * Otherwise throws IllegalArgumentException. + */ + protected boolean parseBoolean(String value) { + if ("true".equalsIgnoreCase(value)) { + return true; + } + if ("false".equalsIgnoreCase(value)) { + return false; + } + throw new IllegalArgumentException(); + } + + /** + * Method checks string array for presence at least one + * empty string. If present - throws IllegalArgumentException. + * + * @param parameters string array. + */ + protected void checkParametersForEmpties(String[] parameters) { + boolean isEmptyPresent = Stream.of(parameters) + .anyMatch(String::isEmpty); + if (isEmptyPresent) { + throw new IllegalArgumentException(); + } + } +} diff --git a/src/main/java/com/ecobike/parser/FoldingBikeFileParser.java b/src/main/java/com/ecobike/parser/FoldingBikeFileParser.java new file mode 100644 index 0000000..4293fb8 --- /dev/null +++ b/src/main/java/com/ecobike/parser/FoldingBikeFileParser.java @@ -0,0 +1,18 @@ +package com.ecobike.parser; + +import com.ecobike.model.Bike; +import com.ecobike.model.BikeType; +import com.ecobike.model.FoldingBike; + +public class FoldingBikeFileParser extends FileBikeParser { + + @Override + public Bike parseBike(String line) { + String[] parameters = line.replace(BikeType.FOLDING_BIKE + " ", "").split("; "); + checkParametersForEmpties(parameters); + return new FoldingBike(parameters[0], Integer.parseInt(parameters[1]), + Integer.parseInt(parameters[2]), Integer.parseInt(parameters[3]), + parseBoolean(parameters[4]), parameters[5], + Integer.parseInt(parameters[6])); + } +} diff --git a/src/main/java/com/ecobike/parser/SpedelecFileParser.java b/src/main/java/com/ecobike/parser/SpedelecFileParser.java new file mode 100644 index 0000000..6422c82 --- /dev/null +++ b/src/main/java/com/ecobike/parser/SpedelecFileParser.java @@ -0,0 +1,18 @@ +package com.ecobike.parser; + +import com.ecobike.model.Bike; +import com.ecobike.model.BikeType; +import com.ecobike.model.SpeedelecBike; + +public class SpedelecFileParser extends FileBikeParser { + + @Override + public Bike parseBike(String line) { + String[] parameters = line.replace(BikeType.SPEEDELEC + " ", "").split("; "); + checkParametersForEmpties(parameters); + return new SpeedelecBike(parameters[0], Integer.parseInt(parameters[1]), + Integer.parseInt(parameters[2]), parseBoolean(parameters[3]), + Integer.parseInt(parameters[4]), parameters[5], + Integer.parseInt(parameters[6])); + } +} From 723c08a197d032b2181686830b31f7bcd392f6f2 Mon Sep 17 00:00:00 2001 From: Leonid Date: Sat, 8 Aug 2020 17:30:34 +0300 Subject: [PATCH 15/19] bike dao refactoring --- src/main/java/com/ecobike/DataHolder.java | 24 ++++++----- .../java/com/ecobike/EcoBikeApplication.java | 3 +- .../java/com/ecobike/command/Command.java | 2 +- .../ecobike/command/WriteToFileCommand.java | 2 +- src/main/java/com/ecobike/dao/BikeDao.java | 9 +++-- .../java/com/ecobike/dao/FileBikeDao.java | 18 ++++----- src/test/java/com/ecobike/DataHolderTest.java | 15 +------ ...arserDaoTest.java => FileBikeDaoTest.java} | 40 ++++++++++--------- 8 files changed, 54 insertions(+), 59 deletions(-) rename src/test/java/com/ecobike/{FileBikeParserDaoTest.java => FileBikeDaoTest.java} (57%) diff --git a/src/main/java/com/ecobike/DataHolder.java b/src/main/java/com/ecobike/DataHolder.java index 821ceb1..c6cac8c 100644 --- a/src/main/java/com/ecobike/DataHolder.java +++ b/src/main/java/com/ecobike/DataHolder.java @@ -1,11 +1,12 @@ package com.ecobike; +import com.ecobike.dao.BikeDao; +import com.ecobike.exception.IllegalDataSourceException; import com.ecobike.model.AbstractElectroBike; import com.ecobike.model.Bike; import com.ecobike.model.BikeType; import com.ecobike.model.FoldingBike; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -23,6 +24,7 @@ public class DataHolder { */ private final List bikes = new ArrayList<>(); + private BikeDao bikeDao; /** * We need this Latch to be sure that our new Thread in addBikes() method, * that sorts Bike storage, will take monitor before any other thread. @@ -38,18 +40,17 @@ public class DataHolder { private DataHolder() { } - public static DataHolder getInstance() { + public static DataHolder getInstance(BikeDao bikeDao) { + instance.bikeDao = bikeDao; return instance; } /** - * Method adds Bikes from collection to Bikes container. - * - * @param bikesToAdd collection with Bikes for adding. + * Method adds Bikes from data source to Bikes container. */ - public synchronized void init(Collection bikesToAdd) { + public synchronized void init() throws IllegalDataSourceException { bikes.clear(); - bikes.addAll(bikesToAdd); + bikes.addAll(bikeDao.readBikes()); countDownLatch = new CountDownLatch(1); new Thread(() -> { sort(); @@ -107,7 +108,8 @@ public List findBikesByParameter(SearchParameterContainer parameters) { } synchronized (this) { Stream bikeStream = bikes.parallelStream() - .filter(bike -> bike.getBikeType() == parameters.getBikeType() + .filter(bike -> + bike.getBikeType() == parameters.getBikeType() && bike.getBrand().equalsIgnoreCase(parameters.getBrand()) && bike.getWeight() >= parameters.getMinWeight() && (parameters.getMaxWeight() == 0 @@ -122,7 +124,8 @@ public List findBikesByParameter(SearchParameterContainer parameters) { if (parameters.getBikeType() == BikeType.FOLDING_BIKE) { return bikeStream.map(bike -> (FoldingBike) bike) - .filter(bike -> bike.getWheelSize() >= parameters.getMinWheelSize() + .filter(bike -> + bike.getWheelSize() >= parameters.getMinWheelSize() && (parameters.getMaxWheelSize() == 0 || bike.getWheelSize() <= parameters.getMaxWheelSize()) && bike.getNumberOfGears() >= parameters.getMinNumberOfGears() @@ -131,7 +134,8 @@ public List findBikesByParameter(SearchParameterContainer parameters) { .collect(Collectors.toList()); } return bikeStream.map(bike -> (AbstractElectroBike) bike) - .filter(bike -> bike.getMaxSpeed() >= parameters.getMinMaxBikeSpeed() + .filter(bike -> + bike.getMaxSpeed() >= parameters.getMinMaxBikeSpeed() && (parameters.getMaxMaxBikeSpeed() == 0 || bike.getMaxSpeed() <= parameters.getMaxMaxBikeSpeed()) && bike.getBatteryCapacity() >= parameters.getMinBatteryCapacity() diff --git a/src/main/java/com/ecobike/EcoBikeApplication.java b/src/main/java/com/ecobike/EcoBikeApplication.java index f94b41a..056f6ee 100644 --- a/src/main/java/com/ecobike/EcoBikeApplication.java +++ b/src/main/java/com/ecobike/EcoBikeApplication.java @@ -51,7 +51,8 @@ private static void initFileBikeDao() { } while (!Files.isRegularFile(bikeDataFile)); bikeDao = new FileBikeDao(bikeDataFile); try { - bikeDao.loadBikes(); + DataHolder dataHolder = DataHolder.getInstance(bikeDao); + dataHolder.init(); isFileParsed = true; } catch (IllegalDataSourceException e) { communicator.writeMessage("File has wrong format or empty"); diff --git a/src/main/java/com/ecobike/command/Command.java b/src/main/java/com/ecobike/command/Command.java index b1b6ae7..fb9894b 100644 --- a/src/main/java/com/ecobike/command/Command.java +++ b/src/main/java/com/ecobike/command/Command.java @@ -10,7 +10,7 @@ */ public interface Command { Communicator communicator = EcoBikeApplication.communicator; - DataHolder dataHolder = DataHolder.getInstance(); + DataHolder dataHolder = DataHolder.getInstance(EcoBikeApplication.bikeDao); void execute(); } diff --git a/src/main/java/com/ecobike/command/WriteToFileCommand.java b/src/main/java/com/ecobike/command/WriteToFileCommand.java index 62433af..bd02f74 100644 --- a/src/main/java/com/ecobike/command/WriteToFileCommand.java +++ b/src/main/java/com/ecobike/command/WriteToFileCommand.java @@ -14,7 +14,7 @@ public void execute() { } else { String confirmMessage = "write data to file"; if (communicator.confirmAction(confirmMessage)) { - EcoBikeApplication.bikeDao.saveBikes(); + EcoBikeApplication.bikeDao.saveBikes(dataHolder.getUnmodifiableBikeList()); dataHolder.setDataChanged(true); communicator.writeMessage("Data has been written successfully."); } diff --git a/src/main/java/com/ecobike/dao/BikeDao.java b/src/main/java/com/ecobike/dao/BikeDao.java index 054d3ea..b81d1df 100644 --- a/src/main/java/com/ecobike/dao/BikeDao.java +++ b/src/main/java/com/ecobike/dao/BikeDao.java @@ -1,6 +1,8 @@ package com.ecobike.dao; import com.ecobike.exception.IllegalDataSourceException; +import com.ecobike.model.Bike; +import java.util.List; /** * Interface should be implemented by class @@ -9,16 +11,15 @@ public interface BikeDao { /** - * Method reads data from data source and - * loads parsed Bike objects to DataHolder. + * Method reads data from data source. * * @throws IllegalDataSourceException if no one Bike object parsed from file. */ - void loadBikes() throws IllegalDataSourceException; + List readBikes() throws IllegalDataSourceException; /** * Method writes to data source Bike objects. * Old data must be replaced with new one. */ - void saveBikes(); + void saveBikes(List bikes); } diff --git a/src/main/java/com/ecobike/dao/FileBikeDao.java b/src/main/java/com/ecobike/dao/FileBikeDao.java index c2f1274..0f9f146 100644 --- a/src/main/java/com/ecobike/dao/FileBikeDao.java +++ b/src/main/java/com/ecobike/dao/FileBikeDao.java @@ -1,18 +1,15 @@ package com.ecobike.dao; import com.ecobike.Communicator; -import com.ecobike.DataHolder; import com.ecobike.EcoBikeApplication; import com.ecobike.exception.IllegalDataSourceException; import com.ecobike.model.Bike; -import com.ecobike.model.BikeType; import com.ecobike.parser.BikeParser; import com.ecobike.parser.FileBikeParser; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -23,25 +20,24 @@ */ public class FileBikeDao implements BikeDao { - private static final DataHolder dataHolder = DataHolder.getInstance(); private static final Communicator communicator = EcoBikeApplication.communicator; /** * Path to data file. */ - private Path file; + private final Path file; public FileBikeDao(Path file) { this.file = file; } /** - * Method reads text from file and loads parsed Bike objects to DataHolder. + * Method reads data from data source. * * @throws IllegalDataSourceException if no one Bike object parsed from file. */ @Override - public void loadBikes() throws IllegalDataSourceException { - List lines = Collections.emptyList(); + public List readBikes() throws IllegalDataSourceException { + List lines; try { lines = Files.readAllLines(file); } catch (IOException e) { @@ -62,8 +58,8 @@ public void loadBikes() throws IllegalDataSourceException { throw new IllegalDataSourceException(); } wrongLinesInfo.forEach(communicator::writeMessage); - dataHolder.init(bikes); communicator.writeMessage(bikes.size() + " bike items has been read from the file"); + return bikes; } /** @@ -71,8 +67,8 @@ public void loadBikes() throws IllegalDataSourceException { * Old data in file will be replaced with new one. */ @Override - public void saveBikes() { - List dataToWrite = dataHolder.getUnmodifiableBikeList().stream() + public void saveBikes(List bikes) { + List dataToWrite = bikes.stream() .map(Bike::toFileWriterString) .collect(Collectors.toList()); try { diff --git a/src/test/java/com/ecobike/DataHolderTest.java b/src/test/java/com/ecobike/DataHolderTest.java index f47984a..d5f44a1 100644 --- a/src/test/java/com/ecobike/DataHolderTest.java +++ b/src/test/java/com/ecobike/DataHolderTest.java @@ -21,7 +21,7 @@ public class DataHolderTest { private static final BikeDao BIKE_DAO = new FileBikeDao(Path.of("src/main/resources/test/fileWithFiveTrueBikes.txt")); - private static final DataHolder DATA_HOLDER = DataHolder.getInstance(); + private static final DataHolder DATA_HOLDER = DataHolder.getInstance(BIKE_DAO); private static final int START_EXPECTED_LIST_SIZE = 5; private static final int ADDBIKES_EXPECTED_LIST_SIZE = 2; private static final int ADDBIKE_EXPECTED_LIST_SIZE = 7; @@ -47,7 +47,7 @@ public class DataHolderTest { @Before public void before() throws IllegalDataSourceException { - BIKE_DAO.loadBikes(); + DATA_HOLDER.init(); } @Test @@ -56,17 +56,6 @@ public void getUnmodifiableBikeList() { assertEquals(START_EXPECTED_LIST_SIZE, loadedBikes.size()); } - @Test - public void addBikes() { - List bikesToAdd = Arrays.asList(FIRST_BIKE_TO_ADD, LAST_BIKE_TO_ADD); - DATA_HOLDER.init(bikesToAdd); - List loadedBikes = DATA_HOLDER.getUnmodifiableBikeList(); - - assertEquals(ADDBIKES_EXPECTED_LIST_SIZE, loadedBikes.size()); - assertEquals(FIRST_BIKE_TO_ADD, loadedBikes.get(0)); - assertEquals(LAST_BIKE_TO_ADD, loadedBikes.get(1)); - } - @Test public void addBike() { DATA_HOLDER.addBike(FIRST_BIKE_TO_ADD); diff --git a/src/test/java/com/ecobike/FileBikeParserDaoTest.java b/src/test/java/com/ecobike/FileBikeDaoTest.java similarity index 57% rename from src/test/java/com/ecobike/FileBikeParserDaoTest.java rename to src/test/java/com/ecobike/FileBikeDaoTest.java index e8dc7cd..ff60f72 100644 --- a/src/test/java/com/ecobike/FileBikeParserDaoTest.java +++ b/src/test/java/com/ecobike/FileBikeDaoTest.java @@ -4,6 +4,7 @@ import com.ecobike.dao.FileBikeDao; import com.ecobike.exception.IllegalDataSourceException; import com.ecobike.model.Bike; +import com.ecobike.model.FoldingBike; import com.ecobike.model.SpeedelecBike; import org.junit.Assert; import org.junit.BeforeClass; @@ -14,12 +15,14 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; -public class FileBikeParserDaoTest { +public class FileBikeDaoTest { private static final int EXPECTED_LIST_SIZE = 5; - private static final Bike LAST_BIKE = new SpeedelecBike("EcoRide", 50, - 8400, false, 8600, "brown", 1609); + private static final Bike FIRST_BIKE = new SpeedelecBike("EcoRide", 50, 8400, + false, 8600, "brown", 1609); + private static final List listToSave = new ArrayList<>(); private static final Path NEW_FILE = Path.of("src/main/resources/test/newFile.txt"); private BikeDao bikeDao; @@ -28,37 +31,38 @@ public class FileBikeParserDaoTest { @BeforeClass public static void beforeClass() { - + listToSave.add(FIRST_BIKE); + listToSave.add(new FoldingBike("EcoRide", 50, 8400, + 8600, false, "brown", 1609)); } @Test - public void writeBikesTest() throws IOException, IllegalDataSourceException, InterruptedException { + public void saveBikesTest() throws IOException, IllegalDataSourceException, InterruptedException { Files.deleteIfExists(NEW_FILE); Files.createFile(NEW_FILE); - Files.writeString(NEW_FILE, "FOLDING BIKE BMW; 20; 7; 14400; false; lemon; 1085"); bikeDao = new FileBikeDao(Path.of("src/main/resources/test/newFile.txt")); - bikeDao.loadBikes(); - DataHolder dataHolder = DataHolder.getInstance(); - dataHolder.addBike(LAST_BIKE); - bikeDao.saveBikes(); + bikeDao.saveBikes(listToSave); + List fileLines = Files.readAllLines(NEW_FILE); Assert.assertEquals(2, fileLines.size()); - Assert.assertEquals("FOLDING BIKE BMW; 20; 7; 14400; false; lemon; 1085", fileLines.get(0)); - Assert.assertEquals("SPEEDELEC EcoRide; 50; 8400; false; 8600; brown; 1609", fileLines.get(1)); + Assert.assertEquals("SPEEDELEC EcoRide; 50; 8400; false; 8600; brown; 1609", fileLines.get(0)); + Assert.assertEquals("FOLDING BIKE EcoRide; 50; 8400; 8600; false; brown; 1609", fileLines.get(1)); } @Test - public void loadBikesTest() throws IllegalDataSourceException { + public void readBikesTest() throws IllegalDataSourceException { bikeDao = new FileBikeDao(Path.of("src/main/resources/test/fileWithFiveTrueBikes.txt")); - bikeDao.loadBikes(); - DataHolder dataHolder = DataHolder.getInstance(); - List loadedBikes = dataHolder.getUnmodifiableBikeList(); + List loadedBikes = bikeDao.readBikes(); Assert.assertEquals(EXPECTED_LIST_SIZE, loadedBikes.size()); - Assert.assertEquals(loadedBikes.size() - 1, loadedBikes.indexOf(LAST_BIKE)); + Assert.assertEquals(0, loadedBikes.indexOf(FIRST_BIKE)); expectedEx.expect(IllegalDataSourceException.class); bikeDao = new FileBikeDao(Path.of("src/main/resources/test/wrongBikeFormat.txt")); - bikeDao.loadBikes(); + bikeDao.readBikes(); + + expectedEx.expect(IllegalDataSourceException.class); + bikeDao = new FileBikeDao(Path.of("wrongFile")); + bikeDao.readBikes(); } } \ No newline at end of file From a8942b1cee7510282c3120bdeb9ee58f89a3977c Mon Sep 17 00:00:00 2001 From: Leonid Date: Sat, 8 Aug 2020 19:01:01 +0300 Subject: [PATCH 16/19] tests added --- src/main/java/com/ecobike/dao/BikeDao.java | 2 +- .../java/com/ecobike/dao/FileBikeDao.java | 2 +- .../ecobike/parser/EbikeFileParserTest.java | 21 ++++++++++++ .../ecobike/parser/FileBikeParserTest.java | 32 +++++++++++++++++++ .../parser/FoldingBikeFileParserTest.java | 22 +++++++++++++ .../parser/SpedelecFileParserTest.java | 21 ++++++++++++ 6 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/ecobike/parser/EbikeFileParserTest.java create mode 100644 src/test/java/com/ecobike/parser/FileBikeParserTest.java create mode 100644 src/test/java/com/ecobike/parser/FoldingBikeFileParserTest.java create mode 100644 src/test/java/com/ecobike/parser/SpedelecFileParserTest.java diff --git a/src/main/java/com/ecobike/dao/BikeDao.java b/src/main/java/com/ecobike/dao/BikeDao.java index b81d1df..0bb7d0f 100644 --- a/src/main/java/com/ecobike/dao/BikeDao.java +++ b/src/main/java/com/ecobike/dao/BikeDao.java @@ -21,5 +21,5 @@ public interface BikeDao { * Method writes to data source Bike objects. * Old data must be replaced with new one. */ - void saveBikes(List bikes); + void saveBikes(List bikes); } diff --git a/src/main/java/com/ecobike/dao/FileBikeDao.java b/src/main/java/com/ecobike/dao/FileBikeDao.java index 0f9f146..02eb3b4 100644 --- a/src/main/java/com/ecobike/dao/FileBikeDao.java +++ b/src/main/java/com/ecobike/dao/FileBikeDao.java @@ -67,7 +67,7 @@ public List readBikes() throws IllegalDataSourceException { * Old data in file will be replaced with new one. */ @Override - public void saveBikes(List bikes) { + public void saveBikes(List bikes) { List dataToWrite = bikes.stream() .map(Bike::toFileWriterString) .collect(Collectors.toList()); diff --git a/src/test/java/com/ecobike/parser/EbikeFileParserTest.java b/src/test/java/com/ecobike/parser/EbikeFileParserTest.java new file mode 100644 index 0000000..7a3518e --- /dev/null +++ b/src/test/java/com/ecobike/parser/EbikeFileParserTest.java @@ -0,0 +1,21 @@ +package com.ecobike.parser; + +import com.ecobike.model.Bike; +import com.ecobike.model.EBike; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class EbikeFileParserTest { + + private static final String BIKE_STR = "E-BIKE Porshe Design; 45; 28800; true; 13000; dark gray; 2789"; + private static final EBike BIKE = new EBike("Porshe Design", 45, 28800, + true, 13000, "dark gray", 2789); + + @Test + public void parseBike() { + EbikeFileParser parser = new EbikeFileParser(); + Bike parseBike = parser.parseBike(BIKE_STR); + assertEquals(BIKE, parseBike); + } +} \ No newline at end of file diff --git a/src/test/java/com/ecobike/parser/FileBikeParserTest.java b/src/test/java/com/ecobike/parser/FileBikeParserTest.java new file mode 100644 index 0000000..400cfa2 --- /dev/null +++ b/src/test/java/com/ecobike/parser/FileBikeParserTest.java @@ -0,0 +1,32 @@ +package com.ecobike.parser; + +import com.ecobike.exception.IllegalDataSourceException; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.junit.Assert.*; + +public class FileBikeParserTest { + + private static final String E_BIKE = "E-BIKE Porshe Design; 45; 28800; true; 13000; dark gray; 2789"; + private static final String FOLDING_BIKE = "FOLDING BIKE Dahon; 14; 21; 12200; true; golden; 1169"; + private static final String SPEEDELEC = "SPEEDELEC Peugeot; 60; 19800; true; 12200; yellow; 885"; + private static final String WRONG = "WRONG"; + + @Rule + public ExpectedException expectedEx = ExpectedException.none(); + + @Test + public void getBikeParser() { + BikeParser bikeParser = FileBikeParser.getBikeParser(E_BIKE); + assertEquals(EbikeFileParser.class, bikeParser.getClass()); + bikeParser = FileBikeParser.getBikeParser(FOLDING_BIKE); + assertEquals(FoldingBikeFileParser.class, bikeParser.getClass()); + bikeParser = FileBikeParser.getBikeParser(SPEEDELEC); + assertEquals(SpedelecFileParser.class, bikeParser.getClass()); + expectedEx.expect(IllegalArgumentException.class); + bikeParser = FileBikeParser.getBikeParser(WRONG); + } +} \ No newline at end of file diff --git a/src/test/java/com/ecobike/parser/FoldingBikeFileParserTest.java b/src/test/java/com/ecobike/parser/FoldingBikeFileParserTest.java new file mode 100644 index 0000000..821ffbb --- /dev/null +++ b/src/test/java/com/ecobike/parser/FoldingBikeFileParserTest.java @@ -0,0 +1,22 @@ +package com.ecobike.parser; + +import com.ecobike.model.Bike; +import com.ecobike.model.EBike; +import com.ecobike.model.FoldingBike; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class FoldingBikeFileParserTest { + + private static final String BIKE_STR = "FOLDING BIKE Author; 20; 9; 11900; true; brown; 209"; + private static final FoldingBike BIKE = new FoldingBike("Author", 20, 9, + 11900, true, "brown", 209); + + @Test + public void parseBike() { + FoldingBikeFileParser parser = new FoldingBikeFileParser(); + Bike parsedBike = parser.parseBike(BIKE_STR); + assertEquals(BIKE, parsedBike); + } +} \ No newline at end of file diff --git a/src/test/java/com/ecobike/parser/SpedelecFileParserTest.java b/src/test/java/com/ecobike/parser/SpedelecFileParserTest.java new file mode 100644 index 0000000..366b64a --- /dev/null +++ b/src/test/java/com/ecobike/parser/SpedelecFileParserTest.java @@ -0,0 +1,21 @@ +package com.ecobike.parser; + +import com.ecobike.model.Bike; +import com.ecobike.model.SpeedelecBike; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class SpedelecFileParserTest { + + private static final String BIKE_STR = "SPEEDELEC Segway; 10; 11700; false; 12000; pink; 1365"; + private static final SpeedelecBike BIKE = new SpeedelecBike("Segway", 10, 11700, + false, 12000, "pink", 1365); + + @Test + public void parseBike() { + SpedelecFileParser parser = new SpedelecFileParser(); + Bike parseBike = parser.parseBike(BIKE_STR); + assertEquals(BIKE, parseBike); + } +} \ No newline at end of file From 99d954248e55660efc7f986b2e4afdc1dbc2f1d0 Mon Sep 17 00:00:00 2001 From: Leonid Date: Sun, 9 Aug 2020 16:21:11 +0300 Subject: [PATCH 17/19] find command refactored --- src/main/java/com/ecobike/command/FindCommand.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/ecobike/command/FindCommand.java b/src/main/java/com/ecobike/command/FindCommand.java index 9a5b9e1..a920bf1 100644 --- a/src/main/java/com/ecobike/command/FindCommand.java +++ b/src/main/java/com/ecobike/command/FindCommand.java @@ -15,11 +15,12 @@ public void execute() { SearchParameterContainer paramContainer = new SearchParameterContainer(); while (true) { communicator.writeMessage("Select bike type you wont to find"); - communicator.writeMessage("\t 1 - " + BikeType.FOLDING_BIKE); - communicator.writeMessage("\t 2 - " + BikeType.E_BIKE); - communicator.writeMessage("\t 3 - " + BikeType.SPEEDELEC); + int numberOfBikeTypes = BikeType.values().length; + for (int i = 1; i <= numberOfBikeTypes ; i++) { + communicator.writeMessage("\t " + i + " - " + BikeType.values()[i - 1]); + } int bikeTypeNumber; - if ((bikeTypeNumber = communicator.readInt()) >= 1 && bikeTypeNumber <= 3) { + if ((bikeTypeNumber = communicator.readInt()) >= 1 && bikeTypeNumber <= numberOfBikeTypes) { paramContainer.setBikeType(BikeType.values()[bikeTypeNumber - 1]); break; } From 34b9951b0094c4963efdd116604f0b5bba484a4b Mon Sep 17 00:00:00 2001 From: Leonid Date: Sun, 9 Aug 2020 16:40:40 +0300 Subject: [PATCH 18/19] modifiers fixed --- src/main/java/com/ecobike/EcoBikeApplication.java | 12 ++++++++++-- src/main/java/com/ecobike/command/Command.java | 4 ++-- src/main/java/com/ecobike/command/FindCommand.java | 5 +++-- .../java/com/ecobike/command/WriteToFileCommand.java | 2 +- src/main/java/com/ecobike/dao/FileBikeDao.java | 2 +- 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/ecobike/EcoBikeApplication.java b/src/main/java/com/ecobike/EcoBikeApplication.java index 056f6ee..51123a2 100644 --- a/src/main/java/com/ecobike/EcoBikeApplication.java +++ b/src/main/java/com/ecobike/EcoBikeApplication.java @@ -13,11 +13,11 @@ public class EcoBikeApplication { /** * Communicator for communication with user. */ - public static final Communicator communicator = new ConsoleCommunicator(); + private static final Communicator communicator = new ConsoleCommunicator(); /** * Object for loading data from and writing data to file. */ - public static BikeDao bikeDao; + private static BikeDao bikeDao; /** * Main method of the application. @@ -41,6 +41,14 @@ public static void main(String[] args) { } } + public static Communicator getCommunicator() { + return communicator; + } + + public static BikeDao getBikeDao() { + return bikeDao; + } + private static void initFileBikeDao() { boolean isFileParsed = false; while (!isFileParsed) { diff --git a/src/main/java/com/ecobike/command/Command.java b/src/main/java/com/ecobike/command/Command.java index fb9894b..c120839 100644 --- a/src/main/java/com/ecobike/command/Command.java +++ b/src/main/java/com/ecobike/command/Command.java @@ -9,8 +9,8 @@ * operation specified by user. */ public interface Command { - Communicator communicator = EcoBikeApplication.communicator; - DataHolder dataHolder = DataHolder.getInstance(EcoBikeApplication.bikeDao); + Communicator communicator = EcoBikeApplication.getCommunicator(); + DataHolder dataHolder = DataHolder.getInstance(EcoBikeApplication.getBikeDao()); void execute(); } diff --git a/src/main/java/com/ecobike/command/FindCommand.java b/src/main/java/com/ecobike/command/FindCommand.java index a920bf1..cc2719d 100644 --- a/src/main/java/com/ecobike/command/FindCommand.java +++ b/src/main/java/com/ecobike/command/FindCommand.java @@ -16,11 +16,12 @@ public void execute() { while (true) { communicator.writeMessage("Select bike type you wont to find"); int numberOfBikeTypes = BikeType.values().length; - for (int i = 1; i <= numberOfBikeTypes ; i++) { + for (int i = 1; i <= numberOfBikeTypes; i++) { communicator.writeMessage("\t " + i + " - " + BikeType.values()[i - 1]); } int bikeTypeNumber; - if ((bikeTypeNumber = communicator.readInt()) >= 1 && bikeTypeNumber <= numberOfBikeTypes) { + if ((bikeTypeNumber = communicator.readInt()) >= 1 + && bikeTypeNumber <= numberOfBikeTypes) { paramContainer.setBikeType(BikeType.values()[bikeTypeNumber - 1]); break; } diff --git a/src/main/java/com/ecobike/command/WriteToFileCommand.java b/src/main/java/com/ecobike/command/WriteToFileCommand.java index bd02f74..68d3025 100644 --- a/src/main/java/com/ecobike/command/WriteToFileCommand.java +++ b/src/main/java/com/ecobike/command/WriteToFileCommand.java @@ -14,7 +14,7 @@ public void execute() { } else { String confirmMessage = "write data to file"; if (communicator.confirmAction(confirmMessage)) { - EcoBikeApplication.bikeDao.saveBikes(dataHolder.getUnmodifiableBikeList()); + EcoBikeApplication.getBikeDao().saveBikes(dataHolder.getUnmodifiableBikeList()); dataHolder.setDataChanged(true); communicator.writeMessage("Data has been written successfully."); } diff --git a/src/main/java/com/ecobike/dao/FileBikeDao.java b/src/main/java/com/ecobike/dao/FileBikeDao.java index 02eb3b4..43db231 100644 --- a/src/main/java/com/ecobike/dao/FileBikeDao.java +++ b/src/main/java/com/ecobike/dao/FileBikeDao.java @@ -20,7 +20,7 @@ */ public class FileBikeDao implements BikeDao { - private static final Communicator communicator = EcoBikeApplication.communicator; + private static final Communicator communicator = EcoBikeApplication.getCommunicator(); /** * Path to data file. */ From 79b45d6bbdbeb7d80351b89f4f383066a39e566c Mon Sep 17 00:00:00 2001 From: Leonid Date: Wed, 26 Aug 2020 12:09:57 +0300 Subject: [PATCH 19/19] parser generified --- src/main/java/com/ecobike/Communicator.java | 2 +- src/main/java/com/ecobike/ConsoleCommunicator.java | 2 +- src/main/java/com/ecobike/dao/FileBikeDao.java | 2 +- src/main/java/com/ecobike/model/AbstractElectroBike.java | 1 - src/main/java/com/ecobike/parser/BikeParser.java | 4 ++-- src/main/java/com/ecobike/parser/EbikeFileParser.java | 5 ++--- src/main/java/com/ecobike/parser/FileBikeParser.java | 3 ++- src/main/java/com/ecobike/parser/FoldingBikeFileParser.java | 5 ++--- src/main/java/com/ecobike/parser/SpedelecFileParser.java | 5 ++--- src/test/java/com/ecobike/DataHolderTest.java | 2 +- src/test/java/com/ecobike/FileBikeDaoTest.java | 2 +- src/test/java/com/ecobike/parser/EbikeFileParserTest.java | 2 +- src/test/java/com/ecobike/parser/FileBikeParserTest.java | 2 +- .../java/com/ecobike/parser/FoldingBikeFileParserTest.java | 2 +- src/test/java/com/ecobike/parser/SpedelecFileParserTest.java | 2 +- 15 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/ecobike/Communicator.java b/src/main/java/com/ecobike/Communicator.java index e9a49fb..f83e73b 100644 --- a/src/main/java/com/ecobike/Communicator.java +++ b/src/main/java/com/ecobike/Communicator.java @@ -53,7 +53,7 @@ public interface Communicator { * * @param bikes list of bikes for printing */ - void printBikes(List bikes); + void printBikes(List bikes); /** * Ask user to confirm operation. diff --git a/src/main/java/com/ecobike/ConsoleCommunicator.java b/src/main/java/com/ecobike/ConsoleCommunicator.java index 488d246..37d227e 100644 --- a/src/main/java/com/ecobike/ConsoleCommunicator.java +++ b/src/main/java/com/ecobike/ConsoleCommunicator.java @@ -124,7 +124,7 @@ public boolean readBoolean() { * @param bikes list of bikes for printing */ @Override - public void printBikes(List bikes) { + public void printBikes(List bikes) { for (int i = 0; i < bikes.size(); i++) { writeMessage(i + 1 + ". " + bikes.get(i).toOutputString()); if ((i + 1) % BIKES_ON_PAGE_QUANTITY == 0) { diff --git a/src/main/java/com/ecobike/dao/FileBikeDao.java b/src/main/java/com/ecobike/dao/FileBikeDao.java index 43db231..8a52c2f 100644 --- a/src/main/java/com/ecobike/dao/FileBikeDao.java +++ b/src/main/java/com/ecobike/dao/FileBikeDao.java @@ -74,7 +74,7 @@ public void saveBikes(List bikes) { try { Files.write(file, dataToWrite); } catch (IOException e) { - e.printStackTrace(); + throw new RuntimeException(e); } } } diff --git a/src/main/java/com/ecobike/model/AbstractElectroBike.java b/src/main/java/com/ecobike/model/AbstractElectroBike.java index 85d8f3b..cf38298 100644 --- a/src/main/java/com/ecobike/model/AbstractElectroBike.java +++ b/src/main/java/com/ecobike/model/AbstractElectroBike.java @@ -73,5 +73,4 @@ public int getMaxSpeed() { public int getBatteryCapacity() { return batteryCapacity; } - } diff --git a/src/main/java/com/ecobike/parser/BikeParser.java b/src/main/java/com/ecobike/parser/BikeParser.java index e59032f..4608601 100644 --- a/src/main/java/com/ecobike/parser/BikeParser.java +++ b/src/main/java/com/ecobike/parser/BikeParser.java @@ -2,12 +2,12 @@ import com.ecobike.model.Bike; -public interface BikeParser { +public interface BikeParser { /** * Method parses single String line to Bike object. * * @param line string to be parsed. * @return parsed Bike object */ - Bike parseBike(String line); + T parseBike(String line); } diff --git a/src/main/java/com/ecobike/parser/EbikeFileParser.java b/src/main/java/com/ecobike/parser/EbikeFileParser.java index f1456fb..93a2efc 100644 --- a/src/main/java/com/ecobike/parser/EbikeFileParser.java +++ b/src/main/java/com/ecobike/parser/EbikeFileParser.java @@ -1,13 +1,12 @@ package com.ecobike.parser; -import com.ecobike.model.Bike; import com.ecobike.model.BikeType; import com.ecobike.model.EBike; -public class EbikeFileParser extends FileBikeParser { +public class EbikeFileParser extends FileBikeParser { @Override - public Bike parseBike(String line) { + public EBike parseBike(String line) { String[] parameters = line.replace(BikeType.E_BIKE + " ", "").split("; "); checkParametersForEmpties(parameters); return new EBike(parameters[0], Integer.parseInt(parameters[1]), diff --git a/src/main/java/com/ecobike/parser/FileBikeParser.java b/src/main/java/com/ecobike/parser/FileBikeParser.java index dab5842..16ebcef 100644 --- a/src/main/java/com/ecobike/parser/FileBikeParser.java +++ b/src/main/java/com/ecobike/parser/FileBikeParser.java @@ -1,9 +1,10 @@ package com.ecobike.parser; +import com.ecobike.model.Bike; import com.ecobike.model.BikeType; import java.util.stream.Stream; -public abstract class FileBikeParser implements BikeParser { +public abstract class FileBikeParser implements BikeParser { public static BikeParser getBikeParser(String line) { if (line.startsWith(BikeType.E_BIKE.toString())) { diff --git a/src/main/java/com/ecobike/parser/FoldingBikeFileParser.java b/src/main/java/com/ecobike/parser/FoldingBikeFileParser.java index 4293fb8..67a8397 100644 --- a/src/main/java/com/ecobike/parser/FoldingBikeFileParser.java +++ b/src/main/java/com/ecobike/parser/FoldingBikeFileParser.java @@ -1,13 +1,12 @@ package com.ecobike.parser; -import com.ecobike.model.Bike; import com.ecobike.model.BikeType; import com.ecobike.model.FoldingBike; -public class FoldingBikeFileParser extends FileBikeParser { +public class FoldingBikeFileParser extends FileBikeParser { @Override - public Bike parseBike(String line) { + public FoldingBike parseBike(String line) { String[] parameters = line.replace(BikeType.FOLDING_BIKE + " ", "").split("; "); checkParametersForEmpties(parameters); return new FoldingBike(parameters[0], Integer.parseInt(parameters[1]), diff --git a/src/main/java/com/ecobike/parser/SpedelecFileParser.java b/src/main/java/com/ecobike/parser/SpedelecFileParser.java index 6422c82..417d629 100644 --- a/src/main/java/com/ecobike/parser/SpedelecFileParser.java +++ b/src/main/java/com/ecobike/parser/SpedelecFileParser.java @@ -1,13 +1,12 @@ package com.ecobike.parser; -import com.ecobike.model.Bike; import com.ecobike.model.BikeType; import com.ecobike.model.SpeedelecBike; -public class SpedelecFileParser extends FileBikeParser { +public class SpedelecFileParser extends FileBikeParser { @Override - public Bike parseBike(String line) { + public SpeedelecBike parseBike(String line) { String[] parameters = line.replace(BikeType.SPEEDELEC + " ", "").split("; "); checkParametersForEmpties(parameters); return new SpeedelecBike(parameters[0], Integer.parseInt(parameters[1]), diff --git a/src/test/java/com/ecobike/DataHolderTest.java b/src/test/java/com/ecobike/DataHolderTest.java index d5f44a1..94d919a 100644 --- a/src/test/java/com/ecobike/DataHolderTest.java +++ b/src/test/java/com/ecobike/DataHolderTest.java @@ -74,4 +74,4 @@ public void findBikesByParameter() { assertTrue(foundBikes.contains(FIRST_FOUND_BIKE)); assertTrue(foundBikes.contains(SECOND_FOUND_BIKE)); } -} \ No newline at end of file +} diff --git a/src/test/java/com/ecobike/FileBikeDaoTest.java b/src/test/java/com/ecobike/FileBikeDaoTest.java index ff60f72..46b7f3f 100644 --- a/src/test/java/com/ecobike/FileBikeDaoTest.java +++ b/src/test/java/com/ecobike/FileBikeDaoTest.java @@ -65,4 +65,4 @@ public void readBikesTest() throws IllegalDataSourceException { bikeDao = new FileBikeDao(Path.of("wrongFile")); bikeDao.readBikes(); } -} \ No newline at end of file +} diff --git a/src/test/java/com/ecobike/parser/EbikeFileParserTest.java b/src/test/java/com/ecobike/parser/EbikeFileParserTest.java index 7a3518e..eee5bb1 100644 --- a/src/test/java/com/ecobike/parser/EbikeFileParserTest.java +++ b/src/test/java/com/ecobike/parser/EbikeFileParserTest.java @@ -18,4 +18,4 @@ public void parseBike() { Bike parseBike = parser.parseBike(BIKE_STR); assertEquals(BIKE, parseBike); } -} \ No newline at end of file +} diff --git a/src/test/java/com/ecobike/parser/FileBikeParserTest.java b/src/test/java/com/ecobike/parser/FileBikeParserTest.java index 400cfa2..2fc421f 100644 --- a/src/test/java/com/ecobike/parser/FileBikeParserTest.java +++ b/src/test/java/com/ecobike/parser/FileBikeParserTest.java @@ -29,4 +29,4 @@ public void getBikeParser() { expectedEx.expect(IllegalArgumentException.class); bikeParser = FileBikeParser.getBikeParser(WRONG); } -} \ No newline at end of file +} diff --git a/src/test/java/com/ecobike/parser/FoldingBikeFileParserTest.java b/src/test/java/com/ecobike/parser/FoldingBikeFileParserTest.java index 821ffbb..8093cb4 100644 --- a/src/test/java/com/ecobike/parser/FoldingBikeFileParserTest.java +++ b/src/test/java/com/ecobike/parser/FoldingBikeFileParserTest.java @@ -19,4 +19,4 @@ public void parseBike() { Bike parsedBike = parser.parseBike(BIKE_STR); assertEquals(BIKE, parsedBike); } -} \ No newline at end of file +} diff --git a/src/test/java/com/ecobike/parser/SpedelecFileParserTest.java b/src/test/java/com/ecobike/parser/SpedelecFileParserTest.java index 366b64a..e49d31e 100644 --- a/src/test/java/com/ecobike/parser/SpedelecFileParserTest.java +++ b/src/test/java/com/ecobike/parser/SpedelecFileParserTest.java @@ -18,4 +18,4 @@ public void parseBike() { Bike parseBike = parser.parseBike(BIKE_STR); assertEquals(BIKE, parseBike); } -} \ No newline at end of file +}