org.dc.file.search.ui.DashboardForm.java Source code

Java tutorial

Introduction

Here is the source code for org.dc.file.search.ui.DashboardForm.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.dc.file.search.ui;

import com.google.gson.Gson;
import org.apache.commons.lang.RandomStringUtils;
import org.dc.file.search.Constants.MessageType;
import org.dc.file.search.MessageUtils;
import org.dc.file.search.SearchRequest;
import org.dc.file.search.SearchResult;
import org.dc.file.search.Store;
import org.dc.file.search.dto.Comment;
import org.dc.file.search.dto.DFile;
import org.dc.file.search.dto.Peer;
import org.dc.file.search.dto.Rating;

import javax.swing.*;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import java.awt.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.EventObject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import static org.dc.file.search.ui.DashboardForm.resultFiles;
import static org.dc.file.search.ui.DashboardForm.selectedFile;

/**
 * @author rasikaperera
 */
public class DashboardForm extends javax.swing.JFrame {

    private static final int DEFAULT_SEARCH_HOPE_COUNT = 2;
    private static final int DEFAULT_SEARCH_TIMEOUT_SEC = 5;
    private boolean advancedSearchEnabled = false;

    public final static int PEER_COL_INDEX = 0;
    public final static int HOP_COUNT_COL_INDEX = 1;
    public final static int FILE_COL_INDEX = 2;
    public final static int STAR_RATINGS_COL_INDEX = 3;

    public final static int COMMENT_COL_INDEX = 0;
    public final static int COMMENT_RATING_COL_INDEX = 2;
    public final static int COMMENT_REPLY_COL_INDEX = 3;

    private final static int UUID_LEN = 8;
    private final static int MAX_COLS = 4;

    private final String[] searchColumnNames = { "Peer", "Hop Count", "File", "Ratings" };
    private final String[] commentColumnNames = { "id", "Comment", "Ratings", "Reply" };

    public static int MAX_RATING = 5;

    static volatile Map<String, DFile> resultFiles;
    static volatile String selectedFile = "";

    public enum StarRatingsType {
        COMMENT, FILE, DEFAULT
    }

    /**
     * Creates new form DashboardFormNew
     */
    public DashboardForm() {
        initComponents();
        setLocationRelativeTo(null);

        setVisibleAdvancedSearchPanel(false);
        tblSearchResults.setVisible(false);
        progressBar.setVisible(false);

        initSearchResultsTable();
        initCommentResultsTable();

        Peer localPeer = Store.getInstance().getLocalPeer();
        setTitle("Dashboard :" + localPeer.getUsername() + "(" + localPeer.getKey() + ")");
    }

    private void initCommentResultsTable() {
        txtCommentThread.setText("");
        Object[][] initData = {};
        DefaultTableModel commentModel = new DefaultTableModel(initData, commentColumnNames);
        tblComments.setModel(commentModel);
        tblComments.setRowHeight(32);

        TableColumn commentRatingColumn = tblComments.getColumnModel().getColumn(COMMENT_RATING_COL_INDEX);
        commentRatingColumn.setCellRenderer(new StarRatingsRenderer(tblComments, StarRatingsType.COMMENT));
        commentRatingColumn.setCellEditor(new StarRatingsEditor(tblComments, StarRatingsType.COMMENT));
        commentRatingColumn.setPreferredWidth(30);

        TableColumn commentReply = tblComments.getColumnModel().getColumn(COMMENT_REPLY_COL_INDEX);
        commentReply.setCellRenderer(new ButtonRenderer(tblComments));
        commentReply.setCellEditor(new ButtonEditor(tblComments));

        tblComments.getSelectionModel().addListSelectionListener(event -> {
            if (tblComments.getSelectedRow() >= 0) {
                txtCommentThread.setText("");
                DFile dFile = resultFiles.get(selectedFile);
                String commentId = tblComments.getValueAt(tblComments.getSelectedRow(), 0).toString();
                Comment parentComment = null;
                List<Comment> comments = dFile.getComments();
                for (Comment c : comments) {
                    if (c.getCommentId().equals(commentId)) {
                        parentComment = c;
                        break;
                    }
                }
                if (parentComment == null) {
                    return;
                }
                StringBuilder stringBuilder = new StringBuilder();
                for (Comment replyComment : parentComment.getReplies()) {
                    stringBuilder.append(replyComment.getUserName());
                    stringBuilder.append(": ");
                    stringBuilder.append(replyComment.getText());
                    stringBuilder.append("\n----------------------------------\n");
                }
                txtCommentThread.setText(stringBuilder.toString());
            }
        });
    }

