master
mms37 2 years ago
commit f8a4952127

BIN
a4.pdf

Binary file not shown.

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

@ -0,0 +1,10 @@
<component name="libraryTable">
<library name="github.lgooddatepicker.LGoodDatePicker" type="repository">
<properties maven-id="com.github.lgooddatepicker:LGoodDatePicker:11.2.1" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/github/lgooddatepicker/LGoodDatePicker/11.2.1/LGoodDatePicker-11.2.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

@ -0,0 +1,10 @@
<component name="libraryTable">
<library name="google.code.gson" type="repository">
<properties maven-id="com.google.code.gson:gson:2.9.0" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/google/code/gson/gson/2.9.0/gson-2.9.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_18" default="true" project-jdk-name="18" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/assignment4.iml" filepath="$PROJECT_DIR$/assignment4.iml" />
</modules>
</component>
</project>

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../../../.." vcs="Git" />
</component>
</project>

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="google.code.gson" level="project" />
<orderEntry type="library" name="github.lgooddatepicker.LGoodDatePicker" level="project" />
</component>
</module>

@ -0,0 +1 @@
[{"author":"test","name":"test","note":"","price":12.0,"weight":12.0,"expectedDate":"2022-07-29T12:01","delivered":false,"type":"book"}]

@ -0,0 +1,15 @@
package cmpt213.assignment4.packagedeliveries.client;
import cmpt213.assignment4.packagedeliveries.client.view.JavaSwingUI;
import javax.swing.*;
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
SwingUtilities.invokeLater(() -> {
JavaSwingUI ui = new JavaSwingUI();
ui.displayMainPage();
});
}
}

