Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added .github/.keep
Empty file.
51 changes: 51 additions & 0 deletions src/main/java/com/example/Category.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.example;
import java.util.HashMap;
import java.util.Map;

public class Category {
private String name;

// Constructor
private Category(String name) {
this.name = name;
}

// Create 1 static cache map when initialized, then points to the HashMap reference.
private static final Map<String, Category> categories = new HashMap<>();

// Factory
public static Category of(String nameOf){
if (nameOf == null){
throw new IllegalArgumentException("Category name can't be null");
}

if (nameOf.isBlank()){
throw new IllegalArgumentException("Category name can't be blank");
}

// Normalize to capital letter
String normalized = normalize(nameOf);

// Check the cache, create new or return cached instance
if (categories.containsKey(normalized)){
return categories.get(normalized);
}
else {
Category category = new Category(normalized);
categories.put(normalized, category);
return category;
}
}

// Method to normalize to capital letter
private static String normalize(String input) {
input = input.trim().toLowerCase();
if (input.isEmpty()) throw new IllegalArgumentException("Category name must not be blank");
input = input.substring(0,1).toUpperCase() + input.substring(1);
return input;
}

public String getName(){
return this.name;
}
}
55 changes: 55 additions & 0 deletions src/main/java/com/example/ElectronicsProduct.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.example;

import java.math.BigDecimal;
import java.util.UUID;

public class ElectronicsProduct extends Product implements Shippable {
private final int warrantyMonths;
private final BigDecimal weight; // kg

public ElectronicsProduct(UUID uuid, String nameInput, Category categoryInput, BigDecimal priceInput, int warrantyMonths, BigDecimal weightInput) {
super(uuid, nameInput, categoryInput, ensurePositive(priceInput, "Price"));

this.warrantyMonths = ensurePositiveInt(warrantyMonths);
this.weight = ensurePositive(weightInput, "Weight");
}

// Make sure price, weight is positive
private static BigDecimal ensurePositive(BigDecimal value, String valueType) {
// Uses built in constant for 0
if (value.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException(valueType + " cannot be negative.");
}
return value;
}

// Make sure warranty is positive
private static int ensurePositiveInt(int value) {
if (value < 0) {
throw new IllegalArgumentException("Warranty months cannot be negative.");
}
return value;
}

@Override
public String productDetails() {
return "Electronics: " + name() + ", Warranty: " + warrantyMonths + " months";
}

@Override
public BigDecimal calculateShippingCost() {
BigDecimal baseShipping = BigDecimal.valueOf(79.0);
BigDecimal heavyShipping = baseShipping.add(BigDecimal.valueOf(49.0));
BigDecimal maxWeight = BigDecimal.valueOf(5.0);
// See if weight is larger than 0
if (weight.compareTo(maxWeight) > 0) {
return heavyShipping;
}
return baseShipping;
}

@Override
public Double weight() {
return Double.parseDouble(String.valueOf(weight));
}
}
46 changes: 46 additions & 0 deletions src/main/java/com/example/FoodProduct.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.example;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;