    private void initSearchResultsTable() {
        Object[][] initData = {};
        DefaultTableModel searchModel = new DefaultTableModel(initData, searchColumnNames);
        tblSearchResults.setModel(searchModel);
        tblSearchResults.setRowHeight(32);

        tblSearchResults.getColumnModel().getColumn(PEER_COL_INDEX).setPreferredWidth(30);
        tblSearchResults.getColumnModel().getColumn(HOP_COUNT_COL_INDEX).setPreferredWidth(20);
        tblSearchResults.getColumnModel().getColumn(FILE_COL_INDEX).setPreferredWidth(20);

        TableColumn starRatingsColumn = tblSearchResults.getColumnModel().getColumn(STAR_RATINGS_COL_INDEX);
        starRatingsColumn.setCellRenderer(new StarRatingsRenderer(tblSearchResults, StarRatingsType.FILE));
        starRatingsColumn.setCellEditor(new StarRatingsEditor(tblSearchResults, StarRatingsType.FILE));
        starRatingsColumn.setPreferredWidth(30);

        tblSearchResults.getSelectionModel().addListSelectionListener(event -> {
            if (tblSearchResults.getSelectedRow() >= 0) {
                updateCommentTable();
            }
        });
    }

    private void updateCommentTable() {
        initCommentResultsTable();
        DefaultTableModel model = (DefaultTableModel) tblComments.getModel();
        model.setRowCount(0);

        selectedFile = tblSearchResults.getValueAt(tblSearchResults.getSelectedRow(), FILE_COL_INDEX).toString();
        DFile dFile = resultFiles.get(selectedFile);
        for (int i = 0; i < dFile.getComments().size(); i++) {
            Object[] data = new Object[MAX_COLS];
            data[0] = dFile.getComments().get(i).getCommentId();
            data[1] = dFile.getComments().get(i).getText();
            data[2] = new StarRater(5, dFile.getComments().get(i).getTotalRating(), 0);
            data[3] = new ButtonRenderer(tblComments);
            model.addRow(data);
        }
        tblComments.setModel(model);
        model.fireTableDataChanged();
    }