@ -0,0 +1,206 @@
package cmpt213.assignment4.packagedeliveries.client.control;
import cmpt213.assignment4.packagedeliveries.client.gson.extras.RuntimeTypeAdapterFactory;
import cmpt213.assignment4.packagedeliveries.client.model.BookPackage;
import cmpt213.assignment4.packagedeliveries.client.model.ElectronicPackage;
import cmpt213.assignment4.packagedeliveries.client.model.PackageInfo;
import cmpt213.assignment4.packagedeliveries.client.model.PerishablePackage;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.*;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
/**
* It's a class that contains a main method that creates a TextMenu object, loads a list of packages
* from a file, and then loops through a menu of options until the user chooses to quit
*/
public class PackageDeliveriesTracker {
private static final String fileName = "list.json";
private static final RuntimeTypeAdapterFactory<PackageInfo> r = RuntimeTypeAdapterFactory.of(PackageInfo.class, "type")
.registerSubtype(BookPackage.class, "book")
.registerSubtype(PerishablePackage.class, "perishable")
.registerSubtype(ElectronicPackage.class, "electronic");
private static final Gson gson = new GsonBuilder().registerTypeAdapter(LocalDateTime.class,
new TypeAdapter<LocalDateTime>() {
@Override
public void write(JsonWriter jsonWriter,
LocalDateTime localDateTime) throws IOException {
jsonWriter.value(localDateTime.toString());
}
@Override
public LocalDateTime read(JsonReader jsonReader) throws IOException {
return LocalDateTime.parse(jsonReader.nextString());
}
}).registerTypeAdapterFactory(r).create();
private static ArrayList<PackageInfo> packageList = new ArrayList<>();
private static PackageDeliveriesTracker instance;
public static PackageDeliveriesTracker getInstance() {
if (instance == null) {
instance = new PackageDeliveriesTracker();
}
return instance;
}
public void addPackage(PackageInfo p) {
packageList.add(p);
Collections.sort(packageList);
}
/**
* It saves the packageList to a file.
*/
public void save() {
try {
Writer w = new FileWriter(fileName);
gson.toJson(packageList, w);
w.flush();
w.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* It reads the json file, converts it to a list of PackageInfo objects, and then sets the type of
* each object to the appropriate type
*/
public void load() {
//File file = new File(fileName);
try {
String json = Files.readString(Paths.get(fileName));
Type lType = new TypeToken<ArrayList<PackageInfo>>() {
}.getType();
packageList = gson.fromJson(json, lType);
for (PackageInfo p : packageList) {
if (p instanceof BookPackage) {
p.setType("book");
} else if (p instanceof PerishablePackage) {
p.setType("perishable");
} else if (p instanceof ElectronicPackage) {
p.setType("electronic");
}
}
System.out.println("packages loaded");
} catch (FileNotFoundException e) {
System.out.println("no packages to load");
} catch (NoSuchFileException e) {
createFile();
//throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void createFile() {
System.out.println("CreateFile");
try {
File packages = new File(fileName);
if(packages.createNewFile()) {
System.out.println("New file list.json created");
}
}
catch (IOException e) {
System.out.println("No file exists, creating a new file");
e.printStackTrace();
}
}
public ArrayList<String> getAllPackages() {
ArrayList<String> b = new ArrayList<>();
if (packageList.size() == 0) {
b.add("No packages to show");
} else {
Collections.sort(packageList);
for (int i = 0; i < packageList.size(); i++) {
String bString = ("Package #" + (i + 1)) + "\n" + packageList.get(i) + "\n\n";
b.add(bString);
}
}
return b;
}
/**
* This function takes in a list of packages, a boolean for whether or not the user wants to see
* packages that are due, and a boolean for whether or not the user wants to see all packages. It
* then returns a list of packages that are sorted by their expected delivery date
*
* @param pList the list of packages to be sorted
* @param due true if you want to sort the list by due date, false if you want to sort the list by
* expected date
* @return An ArrayList of PackageInfo objects.
*/
//due=true returns overdue packages, else upcoming packages
public ArrayList<PackageInfo> sortList(ArrayList<PackageInfo> pList, boolean due) {
ArrayList<PackageInfo> sortedList = new ArrayList<>();
LocalDateTime today = LocalDateTime.now();
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyy-MM-dd HH:mm");
today.format(format);
for (int i = 0; i < pList.size(); i++) {
PackageInfo p = pList.get(i);
if (!p.getDelivered()) {
if (due && today.isAfter(p.getExpectedDate())) {
sortedList.add(p);
} else if (!due && today.isBefore(p.getExpectedDate())) {
sortedList.add(p);
}
}
}
Collections.sort(sortedList);
return sortedList;
}
public ArrayList<String> overDuePackages() {
ArrayList<String> b = new ArrayList<>();
ArrayList<PackageInfo> overdue = sortList(packageList, true);
if (overdue.size() == 0) {
b.add("no overdue packages to show");
}
for (int i = 0; i < overdue.size(); i++) {
String bString = ("Package #" + (i + 1)) + "\n" + overdue.get(i) + "\n\n";
b.add(bString);
}
return b;
}
public ArrayList<String> upcomingPackages() {
ArrayList<String> b = new ArrayList<>();
ArrayList<PackageInfo> upcoming = sortList(packageList, false);
if (upcoming.size() == 0) {
b.add("no upcoming packages to show");
}
for (int i = 0; i < upcoming.size(); i++) {
String bString = ("Package #" + (i + 1)) + "\n" + upcoming.get(i) + "\n\n";
b.add(bString);
}
return b;
}
public void removePcakage(int i) {
packageList.remove(i);
}
public boolean isDelivered(int i) {
return packageList.get(i).getDelivered();
}
public void setDelivered(int i) {
packageList.get(i).setDelivered(true);
}
}

@ -0,0 +1,73 @@
package cmpt213.assignment4.packagedeliveries.client.control;
import cmpt213.assignment4.packagedeliveries.client.model.BookPackage;
import cmpt213.assignment4.packagedeliveries.client.model.ElectronicPackage;
import cmpt213.assignment4.packagedeliveries.client.model.PackageInfo;
import cmpt213.assignment4.packagedeliveries.client.model.PerishablePackage;
import java.time.LocalDateTime;
/**
* The PackageFactory class is a factory class that creates a package object based on the type of
* package that is passed in
*/
public class PackageFactory {
/**
* > This function creates a new package object based on the package type
*
* @param type The type of package.
* @param name The name of the package.
* @param notes A string of notes about the package.
* @param price The price of the package
* @param weight The weight of the package in kilograms
* @param delivered Whether or not the package has been delivered
* @param expectDate The date the package is expected to be delivered.
* @param author The author of the book.
* @param expiryDate The date the package expires.
* @param handlingFee The handling fee for the package.
* @return A new instance of the package type.
*/
public static PackageInfo create(PackageType type, String name, String notes, double price, double weight, boolean delivered, LocalDateTime expectDate, String author, LocalDateTime expiryDate, double handlingFee) {
return type.getInstance(name, notes, price, weight, delivered, expectDate, author, expiryDate, handlingFee);
}
// Creating an enum called PackageType.
public enum PackageType {
Book {
public PackageInfo getInstance(String name, String notes, double price, double weight, boolean delivered, LocalDateTime expectedDate, String author, LocalDateTime expiryDate, double handlingFee) {
return new BookPackage(name, notes, price, weight, delivered, expectedDate, author);
}
},
Perishable {
public PackageInfo getInstance(String name, String notes, double price, double weight, boolean delivered, LocalDateTime expectedDate, String author, LocalDateTime expiryDate, double handlingFee) {
return new PerishablePackage(name, notes, price, weight, delivered, expectedDate, expiryDate);
}
},
Electronic {
public PackageInfo getInstance(String name, String notes, double price, double weight, boolean delivered, LocalDateTime expectedDate, String author, LocalDateTime expiryDate, double handlingFee) {
return new ElectronicPackage(name, notes, price, weight, delivered, expectedDate, handlingFee);
}
};
/**
* It returns a PackageInfo object.
*
* @param name The name of the package.
* @param notes a string that describes the package
* @param price The price of the package
* @param weight the weight of the package in kg
* @param delivered true if the package has been delivered, false otherwise
* @param expectedDate The date the package is expected to be delivered.
* @param author The name of the author of the book.
* @param expiryDate The date when the package expires.
* @param handlingFee The handling fee for the package.
* @return A package object
*/
public abstract PackageInfo getInstance(String name, String notes, double price, double weight, boolean delivered, LocalDateTime expectedDate, String author, LocalDateTime expiryDate, double handlingFee);
}
}

@ -0,0 +1,256 @@
package cmpt213.assignment4.packagedeliveries.client.gson.extras;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Adapts values whose runtime type may differ from their declaration type. This
* is necessary when a field's type is not the same type that GSON should create
* when deserializing that field. For example, consider these types:
* <pre> {@code
* abstract class Shape {
* int x;
* int y;
* }
* class Circle extends Shape {
* int radius;
* }
* class Rectangle extends Shape {
* int width;
* int height;
* }
* class Diamond extends Shape {
* int width;
* int height;
* }
* class Drawing {
* Shape bottomShape;
* Shape topShape;
* }
* }</pre>
* <p>Without additional type information, the serialized JSON is ambiguous. Is
* the bottom shape in this drawing a rectangle or a diamond? <pre> {@code
* {
* "bottomShape": {
* "width": 10,
* "height": 5,
* "x": 0,
* "y": 0
* },
* "topShape": {
* "radius": 2,
* "x": 4,
* "y": 1
* }
* }}</pre>
* This class addresses this problem by adding type information to the
* serialized JSON and honoring that type information when the JSON is
* deserialized: <pre> {@code
* {
* "bottomShape": {
* "type": "Diamond",
* "width": 10,
* "height": 5,
* "x": 0,
* "y": 0
* },
* "topShape": {
* "type": "Circle",
* "radius": 2,
* "x": 4,
* "y": 1
* }
* }}</pre>
* Both the type field name ({@code "type"}) and the type labels ({@code
* "Rectangle"}) are configurable.
*
* <h3>Registering Types</h3>
* Create a {@code cmpt213.assignment2.packagedeliveriestracker.gson.extras.RuntimeTypeAdapterFactory} by passing the base type and type field
* name to the {@link #of} factory method. If you don't supply an explicit type
* field name, {@code "type"} will be used. <pre> {@code
* cmpt213.assignment2.packagedeliveriestracker.gson.extras.RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory
* = cmpt213.assignment2.packagedeliveriestracker.gson.extras.RuntimeTypeAdapterFactory.of(Shape.class, "type");
* }</pre>
* Next register all of your subtypes. Every subtype must be explicitly
* registered. This protects your application from injection attacks. If you
* don't supply an explicit type label, the type's simple name will be used.
* <pre> {@code
* shapeAdapterFactory.registerSubtype(Rectangle.class, "Rectangle");
* shapeAdapterFactory.registerSubtype(Circle.class, "Circle");
* shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond");
* }</pre>
* Finally, register the type adapter factory in your application's GSON builder:
* <pre> {@code
* Gson gson = new GsonBuilder()
* .registerTypeAdapterFactory(shapeAdapterFactory)
* .create();
* }</pre>
* Like {@code GsonBuilder}, this API supports chaining: <pre> {@code
* cmpt213.assignment2.packagedeliveriestracker.gson.extras.RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory = cmpt213.assignment2.packagedeliveriestracker.gson.extras.RuntimeTypeAdapterFactory.of(Shape.class)
* .registerSubtype(Rectangle.class)
* .registerSubtype(Circle.class)
* .registerSubtype(Diamond.class);
* }</pre>
*
* <h3>Serialization and deserialization</h3>
* In order to serialize and deserialize a polymorphic object,
* you must specify the base type explicitly.
* <pre> {@code
* Diamond diamond = new Diamond();
* String json = gson.toJson(diamond, Shape.class);
* }</pre>
* And then:
* <pre> {@code
* Shape shape = gson.fromJson(json, Shape.class);
* }</pre>
*/
public final class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
private final Class<?> baseType;
private final String typeFieldName;
private final Map<String, Class<?>> labelToSubtype = new LinkedHashMap<>();
private final Map<Class<?>, String> subtypeToLabel = new LinkedHashMap<>();
private final boolean maintainType;
private RuntimeTypeAdapterFactory(Class<?> baseType, String typeFieldName, boolean maintainType) {
if (typeFieldName == null || baseType == null) {
throw new NullPointerException();
}
this.baseType = baseType;
this.typeFieldName = typeFieldName;
this.maintainType = maintainType;
}
/**
* Creates a new runtime type adapter using for {@code baseType} using {@code
* typeFieldName} as the type field name. Type field names are case sensitive.
* {@code maintainType} flag decide if the type will be stored in pojo or not.
*/
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName, boolean maintainType) {
return new RuntimeTypeAdapterFactory<>(baseType, typeFieldName, maintainType);
}
/**
* Creates a new runtime type adapter using for {@code baseType} using {@code
* typeFieldName} as the type field name. Type field names are case sensitive.
*/
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName) {
return new RuntimeTypeAdapterFactory<>(baseType, typeFieldName, false);
}
/**
* Creates a new runtime type adapter for {@code baseType} using {@code "type"} as
* the type field name.
*/
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType) {
return new RuntimeTypeAdapterFactory<>(baseType, "type", false);
}
/**
* Registers {@code type} identified by {@code label}. Labels are case
* sensitive.
*
* @throws IllegalArgumentException if either {@code type} or {@code label}
* have already been registered on this type adapter.
*/
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type, String label) {
if (type == null || label == null) {
throw new NullPointerException();
}
if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) {
throw new IllegalArgumentException("types and labels must be unique");
}
labelToSubtype.put(label, type);
subtypeToLabel.put(type, label);
return this;
}
/**
* Registers {@code type} identified by its {@link Class#getSimpleName simple
* name}. Labels are case sensitive.
*
* @throws IllegalArgumentException if either {@code type} or its simple name
* have already been registered on this type adapter.
*/
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) {
return registerSubtype(type, type.getSimpleName());
}
@Override
public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) {
if (type.getRawType() != baseType) {
return null;
}
final TypeAdapter<JsonElement> jsonElementAdapter = gson.getAdapter(JsonElement.class);
final Map<String, TypeAdapter<?>> labelToDelegate = new LinkedHashMap<>();
final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate = new LinkedHashMap<>();
for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
labelToDelegate.put(entry.getKey(), delegate);
subtypeToDelegate.put(entry.getValue(), delegate);
}
return new TypeAdapter<R>() {
@Override
public R read(JsonReader in) throws IOException {
JsonElement jsonElement = jsonElementAdapter.read(in);
JsonElement labelJsonElement;
if (maintainType) {
labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName);
} else {
labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);
}
if (labelJsonElement == null) {
throw new JsonParseException("cannot deserialize " + baseType
+ " because it does not define a field named " + typeFieldName);
}
String label = labelJsonElement.getAsString();
@SuppressWarnings("unchecked") // registration requires that subtype extends T
TypeAdapter<R> delegate = (TypeAdapter<R>) labelToDelegate.get(label);
if (delegate == null) {
throw new JsonParseException("cannot deserialize " + baseType + " subtype named "
+ label + "; did you forget to register a subtype?");
}
return delegate.fromJsonTree(jsonElement);
}
@Override
public void write(JsonWriter out, R value) throws IOException {
Class<?> srcType = value.getClass();
String label = subtypeToLabel.get(srcType);
@SuppressWarnings("unchecked") // registration requires that subtype extends T
TypeAdapter<R> delegate = (TypeAdapter<R>) subtypeToDelegate.get(srcType);
if (delegate == null) {
throw new JsonParseException("cannot serialize " + srcType.getName()
+ "; did you forget to register a subtype?");
}
JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
if (maintainType) {
jsonElementAdapter.write(out, jsonObject);
return;
}
JsonObject clone = new JsonObject();
if (jsonObject.has(typeFieldName)) {
throw new JsonParseException("cannot serialize " + srcType.getName()
+ " because it already defines a field named " + typeFieldName);
}
clone.add(typeFieldName, new JsonPrimitive(label));
for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) {
clone.add(e.getKey(), e.getValue());
}
jsonElementAdapter.write(out, clone);
}
}.nullSafe();
}
}

