From e7537e339d5432bc18ed322d473789aad416db53 Mon Sep 17 00:00:00 2001 From: Rohit Awate Date: Mon, 30 Jul 2018 17:02:46 +0530 Subject: [PATCH] Refactor Highlighting and add Formatting frameworks for future Extension API --- .../controllers/BodyTabController.java | 49 +++++-------- .../controllers/DashboardController.java | 71 +++++++++---------- .../everest/controllers/Visualizer.java | 7 +- .../controllers/codearea/EverestCodeArea.java | 67 +++++++++-------- .../codearea/highlighters/Highlighter.java | 9 +++ .../highlighters/HighlighterFactory.java | 47 ++++++++++++ .../DuplicateFormatterException.java | 7 ++ .../DuplicateHighlighterException.java | 7 ++ .../rohitawate/everest/format/Formatter.java | 16 +++++ .../everest/format/FormatterFactory.java | 42 +++++++++++ .../everest/format/JSONFormatter.java | 30 ++++++++ .../requestmanager/RequestManagersPool.java | 1 - 12 files changed, 246 insertions(+), 107 deletions(-) create mode 100644 src/main/java/com/rohitawate/everest/controllers/codearea/highlighters/HighlighterFactory.java create mode 100644 src/main/java/com/rohitawate/everest/exceptions/DuplicateFormatterException.java create mode 100644 src/main/java/com/rohitawate/everest/exceptions/DuplicateHighlighterException.java create mode 100644 src/main/java/com/rohitawate/everest/format/Formatter.java create mode 100644 src/main/java/com/rohitawate/everest/format/FormatterFactory.java create mode 100644 src/main/java/com/rohitawate/everest/format/JSONFormatter.java diff --git a/src/main/java/com/rohitawate/everest/controllers/BodyTabController.java b/src/main/java/com/rohitawate/everest/controllers/BodyTabController.java index bd6f5c1..1b6f89f 100644 --- a/src/main/java/com/rohitawate/everest/controllers/BodyTabController.java +++ b/src/main/java/com/rohitawate/everest/controllers/BodyTabController.java @@ -17,9 +17,10 @@ package com.rohitawate.everest.controllers; import com.rohitawate.everest.controllers.codearea.EverestCodeArea; -import com.rohitawate.everest.controllers.codearea.EverestCodeArea.HighlightMode; +import com.rohitawate.everest.controllers.codearea.highlighters.HighlighterFactory; import com.rohitawate.everest.controllers.state.ComposerState; import com.rohitawate.everest.controllers.state.FieldState; +import com.rohitawate.everest.format.FormatterFactory; import com.rohitawate.everest.misc.Services; import com.rohitawate.everest.misc.ThemeManager; import javafx.fxml.FXML; @@ -71,21 +72,20 @@ public class BodyTabController implements Initializable { rawInputTypeBox.valueProperty().addListener(change -> { String type = rawInputTypeBox.getValue(); - HighlightMode mode; - switch (type) { - case "JSON": - mode = HighlightMode.JSON; - break; - case "XML": - mode = HighlightMode.XML; - break; - case "HTML": - mode = HighlightMode.HTML; - break; - default: - mode = HighlightMode.PLAIN; + + if (type.equals("JSON")) { + try { + rawInputArea.setText(rawInputArea.getText(), + FormatterFactory.getHighlighter(type), + HighlighterFactory.getHighlighter(type)); + } catch (IOException e) { + Services.loggingService.logWarning("Response could not be parsed.", e, LocalDateTime.now()); + } + + return; } - rawInputArea.setMode(mode); + + rawInputArea.setHighlighter(HighlighterFactory.getHighlighter(type)); }); try { @@ -185,25 +185,10 @@ public class BodyTabController implements Initializable { } private void setRawTab(ComposerState state) { - HighlightMode mode; - if (state.rawBodyType != null && state.rawBody != null) { - switch (state.rawBodyType) { - case "JSON": - mode = HighlightMode.JSON; - break; - case "XML": - mode = HighlightMode.XML; - break; - case "HTML": - mode = HighlightMode.HTML; - break; - default: - mode = HighlightMode.PLAIN; - } - rawInputArea.setText(state.rawBody, mode); + rawInputArea.setText(state.rawBody, HighlighterFactory.getHighlighter(state.rawBodyType)); } else { - rawInputArea.setText("", HighlightMode.PLAIN); + rawInputArea.setHighlighter(HighlighterFactory.getHighlighter("PLAIN TEXT")); } } } diff --git a/src/main/java/com/rohitawate/everest/controllers/DashboardController.java b/src/main/java/com/rohitawate/everest/controllers/DashboardController.java index c1cd248..4d57714 100644 --- a/src/main/java/com/rohitawate/everest/controllers/DashboardController.java +++ b/src/main/java/com/rohitawate/everest/controllers/DashboardController.java @@ -15,18 +15,17 @@ */ package com.rohitawate.everest.controllers; -import com.fasterxml.jackson.databind.JsonNode; import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXProgressBar; import com.jfoenix.controls.JFXSnackbar; import com.rohitawate.everest.controllers.codearea.EverestCodeArea; -import com.rohitawate.everest.controllers.codearea.EverestCodeArea.HighlightMode; +import com.rohitawate.everest.controllers.codearea.highlighters.HighlighterFactory; import com.rohitawate.everest.controllers.state.ComposerState; import com.rohitawate.everest.controllers.state.DashboardState; import com.rohitawate.everest.controllers.state.FieldState; import com.rohitawate.everest.exceptions.RedirectException; import com.rohitawate.everest.exceptions.UnreliableResponseException; -import com.rohitawate.everest.misc.EverestUtilities; +import com.rohitawate.everest.format.FormatterFactory; import com.rohitawate.everest.misc.Services; import com.rohitawate.everest.misc.ThemeManager; import com.rohitawate.everest.models.requests.DELETERequest; @@ -175,26 +174,20 @@ public class DashboardController implements Initializable { responseTypeBox.valueProperty().addListener(change -> { String type = responseTypeBox.getValue(); - HighlightMode mode; - switch (type) { - case "JSON": - try { - JsonNode node = EverestUtilities.jsonMapper.readTree(responseArea.getText()); - responseArea.setText(EverestUtilities.jsonMapper.writeValueAsString(node), HighlightMode.JSON); - } catch (IOException e) { - Services.loggingService.logWarning("Response could not be parsed.", e, LocalDateTime.now()); - } - return; - case "XML": - mode = HighlightMode.XML; - break; - case "HTML": - mode = HighlightMode.XML; - break; - default: - mode = HighlightMode.PLAIN; + + if (type.equals("JSON")) { + try { + responseArea.setText(responseArea.getText(), + FormatterFactory.getHighlighter(type), + HighlighterFactory.getHighlighter(type)); + } catch (IOException e) { + Services.loggingService.logWarning("Response could not be parsed.", e, LocalDateTime.now()); + } + + return; } - responseArea.setMode(mode); + + responseArea.setHighlighter(HighlighterFactory.getHighlighter(type)); }); visualizer = new Visualizer(); @@ -444,25 +437,22 @@ public class DashboardController implements Initializable { visualizer.clear(); try { + String simplifiedContentType; if (contentType != null) { // Selects only the part preceding the ';', skipping the character encoding contentType = contentType.split(";")[0]; switch (contentType.toLowerCase()) { case "application/json": - responseTypeBox.setValue("JSON"); - JsonNode node = EverestUtilities.jsonMapper.readTree(body); - responseArea.setText(EverestUtilities.jsonMapper.writeValueAsString(node), HighlightMode.JSON); + simplifiedContentType = "JSON"; visualizerTab.setDisable(false); - visualizer.populate(node); + visualizer.populate(body); break; case "application/xml": - responseTypeBox.setValue("XML"); - responseArea.setText(body, HighlightMode.XML); + simplifiedContentType = "XML"; break; case "text/html": - responseTypeBox.setValue("HTML"); - responseArea.setText(body, HighlightMode.HTML); + simplifiedContentType = "HTML"; if (Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { snackbar.show("Open link in browser?", "YES", 5000, e -> { snackbar.close(); @@ -477,19 +467,24 @@ public class DashboardController implements Initializable { } break; default: - responseTypeBox.setValue("PLAIN TEXT"); - responseArea.setText(body, HighlightMode.PLAIN); + simplifiedContentType = "PLAIN TEXT"; } } else { - responseTypeBox.setValue("PLAIN"); - responseArea.setText("No body found in the response.", HighlightMode.PLAIN); + simplifiedContentType = "PLAIN TEXT"; } + + responseArea.setText(body, + FormatterFactory.getHighlighter(simplifiedContentType), + HighlighterFactory.getHighlighter(simplifiedContentType)); + + responseTypeBox.setValue(simplifiedContentType); } catch (Exception e) { - snackbar.show("Response could not be parsed.", 5000); - Services.loggingService.logSevere("Response could not be parsed.", e, LocalDateTime.now()); - showLayer(ResponseLayer.ERROR); + String errorMessage = "Response could not be parsed."; + snackbar.show(errorMessage, 5000); + Services.loggingService.logSevere(errorMessage, e, LocalDateTime.now()); errorTitle.setText("Parsing Error"); - errorDetails.setText("Everest could not parse the response."); + errorDetails.setText(errorMessage); + showLayer(ResponseLayer.ERROR); } } diff --git a/src/main/java/com/rohitawate/everest/controllers/Visualizer.java b/src/main/java/com/rohitawate/everest/controllers/Visualizer.java index 1156422..be96c03 100644 --- a/src/main/java/com/rohitawate/everest/controllers/Visualizer.java +++ b/src/main/java/com/rohitawate/everest/controllers/Visualizer.java @@ -17,9 +17,11 @@ package com.rohitawate.everest.controllers; import com.fasterxml.jackson.databind.JsonNode; +import com.rohitawate.everest.misc.EverestUtilities; import javafx.scene.control.*; import javafx.scene.layout.HBox; +import java.io.IOException; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -37,8 +39,9 @@ class Visualizer extends ScrollPane { setFitToWidth(true); } - void populate(JsonNode node) { - this.populate(new TreeItem<>(), "root", node); + void populate(String body) throws IOException { + JsonNode tree = EverestUtilities.jsonMapper.readTree(body); + this.populate(new TreeItem<>(), "root", tree); } private void populate(TreeItem rootItem, String rootName, JsonNode root) { diff --git a/src/main/java/com/rohitawate/everest/controllers/codearea/EverestCodeArea.java b/src/main/java/com/rohitawate/everest/controllers/codearea/EverestCodeArea.java index 5f42df0..280c0c3 100644 --- a/src/main/java/com/rohitawate/everest/controllers/codearea/EverestCodeArea.java +++ b/src/main/java/com/rohitawate/everest/controllers/codearea/EverestCodeArea.java @@ -17,63 +17,62 @@ package com.rohitawate.everest.controllers.codearea; import com.rohitawate.everest.controllers.codearea.highlighters.Highlighter; -import com.rohitawate.everest.controllers.codearea.highlighters.JSONHighlighter; -import com.rohitawate.everest.controllers.codearea.highlighters.PlaintextHighlighter; -import com.rohitawate.everest.controllers.codearea.highlighters.XMLHighlighter; +import com.rohitawate.everest.format.Formatter; import com.rohitawate.everest.settings.Settings; import javafx.geometry.Insets; import org.fxmisc.richtext.CodeArea; +import java.io.IOException; import java.time.Duration; public class EverestCodeArea extends CodeArea { - public enum HighlightMode { - JSON, XML, HTML, PLAIN - } - private Highlighter highlighter; - private static JSONHighlighter jsonHighlighter; - private static XMLHighlighter xmlHighlighter; - private static PlaintextHighlighter plaintextHighlighter; - public EverestCodeArea() { this.getStylesheets().add(getClass().getResource("/css/syntax/Moondust.css").toString()); this.getStyleClass().add("everest-code-area"); this.setWrapText(Settings.editorWrapText); this.setPadding(new Insets(5)); - jsonHighlighter = new JSONHighlighter(); - xmlHighlighter = new XMLHighlighter(); - plaintextHighlighter = new PlaintextHighlighter(); - - setMode(HighlightMode.PLAIN); - this.multiPlainChanges() .successionEnds(Duration.ofMillis(1)) - .subscribe(ignore -> this.setStyleSpans(0, highlighter.computeHighlighting(getText()))); + .subscribe(ignore -> highlight()); } - public void setMode(HighlightMode mode) { - switch (mode) { - case JSON: - highlighter = jsonHighlighter; - break; - case XML: - case HTML: - highlighter = xmlHighlighter; - break; - default: - highlighter = plaintextHighlighter; - } - - // Re-computes the highlighting for the new mode + private void highlight() { this.setStyleSpans(0, highlighter.computeHighlighting(getText())); } - public void setText(String text, HighlightMode mode) { + public void setHighlighter(Highlighter highlighter) { + this.highlighter = highlighter; + + // Re-computes the highlighting using the new Highlighter + this.highlight(); + } + + /** + * Sets the text and then computes the highlighting. + */ + public void setText(String text, Highlighter highlighter) { clear(); appendText(text); - setMode(mode); + setHighlighter(highlighter); + } + + /** + * Formats the text with the provided Formatter if it is not null, + * sets the text and then computes the highlighting. + * + * @throws IOException If the formatter fails to format the text given a syntactic error. + */ + public void setText(String text, Formatter formatter, Highlighter highlighter) throws IOException { + clear(); + String formattedText = text; + + if (formatter != null) + formattedText = formatter.format(text); + + appendText(formattedText); + setHighlighter(highlighter); } } diff --git a/src/main/java/com/rohitawate/everest/controllers/codearea/highlighters/Highlighter.java b/src/main/java/com/rohitawate/everest/controllers/codearea/highlighters/Highlighter.java index 138a6eb..528f858 100644 --- a/src/main/java/com/rohitawate/everest/controllers/codearea/highlighters/Highlighter.java +++ b/src/main/java/com/rohitawate/everest/controllers/codearea/highlighters/Highlighter.java @@ -20,6 +20,15 @@ import org.fxmisc.richtext.model.StyleSpans; import java.util.Collection; +/** + * Highlights strings in various data formats. + */ public interface Highlighter { + /** + * Returns the StyleSpans needed for highlighting the given text which + * can then be used by EverestCodeArea. + * + * @param text The string to highlight + */ StyleSpans> computeHighlighting(String text); } diff --git a/src/main/java/com/rohitawate/everest/controllers/codearea/highlighters/HighlighterFactory.java b/src/main/java/com/rohitawate/everest/controllers/codearea/highlighters/HighlighterFactory.java new file mode 100644 index 0000000..b51b578 --- /dev/null +++ b/src/main/java/com/rohitawate/everest/controllers/codearea/highlighters/HighlighterFactory.java @@ -0,0 +1,47 @@ +package com.rohitawate.everest.controllers.codearea.highlighters; + +import com.rohitawate.everest.exceptions.DuplicateHighlighterException; + +import java.util.HashMap; + +/** + * Provides Highlighters for computing syntax highlighting + * of specific data formats. + *

+ * Everest, by default, comes with Highlighters for JSON, XML (HTML uses the same) and PLAIN TEXT. + */ +public class HighlighterFactory { + private static HashMap highlighters; + + static { + highlighters = new HashMap<>(); + highlighters.put("JSON", new JSONHighlighter()); + + XMLHighlighter xmlHighlighter = new XMLHighlighter(); + highlighters.put("XML", xmlHighlighter); + highlighters.put("HTML", xmlHighlighter); + + highlighters.put("PLAIN TEXT", new PlaintextHighlighter()); + } + + public static Highlighter getHighlighter(String name) { + return highlighters.get(name); + } + + /** + * Provisional method for the future Extension API which will enable + * developers to write and load custom Highlighters. + * + * @param name The display name for the Highlighter. + * @param highlighter The Highlighter object + * @throws DuplicateHighlighterException If a Highlighter is already loaded by Everest with the same name. + */ + public static void addHighlighter(String name, Highlighter highlighter) + throws DuplicateHighlighterException { + if (highlighters.containsKey(name)) { + throw new DuplicateHighlighterException("Highlighter already exists for the following type: " + name); + } + + highlighters.put(name, highlighter); + } +} diff --git a/src/main/java/com/rohitawate/everest/exceptions/DuplicateFormatterException.java b/src/main/java/com/rohitawate/everest/exceptions/DuplicateFormatterException.java new file mode 100644 index 0000000..1084fe7 --- /dev/null +++ b/src/main/java/com/rohitawate/everest/exceptions/DuplicateFormatterException.java @@ -0,0 +1,7 @@ +package com.rohitawate.everest.exceptions; + +public class DuplicateFormatterException extends Exception { + public DuplicateFormatterException(String message) { + super(message); + } +} diff --git a/src/main/java/com/rohitawate/everest/exceptions/DuplicateHighlighterException.java b/src/main/java/com/rohitawate/everest/exceptions/DuplicateHighlighterException.java new file mode 100644 index 0000000..12a6d8a --- /dev/null +++ b/src/main/java/com/rohitawate/everest/exceptions/DuplicateHighlighterException.java @@ -0,0 +1,7 @@ +package com.rohitawate.everest.exceptions; + +public class DuplicateHighlighterException extends Exception { + public DuplicateHighlighterException(String message) { + super(message); + } +} diff --git a/src/main/java/com/rohitawate/everest/format/Formatter.java b/src/main/java/com/rohitawate/everest/format/Formatter.java new file mode 100644 index 0000000..5abb413 --- /dev/null +++ b/src/main/java/com/rohitawate/everest/format/Formatter.java @@ -0,0 +1,16 @@ +package com.rohitawate.everest.format; + +import java.io.IOException; + +/** + * Formats strings in various data formats. + */ +public interface Formatter { + /** + * Returns a string formatted appropriate to the data format. + * + * @param unformatted The unformatted string + * @throws IOException If the Formatter fails to format the given string. + */ + String format(String unformatted) throws IOException; +} \ No newline at end of file diff --git a/src/main/java/com/rohitawate/everest/format/FormatterFactory.java b/src/main/java/com/rohitawate/everest/format/FormatterFactory.java new file mode 100644 index 0000000..0b393a0 --- /dev/null +++ b/src/main/java/com/rohitawate/everest/format/FormatterFactory.java @@ -0,0 +1,42 @@ +package com.rohitawate.everest.format; + +import com.rohitawate.everest.exceptions.DuplicateFormatterException; +import com.rohitawate.everest.exceptions.DuplicateHighlighterException; + +import java.util.HashMap; + +/** + * Provides Formatters for formatting strings of specific data formats. + *

+ * Everest, by default, comes with a Formatter for JSON. + */ +public class FormatterFactory { + private static HashMap formatters; + + static { + formatters = new HashMap<>(); + formatters.put("JSON", new JSONFormatter()); + } + + public static Formatter getHighlighter(String type) { + return formatters.get(type); + } + + + /** + * Provisional method for the future Extension API which will enable + * developers to write and load custom Formatters. + * + * @param name The display name for the Formatter. + * @param formatter The Formatter object. + * @throws DuplicateHighlighterException If a Formatter is already loaded by Everest with the same name. + */ + public static void addFormatter(String name, Formatter formatter) + throws DuplicateFormatterException { + if (formatters.containsKey(name)) { + throw new DuplicateFormatterException("Formatter already exists for the following type: " + name); + } + + formatters.put(name, formatter); + } +} diff --git a/src/main/java/com/rohitawate/everest/format/JSONFormatter.java b/src/main/java/com/rohitawate/everest/format/JSONFormatter.java new file mode 100644 index 0000000..97c5f57 --- /dev/null +++ b/src/main/java/com/rohitawate/everest/format/JSONFormatter.java @@ -0,0 +1,30 @@ +package com.rohitawate.everest.format; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +import java.io.IOException; + +/** + * Formats JSON strings using Jackson's ObjectMapper. + */ +public class JSONFormatter implements Formatter { + private static ObjectMapper mapper; + + JSONFormatter() { + mapper = new ObjectMapper(); + mapper.configure(SerializationFeature.INDENT_OUTPUT, true); + } + + @Override + public String format(String unformatted) throws IOException { + JsonNode tree = mapper.readTree(unformatted); + return mapper.writeValueAsString(tree); + } + + @Override + public String toString() { + return this.getClass().getCanonicalName(); + } +} diff --git a/src/main/java/com/rohitawate/everest/requestmanager/RequestManagersPool.java b/src/main/java/com/rohitawate/everest/requestmanager/RequestManagersPool.java index c72625a..e9d1204 100644 --- a/src/main/java/com/rohitawate/everest/requestmanager/RequestManagersPool.java +++ b/src/main/java/com/rohitawate/everest/requestmanager/RequestManagersPool.java @@ -50,7 +50,6 @@ public class RequestManagersPool { GETRequestManager newManager = new GETRequestManager(); getManagers.add(newManager); - System.out.println(getManagers.size()); return newManager; }