aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorj-james2022-10-29 04:22:06 +0000
committerj-james2022-10-29 04:22:06 +0000
commit106a0fe85effd27f8da28d17cf1053d5c50cd5fc (patch)
treea5f706008452fb30091e59752576a09aaf6ca390 /src/main
parent9a512732accf6869764fedc7c5d1abad57c6cb28 (diff)
Implement functionality for P2
Diffstat (limited to 'src/main')
-rw-r--r--src/main/model/html/ElementNode.java9
-rw-r--r--src/main/model/html/HtmlParser.java8
-rw-r--r--src/main/model/html/TextNode.java9
-rw-r--r--src/main/model/util/Node.java4
-rw-r--r--src/main/persistance/JsonAble.java7
-rw-r--r--src/main/persistance/JsonUtils.java44
-rw-r--r--src/main/ui/BrowserApp.java184
7 files changed, 243 insertions, 22 deletions
diff --git a/src/main/model/html/ElementNode.java b/src/main/model/html/ElementNode.java
index 16438f9..5ff4a0f 100644
--- a/src/main/model/html/ElementNode.java
+++ b/src/main/model/html/ElementNode.java
@@ -2,6 +2,8 @@ package model.html;
import model.util.Node;
import org.javatuples.Pair;
+import org.json.JSONObject;
+import persistance.JsonAble;
import java.util.ArrayList;
import java.util.Optional;
@@ -9,7 +11,7 @@ import java.util.Optional;
/**
* This ElementNode class represents an HTML tag and nested tags.
*/
-public class ElementNode implements Node {
+public class ElementNode implements Node, JsonAble {
private String tag;
private ArrayList<Pair<String,String>> attributes;
@@ -67,4 +69,9 @@ public class ElementNode implements Node {
public String getData() {
return getTag() + " " + getAttributes().toString();
}
+
+ @Override
+ public JSONObject serialize() {
+ return new JSONObject(this);
+ }
}
diff --git a/src/main/model/html/HtmlParser.java b/src/main/model/html/HtmlParser.java
index d07a2ff..f0829f4 100644
--- a/src/main/model/html/HtmlParser.java
+++ b/src/main/model/html/HtmlParser.java
@@ -4,6 +4,8 @@ import java.util.*;
import model.util.Node;
import org.javatuples.*;
+import org.json.JSONObject;
+import persistance.JsonAble;
/**
* This class represents the state of and implements an LL(1) HTML parser.
@@ -18,7 +20,7 @@ import org.javatuples.*;
* SELF_CLOSING_TAG ::= 'img' | ...
* (note that \forall T \in SELF_CLOSING_TAG, T \notin TAG)
*/
-public class HtmlParser {
+public class HtmlParser implements JsonAble {
/**
* HTML is not nice to parse. We manage to get away with a relatively small number of parser states regardless.
@@ -343,6 +345,10 @@ public class HtmlParser {
return false;
}
}
+
+ public JSONObject serialize() {
+ return new JSONObject(this);
+ }
}
/*
diff --git a/src/main/model/html/TextNode.java b/src/main/model/html/TextNode.java
index f6d3ce1..2e89326 100644
--- a/src/main/model/html/TextNode.java
+++ b/src/main/model/html/TextNode.java
@@ -1,11 +1,13 @@
package model.html;
import model.util.Node;
+import org.json.JSONObject;
+import persistance.JsonAble;
/**
* This TextNode class represents raw text, with no nested tags.
*/
-public class TextNode implements Node {
+public class TextNode implements Node, JsonAble {
private String text = "";
/**
@@ -24,4 +26,9 @@ public class TextNode implements Node {
public String getData() {
return getText();
}
+
+ @Override
+ public JSONObject serialize() {
+ return new JSONObject(this);
+ }
}
diff --git a/src/main/model/util/Node.java b/src/main/model/util/Node.java
index 4057fab..a6fedaf 100644
--- a/src/main/model/util/Node.java
+++ b/src/main/model/util/Node.java
@@ -1,5 +1,7 @@
package model.util;
+import org.json.JSONObject;
+
/**
* This Node represents an abstract relationship between ElementNode and TextNode.
* It's extremely helpful / necessary for Lists of arbitrary ElementNodes/TextNodes.
@@ -7,4 +9,6 @@ package model.util;
public interface Node {
// Return a representation of the Node. Useful for debugging.
public String getData();
+
+ public JSONObject serialize();
}
diff --git a/src/main/persistance/JsonAble.java b/src/main/persistance/JsonAble.java
new file mode 100644
index 0000000..408cb06
--- /dev/null
+++ b/src/main/persistance/JsonAble.java
@@ -0,0 +1,7 @@
+package persistance;
+
+import org.json.JSONObject;
+
+public interface JsonAble {
+ public JSONObject serialize();
+}
diff --git a/src/main/persistance/JsonUtils.java b/src/main/persistance/JsonUtils.java
new file mode 100644
index 0000000..4e11a18
--- /dev/null
+++ b/src/main/persistance/JsonUtils.java
@@ -0,0 +1,44 @@
+package persistance;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class JsonUtils {
+
+ /**
+ * REQUIRES: A valid filepath path, a writeable JSONObject json
+ * EFFECTS: writes a String to a file
+ */
+ public static void writeToFile(JSONArray json, String path) {
+ PrintWriter writer;
+ try {
+ writer = new PrintWriter(path);
+ writer.print(json);
+ writer.close();
+ } catch (Exception e) {
+ System.out.printf("Write to file failed with %s", e.toString());
+ }
+ }
+
+ /**
+ * REQUIRES: a path to a valid file containing JSONObject-serialized data
+ * EFFECTS: reads a serialized String into a JSONObject
+ */
+ public static JSONArray readFromFile(String path) {
+ String content;
+ JSONArray deread;
+ try {
+ content = Files.readString(Paths.get(path));
+ deread = new JSONArray(content);
+ } catch (Exception e) {
+ System.out.println("Read from file failed with %s");
+ deread = new JSONArray();
+ }
+ return deread;
+ }
+}
diff --git a/src/main/ui/BrowserApp.java b/src/main/ui/BrowserApp.java
index 8a50ea4..194f9e1 100644
--- a/src/main/ui/BrowserApp.java
+++ b/src/main/ui/BrowserApp.java
@@ -4,7 +4,11 @@ import model.html.ElementNode;
import model.html.HtmlParser;
import model.html.TextNode;
import model.util.Node;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import persistance.JsonUtils;
+import java.io.File;
import java.nio.file.*;
import java.util.*;
@@ -14,37 +18,93 @@ import java.util.*;
public class BrowserApp {
private Scanner input;
private static final String border = "===============================================";
+ private static final String storagePath = "data/apus.cache";
+ private String pathString;
+ private ArrayList<Node> parsed;
+ private ArrayDeque<String> tabs;
/**
* EFFECTS: Renders an arbitrary HTML page and arbitrary HTML input.
*/
public BrowserApp() {
println("apus: currently a barebones html/css renderer");
- println("please provide a path to a file (examples located in data/*):");
+ this.input = new Scanner(System.in);
+ this.tabs = new ArrayDeque<>();
- input = new Scanner(System.in);
- String pathString = input.next();
- Path path = Paths.get(pathString);
- try {
- String file = new String(Files.readAllBytes(path));
- HtmlParser parser = new HtmlParser();
- println(border);
- renderHtml(parser.parseHtml(file));
- println(border);
- ArrayList<String> rawHtml = new ArrayList<>();
- rawHtml.add(file);
- mainLoop(rawHtml, border, parser);
- } catch (Exception e) {
- println("Reading from the file failed with " + e.toString());
- println("Please try again.");
- }
+ askToRestoreTabs();
+ mainLoop();
}
+ /**
+ * EFFECTS: Asks the user if they'd like to restore previously closed tabs.
+ */
+ private void askToRestoreTabs() {
+ if (new File(storagePath).length() > 2) {
+ println("Would you like to restore your previously closed tabs? (Y/N)");
+ String answer;
+ while (true) {
+ answer = this.input.next();
+ if (answer.equalsIgnoreCase("y")) {
+ restoreClosedTabs();
+ break;
+ } else if (answer.equalsIgnoreCase("n")) {
+ JsonUtils.writeToFile(new JSONArray(), storagePath);
+ println("please provide a path to a file (examples located in data/*):");
+ pathString = this.input.next();
+ break;
+ } else {
+ println("Sorry, I didn't quite get that. Please try again.");
+ }
+ }
+ } else {
+ println("please provide a path to a file (examples located in data/*):");
+ pathString = this.input.next();
+ }
+ }
/**
* EFFECTS: Runs the main loop
*/
- private void mainLoop(ArrayList<String> rawHtml, String border, HtmlParser parser) {
+ private void mainLoop() {
+ while (true) {
+ try {
+ Path path = Paths.get(pathString);
+ String file = new String(Files.readAllBytes(path));
+ HtmlParser parser = new HtmlParser();
+ parsed = parser.parseHtml(file);
+ println(border);
+ renderHtml(parsed);
+ println(border);
+ println("Page rendered. Input additional commands if desired.");
+ println("Impemented commands: newuri, newtab, nexttab, quit");
+ handleInput(this.input.next());
+ println(border);
+ } catch (Exception e) {
+ println("Reading from the file failed with " + e.toString());
+ println("Please try again.");
+ }
+ }
+ }
+
+ /**
+ * EFFECTS: restores previous closed tabs from a cache file.
+ */
+ private void restoreClosedTabs() {
+ try {
+ JSONArray state = JsonUtils.readFromFile(storagePath);
+ for (int i = 0; i < state.length(); i++) {
+ println(state.get(i).getClass().getName());
+ tabs.add((String) state.get(i));
+ }
+ pathString = tabs.removeLast();
+ } catch (Exception e) {
+ println("Restoring state from disk failed with " + e.toString());
+ System.exit(0);
+ }
+ }
+
+ /*
+ private void mainLoopII(ArrayList<String> rawHtml, String border, HtmlParser parser) {
while (true) {
println("Page rendered. Input additional raw HTML if desired.");
rawHtml.add(input.next());
@@ -55,7 +115,7 @@ public class BrowserApp {
}
println(border);
}
- }
+ }*/
/**
* EFFECTS: Barebones HTML rendering. Iterates through a list of Nodes and their children and prints any text.
@@ -70,6 +130,92 @@ public class BrowserApp {
}
}
+ /**
+ * EFFECTS: Handles user input after rendering an initial site
+ */
+ private void handleInput(String input) {
+ switch (input) {
+ case "newuri":
+ println("please provide a path to a file (examples located in data/*):");
+ pathString = this.input.next();
+ break;
+ case "newtab":
+ this.tabs.add(pathString);
+ println("please provide a path to a file (examples located in data/*):");
+ pathString = this.input.next();
+ break;
+ case "nexttab":
+ this.tabs.add(pathString);
+ pathString = this.tabs.removeFirst();
+ break;
+ case "quit":
+ handleQuit();
+ System.exit(0);
+ break;
+ default:
+ println("Sorry, I didn't quite get that. Please try again.");
+ break;
+ }
+ }
+
+ /**
+ * Helper function for the quit() case.
+ * EFFECTS: Asks a user whether they'd like to save their tabs, and exists the program.
+ */
+ private void handleQuit() {
+ println("Would you like to save your currently opened tabs to disk? (Y/N)");
+ String answer;
+ while (true) {
+ answer = this.input.next();
+ if (answer.equalsIgnoreCase("y")) {
+ this.tabs.add(pathString);
+ JsonUtils.writeToFile(new JSONArray(tabs), storagePath);
+ break;
+ } else if (answer.equalsIgnoreCase("n")) {
+ JsonUtils.writeToFile(new JSONArray(), storagePath);
+ break;
+ } else {
+ println("Sorry, I didn't quite get that. Please try again.");
+ }
+ }
+ }
+
+ /**
+ * EFFECTS: writes the current program configuration to the disk
+ */
+ private void writeToDisk() {
+ ArrayList<ArrayList<JSONObject>> jsonArray = new ArrayList<>();
+ for (String p : tabs) {
+ ArrayList<JSONObject> jsonArrayII = new ArrayList<>();
+ try {
+ Path path = Paths.get(pathString);
+ String file = new String(Files.readAllBytes(path));
+ HtmlParser parser = new HtmlParser();
+ for (Node n : parser.parseHtml(file)) {
+ jsonArrayII.add(n.serialize());
+ }
+ } catch (Exception e) {
+ System.out.printf("Failed to write to disk with %s", e);
+ }
+ jsonArray.add(jsonArrayII);
+ }
+ JsonUtils.writeToFile(new JSONArray(jsonArray), storagePath);
+ }
+
+ /**
+ * EFFECTS: restores program state from a last written to state
+ */
+ private void restoreFromDisk(JSONArray state) {
+ for (int i = 0; i < state.length(); i++) {
+ Object tab = state.get(i);
+ if (tab instanceof JSONArray) {
+ for (int j = 0; j < ((JSONArray) tab).length(); j++) {
+ tabs.add(((JSONArray) tab).toString());
+ }
+ }
+ }
+ }
+
private void print(String toPrint) {
System.out.print(toPrint);
}