@ -0,0 +1,23 @@
package cmpt213.assignment4.packagedeliveries.client.model;
import java.time.LocalDateTime;
/**
* It's a subclass of PackageInfo that adds an author field
*/
public class BookPackage extends PackageInfo {
String author;
public BookPackage(String name, String note, double price, double weight, boolean delivered, LocalDateTime expectedDate, String author) {
super(name, note, price, weight, delivered, expectedDate);
this.author = author;
this.setType("book");
}
@Override
public String toString() {
return super.toString() + "\nAuthor: " + author;
}
}

@ -0,0 +1,21 @@
package cmpt213.assignment4.packagedeliveries.client.model;
import java.time.LocalDateTime;
/**
* ElectronicPackage is a subclass of PackageInfo that adds a handlingFee attribute
*/
public class ElectronicPackage extends PackageInfo {
double handlingFee;
public ElectronicPackage(String name, String note, double price, double weight, boolean delivered, LocalDateTime expectedDate, double handlingFee) {
super(name, note, price, weight, delivered, expectedDate);
this.handlingFee = handlingFee;
this.setType("electronic");
}
@Override
public String toString() {
return super.toString() + "\nHandling fee: " + handlingFee;
}
}

@ -0,0 +1,85 @@
package cmpt213.assignment4.packagedeliveries.client.model;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import static java.lang.Math.abs;
/**
* PackageInfo is a class that contains information about a package
*/
public class PackageInfo implements Comparable<PackageInfo> {
private final String name;
private final String note;
private final double price;
private final double weight;
private final LocalDateTime expectedDate;
private boolean delivered;
private String type;
public PackageInfo(String name, String note, double price, double weight, boolean delivered, LocalDateTime expectedDate) {
this.name = name;
this.note = note;
this.price = price;
this.weight = weight;
this.delivered = delivered;
this.expectedDate = expectedDate;
}
/**
* This function returns the name of the person.
*
* @return The name of the person.
*/
public String getName() {
return name;
}
public boolean getDelivered() {
return delivered;
}
/**
* This function sets the value of the delivered variable to the value of the delivered parameter.
*
* @param delivered This is a boolean value that indicates whether the message has been delivered
* to the recipient.
*/
public void setDelivered(boolean delivered) {
this.delivered = delivered;
}
public LocalDateTime getExpectedDate() {
return expectedDate;
}
/**
* This function sets the type of the object to the type passed in as a parameter
*
* @param type The type of the event.
*/
public void setType(String type) {
this.type = type;
}
// Comparing the expected date of the package to the expected date of the package passed in as a
// parameter.
@Override
public int compareTo(PackageInfo p) {
return this.expectedDate.compareTo(p.getExpectedDate());
}
// Overriding the toString method.
@Override
public String toString() {
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyy-MM-dd HH:mm");
LocalDateTime today = LocalDateTime.now();
today.format(format);
long diff = ChronoUnit.DAYS.between(today, expectedDate);
String isDelivered = delivered ? "yes" : "no";
return "Name: " + name + "\n" + "Notes: " + note + "\n" + "Price: $" + price + "\n" + "Weight: " + weight + " kg" + "\n" + "Expected Delivery Date: " + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(expectedDate) + "\n" + "Delivered? " + isDelivered + "\n" + ((diff > 0 && !delivered) ? diff + " days remaining" : abs(diff) + " days overdue");
}
}

