diff --git a/src/main/java/com/cys4/sensitivediscoverer/RegexListViewer.java b/src/main/java/com/cys4/sensitivediscoverer/RegexListViewer.java index bc0ed78..eba631b 100644 --- a/src/main/java/com/cys4/sensitivediscoverer/RegexListViewer.java +++ b/src/main/java/com/cys4/sensitivediscoverer/RegexListViewer.java @@ -253,20 +253,12 @@ private JMenuItem createListSaveMenuItem(List regexEntities) { JMenuItem menuItem = new JMenuItem(getLocaleString("options-list-save")); String[] options = {"JSON", "CSV"}; menuItem.addActionListener(actionEvent -> { - int dialog = JOptionPane.showOptionDialog( - null, - getLocaleString("options-list-save-formatDialogMessage"), - getLocaleString("options-list-save-formatDialogTitle"), - JOptionPane.DEFAULT_OPTION, - JOptionPane.QUESTION_MESSAGE, - null, - options, - null - ); - if ("JSON".equals(options[dialog])) { - Utils.saveListToJSON(regexEntities); - } else if ("CSV".equals(options[dialog])) { - Utils.saveListToCSV(regexEntities); + + String fileName = Utils.selectFile(options, getLocaleString("utils-saveToFile-exportFile")); + if (fileName.toUpperCase().endsWith("JSON")) { + Utils.saveListToJSON(fileName, regexEntities); + } else if (fileName.toUpperCase().endsWith("CSV")) { + Utils.saveListToCSV(fileName, regexEntities); } }); @@ -279,21 +271,15 @@ private JMenuItem createListOpenMenuItem(RegexListContext ctx, String[] options = {"JSON", "CSV"}; JMenuItem menuItem = new JMenuItem(getLocaleString("options-list-open")); menuItem.addActionListener(actionEvent -> { - int dialog = JOptionPane.showOptionDialog( - null, - getLocaleString("options-list-open-formatDialogMessage"), - getLocaleString("options-list-open-formatDialogTitle"), - JOptionPane.DEFAULT_OPTION, - JOptionPane.QUESTION_MESSAGE, - null, - options, - null - ); - if ("JSON".equals(options[dialog])) { - Utils.openListFromJSON(ctx); - } else if ("CSV".equals(options[dialog])) { - Utils.openListFromCSV(ctx); + + String fileName = Utils.selectFile(options, getLocaleString("utils-linesFromFile-importFile")); + + if (fileName.toUpperCase().endsWith("JSON")) { + Utils.openListFromJSON(fileName, ctx); + } else if (fileName.toUpperCase().endsWith("CSV")) { + Utils.openListFromCSV(fileName, ctx); } + tableModel.fireTableDataChanged(); tabPaneOptions.validate(); tabPaneOptions.repaint(); diff --git a/src/main/java/com/cys4/sensitivediscoverer/Utils.java b/src/main/java/com/cys4/sensitivediscoverer/Utils.java index ddb1a78..676b94a 100644 --- a/src/main/java/com/cys4/sensitivediscoverer/Utils.java +++ b/src/main/java/com/cys4/sensitivediscoverer/Utils.java @@ -18,6 +18,7 @@ import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.*; import java.util.regex.Matcher; @@ -86,26 +87,37 @@ public static void saveToFile(String extensionName, List lines) { /** * Open JFileChooser to get lines from a file * - * @param extensionName the extension to filter files - * @return The lines from the file, or null if there was an error + * @param extensionNames the extensions to filter files + * @param title the window title + * @return The filename, or null if there was an error */ - public static List linesFromFile(String extensionName) { + public static String selectFile(String[] extensionNames, String title) { JFrame parentFrame = new JFrame(); JFileChooser fileChooser = new JFileChooser(); - FileNameExtensionFilter filter = new FileNameExtensionFilter("." + extensionName, extensionName); - fileChooser.setFileFilter(filter); - fileChooser.setDialogTitle(getLocaleString("utils-linesFromFile-importFile")); + for (String extensionName : extensionNames) { + FileNameExtensionFilter filter = new FileNameExtensionFilter("." + extensionName, extensionName); + fileChooser.addChoosableFileFilter(filter); + } + + fileChooser.setDialogTitle(title); + + int userSelection = title.toLowerCase().contains("import") ? + fileChooser.showOpenDialog(parentFrame) : + fileChooser.showSaveDialog(parentFrame); - int userSelection = fileChooser.showOpenDialog(parentFrame); if (userSelection != JFileChooser.APPROVE_OPTION) return null; - File selectedFile = fileChooser.getSelectedFile(); - try { - return Files.readAllLines(selectedFile.toPath()); - } catch (IOException e) { - e.printStackTrace(); - return null; + String exportFilePath = fileChooser.getSelectedFile().getAbsolutePath(); + + String selectedExt = fileChooser.getFileFilter().getDescription().toLowerCase(); + if (selectedExt.equals("all files")) + return exportFilePath; + + if (!exportFilePath.toLowerCase().endsWith(selectedExt)) { + exportFilePath += selectedExt; } + + return exportFilePath; } /** @@ -177,7 +189,7 @@ public static InputStream getResourceAsStream(String name) { return Utils.class.getClassLoader().getResourceAsStream(name); } - public static void saveListToCSV(List regexEntities) { + public static void saveListToCSV(String csvFile, List regexEntities) { List lines = new ArrayList<>(); lines.add("\"description\",\"regex\",\"sections\""); @@ -190,10 +202,16 @@ public static void saveListToCSV(List regexEntities) { lines.add(String.format("\"%s\",\"%s\",\"%s\"", description, regex, sections)); }); - Utils.saveToFile("csv", lines); + try { + PrintWriter pwt = new PrintWriter(csvFile, StandardCharsets.UTF_8); + lines.forEach(pwt::println); + pwt.close(); + } catch (Exception e) { + e.printStackTrace(); + } } - public static void saveListToJSON(List regexEntities) { + public static void saveListToJSON(String jsonFile, List regexEntities) { List lines = new ArrayList<>(); regexEntities.forEach(regexEntity -> { @@ -210,36 +228,48 @@ public static void saveListToJSON(List regexEntities) { Gson gson = builder.create(); Type tListEntries = (new TypeToken>() { }).getType(); - Utils.saveToFile("json", List.of(gson.toJson(lines, tListEntries))); + + try { + PrintWriter pwt = new PrintWriter(jsonFile, StandardCharsets.UTF_8); + List.of(gson.toJson(lines, tListEntries)).forEach(pwt::println); + pwt.close(); + } catch (Exception e) { + e.printStackTrace(); + } } - // todo: separate this function from the file-chooser GUI modal in Utils.linesFromFile to allow for testing. - public static void openListFromCSV(RegexListContext ctx) { + public static void openListFromCSV(String csvFile, RegexListContext ctx) { StringBuilder alreadyAddedMsg = new StringBuilder(); + List lines; + try { + lines = Files.readAllLines(Path.of(csvFile)); + } catch (IOException e) { + e.printStackTrace(); + return; + } - List lines = Utils.linesFromFile("csv"); - if (Objects.isNull(lines)) return; - - //todo: Allow files without the header line. - - //todo: if first line is not the header line, then don't skip it. + //Skip header line if present + int startRow = (lines.get(0).contains("\"description\",\"regex\"")) ? 1 : 0; - // for each line after the first (Headers Line) - lines.subList(1, lines.size()).forEach(line -> { + // for each line + lines.subList(startRow, lines.size()).forEach(line -> { Matcher matcher = RegexEntity.checkRegexEntityFromCSV(line); - if (!matcher.find()) return; - //todo: when sections are not specified it should still parse the regex and set the sections to all + if (!matcher.find()) + return; + + //load sections if presents, otherwise set all sections + boolean hasSections = !(matcher.group(3) == null || matcher.group(3).isBlank()); String description = matcher.group(1).replaceAll("\"\"", "\""); String regex = matcher.group(2).replaceAll("\"\"", "\""); - List sections = List.of(matcher.group(3).replaceAll("\"\"", "\"").split("\\|")); + List sections = hasSections ? List.of(matcher.group(3).replaceAll("\"\"", "\"").split("\\|")) : null; RegexEntity newRegexEntity = new RegexEntity( description, regex, true, - ProxyItemSection.deserializeSections(sections) + hasSections ? ProxyItemSection.deserializeSections(sections) : ProxyItemSection.ALL ); if (!ctx.getRegexEntities().contains(newRegexEntity)) { @@ -255,12 +285,16 @@ public static void openListFromCSV(RegexListContext ctx) { alreadyAddedMsg.toString()); } - public static void openListFromJSON(RegexListContext ctx) { + public static void openListFromJSON(String jsonFile, RegexListContext ctx) { Gson gson = new Gson(); StringBuilder alreadyAddedMsg = new StringBuilder(); - - List lines = Utils.linesFromFile("json"); - if (Objects.isNull(lines)) return; + List lines; + try { + lines = Files.readAllLines(Path.of(jsonFile)); + } catch (IOException e) { + e.printStackTrace(); + return; + } Type tArrayListRegexEntity = new TypeToken>() { }.getType(); diff --git a/src/main/java/com/cys4/sensitivediscoverer/model/RegexEntity.java b/src/main/java/com/cys4/sensitivediscoverer/model/RegexEntity.java index af40caf..85d0704 100644 --- a/src/main/java/com/cys4/sensitivediscoverer/model/RegexEntity.java +++ b/src/main/java/com/cys4/sensitivediscoverer/model/RegexEntity.java @@ -23,6 +23,7 @@ public class RegexEntity { private final EnumSet sections; private final List tests; private boolean active; + public RegexEntity(String description, String regex) throws IllegalArgumentException { this(description, regex, true, ProxyItemSection.getDefault(), null); } @@ -63,7 +64,7 @@ public RegexEntity(RegexEntity entity) throws IllegalArgumentException { */ public static Matcher checkRegexEntityFromCSV(String input) { return Pattern - .compile("^\\s*[\"'](.*?)[\"']\\s*,\\s*[\"'](.+?)[\"']\\s*,\\s*[\"'](.+?)[\"']\\s*$") + .compile("^[\\t ]*[\\\"'](.+?)[\\\"'][\\t ]*,[\\t ]*[\\\"'](.+?)[\\\"'][\\t ]*(?:,[\\t ]*[\\\"'](.+?)[\\\"'][\\t ]*)?$") .matcher(input); }