Summary Files Commits Branches @commit @diff @stats
path: root / Year 1 / Object-oriented Programming / Project 2 / src / Models / Table.cpp
diff options
context:
space:
mode:
1 files changed, 218 insertions, 0 deletions
-rw-r--r--Year 1/Object-oriented Programming/Project 2/src/Models/Table.cpp218
diff --git a/Year 1/Object-oriented Programming/Project 2/src/Models/Table.cpp b/Year 1/Object-oriented Programming/Project 2/src/Models/Table.cpp
new file mode 100644
index 0000000..0231dea
--- /dev/null
+++ b/Year 1/Object-oriented Programming/Project 2/src/Models/Table.cpp
@@ -0,0 +1,218 @@
+#include "Table.h"
+#include "CellDouble.h"
+#include "CellInt.h"
+#include "CellString.h"
+#include "CellFormula.h"
+#include <cstring>
+#include <fstream>
+#include <exception>
+#include <stdexcept>
+
+#include <iostream> // TODO: remove
+
+// Rough implementation of the grammar
+//
+// S -> W"=A",S | W"A",S | WPD,S | WPD.D,S | ,S | epsilon
+// A -> Aa | Ab | ... | AZ | A0 | A1 | ... | A* | ... | a | b | ... | epsilon
+// D -> D0 | D1 | ... | D9 | 0 | 1 | ... | 9
+// P -> + | - | epsilon
+// W -> W | epsilon
+//
+// over the input file stream
+
+void W(std::ifstream& in) {
+ while (in.peek() == ' ') in.get();
+}
+
+void P(std::ifstream& in, List<char>& buffer) {
+ if (in.peek() == '-' || in.peek() == '+')
+ buffer.add(in.get());
+}
+
+void D(std::ifstream& in, List<char>& buffer) {
+ while (in.peek() >= '0' && in.peek() <= '9')
+ buffer.add(in.get());
+}
+
+void A(std::ifstream& in, List<char>& buffer) {
+ while ((in.peek() >= ' ' && in.peek() <= '~') && in.peek() != '"') {
+ if (in.peek() == '\\') in.get();
+ buffer.add(in.get());
+ }
+}
+
+void throwException(std::ifstream& inFile, unsigned row) {
+ row++;
+ throw std::logic_error((
+ (((((String("Error: Invalid character '") += (char)inFile.peek())
+ += "' at row ") += row) += " and column ") += ((int)inFile.tellg() / row + 1))
+ ).get_cstr());
+}
+
+void Table::readFromFile(std::ifstream& inFile) {
+ unsigned row = longestRow = 0;
+
+ while (inFile.peek() != EOF) {
+ if (cells.get_count() < row + 1)
+ cells.add(List<Cell*>());
+
+ if (inFile.peek() == '\n') {
+ row++;
+ inFile.get();
+ continue;
+ }
+
+ List<char> buffer;
+
+ W(inFile);
+ if (inFile.peek() == '"') { // W"=A",S | W"A",S
+ inFile.get();
+ A(inFile, buffer);
+ buffer.add('\0');
+ if (inFile.peek() != '"') throwException(inFile, row);
+ inFile.get();
+ W(inFile);
+
+ if (inFile.peek() == ',') inFile.get();
+ else if (inFile.peek() != '\n' && inFile.peek() != EOF)
+ throwException(inFile, row);
+
+ cells[row].add((buffer[0] == '=')
+ ? (Cell*)new CellFormula(buffer.raw_data(), &cells)
+ : (Cell*)new CellString(buffer.raw_data()));
+ }
+ else if (inFile.peek() == ',') { // ,S
+ inFile.get();
+ cells[row].add(nullptr);
+ }
+ else { // WPD,S | WPD.D,S
+ P(inFile, buffer);
+ D(inFile, buffer);
+ if (inFile.peek() == '.') {
+ buffer.add(inFile.get());
+ D(inFile, buffer);
+ buffer.add('\0');
+ W(inFile);
+
+ if (inFile.peek() == ',') inFile.get();
+ else if (inFile.peek() != '\n' && inFile.peek() != EOF)
+ throwException(inFile, row);
+
+ cells[row].add((Cell*)new CellDouble(buffer.raw_data()));
+ }
+ else {
+ buffer.add('\0');
+ W(inFile);
+ if (inFile.peek() == ',') inFile.get();
+ else if (inFile.peek() != '\n' && inFile.peek() != EOF)
+ throwException(inFile, row);
+
+ cells[row].add((Cell*)new CellInt(buffer.raw_data()));
+ }
+ }
+
+ if (cells[row].get_count() > longestRow)
+ longestRow = cells[row].get_count();
+ }
+}
+
+#include <iostream>
+Table::Table(const char* filePath) {
+ longestRow = 0;
+ std::ifstream inFile(filePath);
+ if (!inFile.is_open())
+ throw std::logic_error("Could not open file!");
+ cells = List<List<Cell*>>();
+ readFromFile(inFile);
+ inFile.close();
+}
+
+Table::~Table() {
+ for (unsigned i = 0; i < cells.get_count(); i++) {
+ for (unsigned j = 0; j < cells[i].get_count(); j++)
+ delete cells[i][j];
+ }
+}
+
+unsigned Table::get_rows() const {
+ return cells.get_count();
+}
+
+unsigned Table::get_cols() const {
+ return longestRow;
+}
+
+#define abs(a) ((a < 0) ? -a : a)
+#define FLOATING_POINT_PRECISION 0.0001f
+
+void Table::putCell(unsigned row, unsigned col, const char* rawValue) {
+ Cell* newCell;
+
+ while (*rawValue == ' ') rawValue++;
+
+ if (*rawValue == '"') {
+ List<char> buffer;
+ rawValue++;
+ while (*rawValue != '"' && *rawValue != '\0') {
+ if (*rawValue == '\\') rawValue++;
+ buffer.add(*(rawValue++));
+ }
+ if (*rawValue == '"' && *(rawValue + 1) != '\0')
+ throw std::logic_error("Error: Text after closing (inescaped) brace!");
+ else if (*rawValue != '"')
+ throw std::logic_error("Error: No closing brace found!");
+
+ newCell = (buffer[0] == '=')
+ ? (Cell*)new CellFormula(buffer.raw_data(), &cells)
+ : (Cell*)new CellString(buffer.raw_data());
+ }
+ else if ((*rawValue == '+' || *rawValue == '-') || (*rawValue >= '0' && *rawValue <= '9')) {
+ double doubleParse = atof(rawValue);
+ unsigned intPart = abs(doubleParse);
+
+ newCell = (abs(doubleParse) - intPart > FLOATING_POINT_PRECISION)
+ ? (Cell*)new CellDouble(doubleParse)
+ : (Cell*)new CellInt(intPart);
+ }
+ else
+ throw std::logic_error("Error: Could not determine data type.");
+
+ if (cells[row].get_count() - 1 < col) {
+ while (cells[row].get_count() < col)
+ cells[row].add((Cell*)new CellString());
+ cells[row].add(newCell);
+ }
+ else {
+ delete cells[row][col];
+ cells[row][col] = newCell;
+ }
+}
+
+List<String> Table::getAllCells() const {
+ List<String> toRet;
+ for (unsigned i = 0; i < cells.get_count(); i++) {
+ for (unsigned j = 0; j < get_cols(); j++) {
+ if (cells[i].get_count() > j && cells[i][j] != nullptr)
+ toRet.add(cells[i][j]->getValueForPrint());
+ else
+ toRet.add("");
+ }
+ }
+ return toRet;
+}
+
+void Table::saveToFile(const char* filePath) const {
+ std::ofstream outFile(filePath);
+ if (!outFile.is_open())
+ throw std::logic_error("Could not open file!");
+
+ for (unsigned i = 0; i < cells.get_count(); i++) {
+ for (unsigned j = 0; j < cells[i].get_count(); j++) {
+ if (cells[i][j] != nullptr)
+ cells[i][j]->writeToFile(outFile);
+ outFile << ',';
+ }
+ outFile << '\n';
+ }
+ outFile.close();
+}