@ -0,0 +1,24 @@
package cmpt213.assignment4.packagedeliveries.client.model;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* It's a subclass of PackageInfo that adds an expiry date
*/
public class PerishablePackage extends PackageInfo {
LocalDateTime expiryDate;
// It's a constructor that initializes the object with the given parameters.
public PerishablePackage(String name, String note, double price, double weight, boolean delivered, LocalDateTime expectedDate, LocalDateTime expiryDate) {
super(name, note, price, weight, delivered, expectedDate);
this.expiryDate = expiryDate;
this.setType("perishable");
}
// It's a method that returns a string representation of the object.
@Override
public String toString() {
return super.toString() + "\nExpiry date: " + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(expiryDate);
}
}

@ -0,0 +1,238 @@
package cmpt213.assignment4.packagedeliveries.client.view;
import cmpt213.assignment4.packagedeliveries.client.control.PackageDeliveriesTracker;
import cmpt213.assignment4.packagedeliveries.client.control.PackageFactory;
import cmpt213.assignment4.packagedeliveries.client.model.PackageInfo;
import com.github.lgooddatepicker.components.DateTimePicker;
import com.github.lgooddatepicker.optionalusertools.DateTimeChangeListener;
import com.github.lgooddatepicker.zinternaltools.DateTimeChangeEvent;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.LocalDateTime;
public class Input extends JDialog implements ActionListener, DateTimeChangeListener {
private final PackageDeliveriesTracker pInstance = PackageDeliveriesTracker.getInstance();
private final JLabel typeLabel;
private final JPanel typePanel;
private final JTextField typeTf;
private final DateTimePicker dd;
private final DateTimePicker ed;
private LocalDateTime deliveryDate, expiryDate;
private final JTextField nameField;
private final JTextField noteField;
private final JTextField priceField;
private final JTextField weightField;
private final JTextField dateField;
private int pType;
public Input(Frame main) {
super(main, "add a package", true);
JPanel p = new JPanel();
p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
String[] type = {"book", "perishable", "electronic"};
JComboBox<String> packageType = new JComboBox<>(type);
packageType.setPreferredSize(new Dimension(300, 30));
packageType.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String p = (String) packageType.getSelectedItem();
switch (p) {
case "book":
typeTf.setVisible(true);
ed.setVisible(false);
typeLabel.setText("author name");
pType = 0;
break;
case "perishable":
typeTf.setVisible(false);
ed.setVisible(true);
ed.addDateTimeChangeListener(new DateTimeChangeListener() {
@Override
public void dateOrTimeChanged(DateTimeChangeEvent dateTimeChangeEvent) {
expiryDate = ed.getDateTimePermissive();
}
});
typePanel.add(ed);
typeLabel.setText("expiry date");
pType = 1;
break;
case "electronic":
typeTf.setVisible(true);
ed.setVisible(false);
typeLabel.setText("handling fee");
pType = 2;
}
}
});
// Panel for package name
nameField = new JTextField();
JPanel name = new JPanel();
JLabel nameLabel = new JLabel();
name.setLayout(new BoxLayout(name, BoxLayout.X_AXIS));
nameLabel.setText("name:");
nameLabel.setPreferredSize(new Dimension(50, 25));
name.add(nameLabel);
name.add(nameField);
name.setPreferredSize(new Dimension(200, 50));
p.add(packageType);
p.add(name);
JPanel note = new JPanel();
JLabel noteLabel = new JLabel();
noteField = new JTextField();
note.setLayout(new BoxLayout(note, BoxLayout.X_AXIS));
noteLabel.setText("note:");
noteLabel.setPreferredSize(new Dimension(50, 25));
note.add(noteLabel);
note.add(noteField);
note.setPreferredSize(new Dimension(200, 50));
p.add(note);
JPanel price = new JPanel();
JLabel priceLabel = new JLabel();
priceField = new JTextField();
price.setLayout(new BoxLayout(price, BoxLayout.X_AXIS));
priceLabel.setText("price:");
priceLabel.setPreferredSize(new Dimension(50, 25));
price.add(priceLabel);
price.add(priceField);
price.setPreferredSize(new Dimension(200, 50));
p.add(price);
JPanel weight = new JPanel();
JLabel weightLabel = new JLabel();
weightField = new JTextField();
weight.setLayout(new BoxLayout(weight, BoxLayout.X_AXIS));
weightLabel.setText("weight:");
weightLabel.setPreferredSize(new Dimension(50, 25));
weight.add(weightLabel);
weight.add(weightField);
weight.setPreferredSize(new Dimension(200, 50));
p.add(weight);
JPanel date = new JPanel();
JLabel dateLabel = new JLabel();
dateField = new JTextField();
date.setLayout(new BoxLayout(date, BoxLayout.X_AXIS));
dateLabel.setText("date:");
dateLabel.setPreferredSize(new Dimension(50, 25));
dd = new DateTimePicker();
dd.addDateTimeChangeListener(this);
date.add(dateLabel);
date.add(dd);
date.setPreferredSize(new Dimension(200, 50));
p.add(date);
typePanel = new JPanel();
typePanel.setLayout(new BoxLayout(typePanel, BoxLayout.X_AXIS));
typeLabel = new JLabel();
typeLabel.setText("author:");
typeLabel.setPreferredSize(new Dimension(50, 25));
typeTf = new JTextField();
typePanel.add(typeLabel);
typePanel.add(typeTf);
typePanel.setPreferredSize(new Dimension(200, 50));
ed = new DateTimePicker();
p.add(typePanel);
JPanel actionButton = new JPanel();
actionButton.setLayout(new BoxLayout(actionButton, BoxLayout.X_AXIS));
JButton ok = new JButton("ok");
JButton cancel = new JButton("cancel");
ok.addActionListener(this);
cancel.addActionListener(this);
actionButton.add(ok);
actionButton.add(cancel);
p.add(actionButton);
// Dialog box dimensions
getContentPane().setSize(500, 500);
getContentPane().add(p);
pack();
this.setVisible(true);
}
@Override
public void dateOrTimeChanged(DateTimeChangeEvent dateTimeChangeEvent) {
deliveryDate = dd.getDateTimePermissive();
}
@Override
public void actionPerformed(ActionEvent e) {
String actionString = e.getActionCommand();
switch (actionString) {
case "ok":
addPackage();
break;
case "cancel":
this.dispose();
break;
}
}
private void addPackage() {
try {
PackageFactory.PackageType type = PackageFactory.PackageType.Book;
String pName = nameField.getText();
String pNote = noteField.getText();
double pPrice = 0.0;
pPrice = Double.parseDouble(priceField.getText());
if (pPrice < 0) {
JOptionPane.showMessageDialog(this,
"price cannot be less than 0",
"error",
JOptionPane.WARNING_MESSAGE);
}
double pWeight = 0.0;
pWeight = Double.parseDouble(weightField.getText());
if (pWeight < 0) {
JOptionPane.showMessageDialog(this,
"weight cannot be less than 0",
"error",
JOptionPane.WARNING_MESSAGE);
}
String pAuthor = "";
double pHandlingFee = 0.0;
if (pType == 0) {
pAuthor = typeTf.getText();
if (pAuthor.equals("")) {
JOptionPane.showMessageDialog(this,
"author name can not be empty",
"error",
JOptionPane.WARNING_MESSAGE);
return;
}
} else if (pType == 1) {
type = PackageFactory.PackageType.Electronic;
pHandlingFee = Double.parseDouble(typeTf.getText());
if (pHandlingFee < 0) {
JOptionPane.showMessageDialog(this,
"Handling fee cannot be less than 0",
"error",
JOptionPane.WARNING_MESSAGE);
}
} else {
type = PackageFactory.PackageType.Perishable;
}
if (pName.equals("")) {
JOptionPane.showMessageDialog(this,
"name cannot be empty",
"error",
JOptionPane.WARNING_MESSAGE);
return;
}
PackageInfo p = PackageFactory.create(type, pName, pNote, pPrice, pWeight, false, deliveryDate, pAuthor, expiryDate, pHandlingFee);
pInstance.addPackage(p);
dispose();
} catch (Exception e) {
JOptionPane.showMessageDialog(this,
"error in input, please check again",
"error",
JOptionPane.WARNING_MESSAGE);
}
}
}

