diff --git a/app/src/main/java/xl/controller/EditorController.java b/app/src/main/java/xl/controller/EditorController.java new file mode 100644 index 0000000..7b4086e --- /dev/null +++ b/app/src/main/java/xl/controller/EditorController.java @@ -0,0 +1,59 @@ +package xl.controller; + +import xl.model.XLModel; +import xl.gui.Editor; +import xl.gui.SlotLabel; +import xl.gui.StatusLabel; + +/** + * The controller for the editor. + * + * The controller is responsible for handling the editor actions. + */ +public class EditorController { + private XLModel xlModel; + private Editor editor; + private SlotLabel currentSlot; + private StatusLabel statusLabel; + + public EditorController(XLModel xlModel, StatusLabel statusLabel) { + this.xlModel = xlModel; + this.statusLabel = statusLabel; + } + + public void setCurrentSlot(SlotLabel currentSlot) { + this.currentSlot = currentSlot; + } + + public void setEditor(Editor editor) { + this.editor = editor; + } + + public void handleEditorAction() { + String content = editor.getText(); + String address = currentSlot.getAddress(); + + if (content.isEmpty()) { + boolean isOkRemove = xlModel.remove(address); + if (isOkRemove) { + currentSlot.setText(""); + statusLabel.setText(""); + } else { + statusLabel.setText(xlModel.getStatus()); + } + } else if (content.charAt(0) == '#') { + xlModel.add(address, content); + currentSlot.setText(content.substring(1)); + statusLabel.setText(""); + } else { + xlModel.add(address, content); + if (xlModel.getStatus().equals("")) { + content = xlModel.getAddressValue(address); + currentSlot.setText(content); + } else { + statusLabel.setText(xlModel.getStatus()); + } + + } + } +} diff --git a/app/src/main/java/xl/controller/SlotLabelController.java b/app/src/main/java/xl/controller/SlotLabelController.java new file mode 100644 index 0000000..c555c65 --- /dev/null +++ b/app/src/main/java/xl/controller/SlotLabelController.java @@ -0,0 +1,60 @@ +package xl.controller; +import java.awt.Color; +import xl.model.XLModel; +import xl.gui.*; + +/** + * The controller for the slot labels. + * + * The controller is responsible for handling the slot label actions. + */ +public class SlotLabelController { + private XLModel xlModel; + private Editor editor; + private SlotLabel currentSlot; + private EditorController editorController; + private StatusLabel statusLabel; + private CurrentLabel currentLabel; + + public SlotLabelController(XLModel xlModel, Editor editor, StatusLabel statusLabel) { + this.xlModel = xlModel; + this.editor = editor; + this.statusLabel = statusLabel; + } + + public void setCurrentLabel(CurrentLabel currentLabel) { + this.currentLabel = currentLabel; + } + + public void setCurrentSlot(SlotLabel currentSlot) { + this.currentSlot = currentSlot; + } + + public void setEditorController(EditorController editorController) { + this.editorController = editorController; + } + + public void handleSlotClick(SlotLabel slotLabel) { + // Retrieve the address of the clicked slot + String address = slotLabel.getAddress(); + currentLabel.setText(address); + xlModel.setCurrentAddress(address); + + // Clear the status label + statusLabel.setText(""); + + // Reset the background color of the previous slot + currentSlot.setBackground(Color.WHITE); + + // Set the background color of the clicked slot + slotLabel.setBackground(Color.YELLOW); + currentSlot = slotLabel; + editorController.setCurrentSlot(currentSlot); + + // Retrieve the content of the clicked slot + String content = xlModel.getAddressContent(address); + + // Set the content of the clicked slot in the editor + editor.setText(content); + } +} \ No newline at end of file diff --git a/app/src/main/java/xl/gui/Editor.java b/app/src/main/java/xl/gui/Editor.java index 1004b4d..fccba39 100644 --- a/app/src/main/java/xl/gui/Editor.java +++ b/app/src/main/java/xl/gui/Editor.java @@ -3,9 +3,21 @@ package xl.gui; import java.awt.Color; import javax.swing.JTextField; -public class Editor extends JTextField { +import xl.controller.EditorController; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; - public Editor() { +public class Editor extends JTextField { + + public Editor(EditorController editorController) { setBackground(Color.WHITE); + + // Listen for changes in the text + addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent ae) { + editorController.handleEditorAction(); + } + }); } } diff --git a/app/src/main/java/xl/gui/SheetPanel.java b/app/src/main/java/xl/gui/SheetPanel.java index 74ca92d..83d54a5 100644 --- a/app/src/main/java/xl/gui/SheetPanel.java +++ b/app/src/main/java/xl/gui/SheetPanel.java @@ -3,10 +3,15 @@ package xl.gui; import static java.awt.BorderLayout.CENTER; import static java.awt.BorderLayout.WEST; +import xl.controller.EditorController; +import xl.controller.SlotLabelController; +import xl.model.XLModel; + public class SheetPanel extends BorderPanel { - public SheetPanel(int rows, int columns) { + public SheetPanel(int rows, int columns, XLModel xlModel, SlotLabelController slotLabelController, + EditorController editorController) { add(WEST, new RowLabels(rows)); - add(CENTER, new SlotLabels(rows, columns)); + add(CENTER, new SlotLabels(rows, columns, xlModel, slotLabelController, editorController)); } } diff --git a/app/src/main/java/xl/gui/SlotLabel.java b/app/src/main/java/xl/gui/SlotLabel.java index 5807b19..d93e08d 100644 --- a/app/src/main/java/xl/gui/SlotLabel.java +++ b/app/src/main/java/xl/gui/SlotLabel.java @@ -1,10 +1,45 @@ package xl.gui; import java.awt.Color; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import xl.controller.SlotLabelController; +import java.util.Observer; +import java.util.Observable; +import xl.model.XLModel; -public class SlotLabel extends ColoredLabel { +public class SlotLabel extends ColoredLabel implements Observer { + private String address; - public SlotLabel() { + public SlotLabel(SlotLabelController slotLabelController, int row, char column) { super(" ", Color.WHITE, RIGHT); + this.address = "" + column + row; + + // MouseListener for mouse clicks + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + slotLabelController.handleSlotClick(SlotLabel.this); + } + }); } -} + + public String getAddress() { + return address; + } + + public void update(Observable o, Object arg) { + if (o instanceof XLModel) { + XLModel xlModel = (XLModel) o; + if (xlModel.containsKey(address)) { + String tempCurrentAdress = xlModel.getAddressContent(address); + xlModel.setCurrentAddress(address); + String content = xlModel.getAddressValue(address); + setText(content); + xlModel.setCurrentAddress(tempCurrentAdress); + } else { + setText(""); + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/xl/gui/SlotLabels.java b/app/src/main/java/xl/gui/SlotLabels.java index e1497d8..56d6548 100644 --- a/app/src/main/java/xl/gui/SlotLabels.java +++ b/app/src/main/java/xl/gui/SlotLabels.java @@ -4,25 +4,35 @@ import java.awt.Color; import java.util.ArrayList; import java.util.List; import javax.swing.SwingConstants; +import xl.model.XLModel; +import xl.controller.EditorController; +import xl.controller.SlotLabelController; public class SlotLabels extends GridPanel { private List labelList; - public SlotLabels(int rows, int cols) { + public SlotLabels(int rows, int cols, XLModel xlModel, SlotLabelController slotLabelController, + EditorController editorController) { super(rows + 1, cols); labelList = new ArrayList(rows * cols); for (char ch = 'A'; ch < 'A' + cols; ch++) { add(new ColoredLabel(Character.toString(ch), Color.LIGHT_GRAY, SwingConstants.CENTER)); } + for (int row = 1; row <= rows; row++) { for (char ch = 'A'; ch < 'A' + cols; ch++) { - SlotLabel label = new SlotLabel(); + SlotLabel label = new SlotLabel(slotLabelController, row, ch); + xlModel.addObserver(label); add(label); labelList.add(label); } } + SlotLabel firstLabel = labelList.get(0); firstLabel.setBackground(Color.YELLOW); + xlModel.setCurrentAddress("A1"); + slotLabelController.setCurrentSlot(firstLabel); + editorController.setCurrentSlot(firstLabel); } } diff --git a/app/src/main/java/xl/gui/StatusLabel.java b/app/src/main/java/xl/gui/StatusLabel.java index 03389eb..cf92cf8 100644 --- a/app/src/main/java/xl/gui/StatusLabel.java +++ b/app/src/main/java/xl/gui/StatusLabel.java @@ -1,16 +1,10 @@ package xl.gui; import java.awt.Color; -import java.util.Observable; -import java.util.Observer; -public class StatusLabel extends ColoredLabel implements Observer { +public class StatusLabel extends ColoredLabel { public StatusLabel() { super("", Color.WHITE); } - - public void update(Observable observable, Object object) { - setText(""); - } } diff --git a/app/src/main/java/xl/gui/StatusPanel.java b/app/src/main/java/xl/gui/StatusPanel.java index a6087bf..3f1ee6e 100644 --- a/app/src/main/java/xl/gui/StatusPanel.java +++ b/app/src/main/java/xl/gui/StatusPanel.java @@ -3,10 +3,15 @@ package xl.gui; import static java.awt.BorderLayout.CENTER; import static java.awt.BorderLayout.WEST; +import xl.controller.SlotLabelController; +import xl.model.XLModel; + public class StatusPanel extends BorderPanel { - protected StatusPanel(StatusLabel statusLabel) { - add(WEST, new CurrentLabel()); + protected StatusPanel(StatusLabel statusLabel, XLModel solver, SlotLabelController slotLabelController) { + CurrentLabel currentLabel = new CurrentLabel(); + slotLabelController.setCurrentLabel(currentLabel); + add(WEST, currentLabel); add(CENTER, statusLabel); } -} +} \ No newline at end of file diff --git a/app/src/main/java/xl/gui/XL.java b/app/src/main/java/xl/gui/XL.java index d4c3e69..c2b232b 100644 --- a/app/src/main/java/xl/gui/XL.java +++ b/app/src/main/java/xl/gui/XL.java @@ -6,7 +6,11 @@ import static java.awt.BorderLayout.SOUTH; import javax.swing.JFrame; import javax.swing.JPanel; + +import xl.controller.EditorController; +import xl.controller.SlotLabelController; import xl.gui.menu.XLMenuBar; +import xl.model.XLModel; public class XL extends JFrame { @@ -15,23 +19,31 @@ public class XL extends JFrame { private StatusLabel statusLabel = new StatusLabel(); private XLList xlList; - public XL(XL oldXL) { - this(oldXL.xlList, oldXL.counter); + public XL(XL oldXL, XLModel XLModel) { + this(oldXL.xlList, oldXL.counter, XLModel); } - public XL(XLList xlList, XLCounter counter) { - super("Untitled-" + counter); + public XL(XLList xlList, XLCounter counter, XLModel XLModel) { + super("Window " + counter); this.xlList = xlList; this.counter = counter; xlList.add(this); counter.increment(); - JPanel statusPanel = new StatusPanel(statusLabel); - JPanel sheetPanel = new SheetPanel(ROWS, COLUMNS); - Editor editor = new Editor(); + + EditorController editorController = new EditorController(XLModel, statusLabel); + Editor editor = new Editor(editorController); + editorController.setEditor(editor); + + SlotLabelController slotLabelController = new SlotLabelController(XLModel, editor, statusLabel); + slotLabelController.setEditorController(editorController); + + JPanel statusPanel = new StatusPanel(statusLabel, XLModel, slotLabelController); + JPanel sheetPanel = new SheetPanel(ROWS, COLUMNS, XLModel, slotLabelController, editorController); + add(NORTH, statusPanel); add(CENTER, editor); add(SOUTH, sheetPanel); - setJMenuBar(new XLMenuBar(this, xlList, statusLabel)); + setJMenuBar(new XLMenuBar(this, xlList, statusLabel, XLModel)); pack(); setDefaultCloseOperation(EXIT_ON_CLOSE); setResizable(false); @@ -44,6 +56,6 @@ public class XL extends JFrame { } public static void main(String[] args) { - new XL(new XLList(), new XLCounter()); + new XL(new XLList(), new XLCounter(), new XLModel()); } } diff --git a/app/src/main/java/xl/gui/menu/ClearAllMenuItem.java b/app/src/main/java/xl/gui/menu/ClearAllMenuItem.java index dce2215..10d3738 100644 --- a/app/src/main/java/xl/gui/menu/ClearAllMenuItem.java +++ b/app/src/main/java/xl/gui/menu/ClearAllMenuItem.java @@ -4,14 +4,18 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JMenuItem; -class ClearAllMenuItem extends JMenuItem implements ActionListener { +import xl.model.XLModel; - public ClearAllMenuItem() { +class ClearAllMenuItem extends JMenuItem implements ActionListener { + private XLModel xlModel; + + public ClearAllMenuItem(XLModel xlModel) { super("Clear all"); + this.xlModel = xlModel; addActionListener(this); } public void actionPerformed(ActionEvent e) { - // TODO + xlModel.clearAll(); } } diff --git a/app/src/main/java/xl/gui/menu/ClearMenuItem.java b/app/src/main/java/xl/gui/menu/ClearMenuItem.java index c1954f9..0351e09 100644 --- a/app/src/main/java/xl/gui/menu/ClearMenuItem.java +++ b/app/src/main/java/xl/gui/menu/ClearMenuItem.java @@ -3,15 +3,23 @@ package xl.gui.menu; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JMenuItem; +import xl.model.XLModel; +import xl.gui.StatusLabel; class ClearMenuItem extends JMenuItem implements ActionListener { - public ClearMenuItem() { + private XLModel xlModel; + private StatusLabel statusLabel; + + public ClearMenuItem(XLModel xlModel, StatusLabel statusLabel) { super("Clear"); + this.xlModel = xlModel; + this.statusLabel = statusLabel; addActionListener(this); } public void actionPerformed(ActionEvent e) { - // TODO + xlModel.clearCell(); + statusLabel.setText(xlModel.getStatus()); } } diff --git a/app/src/main/java/xl/gui/menu/CloseMenuItem.java b/app/src/main/java/xl/gui/menu/CloseMenuItem.java index 4373217..5126b0e 100644 --- a/app/src/main/java/xl/gui/menu/CloseMenuItem.java +++ b/app/src/main/java/xl/gui/menu/CloseMenuItem.java @@ -3,6 +3,7 @@ package xl.gui.menu; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JMenuItem; + import xl.gui.XL; import xl.gui.XLList; diff --git a/app/src/main/java/xl/gui/menu/LoadMenuItem.java b/app/src/main/java/xl/gui/menu/LoadMenuItem.java index c833104..453c52f 100644 --- a/app/src/main/java/xl/gui/menu/LoadMenuItem.java +++ b/app/src/main/java/xl/gui/menu/LoadMenuItem.java @@ -2,17 +2,22 @@ package xl.gui.menu; import java.io.FileNotFoundException; import javax.swing.JFileChooser; + +import xl.model.XLModel; import xl.gui.StatusLabel; import xl.gui.XL; class LoadMenuItem extends OpenMenuItem { - public LoadMenuItem(XL xl, StatusLabel statusLabel) { + private XLModel xlModel; + + public LoadMenuItem(XL xl, StatusLabel statusLabel, XLModel xlModel) { super(xl, statusLabel, "Load"); + this.xlModel = xlModel; } protected void action(String path) throws FileNotFoundException { - // TODO + xlModel.load(path); } protected int openDialog(JFileChooser fileChooser) { diff --git a/app/src/main/java/xl/gui/menu/NewMenuItem.java b/app/src/main/java/xl/gui/menu/NewMenuItem.java index ae2aeb9..e808916 100644 --- a/app/src/main/java/xl/gui/menu/NewMenuItem.java +++ b/app/src/main/java/xl/gui/menu/NewMenuItem.java @@ -3,19 +3,23 @@ package xl.gui.menu; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JMenuItem; + +import xl.model.XLModel; import xl.gui.XL; class NewMenuItem extends JMenuItem implements ActionListener { private XL xl; + private XLModel model; public NewMenuItem(XL xl) { super("New"); this.xl = xl; + model = new XLModel(); addActionListener(this); } public void actionPerformed(ActionEvent event) { - new XL(xl); + new XL(xl, model); } } diff --git a/app/src/main/java/xl/gui/menu/OpenMenuItem.java b/app/src/main/java/xl/gui/menu/OpenMenuItem.java index 39c3f88..fca8951 100644 --- a/app/src/main/java/xl/gui/menu/OpenMenuItem.java +++ b/app/src/main/java/xl/gui/menu/OpenMenuItem.java @@ -8,6 +8,7 @@ import javax.swing.JFileChooser; import javax.swing.JMenuItem; import javax.swing.filechooser.FileFilter; import javax.swing.filechooser.FileNameExtensionFilter; + import xl.gui.StatusLabel; import xl.gui.XL; diff --git a/app/src/main/java/xl/gui/menu/SaveMenuItem.java b/app/src/main/java/xl/gui/menu/SaveMenuItem.java index 84b5807..ea10dd8 100644 --- a/app/src/main/java/xl/gui/menu/SaveMenuItem.java +++ b/app/src/main/java/xl/gui/menu/SaveMenuItem.java @@ -2,17 +2,22 @@ package xl.gui.menu; import java.io.FileNotFoundException; import javax.swing.JFileChooser; + +import xl.model.XLModel; import xl.gui.StatusLabel; import xl.gui.XL; class SaveMenuItem extends OpenMenuItem { - public SaveMenuItem(XL xl, StatusLabel statusLabel) { + private XLModel xlModel; + + public SaveMenuItem(XL xl, StatusLabel statusLabel, XLModel xlModel) { super(xl, statusLabel, "Save"); + this.xlModel = xlModel; } protected void action(String path) throws FileNotFoundException { - // TODO + xlModel.save(path); } protected int openDialog(JFileChooser fileChooser) { diff --git a/app/src/main/java/xl/gui/menu/WindowMenuItem.java b/app/src/main/java/xl/gui/menu/WindowMenuItem.java index afa6d29..a560ac1 100644 --- a/app/src/main/java/xl/gui/menu/WindowMenuItem.java +++ b/app/src/main/java/xl/gui/menu/WindowMenuItem.java @@ -3,6 +3,7 @@ package xl.gui.menu; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JMenuItem; + import xl.gui.XL; class WindowMenuItem extends JMenuItem implements ActionListener { diff --git a/app/src/main/java/xl/gui/menu/XLMenuBar.java b/app/src/main/java/xl/gui/menu/XLMenuBar.java index cbbf837..1191e19 100644 --- a/app/src/main/java/xl/gui/menu/XLMenuBar.java +++ b/app/src/main/java/xl/gui/menu/XLMenuBar.java @@ -2,21 +2,23 @@ package xl.gui.menu; import javax.swing.JMenu; import javax.swing.JMenuBar; + +import xl.model.XLModel; import xl.gui.StatusLabel; import xl.gui.XL; import xl.gui.XLList; public class XLMenuBar extends JMenuBar { - public XLMenuBar(XL xl, XLList xlList, StatusLabel statusLabel) { + public XLMenuBar(XL xl, XLList xlList, StatusLabel statusLabel, XLModel xlModel) { JMenu file = new JMenu("File"); JMenu edit = new JMenu("Edit"); - file.add(new SaveMenuItem(xl, statusLabel)); - file.add(new LoadMenuItem(xl, statusLabel)); + file.add(new SaveMenuItem(xl, statusLabel, xlModel)); + file.add(new LoadMenuItem(xl, statusLabel, xlModel)); file.add(new NewMenuItem(xl)); file.add(new CloseMenuItem(xl, xlList)); - edit.add(new ClearMenuItem()); - edit.add(new ClearAllMenuItem()); + edit.add(new ClearMenuItem(xlModel, statusLabel)); + edit.add(new ClearAllMenuItem(xlModel)); add(file); add(edit); add(new WindowMenu(xlList)); diff --git a/app/src/main/java/xl/model/BombCell.java b/app/src/main/java/xl/model/BombCell.java new file mode 100644 index 0000000..4f1782d --- /dev/null +++ b/app/src/main/java/xl/model/BombCell.java @@ -0,0 +1,22 @@ +package xl.model; + +import xl.expr.Environment; +import xl.util.XLException; + +/** + * A class representing a cell with a bomb value. + * + * This is essentially a placeholder for an invalid cell reference. + */ +public class BombCell implements Cell { + public BombCell() {} + + public double cellValue(Environment e) { + throw XLException.ERROR_RECURSIVE; + } + + public String displayValue() { + throw XLException.ERROR_RECURSIVE; + } + +} diff --git a/app/src/main/java/xl/model/Cell.java b/app/src/main/java/xl/model/Cell.java new file mode 100644 index 0000000..a65781e --- /dev/null +++ b/app/src/main/java/xl/model/Cell.java @@ -0,0 +1,11 @@ +package xl.model; + +import xl.expr.Environment; + +/** + * Interface for a cell in the spreadsheet. + */ +public interface Cell { + public double cellValue(Environment e); + public String displayValue(); +} diff --git a/app/src/main/java/xl/model/CommentCell.java b/app/src/main/java/xl/model/CommentCell.java new file mode 100644 index 0000000..b9a9550 --- /dev/null +++ b/app/src/main/java/xl/model/CommentCell.java @@ -0,0 +1,21 @@ +package xl.model; + +import xl.expr.Environment; +/** + * A class representing a cell with a comment value. + */ +public class CommentCell implements Cell { + private String commentText; + + public CommentCell(String commentText) { + this.commentText = commentText; + } + + public double cellValue(Environment e) { + return 0; + } + + public String displayValue() { + return commentText; + } +} diff --git a/app/src/main/java/xl/model/ExpressionCell.java b/app/src/main/java/xl/model/ExpressionCell.java new file mode 100644 index 0000000..b720159 --- /dev/null +++ b/app/src/main/java/xl/model/ExpressionCell.java @@ -0,0 +1,26 @@ +package xl.model; + +import xl.expr.Environment; +import xl.expr.Expr; + +/** + * A class representing a cell with an expression value. + */ +public class ExpressionCell implements Cell { + private Expr expr; + + public ExpressionCell(Expr expr) { + this.expr = expr; + } + + public double cellValue(Environment env) { + return expr.value(env); + } + + public String displayValue() { + return expr.toString(); + } + +} + + diff --git a/app/src/main/java/xl/model/XLBufferedReader.java b/app/src/main/java/xl/model/XLBufferedReader.java new file mode 100644 index 0000000..63a6750 --- /dev/null +++ b/app/src/main/java/xl/model/XLBufferedReader.java @@ -0,0 +1,41 @@ +package xl.model; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.util.Map; + +import xl.expr.Expr; +import xl.expr.ExprParser; +import xl.util.XLException; + +/** + * Specialized BufferedReader for reading XL files. + */ +public class XLBufferedReader extends BufferedReader { + + public XLBufferedReader(String name) throws FileNotFoundException { + super(new FileReader(name)); + } + + public void load(Map map) { + try { + while (ready()) { + String string = readLine(); + int i = string.indexOf('='); + String address = string.substring(0, i); + String content = string.substring(i + 1); + + if(content.startsWith("#")){ + map.put(address, new CommentCell(content.substring(1))); + } else { + ExprParser parser = new ExprParser(); + Expr expression = parser.build(content); + map.put(address, new ExpressionCell(expression)); + } + } + } catch (Exception e) { + throw new XLException(e.getMessage()); + } + } +} diff --git a/app/src/main/java/xl/model/XLModel.java b/app/src/main/java/xl/model/XLModel.java new file mode 100644 index 0000000..072b43e --- /dev/null +++ b/app/src/main/java/xl/model/XLModel.java @@ -0,0 +1,208 @@ +package xl.model; + +import java.io.IOException; +import java.util.*; + +import xl.expr.Environment; +import xl.expr.ExprParser; +import xl.util.XLException; + +public class XLModel extends Observable implements Environment { + private Map map; + private String status; + private String currentAddress; + + // The observers that are registered to be notified + private List observers = new ArrayList<>(); + + public XLModel() { + status = ""; + currentAddress = "A1"; + map = new java.util.HashMap(); + } + + public String getCurrentAddress() { + return currentAddress; + } + + public void setCurrentAddress(String currentAddress) { + this.currentAddress = currentAddress; + } + + public Optional getCell(String address) { + return Optional.ofNullable(map.get(address)); + } + + public boolean remove(String address) { + // Save the existing cell at the specified address + Cell oldCell = map.get(address); + + if (oldCell != null) { + map.remove(address); + + // If the output is invalid, restore the old cell + if (hasWrongOutputs()) { + map.put(address, oldCell); + status = "Cannot remove cell"; + return false; + } + } + + return true; + } + + public void add(String address, String text) { + try { + createCell(address, text); + } catch (XLException e) { + status = "Error creating cell"; + return; + } + + // Tell observers that the model has changed + setChanged(); + notifyObservers(); + } + + public String getStatus() { + return status; + } + + public void clearStatus() { + status = ""; + } + + public void clearCell() { + if (remove(currentAddress)) { + notifyObservers(); + } + } + + public void clearAll() { + map.clear(); + clearStatus(); + setChanged(); + notifyObservers(); + } + + public void load(String path) { + try (XLBufferedReader reader = new XLBufferedReader(path)) { + map.clear(); + reader.load(map); + status = ""; + } catch (IOException e) { + status = "Error loading file"; + } + setChanged(); + notifyObservers(); + } + + public void save(String path) { + try (XLPrintStream xlPrintStream = new XLPrintStream(path)) { + xlPrintStream.save(map.entrySet()); + } catch (IOException e) { + status = "Error saving file"; + } + } + + public String getAddressValue(String address) { + if (map.containsKey(address)) { + Cell cell = map.get(address); + if (cell instanceof CommentCell) { + return cell.displayValue(); + } + return Double.toString(cell.cellValue(this)); + } + return ""; + } + + public String getAddressContent(String address) { + if (map.containsKey(address)) { + Cell cell = map.get(address); + if (cell instanceof CommentCell) { + return "#" + map.get(address).displayValue(); + } + return map.get(address).displayValue(); + } else { + return ""; + } + } + + public boolean containsKey(String address) { + return map.containsKey(address); + } + + private void createCell(String address, String text) { + if (text.length() > 0 && text.charAt(0) == '#') { + map.put(address, new CommentCell(text.substring(1))); + } else { + ExprParser parser = new ExprParser(); + try { + Cell newCell = new ExpressionCell(parser.build(text)); + Cell oldCell = map.get(address); + BombCell bomb = new BombCell(); + map.put(address, bomb); + if (hasCircularDependancy(newCell) || hasWrongOutputs()) { + if (oldCell != null) { + map.put(address, oldCell); + } else { + map.remove(address); + } + } + } catch (IOException e) { + throw XLException.ERROR_CREATECELL; + } + } + } + + private boolean hasCircularDependancy(Cell cell) { + try { + cell.cellValue(this); + map.put(currentAddress, cell); + status = ""; + } catch (XLException e) { + status = e.getMessage(); + return true; + } catch (NullPointerException e) { + status = e.toString(); + return true; + } + return false; + } + + private boolean hasWrongOutputs() { + for (Map.Entry entry : map.entrySet()) { + try { + entry.getValue().cellValue(this); + } catch (Exception e) { + status = "cannot update"; + return true; + } + } + return false; + } + + @Override + public double value(String name) { + // Check if the cell is referencing itself + if (currentAddress.equals(name)) { + throw XLException.ERROR_RECURSIVE; + } + // Check if the cell is empty + if (map.containsKey(name)) { + return map.get(name).cellValue(this); + } else { + throw XLException.ERROR_EMPTY_REF; + } + } + + public void addObserver(Observer o) { + observers.add(o); + } + + public void notifyObservers() { + for (Observer o : observers) { + o.update(this, null); + } + } +} diff --git a/app/src/main/java/xl/model/XLPrintStream.java b/app/src/main/java/xl/model/XLPrintStream.java new file mode 100644 index 0000000..3bbe6ca --- /dev/null +++ b/app/src/main/java/xl/model/XLPrintStream.java @@ -0,0 +1,31 @@ +package xl.model; + +import java.io.FileNotFoundException; +import java.io.PrintStream; +import java.util.Map.Entry; +import java.util.Set; + +/** + * Specialized PrintStream for writing XL files. + */ +public class XLPrintStream extends PrintStream { + + public XLPrintStream(String fileName) throws FileNotFoundException { + super(fileName); + } + + public void save(Set> set) { + for (Entry entry : set) { + print(entry.getKey()); + print('='); + Cell cell = entry.getValue(); + if (cell instanceof CommentCell) { + println("#" + cell.displayValue()); + } else { + println(cell.displayValue()); + } + } + flush(); + close(); + } +} diff --git a/app/src/main/java/xl/util/XLBufferedReader.java b/app/src/main/java/xl/util/XLBufferedReader.java deleted file mode 100644 index 34ce774..0000000 --- a/app/src/main/java/xl/util/XLBufferedReader.java +++ /dev/null @@ -1,27 +0,0 @@ -package xl.util; - -import java.io.BufferedReader; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.util.Map; - -// TODO move to another package -public class XLBufferedReader extends BufferedReader { - - public XLBufferedReader(String name) throws FileNotFoundException { - super(new FileReader(name)); - } - - // TODO Change Object to something appropriate - public void load(Map map) { - try { - while (ready()) { - String string = readLine(); - int i = string.indexOf('='); - // TODO - } - } catch (Exception e) { - throw new XLException(e.getMessage()); - } - } -} diff --git a/app/src/main/java/xl/util/XLException.java b/app/src/main/java/xl/util/XLException.java index ef70535..6f0dd55 100644 --- a/app/src/main/java/xl/util/XLException.java +++ b/app/src/main/java/xl/util/XLException.java @@ -2,6 +2,10 @@ package xl.util; public class XLException extends RuntimeException { + public static final XLException ERROR_RECURSIVE = new XLException("Error: Circular Dependency"); + public static final XLException ERROR_EMPTY_REF = new XLException("Empty: Reference to empty cell"); + public static final XLException ERROR_CREATECELL = new XLException("Error: Could not create cell"); + public XLException(String message) { super(message); } diff --git a/app/src/main/java/xl/util/XLPrintStream.java b/app/src/main/java/xl/util/XLPrintStream.java deleted file mode 100644 index 8ff5525..0000000 --- a/app/src/main/java/xl/util/XLPrintStream.java +++ /dev/null @@ -1,25 +0,0 @@ -package xl.util; - -import java.io.FileNotFoundException; -import java.io.PrintStream; -import java.util.Map.Entry; -import java.util.Set; - -// TODO move to another package -public class XLPrintStream extends PrintStream { - - public XLPrintStream(String fileName) throws FileNotFoundException { - super(fileName); - } - - // TODO Change Object to something appropriate - public void save(Set> set) { - for (Entry entry : set) { - print(entry.getKey()); - print('='); - println(entry.getValue()); - } - flush(); - close(); - } -}