package com.cipres.mrBayesPlugin.ui; import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.net.UnknownHostException; import java.net.NoRouteToHostException; import javax.swing.GroupLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.SwingWorker; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableColumn; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.ngbw.directclient.CiCipresException; import org.ngbw.directclient.CiClient; import org.ngbw.directclient.CiJob; import com.cipres.mrBayesPlugin.CipresMrBayesToolbar; import com.cipres.mrBayesPlugin.models.UserModel; import com.cipres.mrBayesPlugin.models.UserModel.Job; import com.cipres.mrBayesPlugin.utilities.CipresUtilities; import com.cipres.mrBayesPlugin.utilities.DataHandlingUtilities; import com.cipres.mrBayesPlugin.utilities.SelectedJobInfo; import com.biomatters.geneious.publicapi.components.Dialogs; import com.biomatters.geneious.publicapi.plugin.Icons; import com.biomatters.geneious.publicapi.utilities.GuiUtilities; import com.biomatters.geneious.publicapi.utilities.IconUtilities; /** * Display user's jobs and allow users to submit/modify jobs * @author rjzheng * @author mgujral * */ @SuppressWarnings("serial") public class JobManagePanel extends JPanel implements ActionListener, TableModelListener{ private static final String NEWLINE = System.getProperty("line.separator"); public static JTable table; public static TableModel table_model; public String selectedFolder = null; //Following line is not needed any more //public static List selected = new ArrayList(); //private List jobNamesListWithDate; private List jobNamesList; public static Boolean fire = true; private static final String SELECT ="Select"; private static final String JOBNAME = "Job Name"; private static final String DATESUBMITTED = "Date Submitted"; private static final String JOBSTATUS = "Job Status"; private static final String LOCALFOLDER = "Local Folder"; private static final String DOWNLOADJOB = "Download Job(s)"; private static final String IMPORTINTOGENEIOUS = "Import into Geneious"; private static final String UPDATELIST = "Update List"; private static final String DELETEJOB = "Delete Job"; private static final String[] BUTTONSLABELS = {"Download now"}; private JFileChooser fldrChsr; private JPanel myPanel; JLabel busyLabel; private boolean importIntoGeneious = false; public static TableModel getTable_model() { return table_model; } public static void setTable_model(TableModel table_model) { JobManagePanel.table_model = table_model; } public static JTable getTable() { return table; } public static void setTable(JTable table) { JobManagePanel.table = table; } /** * Create and layout the job list table and buttons * @param json * @return panel */ public JPanel createPanel(JSONArray json){ Image image = IconUtilities.getImage("activity_white.gif"); ImageIcon busyIcon = new ImageIcon(image); busyLabel = new JLabel(busyIcon); busyLabel.setVisible(false); //Create the Panel JPanel panel = new JPanel(); //Add layout to panel GroupLayout layout = new GroupLayout(panel); //Create the buttons JButton submitButton = new JButton("Submit Job"); JButton updateButton = new JButton(UPDATELIST); JButton downloadButton = new JButton(DOWNLOADJOB); JCheckBox importCheckBox = new JCheckBox(IMPORTINTOGENEIOUS); JButton deleteButton = new JButton(DELETEJOB); updateButton.addActionListener(this); submitButton.addActionListener(this); downloadButton.addActionListener(this); importCheckBox.addActionListener(this); deleteButton.addActionListener(this); //Create the table table = createTable(json); table.getModel().addTableModelListener(this); //Add the table to a scroll panel JScrollPane scroll = new JScrollPane(table); //Set the layout panel.setLayout(layout); layout.setAutoCreateContainerGaps(true); layout.setAutoCreateGaps(true); //Set the horizontal grouping layout.setHorizontalGroup( layout.createSequentialGroup() .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).addComponent(scroll).addComponent(busyLabel)) //.addComponent(scroll) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(updateButton) //.addComponent(submitButton) .addComponent(downloadButton) .addComponent(importCheckBox) //.addComponent(busyLabel) .addComponent(deleteButton)) ); //Set the vertical grouping layout.setVerticalGroup( layout.createSequentialGroup() .addGroup(layout.createParallelGroup() .addComponent(scroll) .addGroup(layout.createSequentialGroup() .addComponent(updateButton) //.addComponent(submitButton) .addComponent(downloadButton) .addComponent(importCheckBox) //.addComponent(busyLabel) .addComponent(deleteButton))) .addComponent(busyLabel) ); myPanel = panel; return panel; } public void tableChanged(TableModelEvent e) { if(fire == true){ int row = e.getFirstRow(); int column = e.getColumn(); TableModel model = (TableModel)e.getSource(); Object data = model.getValueAt(row, column); System.out.println("data is:" + data); //selected_jobs.add((String)data); } } /** * Create the table with user's job list * @param json * @return table */ public JTable createTable(JSONArray json){ try{ List jobList = new ArrayList(); UserModel temp = new UserModel(); SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); for(int i = 0; i < json.size(); i++){ Job job = temp.new Job(); JSONObject obj = (JSONObject) json.get(i); job.setSelected(false); job.setJobName(obj.get("jobName") == null? "": obj.get("jobName").toString()); job.setDate(df.parse(obj.get("date").toString())); job.setJobStage(obj.get("jobStage").toString()); job.setImported(false); job.setLocalFolder(""); jobList.add(job); } table_model = new TableModel(jobList); JTable new_table = new JTable(table_model); return new_table; }catch (ParseException e) { e.printStackTrace(); } return null; } /** * Customized table model * @author rjzheng * */ static class TableModel extends AbstractTableModel { //Default table column names public static String[] columnNames = { SELECT, JOBNAME, DATESUBMITTED, JOBSTATUS, LOCALFOLDER }; public enum ColumnEnum { SELECT, JOBNAME, DATESUBMITTED, JOBSTATUS, LOCALFOLDER }; private List jobList = new ArrayList(); private boolean addLastColumn = false; /** * Empty constructor */ public TableModel(){} /** * Constructor with job list input * @param jobs */ public TableModel(List jobs){ this.jobList = jobs; } /** * Get the column count * @return column count */ public int getColumnCount() { return (columnNames.length - (addLastColumn ? 0 : 1)); } /** * Get the row count * @return job list size */ public int getRowCount() { return jobList.size(); } /** * Get the column name at a given column * @param col * @return column name */ public String getColumnName(int col) { return columnNames[col]; } /** * Get and set the value of a given row and column * @param row * @param col * @return job object */ public Object getValueAt(int row, int col) { Object jobAttribute = null; Job jobObj = jobList.get(row); switch(col){ case 0: jobAttribute = jobObj.getSelected();break; case 1: jobAttribute = jobObj.getJobName(); break; case 2: jobAttribute = jobObj.getDate(); break; case 3: jobAttribute = jobObj.getJobStage(); break; case 4: jobAttribute = jobObj.getLocalFolder(); break; } return jobAttribute; } @Override public void setValueAt(Object val, int row, int col) { Job jobObj = jobList.get(row); switch(col){ case 0: jobObj.setSelected((Boolean)val); break; case 1: jobObj.setJobName((String)val); break; case 2: jobObj.setDate((Date)val); break; case 3: jobObj.setJobStage((String)val); case 4: jobObj.setLocalFolder((String)val); break; } fireTableCellUpdated(row, col); } public boolean isCellEditable(int row, int column) { return (column == ColumnEnum.SELECT.ordinal() || column == ColumnEnum.LOCALFOLDER.ordinal()); } /** * Display checkbox instead of true/false */ @SuppressWarnings({ "unchecked", "rawtypes" }) public Class getColumnClass(int c) { if(c == ColumnEnum.SELECT.ordinal()){ return Boolean.class; }else{ return String.class; } } /** * Add job and update changes to table * @param job */ public void addJob(Job job){ jobList.add(job); fire = false; fireTableDataChanged(); fire = true; } public void deleteJob(int index){ jobList.remove(index); fire = false; fireTableDataChanged(); fire = true; } public void clearTable(){ while(!jobList.isEmpty()){ this.deleteJob(0); } fire = false; fireTableDataChanged(); fire = true; } public void setaddLastColumn(boolean addLastColumn) { this.addLastColumn = addLastColumn; } } public void updateTable(JSONArray json){ try { table_model.clearTable(); SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); UserModel temp = new UserModel(); for(int i = 0; i < json.size(); i++){ Job job = temp.new Job(); JSONObject obj = (JSONObject) json.get(i); job.setSelected(false); job.setJobName(obj.get("jobName") == null? "" : obj.get("jobName").toString()); job.setDate(df.parse(obj.get("date").toString())); job.setJobStage(obj.get("jobStage").toString()); table_model.addJob(job); } }catch (ParseException e1) { e1.printStackTrace(); } } /** * * @param e * extensively modified by Madhusudan Gujral */ @Override public void actionPerformed(ActionEvent e) { try { DataHandlingUtilities handler = DataHandlingUtilities.getInstance(); UserModel user = handler.getUser(); CiClient client = handler.getClient(); Collection allJobs = client.listJobs(); TableModel pTModel = getTable_model(); int rowCount = pTModel.getRowCount(); int colCount = pTModel.getColumnCount(); if (table.isEditing()) { table.getCellEditor().stopCellEditing(); pTModel.fireTableDataChanged(); } if(rowCount == 0){ Dialogs.showMessageDialog("There are no listed jobs."); return; } if(e.getActionCommand() == IMPORTINTOGENEIOUS){ boolean selectedRow = isThereSelectedRow(rowCount, pTModel); if (e.getSource() instanceof JCheckBox) importIntoGeneious = ((JCheckBox)e.getSource()).isSelected(); if (!importIntoGeneious) { TableModel model = (TableModel)table.getModel(); table.removeColumn(table.getColumnModel().getColumn(TableModel.ColumnEnum.LOCALFOLDER.ordinal())); model.setaddLastColumn(false); } else { TableModel model = (TableModel)table.getModel(); model.setaddLastColumn(true); TableColumn col = new TableColumn(model.getColumnCount()-1); col.setHeaderValue(TableModel.columnNames[model.getColumnCount()-1]); col.setCellRenderer(new CellHighlighterRenderer()); table.addColumn(col); Dialogs.showMessageDialog("Please enter a Geneious folder name into the Local Folder column " + (selectedRow? "(highlighted in yellow) for each selected job to which downloaded result files will be imported " : " after selecting job(s) for download/import ") + "and then click Download Job(s) button. " + (selectedRow? "" : "Downloaded files will be imported into the folder(s) you enter.") + "\n\nPlease note: a Geneious folder is not the same as a folder on PC/Mac.\n" + "A Geneious folder is a folder under Local on your Sources Panel on the left-hand side of your Geneious window."); } System.out.println("importIntoGeneious = "+ importIntoGeneious); } else if(e.getActionCommand() == UPDATELIST){ myPanel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); busyLabel.setText("Please wait while updating job list ..."); busyLabel.setVisible(true); SwingWorker myWorker= new SwingWorker() { @Override protected String doInBackground() throws Exception { JSONArray retJSONArray = CipresUtilities.updateList(CipresMrBayesToolbar.myClient, user); busyLabel.setVisible(false); myPanel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); updateTable(retJSONArray); return null; } }; myWorker.execute(); //JSONArray retJSONArray = CipresUtilities.updateList(CipresMrBayesToolbar.myClient, user); //updateTable(retJSONArray); } else if(e.getActionCommand() == DELETEJOB){ boolean selectedRow = isThereSelectedRow(rowCount, pTModel); if(!selectedRow){ Dialogs.showMessageDialog("Please select at least one job to delete."); return; } StringBuilder jobsToBeDeleted = new StringBuilder("Do you want the following jobs to be deleted?" + NEWLINE); List jobsSelectedForDeletion = getSelectedJobs(pTModel, rowCount, colCount); List jobNamesSelectedForDeletion = new ArrayList(); for(SelectedJobInfo slctdJob: jobsSelectedForDeletion){ jobsToBeDeleted.append(slctdJob.getSelectedJobNameWithDate() + NEWLINE); System.out.println("Selected jobs for deletion: " + slctdJob.getSelectedJobNameWithDate()); jobNamesSelectedForDeletion.add(slctdJob.getSelectedJobNameWithDate()); } boolean deleteOkay = Dialogs.showOkCancelDialog(jobsToBeDeleted.toString().trim() , "Delete jobs", new JPanel()); if(!deleteOkay){ return; } //CipresUtilities.deleteJobs(selected_jobs, allJobs); CipresUtilities.deleteJobs(jobNamesSelectedForDeletion, allJobs); JSONArray retJSONArray = CipresUtilities.updateList(CipresMrBayesToolbar.myClient, user); updateTable(retJSONArray); }else if(e.getActionCommand() == "Submit Job"){ //CipresUtilities.submitJob(vParams, inputParams, metadata); System.out.println("Does nothing!"); }else if(e.getActionCommand() == DOWNLOADJOB){ System.out.println("Do something!++"); // It is important to check if there are job listed and at least one of them is selected boolean selectedRow = isThereSelectedRow(rowCount, pTModel); if(!selectedRow){ Dialogs.showMessageDialog("Please select at least one job for download."); return; } boolean importedToo = false; List selected_jobs =getSelectedJobs(pTModel, rowCount, colCount); for(SelectedJobInfo selectedJobInfo: selected_jobs){ if (selectedJobInfo.getImported()) { importedToo = true; if (selectedJobInfo.getLocalFolder() != null) { if (selectedJobInfo.getLocalFolder().indexOf(":") == 1) { Dialogs.showMessageDialog("Input local folder name: " + selectedJobInfo.getLocalFolder() + " is not a valid Geneious folder name.\n\n" + "Geneious folder is not the same as a folder on your PC/Mac.\n" + "Geneious folder is a folder under the Local root on your Sources Panel located on the left-hand side of your Geneious window.\n" + "Please enter a simple folder name (for example: MyFirstFolder) or a folder name with sub-folder(for example: MyFirstFolder/MySubFolder) " + "and it will be created under Local on the Sources Panel for you if it does not already exists."); return; } System.out.println("Local folder is: " + selectedJobInfo.getLocalFolder()); Pattern p = Pattern.compile(".*[\\\":>?].*"); Matcher m = p.matcher(selectedJobInfo.getLocalFolder()); if (m.find() || selectedJobInfo.getLocalFolder().contains("\\") || selectedJobInfo.getLocalFolder().contains("[") || selectedJobInfo.getLocalFolder().contains("]") || selectedJobInfo.getLocalFolder().contains("|") || selectedJobInfo.getLocalFolder().contains("+") || selectedJobInfo.getLocalFolder().contains("*") || selectedJobInfo.getLocalFolder().contains("\n") || selectedJobInfo.getLocalFolder().contains("\t")) { Dialogs.showMessageDialog("Input local folder name: " + selectedJobInfo.getLocalFolder() + " can't be created " + "because it is not allowed to have one or more of the following characters: \\ \" : > ? * + [ ] | new-line tab in the folder name"); return; } } if (selectedJobInfo.getLocalFolder() == null || selectedJobInfo.getLocalFolder().trim().isEmpty()) { //if (!Dialogs.showContinueCancelDialog("Local Folder name is empty for job: " + selectedJobInfo.getSelectedJobNameWithDate() + // ". Result files will be imported directly into Local folder without sub-folder on Sources Panel. Do you want to continue?", // "Continue with download/import?", this, Dialogs.DialogIcon.QUESTION)) Dialogs.showMessageDialog("Local Folder name is empty for job: " + selectedJobInfo.getSelectedJobNameWithDate() + "\nPlease enter a folder name to which result files will be imported."); return; } } } //FolderChooser fldrChsr = new FolderChooser(); fldrChsr = new JFileChooser(); fldrChsr.setDialogTitle("Please choose a target folder"); fldrChsr.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); fldrChsr.addActionListener(actionListener); fldrChsr.showOpenDialog(null); //DialogOptions folderChooserOptions = new DialogOptions(Dialogs.OK_ONLY, "Please select a folder"); //DialogOptions folderChooserOptions = new DialogOptions(BUTTONSLABELS, "Please select a folder"); //Dialogs.showDialog(folderChooserOptions, fldrChsr.process()); //Dialogs.showDialog(folderChooserOptions, fldrChsr); //selectedFolder = fldrChsr.getSelectdFolder(); if(CipresUtilities.checkEmptyString(selectedFolder)){ System.out.println("Returning because no folder is selected."); //Dialogs.getCurrentModalDialog().dispose(); //Dialogs.getCurrentModalDialog().setVisible(false); return; }else{ System.out.println("Selected folder--: " + selectedFolder); } try{ System.out.println("Download folder::: " + selectedFolder); myPanel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); busyLabel.setText("Please wait while downloading" + (importedToo? "/importing " : " ") + "selected job(s)..."); busyLabel.setVisible(true); //busyLabel.revalidate(); //myPanel.revalidate(); SwingWorker myWorker= new SwingWorker() { @Override protected String doInBackground() throws Exception { CipresUtilities.downloadJobs(selected_jobs, allJobs, selectedFolder); busyLabel.setVisible(false); myPanel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); int selectedCol = getSpecificColumnNum(pTModel, colCount, SELECT); for(int rCount = 0; rCount < rowCount; ++rCount){ if((Boolean)pTModel.getValueAt(rCount, selectedCol) == true){ System.out.println("Resetting table value at row: " + rCount); pTModel.setValueAt(false, rCount, selectedCol); } } pTModel.fireTableDataChanged(); return null; } }; myWorker.execute(); //myPanel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); //CipresUtilities.downloadJobs(selected_jobs, allJobs, selectedFolder); /* Loop for unselecting all the selected jobs upon successful download */ /* int selectedCol = getSpecificColumnNum(pTModel, colCount, SELECT); for(int rCount = 0; rCount < rowCount; ++rCount){ if((Boolean)pTModel.getValueAt(rCount, selectedCol) == true){ System.out.println("Resetting table value at row: " + rCount); pTModel.setValueAt(false, rCount, selectedCol); } } pTModel.fireTableDataChanged(); */ }catch(Exception ie){ System.out.println("Caught Exception in 'Download Job' !!!"); ie.printStackTrace(); } } } catch (CiCipresException | org.json.simple.parser.ParseException e1) { e1.printStackTrace(); } catch (Exception ne) { if ((ne instanceof UnknownHostException) || (ne instanceof NoRouteToHostException) || (ne.toString().contains("UnknownHostException")) || (ne.toString().contains("NoRouteToHostException"))) Dialogs.showMessageDialog("CIPRES REST server cannot be reached at this moment.\nThis could be caused by temporary network problem.\nPlease retry your operation in a few minutes. Thank you!"); else throw ne; } } ActionListener actionListener = new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { fldrChsr = (JFileChooser) actionEvent.getSource(); //fldrChsr.setDialogTitle("Select Target Directory"); String command = actionEvent.getActionCommand(); System.out.println("COMMAND: " + command); if (command.equals(JFileChooser.APPROVE_SELECTION)) { String dir = fldrChsr.getSelectedFile().getAbsolutePath(); System.out.println("Selected folder: " + dir); selectedFolder = dir; }else if (command.equals(JFileChooser.CANCEL_SELECTION)) { selectedFolder = null; System.out.println("Cancelled"); } } }; /** * @author Madhusudan Gujral * @param tmMdl * @return selected Jobs */ private List getSelectedJobs(TableModel tblMdl, int numOfRows, int numOfCols){ int selectedJobColumn = -1; int selectedCheckedColumn = -1; int selectedDateSubmittedColumn = -1; int selectedLocalFolderColumn = -1; List jobNamesListWithDate = new ArrayList(); //for(int colNum = 0; colNum < numOfCols; colNum++){ selectedJobColumn = getSpecificColumnNum(tblMdl, numOfCols, JOBNAME); selectedCheckedColumn = getSpecificColumnNum(tblMdl, numOfCols, SELECT); selectedDateSubmittedColumn = getSpecificColumnNum(tblMdl, numOfCols, DATESUBMITTED); selectedLocalFolderColumn = getSpecificColumnNum(tblMdl, numOfCols, LOCALFOLDER); //} for(int rowNum = 0; rowNum < numOfRows; rowNum++){ if((Boolean)tblMdl.getValueAt(rowNum, selectedCheckedColumn)){ jobNamesListWithDate.add(new SelectedJobInfo((String)tblMdl.getValueAt(rowNum, selectedJobColumn) + CipresUtilities.DASH + tblMdl.getValueAt(rowNum, selectedDateSubmittedColumn).toString(), importIntoGeneious, (String)tblMdl.getValueAt(rowNum, selectedLocalFolderColumn)) ); } } return jobNamesListWithDate; } /** * @author Madhusudan Gujral * @param rowCnt * @param tblModel * @return true if at least one record/job is selected */ private boolean isThereSelectedRow(int rowCnt, TableModel tblModel){ boolean slctdRow = false; int selectColumnNum = getSpecificColumnNum(tblModel, tblModel.getColumnCount(), SELECT); for(int rCount = 0; rCount < rowCnt; ++rCount){ if((Boolean)tblModel.getValueAt(rCount, selectColumnNum) == true){ slctdRow = true; System.out.println("There is at least one selected job.: " + rCount); break; } } return slctdRow; } /** * * @param tblModel * @param colCount * @param colName * @return index of specific column name * @author Madhusudan Gujral */ private int getSpecificColumnNum(TableModel tblModel, int colCount, String colName){ int numOfCols = tblModel.getColumnCount(); int specificColumn = -1; for(int colNum = 0; colNum < numOfCols; colNum++){ if(tblModel.getColumnName(colNum).equals(colName)){ specificColumn = colNum; } } return specificColumn; } } class CellHighlighterRenderer extends DefaultTableCellRenderer { /** * */ private static final long serialVersionUID = 1L; @Override public Component getTableCellRendererComponent(JTable table, Object obj, boolean isSelected, boolean hasFocus, int row, int column) { Component cell = super.getTableCellRendererComponent(table, obj, isSelected, hasFocus, row, column); Boolean ticked = (Boolean)table.getModel().getValueAt(row, JobManagePanel.TableModel.ColumnEnum.SELECT.ordinal()); if (ticked) cell.setBackground(Color.YELLOW); else { if (isSelected) cell.setBackground(Color.BLUE); else cell.setBackground(Color.WHITE); } return cell; } }