aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.idea/libraries/json_20210307.xml9
-rw-r--r--README.md7
-rw-r--r--apus.iml1
-rw-r--r--data/apus.cache1
-rw-r--r--lib/json-20210307.jarbin0 -> 69710 bytes
-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
12 files changed, 259 insertions, 24 deletions
diff --git a/.idea/libraries/json_20210307.xml b/.idea/libraries/json_20210307.xml
new file mode 100644
index 0000000..f64aae4
--- /dev/null
+++ b/.idea/libraries/json_20210307.xml
@@ -0,0 +1,9 @@
+<component name="libraryTable">
+ <library name="json-20210307">
+ <CLASSES>
+ <root url="jar://$PROJECT_DIR$/lib/json-20210307.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+</component> \ No newline at end of file
diff --git a/README.md b/README.md
index 78c4a7d..f2b92b1 100644
--- a/README.md
+++ b/README.md
@@ -14,8 +14,11 @@ I've heard that Java Swing has a native HTML rendering component. I hope to enti
- As a user, I want to be able to construct a structural representation of an HTML file.
- As a user, I want to be able to construct a structural representation of a CSS file.
-- As a user, I want to be able to add multiple Nodes to an HTML file.
-- As a user, I want to be able to open and render an arbitrary HTML file.
+- As a user, I want to be able to add multiple tabs to a list of open tabs.
+- As a user, I want to be able to view a rendering of an arbitrary HTML file.
+
+- As a user, I want to be given the option to save my currently open tabs to disk when quitting.
+- As a user, I want to be given the option to restore my previous tabs upon relaunching the application.
## credits
diff --git a/apus.iml b/apus.iml
index 19129c3..5001d20 100644
--- a/apus.iml
+++ b/apus.iml
@@ -28,5 +28,6 @@
</library>
</orderEntry>
<orderEntry type="library" name="javatuples" level="project" />
+ <orderEntry type="library" name="json-20210307" level="project" />
</component>
</module> \ No newline at end of file
diff --git a/data/apus.cache b/data/apus.cache
new file mode 100644
index 0000000..0637a08
--- /dev/null
+++ b/data/apus.cache
@@ -0,0 +1 @@
+[] \ No newline at end of file
diff --git a/lib/json-20210307.jar b/lib/json-20210307.jar
new file mode 100644
index 0000000..6583ea5
--- /dev/null
+++ b/lib/json-20210307.jar
Binary files differ
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);
}