@ -0,0 +1,176 @@
package cmpt213.assignment4.packagedeliveries.client.view;
import cmpt213.assignment4.packagedeliveries.client.control.PackageDeliveriesTracker;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
public class JavaSwingUI implements ActionListener {
private final PackageDeliveriesTracker pTracker = PackageDeliveriesTracker.getInstance();
JFrame uiFrame;
JPanel uiPanel;
JScrollPane scrollView;
PACKAGE_FILTER_OPTION option = PACKAGE_FILTER_OPTION.ALL;
;
public void displayMainPage() {
pTracker.load();
uiFrame = new JFrame("Package deliveries tracker");
uiFrame.setSize(500, 500);
uiFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
uiFrame.setLayout(new BoxLayout(uiFrame.getContentPane(), BoxLayout.Y_AXIS));
uiFrame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
pTracker.save();
super.windowClosing(e);
uiFrame.dispose();
}
});
uiFrame.setVisible(true);
dsplayOptionButtons();
addButtons();
setupScrollPane();
refreshPackageList();
}
private void setupScrollPane() {
uiPanel = new JPanel();
scrollView = new JScrollPane(uiPanel);
uiPanel.setLayout(new BoxLayout(uiPanel, BoxLayout.Y_AXIS));
scrollView.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
uiPanel.add(Box.createVerticalGlue());
uiFrame.getContentPane().add(scrollView);
uiFrame.pack();
}
private void displayPackages(ArrayList<String> packages) {
uiPanel.removeAll();
uiPanel.setLayout(new BoxLayout(uiPanel, BoxLayout.Y_AXIS));
scrollView.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
uiPanel.add(Box.createVerticalGlue());
uiFrame.getContentPane().add(scrollView);
System.out.println(packages);
if (packages.get(0) != "No packages to show") {
for (int i = 0; i < packages.size(); i++) {
JPanel p = new JPanel();
JTextPane pkgPane = new JTextPane();
pkgPane.setEditable(false);
pkgPane.setText(packages.get(i));
p.add(pkgPane);
if (option == PACKAGE_FILTER_OPTION.ALL) {
JButton remove = new JButton();
int finalI = i;
remove.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
pTracker.removePcakage(finalI);
refreshPackageList();
}
});
remove.setText("Remove");
p.add(remove);
JCheckBox delivered = new JCheckBox("Delivered?");
delivered.setBounds(100, 100, 50, 50);
delivered.setSelected(pTracker.isDelivered(i));
int finalI1 = i;
delivered.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
delivered.setSelected(true);
pTracker.setDelivered(finalI1);
refreshPackageList();
}
});
p.add(delivered);
}
uiPanel.add(p);
}
}
uiFrame.validate();
uiFrame.pack();
}
private void addButtons() {
JButton add = new JButton("Add a package");
JPanel addPanel = new JPanel();
addPanel.setLayout(new BoxLayout(addPanel, BoxLayout.X_AXIS));
addPanel.add(add);
addPanel.setPreferredSize(new Dimension(500, 50));
uiFrame.add(addPanel);
add.addActionListener(this);
}
private void dsplayOptionButtons() {
JButton btnAll = new JButton("All");
JButton btnOverdue = new JButton("Overdue");
JButton btnUpcoming = new JButton("Upcoming");
btnAll.addActionListener(this);
btnOverdue.addActionListener(this);
btnUpcoming.addActionListener(this);
JPanel btnPanel = new JPanel();
btnPanel.setLayout(new BoxLayout(btnPanel, BoxLayout.X_AXIS));
btnPanel.add(btnAll);
btnPanel.add(btnOverdue);
btnPanel.add(btnUpcoming);
btnPanel.setPreferredSize(new Dimension(500, 50));
uiFrame.add(btnPanel);
}
@Override
public void actionPerformed(ActionEvent e) {
String actionString = e.getActionCommand();
switch (actionString) {
case "Add a package":
addPackage();
break;
case "All":
option = PACKAGE_FILTER_OPTION.ALL;
refreshPackageList();
break;
case "Overdue":
option = PACKAGE_FILTER_OPTION.OVERDUE;
refreshPackageList();
break;
case "Upcoming":
option = PACKAGE_FILTER_OPTION.UPCOMING;
refreshPackageList();
break;
}
}
private void addPackage() {
new Input(uiFrame);
refreshPackageList();
}
private void refreshPackageList() {
switch (option) {
case ALL:
ArrayList<String> allPackages = pTracker.getAllPackages();
displayPackages(allPackages);
break;
case OVERDUE:
ArrayList<String> overDuePkg = pTracker.overDuePackages();
displayPackages(overDuePkg);
break;
case UPCOMING:
ArrayList<String> upcomingPkg = pTracker.upcomingPackages();
displayPackages(upcomingPkg);
break;
}
}
enum PACKAGE_FILTER_OPTION {ALL, OVERDUE, UPCOMING}
}

@ -0,0 +1,33 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/

Binary file not shown.

@ -0,0 +1,2 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar

@ -0,0 +1,7 @@
CURL COMMANDS:
GET /ping
command : curl -i -X GET localhost:8080
Returns a message "system is up".
GET /listAll

