content: package ciscoroutertool.gui;
  
  import ciscoroutertool.output.OutputManager;
  import ciscoroutertool.rules.Rule;
  import ciscoroutertool.scanner.FullReport;
  import ciscoroutertool.scanner.HostReport;
  
  import javax.swing.*;
  import javax.swing.tree.DefaultMutableTreeNode;
  import javax.swing.tree.DefaultTreeModel;
  import javax.swing.tree.MutableTreeNode;
  import javax.swing.tree.TreeSelectionModel;
  import java.io.File;
  import java.io.IOException;
  import java.util.ArrayList;
  
  /**
   * This window displays the output in a tree format and lets the user remove
   * nodes from the report, and then output it to a file.
   * @version 0.01ALPHA
   * @author Andrew H. Johnston
   */
  public class OutputReview extends javax.swing.JFrame {
  
      /**
       * The object holding all individual host reports as well as some global data
       */
      private FullReport report;
  
      /**
       * The selected tree node (used to find nodes to delete)
       */
      private DefaultMutableTreeNode selected = null;
  
      /**
       * The model that the tree is based on.
       */
      private DefaultTreeModel model;
  
      /**
       * The modal that handles saving files
       */
      private JFileChooser fileChooser = new JFileChooser();
      
      /**
       * Creates new form OutputReview
       */
      public OutputReview(FullReport r) {
          report = r;
          DefaultMutableTreeNode root = new DefaultMutableTreeNode("root");
          ArrayList<HostReport> reports = report.getReports();
          for (HostReport h : reports) {
              DefaultMutableTreeNode host = new DefaultMutableTreeNode(h.getHost().toString());
              ArrayList<Rule> matchedRules = h.getMatchedRules();
              for (Rule rule : matchedRules) {
                  DefaultMutableTreeNode ruleName = new DefaultMutableTreeNode(rule.getName());
                  DefaultMutableTreeNode ruleDesc = new DefaultMutableTreeNode(rule.getDescription());
                  ruleName.add(ruleDesc);
                  host.add(ruleName);
              }
              root.add(host);
          }
          initComponents();
          model = new DefaultTreeModel(root);
          reportTree.setModel(model);
          //The following allows for us to select one node at a time (for deletion)
          reportTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
          //Hide the root node so it looks like the hosts are the root node
          reportTree.setRootVisible(false);
      }
  
      /**
       * This method is called from within the constructor to initialize the form.
       * WARNING: Do NOT modify this code. The content of this method is always
       * regenerated by the Form Editor.
       */
      @SuppressWarnings("unchecked")
      // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
      private void initComponents() {
  
          lblPrompt = new javax.swing.JLabel();
          btnDelete = new javax.swing.JButton();
          btnXML = new javax.swing.JButton();
          btnTXT = new javax.swing.JButton();
          btnCSV = new javax.swing.JButton();
          treeScrollPane = new javax.swing.JScrollPane();
          reportTree = new javax.swing.JTree();
  
          setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
          setTitle("Review Output");
  
          lblPrompt.setText("Please review the following finds. Press delete to remove an item from the list.");
  
          btnDelete.setText("Delete");
          btnDelete.addActionListener(new java.awt.event.ActionListener() {
              public void actionPerformed(java.awt.event.ActionEvent evt) {
                  btnDeleteActionPerformed(evt);
              }
          });
  
          btnXML.setText("Output to XML");
          btnXML.addActionListener(new java.awt.event.ActionListener() {
              public void actionPerformed(java.awt.event.ActionEvent evt) {
                  btnXMLActionPerformed(evt);
              }
          });
  
          btnTXT.setText("Output to TXT");
          btnTXT.addActionListener(new java.awt.event.ActionListener() {
              public void actionPerformed(java.awt.event.ActionEvent evt) {
                  btnTXTActionPerformed(evt);
              }
          });
  
          btnCSV.setText("Output to CSV");
          btnCSV.addActionListener(new java.awt.event.ActionListener() {
              public void actionPerformed(java.awt.event.ActionEvent evt) {
                  btnCSVActionPerformed(evt);
              }
          });
  
          treeScrollPane.setViewportView(reportTree);
  
          javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
          getContentPane().setLayout(layout);
          layout.setHorizontalGroup(
              layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addGroup(layout.createSequentialGroup()
                  .addContainerGap()
                  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                      .addComponent(lblPrompt, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                      .addGroup(layout.createSequentialGroup()
                          .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                              .addGroup(layout.createSequentialGroup()
                                  .addComponent(btnXML)
                                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                  .addComponent(btnCSV)
                                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                  .addComponent(btnTXT))
                              .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                  .addComponent(treeScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 649, javax.swing.GroupLayout.PREFERRED_SIZE)
                                  .addGroup(layout.createSequentialGroup()
                                      .addGap(589, 589, 589)
                                      .addComponent(btnDelete))))
                          .addGap(0, 14, Short.MAX_VALUE)))
                  .addContainerGap())
          );
          layout.setVerticalGroup(
              layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
              .addGroup(layout.createSequentialGroup()
                  .addContainerGap()
                  .addComponent(lblPrompt, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                  .addComponent(btnDelete)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                  .addComponent(treeScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 232, javax.swing.GroupLayout.PREFERRED_SIZE)
                  .addGap(18, 18, 18)
                  .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                      .addComponent(btnTXT)
                      .addComponent(btnCSV)
                      .addComponent(btnXML))
                  .addContainerGap(23, Short.MAX_VALUE))
          );
  
          pack();
      }// </editor-fold>//GEN-END:initComponents
  
      /**
       * Deletes the selected node from the tree and removes the corresponding entry from the report.
       * @param evt
       */
      private void btnDeleteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDeleteActionPerformed
          selected = (DefaultMutableTreeNode) reportTree.getLastSelectedPathComponent();
          if (selected == null) {
              //Nothing is selected
              return;
          }
          //If it's a host
          if (selected.getPath().length == 2) {
              String hostname = selected.toString();
              int toDelete = this.getIndexOfHost(hostname);
              if (toDelete == -1) {
                  return;
              }
              report.getReports().remove(toDelete);
          } else if (selected.getPath().length == 3) { //If it's a rule
              String host = selected.getParent().toString();
              String rule = selected.toString();
              int hostNum = this.getIndexOfHost(host);
              int ruleNum = this.getIndexOfRule(hostNum, rule);
              if (hostNum == -1 || ruleNum == -1) {
                  return;
              }
              report.getReports().get(hostNum).getMatchedRules().remove(ruleNum);
          }
          MutableTreeNode parent = (MutableTreeNode) selected.getParent();
          int index = parent.getIndex(selected);
          parent.remove(selected);
          model.nodesWereRemoved(parent, new int[]{index}, new Object[]{selected});
          reportTree.setModel(model);
  
      }//GEN-LAST:event_btnDeleteActionPerformed
  
      private void btnXMLActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnXMLActionPerformed
          this.genericSaveToFile("xml", OutputManager.XML_OUTPUT);
      }//GEN-LAST:event_btnXMLActionPerformed
  
      private void btnCSVActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCSVActionPerformed
          this.genericSaveToFile("csv", OutputManager.CSV_OUTPUT);
      }//GEN-LAST:event_btnCSVActionPerformed
  
      private void btnTXTActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnTXTActionPerformed
          this.genericSaveToFile("txt", OutputManager.TXT_OUTPUT);
      }//GEN-LAST:event_btnTXTActionPerformed
  
      private void genericSaveToFile(String extension, int saveType) {
          int returnCode = fileChooser.showSaveDialog(this);
          try {
              if (returnCode == JFileChooser.APPROVE_OPTION) {
                  File saveFile = fileChooser.getSelectedFile();
                  saveFile = this.ensureFileExtensionPresent(saveFile, extension);
                  OutputManager manager = new OutputManager(report, saveFile, saveType);
                  manager.saveFile();
              }
          } catch (IOException e) {
              JOptionPane.showMessageDialog(this, "Error",
                      "Tried to save file and got exception: " + e.getMessage(),
                      JOptionPane.ERROR_MESSAGE);
          }
      }
  
      /**
       * Uses the name of the host to find it's position in the ArrayList of hosts in the FullReport
       * @param hostname the name of the host to find
       * @return the index of the host in the array
       */
      private int getIndexOfHost(String hostname) {
          int toDelete = -1;
          for (int i = 0; i < report.getReports().size(); i++) {
              String host = report.getReports().get(i).toString(); //Get this HostReport's host
              if (hostname.compareTo(host) == 0) {
                  toDelete = i;
                  break;
              }
          }
          //While this should *never* happen, it's safer to do it this way
          if (toDelete == -1) {
          } else { //We have an error, let's provide debugging information
              System.err.println("ERROR: Tried to delete host " + hostname + " but couldn't find it.");
          }
          return toDelete;
      }
  
      /**
       * Uses the position of the host in the array and the name of the rule to find the rule's position in the host's report
       * @param host the index of the host in the FullReport's HostReport ArrayList.
       * @param ruleName the name of the rule to find the index of
       * @return the index of the rule.
       */
      private int getIndexOfRule(int host, String ruleName) {
          int toDelete = -1;
          for (int i = 0; i < report.getReports().size(); i++) {
              String rule = report.getReports().get(host).getMatchedRules().get(i).toString(); //Get this Host's rules
              if (rule.compareTo(ruleName) == 0) {
                  toDelete = i;
                  break;
              }
          }
          //While this should *never* happen, it's safer to do it this way
          if (toDelete == -1) {
              System.err.println("ERROR: Tried to delete rule " + ruleName + "on host " +
                      report.getReports().get(host).toString() + " but couldn't find it.");
          }
          return toDelete;
      }
  
      private File ensureFileExtensionPresent(File file, String extension) {
          if (file.getName().toLowerCase().matches("(.*)\\." + extension)) { //If file is already correct
              return file;
          }
          File retFile = new File(file.getPath() + "." + extension);
          return retFile;
      }
  
      // Variables declaration - do not modify//GEN-BEGIN:variables
      private javax.swing.JButton btnCSV;
      private javax.swing.JButton btnDelete;
      private javax.swing.JButton btnTXT;
      private javax.swing.JButton btnXML;
      private javax.swing.JLabel lblPrompt;
      private javax.swing.JTree reportTree;
      private javax.swing.JScrollPane treeScrollPane;
      // End of variables declaration//GEN-END:variables
  }
  