Compare commits
6 commits
7879178b1a
...
3b0eb9052d
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3b0eb9052d | ||
![]() |
adbdfd3b99 | ||
![]() |
7b3ddf17e0 | ||
![]() |
a56a3be9ef | ||
![]() |
81e22d094b | ||
![]() |
4b47ea8d59 |
28 changed files with 630 additions and 95 deletions
59
app/src/main/java/xl/controller/EditorController.java
Normal file
59
app/src/main/java/xl/controller/EditorController.java
Normal file
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
60
app/src/main/java/xl/controller/SlotLabelController.java
Normal file
60
app/src/main/java/xl/controller/SlotLabelController.java
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<SlotLabel> 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<SlotLabel>(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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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("");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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));
|
||||
|
|
22
app/src/main/java/xl/model/BombCell.java
Normal file
22
app/src/main/java/xl/model/BombCell.java
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
11
app/src/main/java/xl/model/Cell.java
Normal file
11
app/src/main/java/xl/model/Cell.java
Normal file
|
@ -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();
|
||||
}
|
21
app/src/main/java/xl/model/CommentCell.java
Normal file
21
app/src/main/java/xl/model/CommentCell.java
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
26
app/src/main/java/xl/model/ExpressionCell.java
Normal file
26
app/src/main/java/xl/model/ExpressionCell.java
Normal file
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
41
app/src/main/java/xl/model/XLBufferedReader.java
Normal file
41
app/src/main/java/xl/model/XLBufferedReader.java
Normal file
|
@ -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<String, Cell> 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());
|
||||
}
|
||||
}
|
||||
}
|
208
app/src/main/java/xl/model/XLModel.java
Normal file
208
app/src/main/java/xl/model/XLModel.java
Normal file
|
@ -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<String, Cell> map;
|
||||
private String status;
|
||||
private String currentAddress;
|
||||
|
||||
// The observers that are registered to be notified
|
||||
private List<Observer> observers = new ArrayList<>();
|
||||
|
||||
public XLModel() {
|
||||
status = "";
|
||||
currentAddress = "A1";
|
||||
map = new java.util.HashMap<String, Cell>();
|
||||
}
|
||||
|
||||
public String getCurrentAddress() {
|
||||
return currentAddress;
|
||||
}
|
||||
|
||||
public void setCurrentAddress(String currentAddress) {
|
||||
this.currentAddress = currentAddress;
|
||||
}
|
||||
|
||||
public Optional<Cell> 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<String, Cell> 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);
|
||||
}
|
||||
}
|
||||
}
|
31
app/src/main/java/xl/model/XLPrintStream.java
Normal file
31
app/src/main/java/xl/model/XLPrintStream.java
Normal file
|
@ -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<Entry<String, Cell>> set) {
|
||||
for (Entry<String, Cell> entry : set) {
|
||||
print(entry.getKey());
|
||||
print('=');
|
||||
Cell cell = entry.getValue();
|
||||
if (cell instanceof CommentCell) {
|
||||
println("#" + cell.displayValue());
|
||||
} else {
|
||||
println(cell.displayValue());
|
||||
}
|
||||
}
|
||||
flush();
|
||||
close();
|
||||
}
|
||||
}
|
|
@ -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<String, Object> map) {
|
||||
try {
|
||||
while (ready()) {
|
||||
String string = readLine();
|
||||
int i = string.indexOf('=');
|
||||
// TODO
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new XLException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<Entry<String, Object>> set) {
|
||||
for (Entry<String, Object> entry : set) {
|
||||
print(entry.getKey());
|
||||
print('=');
|
||||
println(entry.getValue());
|
||||
}
|
||||
flush();
|
||||
close();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue