diff --git a/resources/validator.xsd b/resources/validator.xsd
new file mode 100644
index 0000000..54b1a64
--- /dev/null
+++ b/resources/validator.xsd
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/containers/Backpack.java b/src/containers/Backpack.java
new file mode 100644
index 0000000..54527cd
--- /dev/null
+++ b/src/containers/Backpack.java
@@ -0,0 +1,68 @@
+package containers;
+
+import data.Shape;
+import exception.BackpackOverflow;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+
+public class Backpack implements Iterable {
+ private ArrayList elements;
+ private final int n;
+
+ public Backpack(int n) {
+ this.n = n;
+ try {
+ elements = new ArrayList<>();
+ } catch (NegativeArraySizeException e) {
+ System.out.println("NegativeArraySize");
+ }
+ }
+
+ public int add(T element) {
+ if (elements.size() == n) {
+ throw new BackpackOverflow();
+ } else {
+ elements.add(element);
+ sort();
+ return elements.indexOf(element);
+ }
+ }
+
+ public void remove(int i) {
+ if (elements.size() != 0) {
+ elements.remove(i);
+ }
+ }
+
+ public int size() {
+ return elements.size();
+ }
+
+ public void sort() {
+ elements.sort((a, b) -> (int) (b.getVolume() - a.getVolume()));
+ }
+
+ public void createFromArrayList(ArrayList arrayList) {
+ elements = arrayList;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return elements.iterator();
+ }
+
+ @Override
+ public void forEach(Consumer super T> consumer) {
+ for (T element : elements) {
+ consumer.accept(element);
+ }
+ }
+
+ @Override
+ public Spliterator spliterator() {
+ return null;
+ }
+}
diff --git a/src/controller/Controller.java b/src/controller/Controller.java
new file mode 100644
index 0000000..9e0c9ac
--- /dev/null
+++ b/src/controller/Controller.java
@@ -0,0 +1,115 @@
+package controller;
+
+import containers.Backpack;
+import data.Ball;
+import data.Cube;
+import data.Shape;
+import data.Tetrahedron;
+import gui.GUI;
+import org.w3c.dom.*;
+import org.xml.sax.SAXException;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class Controller {
+ private final int BACKPACK_CAPACITY = 100;
+ private final Backpack backpack = new Backpack<>(BACKPACK_CAPACITY);
+ private final File xsdFile;
+
+ public Controller() {
+ GUI app = new GUI(this);
+ app.setVisible(true);
+ xsdFile = new File("resources/validator.xsd");
+ }
+
+ public String backpackStat() {
+ return "Backpack Size: " + backpack.size() + ", Backpack Capacity: " + BACKPACK_CAPACITY;
+ }
+
+ public int addElementToBackpack(Shape new_element) {
+ return backpack.add(new_element);
+ }
+
+ public void remove(int i) {
+ backpack.remove(i);
+ }
+
+ public void saveToXML(File file) throws TransformerException, FileNotFoundException, ParserConfigurationException {
+ Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+
+ Element root = document.createElement("Shapes");
+ for (Shape shape : backpack) {
+ Element xml_shape = document.createElement("Shape");
+ xml_shape.setAttribute("name", shape.getType());
+ xml_shape.setAttribute("edge", String.valueOf(shape.getEdge()));
+ root.appendChild(xml_shape);
+ }
+ document.appendChild(root);
+
+ Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ DOMSource source = new DOMSource(document);
+ StreamResult result = new StreamResult(new FileOutputStream(file.getAbsolutePath()));
+ transformer.transform(source, result);
+ }
+
+ public void validateXMLSchema(File xmlFile) throws SAXException, IOException {
+ SchemaFactory factory =
+ SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ Schema schema = factory.newSchema(xsdFile);
+ Validator validator = schema.newValidator();
+ validator.validate(new StreamSource(xmlFile));
+ }
+
+ public ArrayList loadFromXML(File file) throws IOException, SAXException, ParserConfigurationException {
+ validateXMLSchema(file);
+
+ Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file);
+ NodeList nodeList = document.getElementsByTagName("Shape");
+ ArrayList arrayList = new ArrayList<>();
+
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node node = nodeList.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ NamedNodeMap nodeMap = node.getAttributes();
+ String shapeType = nodeMap.getNamedItem("name").getNodeValue();
+ double edge = Double.parseDouble(nodeMap.getNamedItem("edge").getNodeValue());
+
+ Shape shape;
+ switch (shapeType) {
+ case "Ball":
+ shape = new Ball(edge);
+ break;
+ case "Cube":
+ shape = new Cube(edge);
+ break;
+ case "Tetrahedron":
+ shape = new Tetrahedron(edge);
+ break;
+ default:
+ throw new IllegalStateException("Unexpected value: " + shapeType);
+ }
+ arrayList.add(shape);
+ }
+ }
+
+ backpack.createFromArrayList(arrayList);
+ return arrayList;
+ }
+}
+
diff --git a/src/data/Ball.java b/src/data/Ball.java
new file mode 100644
index 0000000..e9c7b06
--- /dev/null
+++ b/src/data/Ball.java
@@ -0,0 +1,29 @@
+package data;
+
+public class Ball extends Shape {
+ double radius;
+
+ public Ball(double radius) {
+ this.radius = radius;
+ }
+
+ @Override
+ public double getVolume() {
+ return 4.0 / 3 * Math.PI * Math.pow(radius, 3);
+ }
+
+ @Override
+ public String toString() {
+ return "Ball with radius " + radius + " and Volume " + RoundAvoid(getVolume(), 4);
+ }
+
+ @Override
+ public String getType() {
+ return "Ball";
+ }
+
+ @Override
+ public double getEdge() {
+ return radius;
+ }
+}
diff --git a/src/data/Cube.java b/src/data/Cube.java
new file mode 100644
index 0000000..516a081
--- /dev/null
+++ b/src/data/Cube.java
@@ -0,0 +1,29 @@
+package data;
+
+public class Cube extends Shape {
+ double edge;
+
+ public Cube(double edge) {
+ this.edge = edge;
+ }
+
+ @Override
+ public double getVolume() {
+ return Math.pow(edge, 3);
+ }
+
+ @Override
+ public String toString() {
+ return "Cube with edge " + edge + " and Volume " + RoundAvoid(getVolume(), 4);
+ }
+
+ @Override
+ public String getType() {
+ return "Cube";
+ }
+
+ @Override
+ public double getEdge() {
+ return edge;
+ }
+}
diff --git a/src/data/Shape.java b/src/data/Shape.java
new file mode 100644
index 0000000..efb54d6
--- /dev/null
+++ b/src/data/Shape.java
@@ -0,0 +1,13 @@
+package data;
+
+public abstract class Shape {
+ public abstract double getEdge();
+ public abstract double getVolume();
+ public abstract String getType();
+
+ protected double RoundAvoid(double value, int places) {
+ double scale = Math.pow(10, places);
+ return Math.round(value * scale) / scale;
+ }
+}
+
diff --git a/src/data/Tetrahedron.java b/src/data/Tetrahedron.java
new file mode 100644
index 0000000..729e1b6
--- /dev/null
+++ b/src/data/Tetrahedron.java
@@ -0,0 +1,29 @@
+package data;
+
+public class Tetrahedron extends Shape {
+ double edge;
+
+ public Tetrahedron(double edge) {
+ this.edge = edge;
+ }
+
+ @Override
+ public double getVolume() {
+ return Math.pow(edge, 3) / (6 * Math.sqrt(2));
+ }
+
+ @Override
+ public String toString() {
+ return "Tetrahedron with edge " + edge + " and Volume " + RoundAvoid(getVolume(), 4);
+ }
+
+ @Override
+ public String getType() {
+ return "Tetrahedron";
+ }
+
+ @Override
+ public double getEdge() {
+ return edge;
+ }
+}
diff --git a/src/exception/BackpackOverflow.java b/src/exception/BackpackOverflow.java
new file mode 100644
index 0000000..0c5af45
--- /dev/null
+++ b/src/exception/BackpackOverflow.java
@@ -0,0 +1,7 @@
+package exception;
+
+public class BackpackOverflow extends RuntimeException {
+ public BackpackOverflow() {
+ super("Can`t add anymore to backpack");
+ }
+}
diff --git a/src/gui/GUI.java b/src/gui/GUI.java
new file mode 100644
index 0000000..3864ca0
--- /dev/null
+++ b/src/gui/GUI.java
@@ -0,0 +1,224 @@
+package gui;
+
+import controller.Controller;
+import data.Ball;
+import data.Cube;
+import data.Shape;
+import data.Tetrahedron;
+import org.xml.sax.SAXException;
+
+import javax.swing.*;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerException;
+import java.awt.*;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.regex.Pattern;
+
+public class GUI extends JFrame {
+ private final DefaultListModel listModel = new DefaultListModel<>();
+ private final JList list = new JList<>(listModel);
+
+ private final JButton addCube = new JButton();
+ private final JButton addBall = new JButton();
+ private final JButton addTetrahedron = new JButton();
+ private final JButton deleteComponent = new JButton();
+
+ private final JLabel backpackStat = new JLabel();
+
+ private final Controller controller;
+ private final String[] statuses = new String[]{"edge of cube", "ball radius", "edge of tetrahedron"};
+
+ public GUI(Controller controller) {
+ this.controller = controller;
+ JScrollPane scrollPane = new JScrollPane();
+ scrollPane.add(list);
+
+ setMinimumSize(new Dimension(800, 800));
+ setTitle("Backpack v0.1");
+ setLocationRelativeTo(null);
+ setDefaultCloseOperation(EXIT_ON_CLOSE);
+
+ createLayout(getContentPane());
+ setElementsInfo();
+ setJMenuBar(createMenuBar());
+ }
+
+ private void createLayout(Container pane) {
+ BorderLayout layout = new BorderLayout();
+ pane.setLayout(layout);
+ pane.add(list, BorderLayout.CENTER);
+ pane.add(backpackStat, BorderLayout.SOUTH);
+ JPanel panel = new JPanel();
+ panel.setLayout(new GridLayout(0, 1));
+ panel.add(addCube);
+ panel.add(addBall);
+ panel.add(addTetrahedron);
+ panel.add(deleteComponent);
+ pane.add(panel, BorderLayout.EAST);
+ }
+
+ private final Pattern NUM_EX = Pattern.compile("-?\\d+(\\.\\d+)?");
+
+ private boolean isNumeric(String strNum) {
+ return NUM_EX.matcher(strNum).matches();
+ }
+
+ private void setElementsInfo() {
+ addCube.setText("Add cube");
+ addCube.addActionListener(actionEvent -> displayInputDialog(0));
+ addBall.setText("Add ball");
+ addBall.addActionListener(actionEvent -> displayInputDialog(1));
+ addTetrahedron.setText("Add tetrahedron");
+ addTetrahedron.addActionListener(actionEvent -> displayInputDialog(2));
+
+ deleteComponent.setText("Remove selected shape from Backpack");
+ deleteComponent.addActionListener(actionEvent -> {
+ if (!list.isSelectionEmpty()) {
+ int index = list.getSelectedIndex();
+ listModel.remove(index);
+ controller.remove(index);
+ backpackStat.setText(controller.backpackStat());
+ } else {
+ JOptionPane.showMessageDialog(GUI.this,
+ "Select shape to delete first",
+ "Error",
+ JOptionPane.ERROR_MESSAGE);
+ }
+ });
+
+ list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
+ list.setVisibleRowCount(-1);
+
+ backpackStat.setText(controller.backpackStat());
+ }
+
+ private void displayInputDialog(int type) {
+ String message = JOptionPane.showInputDialog(
+ GUI.this,
+ "Enter " + statuses[type] + " (non-negative number)");
+ if (message != null) {
+ if (isNumeric(message) && Integer.parseInt(message) >= 0) {
+ switch (type) {
+ case 0: {
+ addToBackpack(new Cube(Integer.parseInt(message)));
+ break;
+ }
+ case 1: {
+ addToBackpack(new Ball(Integer.parseInt(message)));
+ break;
+ }
+ case 2: {
+ addToBackpack(new Tetrahedron(Integer.parseInt(message)));
+ break;
+ }
+ }
+ } else {
+ inputError();
+ }
+ }
+ }
+
+ private void addToBackpack(Shape shape) {
+ int index = controller.addElementToBackpack(shape);
+ listModel.add(index, shape);
+ backpackStat.setText(controller.backpackStat());
+ }
+
+ private void inputError() {
+ JOptionPane.showMessageDialog(GUI.this,
+ "Seems, you enter something that is not a non-negative number :)",
+ "Error",
+ JOptionPane.ERROR_MESSAGE);
+ }
+
+ private JMenuBar createMenuBar() {
+ JMenuBar menuBar = new JMenuBar();
+ menuBar.add(createFileMenu());
+ return menuBar;
+ }
+
+ private JMenu createFileMenu() {
+ JMenu menu = new JMenu("File");
+ JMenuItem load = new JMenuItem("Load");
+ JMenuItem save = new JMenuItem("Save");
+ menu.add(load);
+ menu.add(save);
+ load.addActionListener(actionEvent -> loadFile());
+ save.addActionListener(actionEvent -> saveFile());
+ return menu;
+ }
+
+ private void loadFile() {
+ JFileChooser fileChooser = new JFileChooser();
+ fileChooser.setDialogTitle("Load table to xml file");
+ FileNameExtensionFilter xmlFilter = new FileNameExtensionFilter("xml files (*.xml)", "xml");
+ fileChooser.addChoosableFileFilter(xmlFilter);
+ fileChooser.setFileFilter(xmlFilter);
+
+ int userSelection = fileChooser.showOpenDialog(this);
+ if (userSelection == JFileChooser.APPROVE_OPTION) {
+ ArrayList newListModel;
+ try {
+ newListModel = controller.loadFromXML(fileChooser.getSelectedFile());
+ } catch (IOException | SAXException | ParserConfigurationException e) {
+ JOptionPane.showMessageDialog(GUI.this,
+ "Can`t read XML file (It can be corrupted or \"Backpack v0.1\" " +
+ "have insufficient access rights)",
+ "Error",
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ listModel.clear();
+ for (Shape shape : newListModel) {
+ listModel.addElement(shape);
+ }
+
+ backpackStat.setText(controller.backpackStat());
+ }
+ }
+
+ private void saveFile() {
+ JFileChooser fileChooser = new JFileChooser();
+ fileChooser.setDialogTitle("Save table to xml file");
+ FileNameExtensionFilter xmlFilter = new FileNameExtensionFilter("xml files (*.xml)", "xml");
+ fileChooser.addChoosableFileFilter(xmlFilter);
+ fileChooser.setFileFilter(xmlFilter);
+
+ int userSelection = fileChooser.showSaveDialog(this);
+ if (userSelection == JFileChooser.APPROVE_OPTION) {
+ String s = fileChooser.getSelectedFile().getAbsolutePath();
+ if (s.length() <= 4 || !s.endsWith(".xml")) {
+ s += ".xml";
+ }
+ File file = new File(s);
+
+ try {
+ if (!file.createNewFile()) {
+ PrintWriter writer = new PrintWriter(file);
+ writer.print("");
+ writer.close();
+ }
+ } catch (IOException e) {
+ JOptionPane.showMessageDialog(GUI.this,
+ "Can`t create new XML file",
+ "Error",
+ JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ try {
+ controller.saveToXML(file);
+ } catch (TransformerException | ParserConfigurationException | FileNotFoundException e) {
+ JOptionPane.showMessageDialog(GUI.this,
+ "Can`t write data to this XML file",
+ "Error",
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+}
diff --git a/src/main/Main.java b/src/main/Main.java
new file mode 100644
index 0000000..459103d
--- /dev/null
+++ b/src/main/Main.java
@@ -0,0 +1,9 @@
+package main;
+
+import controller.Controller;
+
+public class Main {
+ public static void main(String[] args) {
+ Controller controller = new Controller();
+ }
+}