From b15a0efe23735393d7f0aefe2048580d43de56d3 Mon Sep 17 00:00:00 2001 From: Stas Date: Thu, 8 Oct 2020 20:59:07 +0300 Subject: [PATCH 1/4] Current project --- src/containers/Backpack.java | 44 ++++++++++ src/controller/Controller.java | 27 ++++++ src/data/Ball.java | 19 +++++ src/data/Cube.java | 19 +++++ src/data/Shape.java | 11 +++ src/data/Tetrahedron.java | 19 +++++ src/exception/BackpackOverflow.java | 7 ++ src/gui/GUI.java | 126 ++++++++++++++++++++++++++++ src/main/Main.java | 9 ++ 9 files changed, 281 insertions(+) create mode 100644 src/containers/Backpack.java create mode 100644 src/controller/Controller.java create mode 100644 src/data/Ball.java create mode 100644 src/data/Cube.java create mode 100644 src/data/Shape.java create mode 100644 src/data/Tetrahedron.java create mode 100644 src/exception/BackpackOverflow.java create mode 100644 src/gui/GUI.java create mode 100644 src/main/Main.java diff --git a/src/containers/Backpack.java b/src/containers/Backpack.java new file mode 100644 index 0000000..2f8bdf3 --- /dev/null +++ b/src/containers/Backpack.java @@ -0,0 +1,44 @@ +package containers; + +import data.Shape; +import exception.BackpackOverflow; + +import java.util.ArrayList; + +public class Backpack { + 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())); + } +} diff --git a/src/controller/Controller.java b/src/controller/Controller.java new file mode 100644 index 0000000..ec9e382 --- /dev/null +++ b/src/controller/Controller.java @@ -0,0 +1,27 @@ +package controller; + +import containers.Backpack; +import data.Shape; +import gui.GUI; + +public class Controller { + private final int BACKPACK_CAPACITY = 100; + private final Backpack backpack = new Backpack<>(BACKPACK_CAPACITY); + + public Controller() { + GUI app = new GUI(this); + app.setVisible(true); + } + + 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); + } +} diff --git a/src/data/Ball.java b/src/data/Ball.java new file mode 100644 index 0000000..51799ed --- /dev/null +++ b/src/data/Ball.java @@ -0,0 +1,19 @@ +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); + } +} diff --git a/src/data/Cube.java b/src/data/Cube.java new file mode 100644 index 0000000..307e7cf --- /dev/null +++ b/src/data/Cube.java @@ -0,0 +1,19 @@ +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); + } +} diff --git a/src/data/Shape.java b/src/data/Shape.java new file mode 100644 index 0000000..c152a62 --- /dev/null +++ b/src/data/Shape.java @@ -0,0 +1,11 @@ +package data; + +public abstract class Shape { + public abstract double getVolume(); + + 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..4021ff0 --- /dev/null +++ b/src/data/Tetrahedron.java @@ -0,0 +1,19 @@ +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); + } +} 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..050be01 --- /dev/null +++ b/src/gui/GUI.java @@ -0,0 +1,126 @@ +package gui; + +import controller.Controller; +import data.Ball; +import data.Cube; +import data.Shape; +import data.Tetrahedron; + +import javax.swing.*; +import java.awt.*; +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(); + } + + 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); + } else { + JOptionPane.showMessageDialog(GUI.this, + "Select shape to delete first", + "Error", + JOptionPane.ERROR_MESSAGE); + } + }); + + list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_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 && 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); + } +} 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(); + } +} From 2976cfe5ee4cf65d71f86acd8f94a88b51311e35 Mon Sep 17 00:00:00 2001 From: Stas Date: Thu, 8 Oct 2020 21:18:52 +0300 Subject: [PATCH 2/4] Quick fix --- src/gui/GUI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/GUI.java b/src/gui/GUI.java index 050be01..2d7cd19 100644 --- a/src/gui/GUI.java +++ b/src/gui/GUI.java @@ -80,7 +80,7 @@ private void setElementsInfo() { } }); - list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.setLayoutOrientation(JList.HORIZONTAL_WRAP); list.setVisibleRowCount(-1); From 5c0b45f8c65b42f79567b4cb50da76ee027585b4 Mon Sep 17 00:00:00 2001 From: Stas Date: Thu, 8 Oct 2020 21:27:53 +0300 Subject: [PATCH 3/4] Quick fix 2 --- src/gui/GUI.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/GUI.java b/src/gui/GUI.java index 2d7cd19..a39934e 100644 --- a/src/gui/GUI.java +++ b/src/gui/GUI.java @@ -72,6 +72,7 @@ private void setElementsInfo() { int index = list.getSelectedIndex(); listModel.remove(index); controller.remove(index); + backpackStat.setText(controller.backpackStat()); } else { JOptionPane.showMessageDialog(GUI.this, "Select shape to delete first", From f022b287363bb6c7b3c6f6b356f900bc21919f58 Mon Sep 17 00:00:00 2001 From: Stas Date: Mon, 19 Oct 2020 21:04:24 +0300 Subject: [PATCH 4/4] Add loading and saving to xml format --- resources/validator.xsd | 18 +++++ src/containers/Backpack.java | 28 +++++++- src/controller/Controller.java | 88 +++++++++++++++++++++++ src/data/Ball.java | 10 +++ src/data/Cube.java | 10 +++ src/data/Shape.java | 2 + src/data/Tetrahedron.java | 10 +++ src/gui/GUI.java | 127 +++++++++++++++++++++++++++++---- 8 files changed, 276 insertions(+), 17 deletions(-) create mode 100644 resources/validator.xsd 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 index 2f8bdf3..54527cd 100644 --- a/src/containers/Backpack.java +++ b/src/containers/Backpack.java @@ -4,8 +4,11 @@ import exception.BackpackOverflow; import java.util.ArrayList; +import java.util.Iterator; +import java.util.Spliterator; +import java.util.function.Consumer; -public class Backpack { +public class Backpack implements Iterable { private ArrayList elements; private final int n; @@ -39,6 +42,27 @@ public int size() { } public void sort() { - elements.sort((a, b)-> (int)(b.getVolume() - a.getVolume())); + 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 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 index ec9e382..9e0c9ac 100644 --- a/src/controller/Controller.java +++ b/src/controller/Controller.java @@ -1,16 +1,41 @@ 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() { @@ -24,4 +49,67 @@ public int addElementToBackpack(Shape 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 index 51799ed..e9c7b06 100644 --- a/src/data/Ball.java +++ b/src/data/Ball.java @@ -16,4 +16,14 @@ public double getVolume() { 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 index 307e7cf..516a081 100644 --- a/src/data/Cube.java +++ b/src/data/Cube.java @@ -16,4 +16,14 @@ public double getVolume() { 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 index c152a62..efb54d6 100644 --- a/src/data/Shape.java +++ b/src/data/Shape.java @@ -1,7 +1,9 @@ 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); diff --git a/src/data/Tetrahedron.java b/src/data/Tetrahedron.java index 4021ff0..729e1b6 100644 --- a/src/data/Tetrahedron.java +++ b/src/data/Tetrahedron.java @@ -16,4 +16,14 @@ public double getVolume() { 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/gui/GUI.java b/src/gui/GUI.java index a39934e..3864ca0 100644 --- a/src/gui/GUI.java +++ b/src/gui/GUI.java @@ -5,9 +5,18 @@ 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 { @@ -36,6 +45,7 @@ public GUI(Controller controller) { createLayout(getContentPane()); setElementsInfo(); + setJMenuBar(createMenuBar()); } private void createLayout(Container pane) { @@ -92,23 +102,25 @@ private void displayInputDialog(int type) { String message = JOptionPane.showInputDialog( GUI.this, "

Enter " + statuses[type] + " (non-negative number)"); - if (message != null && 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; + 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(); } - } else { - inputError(); } } @@ -124,4 +136,89 @@ private void inputError() { "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); + } + } + } }