    /**
     * 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() {

        txtSearchKey = new javax.swing.JTextField();
        btnSearch = new javax.swing.JButton();
        jScrollPane1 = new javax.swing.JScrollPane();
        tblSearchResults = new javax.swing.JTable();
        btnFilesList = new javax.swing.JButton();
        btnPeersList = new javax.swing.JButton();
        jLabel1 = new javax.swing.JLabel();
        chkAdvancedSearch = new javax.swing.JCheckBox();
        lblHopCount = new javax.swing.JLabel();
        txtHopCount = new javax.swing.JTextField();
        lblTimeout = new javax.swing.JLabel();
        txtTimeoutSec = new javax.swing.JTextField();
        lblSec = new javax.swing.JLabel();
        progressBar = new javax.swing.JProgressBar();
        sliderHopCount = new javax.swing.JSlider();
        sliderTimout = new javax.swing.JSlider();
        jLabel2 = new javax.swing.JLabel();
        btnNewComment = new javax.swing.JButton();
        jScrollPane3 = new javax.swing.JScrollPane();
        tblComments = new javax.swing.JTable();
        jScrollPane4 = new javax.swing.JScrollPane();
        txtCommentThread = new javax.swing.JTextArea();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
        });

        btnSearch.setText("Search");
        btnSearch.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnSearchActionPerformed(evt);
            }
        });

        tblSearchResults
                .setModel(new javax.swing.table.DefaultTableModel(
                        new Object[][] { { null, null, null, null }, { null, null, null, null },
                                { null, null, null, null }, { null, null, null, null } },
                        new String[] { "Peer", "Hop Count", "File", "Ratings" }));
        jScrollPane1.setViewportView(tblSearchResults);

        btnFilesList.setText("Files List");
        btnFilesList.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnFilesListActionPerformed(evt);
            }
        });

        btnPeersList.setText("Peers List");
        btnPeersList.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnPeersListActionPerformed(evt);
            }
        });

        jLabel1.setText("File Name:");

        chkAdvancedSearch.setText("Enable Advanced Search");
        chkAdvancedSearch.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                chkAdvancedSearchActionPerformed(evt);
            }
        });

        lblHopCount.setLabelFor(txtHopCount);
        lblHopCount.setText("Hop Count:");

        txtHopCount.setEditable(false);
        txtHopCount.setText("2");
        txtHopCount.setToolTipText("");

        lblTimeout.setLabelFor(txtTimeoutSec);
        lblTimeout.setText("Timeout:");

        txtTimeoutSec.setEditable(false);
        txtTimeoutSec.setText("5");

        lblSec.setText("sec");

        sliderHopCount.setMaximum(20);
        sliderHopCount.setMinimum(2);
        sliderHopCount.setValue(2);
        sliderHopCount.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                sliderHopCountStateChanged(evt);
            }
        });

        sliderTimout.setMaximum(60);
        sliderTimout.setMinimum(5);
        sliderTimout.setMinorTickSpacing(5);
        sliderTimout.setValue(5);
        sliderTimout.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                sliderTimoutStateChanged(evt);
            }
        });

        jLabel2.setText("Comments");

        btnNewComment.setText("New Comment");
        btnNewComment.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnNewCommentActionPerformed(evt);
            }
        });

        tblComments
                .setModel(new javax.swing.table.DefaultTableModel(
                        new Object[][] { { null, null, null, null }, { null, null, null, null },
                                { null, null, null, null }, { null, null, null, null } },
                        new String[] { "Id", "Comment", "Ratings", "Reply" }));
        jScrollPane3.setViewportView(tblComments);
        if (tblComments.getColumnModel().getColumnCount() > 0) {
            tblComments.getColumnModel().getColumn(0).setPreferredWidth(250);
        }

        txtCommentThread.setColumns(20);
        txtCommentThread.setRows(5);
        jScrollPane4.setViewportView(txtCommentThread);

        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(progressBar, javax.swing.GroupLayout.DEFAULT_SIZE,
                                        javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .addComponent(jScrollPane1)
                                .addGroup(layout.createSequentialGroup().addComponent(jLabel1)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                        .addComponent(txtSearchKey)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(btnSearch))
                                .addGroup(layout
                                        .createSequentialGroup().addComponent(jLabel2)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED,
                                                javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                        .addComponent(btnNewComment))
                                .addGroup(javax.swing.GroupLayout.Alignment.TRAILING,
                                        layout.createSequentialGroup().addGap(0, 0, Short.MAX_VALUE).addGroup(layout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                                .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout
                                                        .createSequentialGroup().addComponent(btnFilesList)
                                                        .addPreferredGap(
                                                                javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                        .addComponent(btnPeersList))
                                                .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout
                                                        .createSequentialGroup().addComponent(lblHopCount)
                                                        .addPreferredGap(
                                                                javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                        .addComponent(txtHopCount,
                                                                javax.swing.GroupLayout.PREFERRED_SIZE, 33,
                                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                                        .addPreferredGap(
                                                                javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                        .addComponent(sliderHopCount,
                                                                javax.swing.GroupLayout.PREFERRED_SIZE, 170,
                                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                                        .addPreferredGap(
                                                                javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                        .addComponent(lblTimeout)
                                                        .addPreferredGap(
                                                                javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                                        .addComponent(txtTimeoutSec,
                                                                javax.swing.GroupLayout.PREFERRED_SIZE, 33,
                                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                                        .addGap(5, 5, 5).addComponent(lblSec)
                                                        .addPreferredGap(
                                                                javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                                        .addComponent(sliderTimout,
                                                                javax.swing.GroupLayout.PREFERRED_SIZE, 123,
                                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                                        .addGap(5, 5, 5))))
                                .addComponent(jScrollPane3).addGroup(layout.createSequentialGroup()
                                        .addComponent(chkAdvancedSearch).addGap(0, 0, Short.MAX_VALUE))
                                .addComponent(jScrollPane4))
                        .addContainerGap()));
        layout.setVerticalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup().addGap(10, 10, 10).addGroup(layout
                        .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE).addComponent(btnSearch)
                        .addComponent(txtSearchKey, javax.swing.GroupLayout.PREFERRED_SIZE,
                                javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addComponent(jLabel1)).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(chkAdvancedSearch)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(
                                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                        .addGroup(layout
                                                .createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                                .addComponent(lblHopCount)
                                                .addComponent(txtHopCount, javax.swing.GroupLayout.PREFERRED_SIZE,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                        javax.swing.GroupLayout.PREFERRED_SIZE)
                                                .addComponent(lblTimeout)
                                                .addComponent(txtTimeoutSec, javax.swing.GroupLayout.PREFERRED_SIZE,
                                                        javax.swing.GroupLayout.DEFAULT_SIZE,
                                                        javax.swing.GroupLayout.PREFERRED_SIZE)
                                                .addComponent(lblSec))
                                        .addComponent(sliderHopCount, javax.swing.GroupLayout.PREFERRED_SIZE,
                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addComponent(sliderTimout, javax.swing.GroupLayout.PREFERRED_SIZE,
                                                javax.swing.GroupLayout.DEFAULT_SIZE,
                                                javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE,
                                javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 158,
                                javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                .addComponent(btnNewComment).addComponent(jLabel2))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jScrollPane3, javax.swing.GroupLayout.PREFERRED_SIZE, 107,
                                javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jScrollPane4, javax.swing.GroupLayout.DEFAULT_SIZE, 138, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                                .addComponent(btnFilesList).addComponent(btnPeersList))
                        .addContainerGap()));

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void btnSearchActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSearchActionPerformed
        int hopCount = DEFAULT_SEARCH_HOPE_COUNT;
        int timeout = DEFAULT_SEARCH_TIMEOUT_SEC;

        if (advancedSearchEnabled) {
            try {
                hopCount = Integer.parseInt(txtHopCount.getText());
                timeout = Integer.parseInt(txtTimeoutSec.getText());
            } catch (NumberFormatException e) {
                JOptionPane.showMessageDialog(this, "Default search options will be used",
                        "Invalid Search Options!", JOptionPane.ERROR_MESSAGE);
            }
        }

        Store store = Store.getInstance();
        String key = txtSearchKey.getText();
        Peer localPeer = store.getLocalPeer();
        SearchRequest searchRequest = new SearchRequest(Calendar.getInstance().getTimeInMillis(), key, hopCount,
                localPeer);
        store.setMySearchRequest(searchRequest);
        store.addSearchRequest(searchRequest);
        store.setSearchResults(new ArrayList<>());
        List<DFile> results = Store.getInstance().findInFiles(searchRequest.getSearchKey());
        if (!results.isEmpty()) {
            SearchResult searchResult = new SearchResult(key, localPeer, 0, results);
            store.addSearchResult(searchResult);
        }
        for (Map.Entry<String, Peer> entry : Store.getInstance().getPeerMap().entrySet()) {
            Peer peer = entry.getValue();
            MessageUtils.sendUDPMessage(peer.getIp(), peer.getPort(),
                    MessageType.SER + " " + localPeer.getIp() + " " + localPeer.getPort() + " \"" + key + "\" 2");
        }
        Runnable resultTask = () -> {
            try {
                List<SearchResult> searchResults = Store.getInstance().getSearchResults();
                resultFiles = new HashMap<>();
                if (searchResults != null) {
                    DefaultTableModel model = (DefaultTableModel) tblSearchResults.getModel();
                    model.setRowCount(0);
                    for (int i = 0; i < searchResults.size(); i++) {
                        Object[] data = new Object[MAX_COLS];
                        SearchResult searchResult = searchResults.get(i);
                        Peer peer = searchResult.getPeerWithResults();
                        data[0] = peer.getKey();
                        data[1] = searchResult.getHopCount();
                        for (DFile dFile : searchResult.getResults()) {
                            data[2] = dFile.getFileName();
                            data[3] = new StarRater(5, dFile.getTotalRating(), 0);
                            resultFiles.put(dFile.getFileName(), dFile);
                            model.addRow(data);
                        }
                    }
                    tblSearchResults.setModel(model);
                    model.fireTableDataChanged();
                }
            } catch (Throwable t) {
                System.out.println(t.getMessage());
            } finally {
                btnSearch.setEnabled(true);
            }
        };
        initSearchResultsTable();
        initCommentResultsTable();
        btnSearch.setEnabled(false);
        tblSearchResults.setVisible(true);
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.schedule(resultTask, timeout, TimeUnit.SECONDS);
        resetAndStartProgress(timeout);
    }//GEN-LAST:event_btnSearchActionPerformed

    private void btnFilesListActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnFilesListActionPerformed
        new PeersListForm().setVisible(true);
    }//GEN-LAST:event_btnFilesListActionPerformed

    private void btnPeersListActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnPeersListActionPerformed
        new MyFilesListForm().setVisible(true);
    }//GEN-LAST:event_btnPeersListActionPerformed

    private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
        performExit();
    }//GEN-LAST:event_formWindowClosing

    private void chkAdvancedSearchActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chkAdvancedSearchActionPerformed
        if (!advancedSearchEnabled) {
            advancedSearchEnabled = true;
            setVisibleAdvancedSearchPanel(true);
        } else {
            advancedSearchEnabled = false;
            setVisibleAdvancedSearchPanel(false);
        }
    }//GEN-LAST:event_chkAdvancedSearchActionPerformed

    private void sliderHopCountStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_sliderHopCountStateChanged
        txtHopCount.setText(String.valueOf(sliderHopCount.getValue()));
    }//GEN-LAST:event_sliderHopCountStateChanged

    private void sliderTimoutStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_sliderTimoutStateChanged
        txtTimeoutSec.setText(String.valueOf(sliderTimout.getValue()));
    }//GEN-LAST:event_sliderTimoutStateChanged

    private void btnNewCommentActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnNewCommentActionPerformed
        int row = tblSearchResults.getSelectedRow();
        if (row == -1) {
            JOptionPane.showMessageDialog(this, "Please select a file first.", "Information",
                    JOptionPane.INFORMATION_MESSAGE);
            return;
        }
        String fileName = tblSearchResults.getModel().getValueAt(row, 2).toString();
        String commentString = JOptionPane.showInputDialog(null, "Add comment for " + fileName, "Add A New Comment",
                JOptionPane.QUESTION_MESSAGE);
        if (commentString != null && !commentString.isEmpty()) {
            String username = Store.getInstance().getLocalPeer().getUsername();
            DFile commentedFile = resultFiles.get(fileName);
            Comment comment = new Comment();
            comment.setCommentId(RandomStringUtils.randomAlphanumeric(8));
            comment.setFileName(fileName);
            comment.setUserName(username);
            comment.setText(commentString);

            List<Comment> comments = commentedFile.getComments();
            comments.add(comment);

            Store.getInstance().addComment(comment);
            final String commentJSON = new Gson().toJson(comment);
            Store.getInstance().getPeerList().forEach(stringPeerEntry -> {
                String peerIP = stringPeerEntry.getValue().getIp();
                int peerPort = stringPeerEntry.getValue().getPort();
                Peer localPeer = Store.getInstance().getLocalPeer();
                MessageUtils.sendUDPMessage(peerIP, peerPort, MessageType.COMMENT + " " + localPeer.getIp() + " "
                        + localPeer.getPort() + " " + commentJSON);
            });

            //Update comments table again
            updateCommentTable();
        }
    }//GEN-LAST:event_btnNewCommentActionPerformed

    private void performExit() {
        Store store = Store.getInstance();
        Peer localPeer = store.getLocalPeer();
        for (Map.Entry<String, Peer> entry : Store.getInstance().getPeerMap().entrySet()) {
            Peer peer = entry.getValue();
            MessageUtils.sendUDPMessage(peer.getIp(), peer.getPort(),
                    MessageType.LEAVE + " " + localPeer.getIp() + " " + localPeer.getPort());
        }
        MessageUtils.sendTCPMessage(store.getServerIp(), store.getServerPort(), MessageType.UNREG + " "
                + localPeer.getIp() + " " + localPeer.getPort() + " " + localPeer.getUsername());
    }

    private void resetAndStartProgress(int durationSec) {
        int maxValue = durationSec * 1000;
        progressBar.setMinimum(0);
        progressBar.setMaximum(maxValue);
        progressBar.setValue(0);
        progressBar.setVisible(true);

        new Thread(() -> {
            int count = 0;
            while (count < maxValue) {
                count = count + 100;
                progressBar.setValue(count);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    //do nothing
                }
            }
            progressBar.setVisible(false);
        }).start();
    }

    private void setVisibleAdvancedSearchPanel(boolean visibility) {
        lblHopCount.setVisible(visibility);
        txtHopCount.setVisible(visibility);
        lblTimeout.setVisible(visibility);
        txtTimeoutSec.setVisible(visibility);
        lblSec.setVisible(visibility);
        sliderHopCount.setVisible(visibility);
        sliderTimout.setVisible(visibility);
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton btnFilesList;
    private javax.swing.JButton btnNewComment;
    private javax.swing.JButton btnPeersList;
    private javax.swing.JButton btnSearch;
    private javax.swing.JCheckBox chkAdvancedSearch;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JScrollPane jScrollPane4;
    private javax.swing.JLabel lblHopCount;
    private javax.swing.JLabel lblSec;
    private javax.swing.JLabel lblTimeout;
    private javax.swing.JProgressBar progressBar;
    private javax.swing.JSlider sliderHopCount;
    private javax.swing.JSlider sliderTimout;
    private javax.swing.JTable tblComments;
    private javax.swing.JTable tblSearchResults;
    private javax.swing.JTextArea txtCommentThread;
    private javax.swing.JTextField txtHopCount;
    private javax.swing.JTextField txtSearchKey;
    private javax.swing.JTextField txtTimeoutSec;
    // End of variables declaration//GEN-END:variables
}

class StarRatingsPanel extends JPanel {

    private static String DEFAULT = "0";
    protected volatile StarRater starRater = new StarRater(DashboardForm.MAX_RATING, 0, 0);

    public StarRatingsPanel() {
        setLayout(new GridLayout());
        setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        Store store = Store.getInstance();
        starRater.addStarListener(selection -> {
            if (selection > DashboardForm.MAX_RATING) {
                selection = DashboardForm.MAX_RATING;
            }
            Map<String, Object> properties = starRater.getProperties();
            String fileName = null;
            String commentId = null;
            DashboardForm.StarRatingsType type = null;
            if (properties != null && !properties.isEmpty()) {
                fileName = (String) properties.get("fileName");
                commentId = (String) properties.get("comment");
                type = (DashboardForm.StarRatingsType) properties.get("type");
            }

            String username = Store.getInstance().getLocalPeer().getUsername();
            DFile ratedFile = resultFiles.get(fileName);
            Rating rating = new Rating();
            rating.setFileName(fileName);
            rating.setCommentId(commentId);
            rating.setRatingId(RandomStringUtils.randomAlphanumeric(8));
            rating.setUserName(username);
            rating.setValue(selection);

            if (DashboardForm.StarRatingsType.COMMENT == type) {
                List<Comment> comments = ratedFile.getComments();
                Comment comment = null;
                for (Comment c : comments) {
                    if (c.getCommentId().equals(commentId)) {
                        comment = c;
                        break;
                    }
                }
                if (comment != null) {
                    List<Rating> commentRatings = comment.getRatings();
                    boolean isNotRated = true;
                    for (Rating r : commentRatings) {
                        if (r.getUserName().equals(username)) {
                            r.setValue(selection);
                            isNotRated = false;
                        }
                    }
                    if (isNotRated) {
                        commentRatings.add(rating);
                    }
                }
            } else {
                List<Rating> fileRatings = ratedFile.getRatings();
                boolean isNotRated = true;
                for (Rating r : fileRatings) {
                    if (r.getUserName().equals(username)) {
                        r.setValue(selection);
                        isNotRated = false;
                    }
                }
                if (isNotRated) {
                    fileRatings.add(rating);
                }
            }

            Store.getInstance().addRating(rating);
            final String ratingJSON = new Gson().toJson(rating);
            store.getPeerList().forEach(stringPeerEntry -> {
                String peerIP = stringPeerEntry.getValue().getIp();
                int peerPort = stringPeerEntry.getValue().getPort();
                Peer localPeer = store.getLocalPeer();
                MessageUtils.sendUDPMessage(peerIP, peerPort,
                        MessageType.RATE + " " + localPeer.getIp() + " " + localPeer.getPort() + " " + ratingJSON);
            });
        });
        add(starRater);
    }

    public void updateValue(StarRater bt, String fileName, String comment, DashboardForm.StarRatingsType type) {
        starRater.setRating(bt.getRating());
        Map<String, Object> properties = new HashMap<>();
        properties.put("fileName", fileName);
        properties.put("comment", comment);
        properties.put("type", type);
        starRater.setProperties(properties);
    }
}

class ButtonPanel extends JPanel {

    private static String DEFAULT = "0";
    protected JTable jTable;
    protected volatile JButton button = new JButton("Reply");

    public ButtonPanel(JTable jTable) {
        this.jTable = jTable;
        setLayout(new GridLayout());
        setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));

        button.addActionListener(e -> {
            int row = jTable.getSelectedRow();
            if (row == -1) {
                JOptionPane.showMessageDialog(this, "Please select a comment first.", "Information",
                        JOptionPane.INFORMATION_MESSAGE);
                return;
            }
            String commentId = jTable.getValueAt(jTable.getSelectedRow(), 0).toString();
            String commentString = JOptionPane.showInputDialog(null, "Add reply for " + commentId,
                    "Add A New Reply", JOptionPane.QUESTION_MESSAGE);
            if (commentString != null && !commentString.isEmpty()) {
                String username = Store.getInstance().getLocalPeer().getUsername();
                DFile commentedFile = resultFiles.get(selectedFile);
                List<Comment> comments = commentedFile.getComments();
                Comment selectedComment = null;
                for (Comment c : comments) {
                    if (c.getCommentId().equals(commentId)) {
                        selectedComment = c;
                        break;
                    }
                }

                if (selectedComment != null) {
                    Comment replyComment = new Comment();
                    replyComment.setCommentId(RandomStringUtils.randomAlphanumeric(8));
                    replyComment.setFileName(selectedFile);
                    replyComment.setParentId(commentId);
                    replyComment.setUserName(username);
                    replyComment.setText(commentString);

                    selectedComment.getReplies().add(replyComment);
                    Store.getInstance().addComment(replyComment);
                    final String commentJSON = new Gson().toJson(replyComment);
                    Store.getInstance().getPeerList().forEach(stringPeerEntry -> {
                        String peerIP = stringPeerEntry.getValue().getIp();
                        int peerPort = stringPeerEntry.getValue().getPort();
                        Peer localPeer = Store.getInstance().getLocalPeer();
                        MessageUtils.sendUDPMessage(peerIP, peerPort, MessageType.COMMENT + " " + localPeer.getIp()
                                + " " + localPeer.getPort() + " " + commentJSON);
                    });
                }
            }
        });
        add(button);
    }
}

class ButtonRenderer extends ButtonPanel implements TableCellRenderer {

    public ButtonRenderer(JTable jTable) {
        super(jTable);
        setOpaque(true);
    }

    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
            int row, int column) {
        setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
        return this;
    }
}

class ButtonEditor extends ButtonPanel implements TableCellEditor {
    protected transient ChangeEvent changeEvent;

    public ButtonEditor(JTable jTable) {
        super(jTable);
    }

    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
            int column) {
        this.setBackground(table.getSelectionBackground());
        return this;
    }

    public Object getCellEditorValue() {
        return button;
    }

    @Override
    public boolean isCellEditable(EventObject anEvent) {
        return true;
    }

    @Override
    public boolean shouldSelectCell(EventObject anEvent) {
        return false;
    }

    public boolean stopCellEditing() {
        fireEditingStopped();
        return true;
    }

    @Override
    public void cancelCellEditing() {
        fireEditingCanceled();
    }

    @Override
    public void addCellEditorListener(CellEditorListener l) {
        listenerList.add(CellEditorListener.class, l);
    }

    @Override
    public void removeCellEditorListener(CellEditorListener l) {
        listenerList.remove(CellEditorListener.class, l);
    }

    public CellEditorListener[] getCellEditorListeners() {
        return listenerList.getListeners(CellEditorListener.class);
    }

    protected void fireEditingStopped() {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == CellEditorListener.class) {
                // Lazily create the event:
                if (Objects.isNull(changeEvent)) {
                    changeEvent = new ChangeEvent(this);
                }
                ((CellEditorListener) listeners[i + 1]).editingStopped(changeEvent);
            }
        }
    }

    protected void fireEditingCanceled() {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == CellEditorListener.class) {
                // Lazily create the event:
                if (Objects.isNull(changeEvent)) {
                    changeEvent = new ChangeEvent(this);
                }
                ((CellEditorListener) listeners[i + 1]).editingCanceled(changeEvent);
            }
        }
    }
}

class StarRatingsRenderer extends StarRatingsPanel implements TableCellRenderer {

    public static final DefaultTableCellRenderer DEFAULT_RENDERER = new DefaultTableCellRenderer();
    public static final int TYPE_COMMENT = 1;
    public static final int TYPE_FILE = 2;

    private final JTable jTable;
    private final DashboardForm.StarRatingsType type;

    public StarRatingsRenderer() {
        super();
        setName("Table.cellRenderer");
        this.jTable = null;
        this.type = DashboardForm.StarRatingsType.DEFAULT;
    }

    public StarRatingsRenderer(JTable jTable, DashboardForm.StarRatingsType type) {
        this.jTable = jTable;
        this.type = type;
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
            int row, int column) {
        setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
        if (jTable != null) {
            String fileName;
            String comment = "";
            if (type == DashboardForm.StarRatingsType.COMMENT) {
                comment = (String) jTable.getModel().getValueAt(row, DashboardForm.COMMENT_COL_INDEX);
                fileName = selectedFile;
            } else {
                fileName = (String) jTable.getModel().getValueAt(row, DashboardForm.FILE_COL_INDEX);
            }
            updateValue((StarRater) value, fileName, comment, type);
        }
        return this;
    }
}

class StarRatingsEditor extends StarRatingsPanel implements TableCellEditor {

    private final JTable jTable;
    private final DashboardForm.StarRatingsType type;
    protected transient ChangeEvent changeEvent;

    public StarRatingsEditor(JTable jTable, DashboardForm.StarRatingsType type) {
        this.jTable = jTable;
        this.type = type;
    }

    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
            int column) {
        this.setBackground(table.getSelectionBackground());
        String fileName;
        String comment = "";
        if (type == DashboardForm.StarRatingsType.COMMENT) {
            comment = (String) jTable.getModel().getValueAt(row, DashboardForm.COMMENT_COL_INDEX);
            fileName = selectedFile;
        } else {
            fileName = (String) jTable.getModel().getValueAt(row, DashboardForm.FILE_COL_INDEX);
        }
        jTable.getModel().setValueAt(value, row, DashboardForm.STAR_RATINGS_COL_INDEX);
        updateValue((StarRater) value, fileName, comment, type);
        return this;
    }

    @Override
    public Object getCellEditorValue() {
        return starRater;
    }

    @Override
    public boolean isCellEditable(EventObject e) {
        return true;
    }

    @Override
    public boolean shouldSelectCell(EventObject anEvent) {
        return true;
    }

    @Override
    public boolean stopCellEditing() {
        fireEditingStopped();
        return true;
    }

    @Override
    public void cancelCellEditing() {
        fireEditingCanceled();
    }

    @Override
    public void addCellEditorListener(CellEditorListener l) {
        listenerList.add(CellEditorListener.class, l);
    }

    @Override
    public void removeCellEditorListener(CellEditorListener l) {
        listenerList.remove(CellEditorListener.class, l);
    }

    public CellEditorListener[] getCellEditorListeners() {
        return listenerList.getListeners(CellEditorListener.class);
    }

    protected void fireEditingStopped() {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == CellEditorListener.class) {
                // Lazily create the event:
                if (Objects.isNull(changeEvent)) {
                    changeEvent = new ChangeEvent(this);
                }
                ((CellEditorListener) listeners[i + 1]).editingStopped(changeEvent);
            }
        }
    }

    protected void fireEditingCanceled() {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == CellEditorListener.class) {
                // Lazily create the event:
                if (Objects.isNull(changeEvent)) {
                    changeEvent = new ChangeEvent(this);
                }
                ((CellEditorListener) listeners[i + 1]).editingCanceled(changeEvent);
            }
        }
    }
}