diff options
author | JJ | 2022-12-27 16:36:56 +0000 |
---|---|---|
committer | JJ | 2022-12-27 16:36:56 +0000 |
commit | 69c8bcbda55c4eb8e79b89736568781440484319 (patch) | |
tree | b1db42212037747ff7109b65816839a2047be512 /src/main | |
parent | c14e53775591cb4d75b486d21f4849552d5c7c8c (diff) |
Convert project to Java 19 (at long last)
Diffstat (limited to 'src/main')
-rw-r--r-- | src/main/model/BrowserState.java | 10 | ||||
-rw-r--r-- | src/main/model/css/CssParser.java | 169 | ||||
-rw-r--r-- | src/main/model/html/ElementNode.java | 18 | ||||
-rw-r--r-- | src/main/model/html/HtmlParser.java | 206 | ||||
-rw-r--r-- | src/main/model/html/TextNode.java | 18 | ||||
-rw-r--r-- | src/main/model/layout/BlockLayout.java | 10 | ||||
-rw-r--r-- | src/main/model/layout/DocumentLayout.java | 5 | ||||
-rw-r--r-- | src/main/model/layout/InlineLayout.java | 22 | ||||
-rw-r--r-- | src/main/model/layout/Layout.java | 6 | ||||
-rw-r--r-- | src/main/ui/BrowserApp.java | 57 | ||||
-rw-r--r-- | src/main/ui/BrowserBar.java | 75 | ||||
-rw-r--r-- | src/main/ui/BrowserCanvas.java | 29 | ||||
-rw-r--r-- | src/main/ui/BrowserWindow.java | 23 |
13 files changed, 198 insertions, 450 deletions
diff --git a/src/main/model/BrowserState.java b/src/main/model/BrowserState.java index da21c41..7b05f8c 100644 --- a/src/main/model/BrowserState.java +++ b/src/main/model/BrowserState.java @@ -2,13 +2,11 @@ package model; import java.util.ArrayDeque; -// This BrowserState function collects the stateful portions of the browser into one modelable class. +// This BrowserState class collects the stateful portions of the browser into one modelable class. public class BrowserState { private ArrayDeque<String> tabs; private String currentTab; - // EFFECTS: constructs a new BrowserState - // MODIFIES: this public BrowserState(ArrayDeque<String> tabs, String currentTab) { this.tabs = tabs; this.currentTab = currentTab; @@ -22,22 +20,16 @@ public class BrowserState { return this.currentTab; } - // MODIFIES: this - // EFFECTS: Sets the current tab public void setCurrentTab(String tab) { this.currentTab = tab; } - // MODIFIES: this - // EFFECTS: add a new tab public void addTab(String added) { if (!this.tabs.contains(added)) { this.tabs.add(added); } } - // MODIFIES: this - // EFFECTS: removes a tab from the tablist public void removeTab(String removed) { this.tabs.remove(removed); } diff --git a/src/main/model/css/CssParser.java b/src/main/model/css/CssParser.java index a382b14..8bf4059 100644 --- a/src/main/model/css/CssParser.java +++ b/src/main/model/css/CssParser.java @@ -60,10 +60,6 @@ public class CssParser { * however we do keep a previousChar value for dealing with (annoying) escaped quotes. * It should be fast - I'd say something about time complexity if I knew anything about time complexity. * No guarantees are made about invalid CSS files. Also, no guarantees are made about valid CSS files, lol. - * <br> - * REQUIRES: A valid CSS file, as a raw String. - * MODIFIES: this - * EFFECTS: Returns a parsed CSS representation as several nested ArrayLists and Pairs of Strings. */ public ArrayList<Pair<String, ArrayList<Pair<String, String>>>> parseCSS(String input) { @@ -71,77 +67,52 @@ public class CssParser { // System.out.print(state); // System.out.println(" " + c); switch (state) { - case SELECTORS: caseSelectors(c); - break; - case MEDIA_SELECTORS: caseMediaSelectors(c); - break; - case ATTRIBUTE: caseAttribute(c); - break; - case VALUE: caseValue(c); - break; - case SINGLE_QUOTES: caseSingleQuotes(c); - break; - case DOUBLE_QUOTES: caseDoubleQuotes(c); - break; + case SELECTORS -> caseSelectors(c); + case MEDIA_SELECTORS -> caseMediaSelectors(c); + case ATTRIBUTE -> caseAttribute(c); + case VALUE -> caseValue(c); + case SINGLE_QUOTES -> caseSingleQuotes(c); + case DOUBLE_QUOTES -> caseDoubleQuotes(c); } } return result; } - /** - * EFFECTS: Handles and updates parser state/buffers for a single character while in the SELECTORS state. - * See also: the (slightly wrong) context-free grammar commented at the start of this file. - * MODIFIES: this - */ + // Handles and updates parser state/buffers for a single character while in the SELECTORS state. + // See also: the (slightly wrong) context-free grammar commented at the start of this file. private void caseSelectors(char c) { switch (c) { - case '@': + case '@' -> { if (currentSelector.equals("")) { state = ParserState.MEDIA_SELECTORS; } else { currentSelector += c; } - break; - case '{': - state = ParserState.ATTRIBUTE; - break; - case ' ': case '\n': - break; + } + case '{' -> state = ParserState.ATTRIBUTE; + case ' ', '\n' -> {} // todo: do better than blindly create a string; pattern match on css selectors - default: - currentSelector += c; - break; + default -> currentSelector += c; } } - /** - * EFFECTS: Handles and updates parser state/buffers for a single character while in the MEDIA_SELECTORS state. - * MODIFIES: this - */ + // Handles and updates parser state/buffers for a single character while in the MEDIA_SELECTORS state. private void caseMediaSelectors(char c) { switch (c) { // todo: don't entirely disregard media queries, also split between @media/@... - case '{': + case '{' -> { state = ParserState.SELECTORS; - // discard currentSelector currentSelector = ""; - break; - default: - currentSelector += c; - break; + } + default -> currentSelector += c; } } - /** - * EFFECTS: Handles and updates parser state/buffers for a single character while in the ATTRIBUTE state. - * MODIFIES: this - */ + // Handles and updates parser state/buffers for a single character while in the ATTRIBUTE state. private void caseAttribute(char c) { switch (c) { - case ':': - state = ParserState.VALUE; - break; - case '}': + case ':' -> state = ParserState.VALUE; + case '}' -> { state = ParserState.SELECTORS; if (!currentValue.equals("") || !currentProperty.equals("")) { // System.out.println("something's wrong"); @@ -151,26 +122,20 @@ public class CssParser { result.add(new Pair<>(currentSelector, currentRule)); currentSelector = ""; currentRule = new ArrayList<>(); - break; - case ' ': case '\n': - break; - default: - currentProperty += c; - break; + } + case ' ', '\n' -> {} + default -> currentProperty += c; } } - /** - * EFFECTS: Handles and updates parser state/buffers for a single character while in the VALUE state. - * MODIFIES: this - */ + // Handles and updates parser state/buffers for a single character while in the VALUE state. private void caseValue(char c) { switch (c) { - case ';': + case ';' -> { state = ParserState.ATTRIBUTE; updateCurrentRule(); - break; - case '}': + } + case '}' -> { state = ParserState.SELECTORS; if (!currentValue.equals("") || !currentProperty.equals("")) { updateCurrentRule(); @@ -178,25 +143,22 @@ public class CssParser { result.add(new Pair<>(currentSelector, currentRule)); currentSelector = ""; currentRule = new ArrayList<>(); - break; + } // todo: handle spaces better: they're actually important inside values - case ' ': case '\n': break; // believe me, i think this is ugly too but it passes checkstyle - case '\'': + case ' ', '\n' -> {} + case '\'' -> { state = ParserState.SINGLE_QUOTES; currentValue += c; - break; - // intentional use of TERRIBLE SMOKING FOOTGUN behavior to check boxes - case '\"': state = ParserState.DOUBLE_QUOTES; - default: currentValue += c; - break; + } + case '\"' -> { + state = ParserState.DOUBLE_QUOTES; + currentValue += c; + } + default -> currentValue += c; } } - /** - * Helper function to check method length boxes. - * EFFECTS: Adds a new property to the current rule. - * MODIFIES: this - */ + // Helper function to check method length boxes. private void updateCurrentRule() { currentRule.add(new Pair<>(currentProperty, currentValue)); currentProperty = ""; @@ -205,13 +167,10 @@ public class CssParser { // todo: handle additional escaped characters, though what we have right now isn't bad - /** - * EFFECTS: Handles and updates parser state/buffers for a single character while in the SINGLE_QUOTES state. - * MODIFIES: this - */ + // Handles and updates parser state/buffers for a single character while in the SINGLE_QUOTES state. private void caseSingleQuotes(char c) { switch (c) { - case '\'': + case '\'' -> { if (previousChar != '\\') { state = ParserState.VALUE; // quotes in css are exclusively? for paths: so we want to include the quotes themselves @@ -223,21 +182,18 @@ public class CssParser { currentValue += c; previousChar = c; } - break; - default: + } + default -> { currentValue += c; previousChar = c; - break; + } } } - /** - * EFFECTS: Handles and updates parser state/buffers for a single character while in the DOUBLE_QUOTES state. - * MODIFIES: this - */ + // Handles and updates parser state/buffers for a single character while in the DOUBLE_QUOTES state. private void caseDoubleQuotes(char c) { switch (c) { - case '\"': + case '\"' -> { if (previousChar != '\\') { state = ParserState.VALUE; currentValue += c; @@ -247,11 +203,11 @@ public class CssParser { currentValue += c; previousChar = c; } - break; - default: + } + default -> { currentValue += c; previousChar = c; - break; + } } } @@ -260,8 +216,8 @@ public class CssParser { * When given an invalid string (i.e. "12p53x"), it will produce an invalid result instead of throwing. * However, it should parse every valid string correctly. * <br> - * REQUIRES: A string of the form [NUMBER][VALIDUNIT] - * EFFECTS: Returns a number, in pixels, that has been converted appropriately + * Takes a string of the form [NUMBER][VALIDUNIT] + * Returns a number, in pixels, that has been converted appropriately */ public static double parseUnits(String input) { String numbers = ""; @@ -286,26 +242,23 @@ public class CssParser { } /** - * REQUIRES: a String that is a unit, otherwise defaults to pixels - * EFFECTS: converts a value in some units to a value in pixels + * Takes a String that is a unit, otherwise defaults to pixels + * Converts the value in some units to a value in pixels */ private static double convertUnits(String units, double value) { - // god case/break is such a fault-provoking design i hate it - // good thing we avoid breaks entirely here lmao - switch (units) { + return switch (units) { // absolute units - case "px": return value; - case "pc": return value * 16; - case "pt": return value * (4.0 / 3.0); - case "cm": return value * 37.8; - case "mm": return value * 378; - case "Q": return value * 1512; - case "in": return value * 96; + case "px" -> value; + case "pc" -> value * 16; + case "pt" -> value * (4.0 / 3.0); + case "cm" -> value * 37.8; + case "mm" -> value * 378; + case "Q" -> value * 1512; + case "in" -> value * 96; // not handled: % em ex ch rem lh rlh vw vh vmin vmax vb vi svw svh lvw lvh dvw dvh - default: + default -> value; // System.out.printf("Unit %s not implemented, defaulting to %s in pixels...%n", units, value); - return value; - } + }; } } diff --git a/src/main/model/html/ElementNode.java b/src/main/model/html/ElementNode.java index 6f0a556..3faf4ae 100644 --- a/src/main/model/html/ElementNode.java +++ b/src/main/model/html/ElementNode.java @@ -8,15 +8,11 @@ import java.util.ArrayList; * This ElementNode class represents an HTML tag and nested tags. */ public class ElementNode implements Node { - private String tag; - private ArrayList<Pair<String,String>> attributes; + private final String tag; + private final ArrayList<Pair<String,String>> attributes; - private ArrayList<Node> children; + private final ArrayList<Node> children; - /** - * EFFECTS: Constructs a new ElementNode from the arguments provided. - * MODIFIES: this - */ public ElementNode(String tag, ArrayList<Pair<String, String>> attributes, ArrayList<Node> children) { this.tag = tag; this.attributes = attributes; @@ -25,8 +21,6 @@ public class ElementNode implements Node { /** * Overloads the constructor for ease of use. We often don't provide children, at first. - * EFFECTS: Constructs a new ElementNode from the arguments provided. - * MODIFIES: this */ public ElementNode(String tag, ArrayList<Pair<String, String>> attributes) { this(tag, attributes, new ArrayList<>()); @@ -34,17 +28,11 @@ public class ElementNode implements Node { /** * Overloads the constructor for ease of use. Should probably only be used for tests. - * EFFECTS: Constructs a new ElementNode from the arguments provided. - * MODIFIES: this */ public ElementNode(String tag) { this(tag, new ArrayList<>(), new ArrayList<>()); } - /** - * EFFECTS: Adds a child to the children ArrayList. - * MODIFIES: this - */ public void addChild(Node child) { this.children.add(child); } diff --git a/src/main/model/html/HtmlParser.java b/src/main/model/html/HtmlParser.java index e50f713..5e5e7ce 100644 --- a/src/main/model/html/HtmlParser.java +++ b/src/main/model/html/HtmlParser.java @@ -43,8 +43,6 @@ public class HtmlParser { private ParserState state; - // EFFECTS: constructs a new HTML parser. - // MODIFIES: this public HtmlParser() { result = new ArrayList<>(); unfinished = new ArrayDeque<>(); @@ -65,118 +63,76 @@ public class HtmlParser { // System.out.print(state); // System.out.println(" " + c + " " + currentText); switch (state) { - case HTML: caseHtml(c); - break; - case UNKNOWN_TAG: caseUnknownTag(c); - break; // FOOTGUN LANGUAGE DESIGN STRIKES AGAIN - case IGNORED: caseIgnored(c); - break; - case OPENING_TAG: caseOpeningTag(c); - break; - case CLOSING_TAG: caseClosingTag(c); - break; - case KEY: caseKey(c); - break; - case VALUE: caseValue(c); - break; - case SINGLE_QUOTES: caseSingleQuotes(c); - break; - case DOUBLE_QUOTES: caseDoubleQuotes(c); - break; + case HTML -> caseHtml(c); + case UNKNOWN_TAG -> caseUnknownTag(c); + case IGNORED -> caseIgnored(c); + case OPENING_TAG -> caseOpeningTag(c); + case CLOSING_TAG -> caseClosingTag(c); + case KEY -> caseKey(c); + case VALUE -> caseValue(c); + case SINGLE_QUOTES -> caseSingleQuotes(c); + case DOUBLE_QUOTES -> caseDoubleQuotes(c); } } return result; } - /** - * EFFECTS: Handles and updates parser state/buffers for a single character while in the HTML state. - * MODIFIES: this - */ private void caseHtml(char c) { switch (c) { - case '<': + case '<' -> { state = ParserState.UNKNOWN_TAG; if (!currentText.equals("")) { addNewTextNode(); } - break; // FOOTGUN LANGUAGE DESIGN - case ' ': case '\n': + } + case ' ', '\n' -> { if (previousChar != ' ') { currentText += ' '; } previousChar = ' '; - break; - default: + } + default -> { currentText += c; previousChar = c; - break; + } } } - /** - * EFFECTS: Handles and updates parser state/buffers for a single character while in the UNKNOWN_TAG state. - * MODIFIES: this - */ private void caseUnknownTag(char c) { switch (c) { - case '/': - state = ParserState.CLOSING_TAG; - break; - case '>': // Why would you put <> in your HTML??? go away + case '/' -> state = ParserState.CLOSING_TAG; + case '>' -> { // Why would you put <> in your HTML??? go away state = ParserState.HTML; currentText += "<>"; - break; - // For now, we'll straight-up ignore anything matching the <!...> syntax: + } + // For now, we'll straight up ignore anything matching the <!...> syntax: // i.e. comments, and <!DOCTYPE html> - case '!': - state = ParserState.IGNORED; - break; - default: + case '!' -> state = ParserState.IGNORED; + default -> { state = ParserState.OPENING_TAG; currentTag += c; - break; + } } } - /** - * EFFECTS: Handles and updates parser state/buffers for a single character while in the IGNORED state. - * MODIFIES: this - */ private void caseIgnored(char c) { switch (c) { - case '>': - state = ParserState.HTML; - break; - default: - break; + case '>' -> state = ParserState.HTML; + default -> {} } } - /** - * EFFECTS: Handles and updates parser state/buffers for a single character while in the OPENING_TAG state. - * MODIFIES: this - */ private void caseOpeningTag(char c) { switch (c) { - case '>': - addNewElementNode(); - break; - case ' ': case '\n': - state = ParserState.KEY; - break; - default: - currentTag += c; - break; + case '>' -> addNewElementNode(); + case ' ', '\n' -> state = ParserState.KEY; + default -> currentTag += c; } } - /** - * EFFECTS: Handles and updates parser state/buffers for a single character while in the CLOSING_TAG state. - * MODIFIES: this - */ private void caseClosingTag(char c) { switch (c) { - case '>': + case '>' -> { state = ParserState.HTML; // IMPORTANT: we don't validate that closing tags correspond to an open tag if (!isSelfClosingTag(currentTag)) { @@ -185,72 +141,46 @@ public class HtmlParser { } } currentTag = ""; - break; - case ' ': case '\n': - break; - default: - currentTag += c; - break; + } + case ' ', '\n' -> {} + default -> currentTag += c; } } - /** - * EFFECTS: Handles and updates parser state/buffers for a single character while in the KEY state. - * MODIFIES: this - */ private void caseKey(char c) { switch (c) { - case '>': - addNewElementNode(); - break; - case '=': - state = ParserState.VALUE; - break; - case ' ': case '\n': - break; - default: - currentKey += c; - break; + case '>' -> addNewElementNode(); + case '=' -> state = ParserState.VALUE; + case ' ', '\n' -> {} + default -> currentKey += c; } } - /** - * EFFECTS: Handles and updates parser state/buffers for a single character while in the VALUE state. - * MODIFIES: this - */ private void caseValue(char c) { switch (c) { - case '\'': state = ParserState.SINGLE_QUOTES; - break; - case '\"': state = ParserState.DOUBLE_QUOTES; - break; - case ' ': case '\n': + case '\'' -> state = ParserState.SINGLE_QUOTES; + case '\"' -> state = ParserState.DOUBLE_QUOTES; + case ' ', '\n' -> { state = ParserState.KEY; currentAttributes.add(new Pair<>(currentKey, currentValue)); currentKey = ""; currentValue = ""; - break; // THE FOOTGUN DESIGN STRIKES AGAIN - case '>': + } + case '>' -> { if (!currentKey.equals("") || !currentValue.equals("")) { currentAttributes.add(new Pair<>(currentKey, currentValue)); currentKey = ""; currentValue = ""; } addNewElementNode(); - break; - default: - currentValue += c; - break; + } + default -> currentValue += c; } } - /** - * EFFECTS: Handles and updates parser state/buffers for a single character while in the SINGLE_QUOTES state. - * MODIFIES: this - */ private void caseSingleQuotes(char c) { switch (c) { - case '\'': + case '\'' -> { if (previousChar != '\\') { state = ParserState.VALUE; previousChar = '\0'; @@ -259,21 +189,17 @@ public class HtmlParser { currentValue += c; previousChar = c; } - break; - default: + } + default -> { currentValue += c; previousChar = c; - break; + } } } - /** - * EFFECTS: Handles and updates parser state/buffers for a single character while in the DOUBLE_QUOTES state. - * MODIFIES: this - */ private void caseDoubleQuotes(char c) { switch (c) { - case '\"': + case '\"' -> { if (previousChar != '\\') { state = ParserState.VALUE; previousChar = '\0'; @@ -282,19 +208,15 @@ public class HtmlParser { currentValue += c; previousChar = c; } - break; // FOOTGUN LANGUAGE DESIGN - default: + } + default -> { currentValue += c; previousChar = c; - break; + } } } - /** - * Helper function to remove code duplication. - * EFFECTS: Creates and adds a new ElementNode from the current buffers to the unfinished and result stacks - * MODIFIES: this - */ + // Helper function to remove code duplication. private void addNewElementNode() { state = ParserState.HTML; ElementNode node = new ElementNode(currentTag, currentAttributes); @@ -313,11 +235,7 @@ public class HtmlParser { currentAttributes = new ArrayList<>(); } - /** - * Helper function to check method length boxes. - * EFFECTS: Creates and adds a new TextNode from the current buffers to the unfinished and result stacks - * MODIFIES: this - */ + // Helper function to check method length boxes. private void addNewTextNode() { if (unfinished.size() != 0) { unfinished.getLast().addChild(new TextNode(currentText)); @@ -328,21 +246,13 @@ public class HtmlParser { previousChar = '\0'; } - /** - * Simple helper function to check if a tag is self-closing. - * EFFECTS: Returns whether a String tag is a self-closing tag. - */ + // Simple helper function to check if a tag is self-closing. private static boolean isSelfClosingTag(String tag) { - switch (tag) { - case "input": case "param": - case "br": case "hr": case "wbr": - case "img": case "embed": case "area": - case "meta": case "base": case "link": - case "source": case "track": case "col": - return true; - default: - return false; - } + return switch (tag) { + case "input", "param", "br", "hr", "wbr", "img", "embed", "area", + "meta", "base", "link", "source", "track", "col" -> true; + default -> false; + }; } } diff --git a/src/main/model/html/TextNode.java b/src/main/model/html/TextNode.java index 464180f..c50ce0f 100644 --- a/src/main/model/html/TextNode.java +++ b/src/main/model/html/TextNode.java @@ -3,23 +3,9 @@ package model.html; /** * This TextNode class represents raw text, with no nested tags. */ -public class TextNode implements Node { - private String text = ""; - - /** - * EFFECTS: Creates a new TextNode from the provided String value. - * MODIFIES: this - */ - public TextNode(String text) { - this.text = text; - } - - public String getText() { - return this.text; - } - +public record TextNode(String text) implements Node { // We implement this method for easy debugging. public String getData() { - return getText(); + return text(); } } diff --git a/src/main/model/layout/BlockLayout.java b/src/main/model/layout/BlockLayout.java index f011847..9204b61 100644 --- a/src/main/model/layout/BlockLayout.java +++ b/src/main/model/layout/BlockLayout.java @@ -4,17 +4,13 @@ import model.html.Node; import java.awt.*; -// A block style layout. public class BlockLayout extends Layout { - // MODIFIES: this - // EFFECTS: constructs a new BlockLayout public BlockLayout(Node node, Layout parent) { super(node, parent); } - // MODIFIES: this - // EFFECTS: recursively constructs the layout tree + // recursively construct the layout tree public void layout() { this.setLocation(this.getParent().getLocation()); this.getPreviousSibling().ifPresent( @@ -28,8 +24,8 @@ public class BlockLayout extends Layout { child.layout(); this.setHeight(this.getHeight() + child.getHeight()); } - System.out.println(this.getAssociatedNode().getData() + this.getLocation()); - System.out.println(System.identityHashCode(this.getLocation())); +// System.out.println(this.getAssociatedNode().getData() + this.getLocation()); +// System.out.println(System.identityHashCode(this.getLocation())); // System.out.println(this.getAssociatedNode().getData() + this.getDimension()); } } diff --git a/src/main/model/layout/DocumentLayout.java b/src/main/model/layout/DocumentLayout.java index 92c0b00..2e9d778 100644 --- a/src/main/model/layout/DocumentLayout.java +++ b/src/main/model/layout/DocumentLayout.java @@ -1,9 +1,7 @@ package model.layout; import java.awt.*; -import java.util.ArrayList; -// root document layout style public class DocumentLayout extends Layout { /* @@ -15,8 +13,7 @@ public class DocumentLayout extends Layout { super(null, null); } - // MODIFIES: this - // EFFECTS: recursively constructs the layout tree + // recursively construct the layout tree public void layout() { this.setLocation(new Point(10, 20)); this.setDimension(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT)); diff --git a/src/main/model/layout/InlineLayout.java b/src/main/model/layout/InlineLayout.java index 8e375d5..c4c51b5 100644 --- a/src/main/model/layout/InlineLayout.java +++ b/src/main/model/layout/InlineLayout.java @@ -6,19 +6,16 @@ import model.html.TextNode; import java.awt.*; -// Inline layout style public class InlineLayout extends Layout { private Point cursor; - // Constructs a new InlineLayout public InlineLayout(Node node, Layout parent) { super(node, parent); cursor = new Point(); } - // MODIFIES: this - // EFFECTS: recursively constructs the layout tree + // recursively construct the layout tree public void layout() { this.setLocation(this.getParent().getLocation()); this.getPreviousSibling().ifPresent( @@ -28,14 +25,17 @@ public class InlineLayout extends Layout { this.setCursor(this.getX(), this.getY()); Node node = this.getAssociatedNode(); - if (node instanceof TextNode) { - if (node.getData().length() > 5) { - this.setHeight(20); -// this.setWidth(this.getWidth() + node.getData().length()); + switch (node) { + case ElementNode e -> { + if (e.getTag().equals("a")) { + this.setX(this.getX() + this.getParent().getWidth()); + } } - } else if (node instanceof ElementNode) { - if (((ElementNode) node).getTag().equals("a")) { - this.setX(this.getX() + this.getParent().getWidth()); + default -> { + if (node.getData().length() > 5) { + this.setHeight(20); +// this.setWidth(this.getWidth() + node.getData().length()); + } } } diff --git a/src/main/model/layout/Layout.java b/src/main/model/layout/Layout.java index 4d9f48f..5a19302 100644 --- a/src/main/model/layout/Layout.java +++ b/src/main/model/layout/Layout.java @@ -31,8 +31,6 @@ public abstract class Layout { // the big function public abstract void layout(); - // MODIFIES: this - // EFFECTS: recursively constructs the layout tree public Layout(Node node, Layout parent) { this.associatedNode = node; @@ -47,8 +45,6 @@ public abstract class Layout { // eh, probably the best place to put this // parent MAY BE nil: a handy call to Optional.ofNullable allows this - // MODIFIES: this - // EFFECTS: recursively constructs the layout tree public static ArrayList<Layout> constructTree(ArrayList<Node> html, Layout parent) { ArrayDeque<Layout> result = new ArrayDeque<>(); for (Node node : html) { @@ -74,8 +70,6 @@ public abstract class Layout { return new ArrayList<>(result); // haha } - // MODIFIES: this - // EFFECTS: recursively constructs the layout tree public static DocumentLayout constructTree(ArrayList<Node> html) { DocumentLayout result = new DocumentLayout(); result.setChildren(constructTree(html, result)); diff --git a/src/main/ui/BrowserApp.java b/src/main/ui/BrowserApp.java index 520cac8..36f2005 100644 --- a/src/main/ui/BrowserApp.java +++ b/src/main/ui/BrowserApp.java @@ -1,9 +1,6 @@ package ui; -import model.html.ElementNode; -import model.html.HtmlParser; -import model.html.TextNode; -import model.html.Node; +import model.html.*; import java.nio.file.*; import java.util.*; @@ -18,24 +15,15 @@ public class BrowserApp { private ArrayList<Node> parsed; private ArrayDeque<String> tabs; - /** - * EFFECTS: Renders an arbitrary HTML page and arbitrary HTML input. - */ + // Renders an arbitrary HTML page and arbitrary HTML input. public BrowserApp() { println("apus: currently a barebones html/css renderer"); this.input = new Scanner(System.in); this.tabs = new ArrayDeque<>(); - mainLoop(); - } - /** - * EFFECTS: Runs the main loop - */ - private void mainLoop() { while (true) { try { - Path path = Paths.get(pathString); - String file = new String(Files.readAllBytes(path)); + String file = Files.readString(Path.of(pathString)); HtmlParser parser = new HtmlParser(); parsed = parser.parseHtml(file); println(border); @@ -52,43 +40,38 @@ public class BrowserApp { } } - /** - * EFFECTS: Barebones HTML rendering. Iterates through a list of Nodes and their children and prints any text. - */ + // Barebones HTML rendering. Iterates through a list of Nodes and their children and prints any text. private void renderHtml(ArrayList<Node> html) { for (Node node: html) { - if (node instanceof TextNode) { - println(node.getData()); - } else { - renderHtml(((ElementNode) node).getChildren()); + switch (node) { + case ElementNode e -> { + renderHtml(e.getChildren()); + } + default -> { + println(node.getData()); + } } } } - /** - * EFFECTS: Handles user input after rendering an initial site - */ + // Handles user input after rendering an initial site private void handleInput(String input) { switch (input) { - case "newuri": + case "newuri" -> { println("please provide a path to a file (examples located in data/*):"); pathString = this.input.next(); - break; - case "newtab": + } + 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": + } + case "nexttab" -> { this.tabs.add(pathString); pathString = this.tabs.removeFirst(); - break; - case "quit": - System.exit(0); - break; - default: - println("Sorry, I didn't quite get that. Please try again."); - break; + } + case "quit" -> System.exit(0); + default -> println("Sorry, I didn't quite get that. Please try again."); } } diff --git a/src/main/ui/BrowserBar.java b/src/main/ui/BrowserBar.java index e9db3c3..131fe19 100644 --- a/src/main/ui/BrowserBar.java +++ b/src/main/ui/BrowserBar.java @@ -2,19 +2,17 @@ package ui; import javax.swing.*; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import java.awt.event.*; import java.util.*; -// BrowserBar public class BrowserBar extends JToolBar { - private BrowserWindow parent; + private final BrowserWindow parent; - private JPopupMenu tabMenu; - private JToggleButton tabButton; - private JTextField uriInput; -// private JButton saveTabsButton; - private JButton openUriButton; + private final JPopupMenu tabMenu; + private final JToggleButton tabButton; + private final JTextField uriInput; +// private final JButton saveTabsButton; + private final JButton openUriButton; public BrowserBar(BrowserWindow parent) { this.parent = parent; @@ -34,36 +32,28 @@ public class BrowserBar extends JToolBar { } - // EFFECTS: opens the content of the text field in the current tab private ActionListener openTab() { - return new ActionListener() { - @Override - public void actionPerformed(ActionEvent actionEvent) { - String uri = uriInput.getText(); - parent.render(uri); - addTab(uri); - } + return actionEvent -> { + String uri = uriInput.getText(); + parent.render(uri); + addTab(uri); }; } - // EFFECTS: adds a new tab pointing to URI in the background public void addTab(String tab) { JToggleButton tabButton = new JToggleButton(tab); - tabButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent actionEvent) { - int action = JOptionPane.showOptionDialog(null, - "Open or close this tab?", "apus", - JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE, - null, new String[]{"Open", "Close"}, "Open"); - if (action == 0) { - parent.render(tab); - } else { - tabMenu.remove(tabButton); - tabMenu.setVisible(false); - parent.getBrowserState().removeTab(tab); - } + tabButton.addActionListener(actionEvent -> { + int action = JOptionPane.showOptionDialog(null, + "Open or close this tab?", "apus", + JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE, + null, new String[]{"Open", "Close"}, "Open"); + if (action == 0) { + parent.render(tab); + } else { + tabMenu.remove(tabButton); + tabMenu.setVisible(false); + parent.getBrowserState().removeTab(tab); } }); @@ -71,20 +61,15 @@ public class BrowserBar extends JToolBar { parent.getBrowserState().addTab(tab); } - // MODIFIES: this - // EFFECTS: toggles the tab menu private ActionListener toggleTabMenu() { - return new ActionListener() { - @Override - public void actionPerformed(ActionEvent actionEvent) { - if (tabButton.isSelected()) { - Point location = tabButton.getLocationOnScreen(); - location.translate(0, 30); // fuck this method lol - tabMenu.setLocation(location); - tabMenu.setVisible(true); - } else { - tabMenu.setVisible(false); - } + return actionEvent -> { + if (tabButton.isSelected()) { + Point location = tabButton.getLocationOnScreen(); + location.translate(0, 30); // fuck this method lol + tabMenu.setLocation(location); + tabMenu.setVisible(true); + } else { + tabMenu.setVisible(false); } }; } diff --git a/src/main/ui/BrowserCanvas.java b/src/main/ui/BrowserCanvas.java index a58f6e1..43481ef 100644 --- a/src/main/ui/BrowserCanvas.java +++ b/src/main/ui/BrowserCanvas.java @@ -1,24 +1,17 @@ package ui; -import model.html.ElementNode; -import model.html.TextNode; -import model.html.Node; -import model.layout.DocumentLayout; -import model.layout.Layout; +import model.html.*; +import model.layout.*; import javax.swing.*; import java.awt.*; import java.util.*; public class BrowserCanvas extends JPanel { - private ArrayList<Node> html; - private DocumentLayout currentLayout; + private final DocumentLayout currentLayout; - // MODIFIES: this - // EFFECTS: constructs a BrowserCanvas object public BrowserCanvas(ArrayList<Node> html) { super(); - this.html = html; this.currentLayout = Layout.constructTree(html); printTree(this.currentLayout.getChildren()); } @@ -30,7 +23,6 @@ public class BrowserCanvas extends JPanel { } } - // EFFECTS: paints a component onto a canvas @Override public void paintComponent(Graphics g) { super.paintComponent(g); @@ -38,20 +30,6 @@ public class BrowserCanvas extends JPanel { renderHtml(this.currentLayout.getChildren(), g, location); } - // EFFECTS: naively renders our html file by printing text nodes - /*private void renderHtml(ArrayList<Node> html, Graphics g, Point location) { - for (Node node : html) { - if (node instanceof TextNode) { - if (node.getData().length() > 5) { - g.drawString(node.getData(), location.x, location.y); - location.translate(0, 20); - } - } else { - renderHtml(((ElementNode) node).getChildren(), g, location); - } - } - }*/ - private void renderHtml(ArrayList<Layout> tree, Graphics g, Point location) { for (Layout layout : tree) { // System.out.println(layout.getLocation()); @@ -67,5 +45,4 @@ public class BrowserCanvas extends JPanel { } } } - } diff --git a/src/main/ui/BrowserWindow.java b/src/main/ui/BrowserWindow.java index bd1cf7f..94bd2d8 100644 --- a/src/main/ui/BrowserWindow.java +++ b/src/main/ui/BrowserWindow.java @@ -5,11 +5,8 @@ import model.html.HtmlParser; import javax.swing.*; import java.awt.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayDeque; -import java.util.ArrayList; +import java.nio.file.*; +import java.util.*; // Broad JFrame usage taken from here: https://docs.oracle.com/javase/tutorial/uiswing/components/frame.html public class BrowserWindow extends JFrame { @@ -17,43 +14,33 @@ public class BrowserWindow extends JFrame { public static final int HEIGHT = 800; private BrowserCanvas canvas; - private BrowserBar browserBar; + private final BrowserBar browserBar; + private final BrowserState state; - private BrowserState state; - - // MODIFIES: this - // EFFECTS: creates a new BrowserWindow program for rendering pages public BrowserWindow() { super("apus"); state = new BrowserState(new ArrayDeque<>(), ""); canvas = new BrowserCanvas(new ArrayList<>()); -// render("data/example.html"); browserBar = new BrowserBar(this); getContentPane().add(browserBar, BorderLayout.SOUTH); -// pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(WIDTH, HEIGHT); render("/home/apropos/Projects/website/j-james/index.html"); -// render("data/example.hctml"); // browserBar.addTab("/home/apropos/Projects/website/j-james/index.html"); setVisible(true); } - // MODIFIES: this - // EFFECTS: Renders an arbitrary page public void render(String uri) { state.setCurrentTab(uri); remove(canvas); // System.out.println(state.getCurrentTab()); try { - Path path = Paths.get(state.getCurrentTab()); - String file = new String(Files.readAllBytes(path)); + String file = Files.readString(Path.of(state.getCurrentTab())); HtmlParser parser = new HtmlParser(); canvas = new BrowserCanvas(parser.parseHtml(file)); } catch (Exception e) { System.out.println("Could not read file, rendering empty page: " + e.getMessage()); - canvas = new BrowserCanvas(new ArrayList<>()); } add(canvas); |