public class FoodProduct extends Product implements Perishable, Shippable {
private final LocalDate expirationDate;
private final BigDecimal weight; // kg

public FoodProduct(UUID uuid, String nameInput, Category categoryInput, BigDecimal priceInput, LocalDate expirationDateInput, BigDecimal weightInput) {
super(uuid, nameInput, categoryInput, ensurePositive(priceInput, "Price"));

this.expirationDate = expirationDateInput;
this.weight = ensurePositive(weightInput, "Weight");
}

// Make sure price, weight is positive
private static BigDecimal ensurePositive(BigDecimal value, String valueType) {
// Uses built in constant for 0
if (value.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException(valueType + " cannot be negative.");
}
return value;
}

@Override
public String productDetails() {
return "Food: " + name() + ", Expires: " + expirationDate;
}

@Override
public BigDecimal calculateShippingCost() {
// Multiply weight with 50
return weight.multiply(BigDecimal.valueOf(50));
}

@Override
public Double weight() {
return Double.parseDouble(String.valueOf(weight));
}

@Override
public LocalDate expirationDate() {
return expirationDate;
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/example/Perishable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example;

import java.time.LocalDate;

interface Perishable {
LocalDate expirationDate();

default boolean isExpired() {
return expirationDate().isBefore(LocalDate.now());
}
}
40 changes: 40 additions & 0 deletions src/main/java/com/example/Product.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.example;

import java.math.BigDecimal;
import java.util.UUID;

public abstract class Product {
private final UUID id;
private final String name;
private final Category category;
private BigDecimal price;

protected Product(UUID uuid, String name, Category category, BigDecimal price) {
this.id = uuid;
this.name = name;
this.category = category;
this.price = price;
}

public UUID uuid() {
return id;
}

public String name() {
return name;
}

public Category category() {
return category;
}

public BigDecimal price() {
return price;
}

public void setPrice(BigDecimal price) {
this.price = price;
}

abstract public String productDetails();
}
11 changes: 11 additions & 0 deletions src/main/java/com/example/Shippable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example;
import java.math.BigDecimal;

interface Shippable {
BigDecimal calculateShippingCost();

// Return 0.0 if no weight override
default Double weight(){
return 0.0;
}
}
122 changes: 122 additions & 0 deletions src/main/java/com/example/Warehouse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package com.example;
import java.math.BigDecimal;
import java.util.*;

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;

public class Warehouse {
// List to save what products are in the warehouse
private final Set<Product> products;
// Set to make sure no duplicates
private final Set<UUID> changedProducts;
private final String name;

// Cache to save warehouses(singletons) by key: name
private static final Map<String, Warehouse> instances = new HashMap<>();

// Constructor
private Warehouse() {
// HashSet with faster add, remove, contains (I've read) and no order needed
this.name = "Default";
this.products = new HashSet<>();
this.changedProducts = new HashSet<>();
}

// Constructor
private Warehouse(String name) {
this.name = name;
// HashSet faster add, remove, contains (I've read) and no order needed
this.products = new HashSet<>();
this.changedProducts = new HashSet<>();
}


/******************************************
*************** METHODS ***************
*******************************************/

public static Warehouse getInstance() {
return getInstance("default");
}

public static Warehouse getInstance(String name) {
if(!instances.containsKey(name)) {
instances.put(name, new Warehouse(name));
}
return instances.get(name);
}

// Get all products
public List<Product> getProducts() {
return Collections.unmodifiableList(new ArrayList<>(products));
}

// Get a single product by id
public Optional<Product> getProductById(UUID uuid) {
return products.stream()
// Find matching ID
.filter(item -> item.uuid().equals(uuid))
// Return Optional
.findFirst();
}

public void updateProductPrice(UUID uuid, BigDecimal newPrice) {
Product item = products.stream()
.filter(p -> p.uuid().equals(uuid))
.findFirst()
.orElseThrow(() -> new NoSuchElementException("Product not found with id: " + uuid));

item.setPrice(newPrice);
changedProducts.add(uuid);
}

public Set<UUID> getChangedProducts() {
return Collections.unmodifiableSet(changedProducts);
}

public void addProduct(Product item) {
if (item == null) {
throw new IllegalArgumentException("Product cannot mvn clean install -Ube null.");
}
else if (products.stream().anyMatch(p -> p.uuid().equals(item.uuid()))) {
throw new IllegalArgumentException("Product with that id already exists, use updateProduct for updates.");
}
products.add(item);
}

public void clearProducts() {
products.clear();
changedProducts.clear();
}

public boolean isEmpty() {
return products.isEmpty();
}

public List<Perishable> expiredProducts() {
return products.stream()
.filter(item -> item instanceof Perishable) // Find every item implementing Perishable
.map(item -> (Perishable) item) // Turn into Perishable instead of Product
.filter(Perishable::isExpired) // Find every item that has expired
.collect(toList()); // Finally return the list
}

public List<Shippable> shippableProducts() {
return products.stream()
.filter(item -> item instanceof Shippable) // Find every item implementing Shippable
.map(item -> (Shippable) item) // Turn Product into Shippable
.collect(toList()); // Finally returns the list
}

public void remove(UUID uuid) {
products.removeIf(item -> item.uuid().equals(uuid));
changedProducts.remove(uuid);
}

public Map<Category, List<Product>> getProductsGroupedByCategories() {
return Collections.unmodifiableMap(products.stream()
.collect(groupingBy(Product::category)));
}
}

Loading
Loading