@ -0,0 +1 @@
[{"author":"test","name":"test","note":"","price":12.0,"weight":12.0,"expectedDate":"2022-07-29T12:01","delivered":false,"type":"book"}]

316
webappserver/mvnw vendored

@ -0,0 +1,316 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /usr/local/etc/mavenrc ] ; then
. /usr/local/etc/mavenrc
fi
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`\\unset -f command; \\command -v java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
if [ -n "$MVNW_REPOURL" ]; then
jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
fi
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if $cygwin; then
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
fi
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl -o "$wrapperJarPath" "$jarUrl" -f
else
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaClass=`cygpath --path --windows "$javaClass"`
fi
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

@ -0,0 +1,188 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM https://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% ^
%JVM_CONFIG_MAVEN_PROPS% ^
%MAVEN_OPTS% ^
%MAVEN_DEBUG_OPTS% ^
-classpath %WRAPPER_JAR% ^
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%"=="on" pause
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
cmd /C exit /B %ERROR_CODE%

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cmpt213.assignment4.packagedeliveries</groupId>
<artifactId>webappserver</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>WebAppServer</name>
<description>web server side of the assignment</description>
<properties>
<java.version>18</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,15 @@
package cmpt213.assignment4.packagedeliveries.webappserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
public class WebAppServerApplication {
public static void main(String[] args) {
SpringApplication.run(WebAppServerApplication.class, args);
}
}

@ -0,0 +1,213 @@
package cmpt213.assignment4.packagedeliveries.webappserver.control;
import cmpt213.assignment4.packagedeliveries.webappserver.gson.extras.RuntimeTypeAdapterFactory;
import cmpt213.assignment4.packagedeliveries.webappserver.model.BookPackage;
import cmpt213.assignment4.packagedeliveries.webappserver.model.ElectronicPackage;
import cmpt213.assignment4.packagedeliveries.webappserver.model.PackageInfo;
import cmpt213.assignment4.packagedeliveries.webappserver.model.PerishablePackage;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.*;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
/**
* It's a class that contains a main method that creates a TextMenu object, loads a list of packages
* from a file, and then loops through a menu of options until the user chooses to quit
*/
public class PackageManager {
private static final String fileName = "list.json";
private static final RuntimeTypeAdapterFactory<PackageInfo> r = RuntimeTypeAdapterFactory.of(PackageInfo.class, "type")
.registerSubtype(BookPackage.class, "book")
.registerSubtype(PerishablePackage.class, "perishable")
.registerSubtype(ElectronicPackage.class, "electronic");
private static final Gson gson = new GsonBuilder().registerTypeAdapter(LocalDateTime.class,
new TypeAdapter<LocalDateTime>() {
@Override
public void write(JsonWriter jsonWriter,
LocalDateTime localDateTime) throws IOException {
jsonWriter.value(localDateTime.toString());
}
@Override
public LocalDateTime read(JsonReader jsonReader) throws IOException {
return LocalDateTime.parse(jsonReader.nextString());
}
}).registerTypeAdapterFactory(r).create();
private static ArrayList<PackageInfo> packageList = new ArrayList<>();
private static PackageManager instance;
public static PackageManager getInstance() {
if (instance == null) {
instance = new PackageManager();
}
return instance;
}
public void addPackage(PackageInfo p) {
packageList.add(p);
Collections.sort(packageList);
}
/**
* It saves the packageList to a file.
*/
public void save() {
try {
Writer w = new FileWriter(fileName);
gson.toJson(packageList, w);
w.flush();
w.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* It reads the json file, converts it to a list of PackageInfo objects, and then sets the type of
* each object to the appropriate type
*/
public void load() {
//File file = new File(fileName);
try {
String json = Files.readString(Paths.get(fileName));
Type lType = new TypeToken<ArrayList<PackageInfo>>() {
}.getType();
packageList = gson.fromJson(json, lType);
for (PackageInfo p : packageList) {
if (p instanceof BookPackage) {
p.setType("book");
} else if (p instanceof PerishablePackage) {
p.setType("perishable");
} else if (p instanceof ElectronicPackage) {
p.setType("electronic");
}
}
System.out.println("packages loaded");
} catch (FileNotFoundException e) {
System.out.println("no packages to load");
} catch (NoSuchFileException e) {
createFile();
//throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void createFile() {
System.out.println("CreateFile");
try {
File packages = new File(fileName);
if(packages.createNewFile()) {
System.out.println("New file list.json created");
}
}
catch (IOException e) {
System.out.println("No file exists, creating a new file");
e.printStackTrace();
}
}
public ArrayList<String> getAllPackages() {
ArrayList<String> b = new ArrayList<>();
if (packageList.size() == 0) {
b.add("No packages to show");
} else {
Collections.sort(packageList);
for (int i = 0; i < packageList.size(); i++) {
String bString = ("Package #" + (i + 1)) + "\n" + packageList.get(i) + "\n\n";
b.add(bString);
}
}
return b;
}
/**
* This function takes in a list of packages, a boolean for whether or not the user wants to see
* packages that are due, and a boolean for whether or not the user wants to see all packages. It
* then returns a list of packages that are sorted by their expected delivery date
*
* @param pList the list of packages to be sorted
* @param due true if you want to sort the list by due date, false if you want to sort the list by
* expected date
* @return An ArrayList of PackageInfo objects.
*/
//due=true returns overdue packages, else upcoming packages
public ArrayList<PackageInfo> sortList(ArrayList<PackageInfo> pList, boolean due) {
ArrayList<PackageInfo> sortedList = new ArrayList<>();
LocalDateTime today = LocalDateTime.now();
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyy-MM-dd HH:mm");
today.format(format);
for (int i = 0; i < pList.size(); i++) {
PackageInfo p = pList.get(i);
if (!p.getDelivered()) {
if (due && today.isAfter(p.getExpectedDate())) {
sortedList.add(p);
} else if (!due && today.isBefore(p.getExpectedDate())) {
sortedList.add(p);
}
}
}
Collections.sort(sortedList);
return sortedList;
}
public ArrayList<String> overDuePackages() {
ArrayList<String> b = new ArrayList<>();
ArrayList<PackageInfo> overdue = sortList(packageList, true);
if (overdue.size() == 0) {
b.add("no overdue packages to show");
}
for (int i = 0; i < overdue.size(); i++) {
String bString = ("Package #" + (i + 1)) + "\n" + overdue.get(i) + "\n\n";
b.add(bString);
}
return b;
}
public ArrayList<String> upcomingPackages() {
ArrayList<String> b = new ArrayList<>();
ArrayList<PackageInfo> upcoming = sortList(packageList, false);
if (upcoming.size() == 0) {
b.add("no upcoming packages to show");
}
for (int i = 0; i < upcoming.size(); i++) {
String bString = ("Package #" + (i + 1)) + "\n" + upcoming.get(i) + "\n\n";
b.add(bString);
}
return b;
}
public void removePcakage(int i) {
packageList.remove(i);
}
public boolean isDelivered(int i) {
return packageList.get(i).getDelivered();
}
public void setDelivered(int i) {
packageList.get(i).setDelivered(true);
}
public PackageInfo deserializePackage(String packageGson){
Type lType = new TypeToken<ArrayList<PackageInfo>>() {
}.getType();
PackageInfo newPackage=gson.fromJson(packageGson, lType);
System.out.println(newPackage.toString() + "-------> newPackage");
return newPackage;
}
}

@ -0,0 +1,44 @@
package cmpt213.assignment4.packagedeliveries.webappserver.controllers;
import cmpt213.assignment4.packagedeliveries.webappserver.control.PackageManager;
import cmpt213.assignment4.packagedeliveries.webappserver.model.PackageInfo;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
@RestController
public class PackageController {
@GetMapping("/ping")
@ResponseStatus(HttpStatus.OK)
public String ping(){
return "system is up";
}
PackageManager manager=PackageManager.getInstance();
@GetMapping("/listAll")
@ResponseStatus(HttpStatus.OK)
public ArrayList<String> getAllPackages(){
return manager.getAllPackages();
}
@PostMapping("/addBook")
@ResponseStatus(HttpStatus.CREATED)
public void addBook(@RequestBody String bookString){
System.out.println(bookString+"----------------->bookString");
PackageInfo p=manager.deserializePackage(bookString);
manager.addPackage(p);
}
@PostMapping("/addPerishable")
@ResponseStatus(HttpStatus.CREATED)
public void addPerishable(@RequestBody String perishableString){
PackageInfo p=manager.deserializePackage(perishableString);
}
@PostMapping("/addElectronic")
@ResponseStatus(HttpStatus.CREATED)
public void addElectronic(@RequestBody String electronicString){
PackageInfo p=manager.deserializePackage(electronicString);
}
}

@ -0,0 +1,258 @@
package cmpt213.assignment4.packagedeliveries.webappserver.gson.extras;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Adapts values whose runtime
*
* type may differ from their declaration type. This
* is necessary when a field's type is not the same type that GSON should create
* when deserializing that field. For example, consider these types:
* <pre> {@code
* abstract class Shape {
* int x;
* int y;
* }
* class Circle extends Shape {
* int radius;
* }
* class Rectangle extends Shape {
* int width;
* int height;
* }
* class Diamond extends Shape {
* int width;
* int height;
* }
* class Drawing {
* Shape bottomShape;
* Shape topShape;
* }
* }</pre>
* <p>Without additional type information, the serialized JSON is ambiguous. Is
* the bottom shape in this drawing a rectangle or a diamond? <pre> {@code
* {
* "bottomShape": {
* "width": 10,
* "height": 5,
* "x": 0,
* "y": 0
* },
* "topShape": {
* "radius": 2,
* "x": 4,
* "y": 1
* }
* }}</pre>
* This class addresses this problem by adding type information to the
* serialized JSON and honoring that type information when the JSON is
* deserialized: <pre> {@code
* {
* "bottomShape": {
* "type": "Diamond",
* "width": 10,
* "height": 5,
* "x": 0,
* "y": 0
* },
* "topShape": {
* "type": "Circle",
* "radius": 2,
* "x": 4,
* "y": 1
* }
* }}</pre>
* Both the type field name ({@code "type"}) and the type labels ({@code
* "Rectangle"}) are configurable.
*
* <h3>Registering Types</h3>
* Create a {@code cmpt213.assignment2.packagedeliveriestracker.gson.extras.RuntimeTypeAdapterFactory} by passing the base type and type field
* name to the {@link #of} factory method. If you don't supply an explicit type
* field name, {@code "type"} will be used. <pre> {@code
* cmpt213.assignment2.packagedeliveriestracker.gson.extras.RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory
* = cmpt213.assignment2.packagedeliveriestracker.gson.extras.RuntimeTypeAdapterFactory.of(Shape.class, "type");
* }</pre>
* Next register all of your subtypes. Every subtype must be explicitly
* registered. This protects your application from injection attacks. If you
* don't supply an explicit type label, the type's simple name will be used.
* <pre> {@code
* shapeAdapterFactory.registerSubtype(Rectangle.class, "Rectangle");
* shapeAdapterFactory.registerSubtype(Circle.class, "Circle");
* shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond");
* }</pre>
* Finally, register the type adapter factory in your application's GSON builder:
* <pre> {@code
* Gson gson = new GsonBuilder()
* .registerTypeAdapterFactory(shapeAdapterFactory)
* .create();
* }</pre>
* Like {@code GsonBuilder}, this API supports chaining: <pre> {@code
* cmpt213.assignment2.packagedeliveriestracker.gson.extras.RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory = cmpt213.assignment2.packagedeliveriestracker.gson.extras.RuntimeTypeAdapterFactory.of(Shape.class)
* .registerSubtype(Rectangle.class)
* .registerSubtype(Circle.class)
* .registerSubtype(Diamond.class);
* }</pre>
*
* <h3>Serialization and deserialization</h3>
* In order to serialize and deserialize a polymorphic object,
* you must specify the base type explicitly.
* <pre> {@code
* Diamond diamond = new Diamond();
* String json = gson.toJson(diamond, Shape.class);
* }</pre>
* And then:
* <pre> {@code
* Shape shape = gson.fromJson(json, Shape.class);
* }</pre>
*/
public final class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
private final Class<?> baseType;
private final String typeFieldName;
private final Map<String, Class<?>> labelToSubtype = new LinkedHashMap<>();
private final Map<Class<?>, String> subtypeToLabel = new LinkedHashMap<>();
private final boolean maintainType;
private RuntimeTypeAdapterFactory(Class<?> baseType, String typeFieldName, boolean maintainType) {
if (typeFieldName == null || baseType == null) {
throw new NullPointerException();
}
this.baseType = baseType;
this.typeFieldName = typeFieldName;
this.maintainType = maintainType;
}
/**
* Creates a new runtime type adapter using for {@code baseType} using {@code
* typeFieldName} as the type field name. Type field names are case sensitive.
* {@code maintainType} flag decide if the type will be stored in pojo or not.
*/
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName, boolean maintainType) {
return new RuntimeTypeAdapterFactory<>(baseType, typeFieldName, maintainType);
}
/**
* Creates a new runtime type adapter using for {@code baseType} using {@code
* typeFieldName} as the type field name. Type field names are case sensitive.
*/
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName) {
return new RuntimeTypeAdapterFactory<>(baseType, typeFieldName, false);
}
/**
* Creates a new runtime type adapter for {@code baseType} using {@code "type"} as
* the type field name.
*/
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType) {
return new RuntimeTypeAdapterFactory<>(baseType, "type", false);
}
/**
* Registers {@code type} identified by {@code label}. Labels are case
* sensitive.
*
* @throws IllegalArgumentException if either {@code type} or {@code label}
* have already been registered on this type adapter.
*/
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type, String label) {
if (type == null || label == null) {
throw new NullPointerException();
}
if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) {
throw new IllegalArgumentException("types and labels must be unique");
}
labelToSubtype.put(label, type);
subtypeToLabel.put(type, label);
return this;
}
/**
* Registers {@code type} identified by its {@link Class#getSimpleName simple
* name}. Labels are case sensitive.
*
* @throws IllegalArgumentException if either {@code type} or its simple name
* have already been registered on this type adapter.
*/
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) {
return registerSubtype(type, type.getSimpleName());
}
@Override
public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) {
if (type.getRawType() != baseType) {
return null;
}
final TypeAdapter<JsonElement> jsonElementAdapter = gson.getAdapter(JsonElement.class);
final Map<String, TypeAdapter<?>> labelToDelegate = new LinkedHashMap<>();
final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate = new LinkedHashMap<>();
for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
labelToDelegate.put(entry.getKey(), delegate);
subtypeToDelegate.put(entry.getValue(), delegate);
}
return new TypeAdapter<R>() {
@Override
public R read(JsonReader in) throws IOException {
JsonElement jsonElement = jsonElementAdapter.read(in);
JsonElement labelJsonElement;
if (maintainType) {
labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName);
} else {
labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);
}
if (labelJsonElement == null) {
throw new JsonParseException("cannot deserialize " + baseType
+ " because it does not define a field named " + typeFieldName);
}
String label = labelJsonElement.getAsString();
@SuppressWarnings("unchecked") // registration requires that subtype extends T
TypeAdapter<R> delegate = (TypeAdapter<R>) labelToDelegate.get(label);
if (delegate == null) {
throw new JsonParseException("cannot deserialize " + baseType + " subtype named "
+ label + "; did you forget to register a subtype?");
}
return delegate.fromJsonTree(jsonElement);
}
@Override
public void write(JsonWriter out, R value) throws IOException {
Class<?> srcType = value.getClass();
String label = subtypeToLabel.get(srcType);
@SuppressWarnings("unchecked") // registration requires that subtype extends T
TypeAdapter<R> delegate = (TypeAdapter<R>) subtypeToDelegate.get(srcType);
if (delegate == null) {
throw new JsonParseException("cannot serialize " + srcType.getName()
+ "; did you forget to register a subtype?");
}
JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
if (maintainType) {
jsonElementAdapter.write(out, jsonObject);
return;
}
JsonObject clone = new JsonObject();
if (jsonObject.has(typeFieldName)) {
throw new JsonParseException("cannot serialize " + srcType.getName()
+ " because it already defines a field named " + typeFieldName);
}
clone.add(typeFieldName, new JsonPrimitive(label));
for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) {
clone.add(e.getKey(), e.getValue());
}
jsonElementAdapter.write(out, clone);
}
}.nullSafe();
}
}

@ -0,0 +1,23 @@
package cmpt213.assignment4.packagedeliveries.webappserver.model;
import java.time.LocalDateTime;
/**
* It's a subclass of PackageInfo that adds an author field
*/
public class BookPackage extends PackageInfo {
String author;
public BookPackage(String name, String note, double price, double weight, boolean delivered, LocalDateTime expectedDate, String author) {
super(name, note, price, weight, delivered, expectedDate);
this.author = author;
this.setType("book");
}
@Override
public String toString() {
return super.toString() + "\nAuthor: " + author;
}
}

@ -0,0 +1,21 @@
package cmpt213.assignment4.packagedeliveries.webappserver.model;
import java.time.LocalDateTime;
/**
* ElectronicPackage is a subclass of PackageInfo that adds a handlingFee attribute
*/
public class ElectronicPackage extends PackageInfo {
double handlingFee;
public ElectronicPackage(String name, String note, double price, double weight, boolean delivered, LocalDateTime expectedDate, double handlingFee) {
super(name, note, price, weight, delivered, expectedDate);
this.handlingFee = handlingFee;
this.setType("electronic");
}
@Override
public String toString() {
return super.toString() + "\nHandling fee: " + handlingFee;
}
}

@ -0,0 +1,85 @@
package cmpt213.assignment4.packagedeliveries.webappserver.model;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import static java.lang.Math.abs;
/**
* PackageInfo is a class that contains information about a package
*/
public class PackageInfo implements Comparable<PackageInfo> {
private final String name;
private final String note;
private final double price;
private final double weight;
private final LocalDateTime expectedDate;
private boolean delivered;
private String type;
public PackageInfo(String name, String note, double price, double weight, boolean delivered, LocalDateTime expectedDate) {
this.name = name;
this.note = note;
this.price = price;
this.weight = weight;
this.delivered = delivered;
this.expectedDate = expectedDate;
}
/**
* This function returns the name of the person.
*
* @return The name of the person.
*/
public String getName() {
return name;
}
public boolean getDelivered() {
return delivered;
}
/**
* This function sets the value of the delivered variable to the value of the delivered parameter.
*
* @param delivered This is a boolean value that indicates whether the message has been delivered
* to the recipient.
*/
public void setDelivered(boolean delivered) {
this.delivered = delivered;
}
public LocalDateTime getExpectedDate() {
return expectedDate;
}
/**
* This function sets the type of the object to the type passed in as a parameter
*
* @param type The type of the event.
*/
public void setType(String type) {
this.type = type;
}
// Comparing the expected date of the package to the expected date of the package passed in as a
// parameter.
@Override
public int compareTo(PackageInfo p) {
return this.expectedDate.compareTo(p.getExpectedDate());
}
// Overriding the toString method.
@Override
public String toString() {
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyy-MM-dd HH:mm");
LocalDateTime today = LocalDateTime.now();
today.format(format);
long diff = ChronoUnit.DAYS.between(today, expectedDate);
String isDelivered = delivered ? "yes" : "no";
return "Name: " + name + "\n" + "Notes: " + note + "\n" + "Price: $" + price + "\n" + "Weight: " + weight + " kg" + "\n" + "Expected Delivery Date: " + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(expectedDate) + "\n" + "Delivered? " + isDelivered + "\n" + ((diff > 0 && !delivered) ? diff + " days remaining" : abs(diff) + " days overdue");
}
}

@ -0,0 +1,25 @@
package cmpt213.assignment4.packagedeliveries.webappserver.model;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* It's a subclass of PackageInfo that adds an expiry date
*/
public class PerishablePackage extends PackageInfo {
LocalDateTime expiryDate;
// It's a constructor that initializes the object with the given parameters.
public PerishablePackage(String name, String note, double price, double weight, boolean delivered, LocalDateTime expectedDate, LocalDateTime expiryDate) {
super(name, note, price, weight, delivered, expectedDate);
this.expiryDate = expiryDate;
this.setType("perishable");
}
// It's a method that returns a string representation of the object.
@Override
public String toString() {
return super.toString() + "\nExpiry date: " + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(expiryDate);
}
}

@ -0,0 +1,13 @@
package cmpt213.assignment4.packagedeliveries.webappserver;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class WebAppServerApplicationTests {
@Test
void contextLoads() {
}
}
Loading…
Cancel
Save