/* * Copyright (C) Jakub Neubauer, 2007 * * This file is part of TaskBlocks * * TaskBlocks is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * TaskBlocks is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package taskblocks.io; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.IOException; import java.io.StringReader; import java.lang.reflect.InvocationTargetException; import java.net.InetAddress; import java.net.UnknownHostException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.prefs.Preferences; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.Icon; import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JPopupMenu; import javax.swing.JRootPane; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.JTextPane; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableCellRenderer; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.html.HTMLEditorKit; import taskblocks.bugzilla.BugzillaSubmitter; import taskblocks.graph.TaskGraphComponent; import taskblocks.modelimpl.ColorLabel; import taskblocks.modelimpl.TaskImpl; import taskblocks.utils.Utils; public class BugzillaExportDialog extends JDialog { String[] COL_NAMES = new String[] { "", "Bug#", "Color", "Summary", "Assignee", "Estimated Time", "Remaining", "Status Whiteboard" }; Class<?>[] COL_CLASSES = new Class[] { Boolean.class, String.class, Icon.class, String.class, String.class, String.class, String.class, String.class }; int INDEX_ENABLED = 0; int INDEX_BUG_ID = 1; int INDEX_COLOR = 2; int INDEX_NAME = 3; int INDEX_MAN = 4; int INDEX_HOURS = 5; int INDEX_REMAINS = 6; int INDEX_STATUS_WHITEBOARD = 7; TaskImpl[] _tasks; ChangeListener _changeListener; JTable _tasksTable; Object[][] _tasksData; TasksModel _tasksModel; JButton okB; JButton cancelB; private AbstractAction _okAction; private AbstractAction _cancelAction; JTextField _baseUrlTF; JTextField _userTF; JTextField _passwdTF; JTextField _productTF; JTextField _versionTF; JTextField _componentTF; JTextField _blocksTF; JTextField _hardwareTF; JTextField _osTF; JTextField _priorTF; JTextField _severTF; JTextPane _logArea; JTextField _keywordsTF; Preferences _prefs = Preferences.userNodeForPackage(this.getClass()); /** * * @param owner * @param tasks * @param changeListener Will be notified when some task changes (to set it as "dirty") */ private BugzillaExportDialog(JFrame owner, TaskImpl[] tasks, ChangeListener changeListener) { super(owner, "Bugzilla export", true); _tasks = new TaskImpl[tasks.length]; System.arraycopy(tasks, 0, _tasks, 0, tasks.length); Arrays.sort(_tasks, new Comparator<TaskImpl>(){ public int compare(TaskImpl o1, TaskImpl o2) { int res = o1.getMan().getName().compareTo(o2.getMan().getName()); if(res != 0) { return res; } return o1.getName().compareTo(o2.getName()); }}); _changeListener = changeListener; constructData(); createActions(); JPanel mainP = new JPanel(new BorderLayout(0, 12)); okB = new JButton(_okAction); cancelB = new JButton(_cancelAction); Box butP = Box.createHorizontalBox(); butP.add(Box.createHorizontalGlue()); butP.add(okB); butP.add(Box.createHorizontalStrut(10)); butP.add(cancelB); mainP.add(butP, BorderLayout.SOUTH); getContentPane().add(mainP); JPanel mainPanel = createMainPanel(); mainP.add(mainPanel, BorderLayout.CENTER); mainP.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12)); setDefaultActions(getRootPane()); this.addWindowListener(new WindowAdapter(){ public void windowClosed(WindowEvent e) { saveTextFields(); } }); } private void saveTextFields() { _prefs.put("baseUrl", _baseUrlTF.getText()); _prefs.put("user", _userTF.getText()); _prefs.put("product", _productTF.getText()); _prefs.put("version", _versionTF.getText()); _prefs.put("component", _componentTF.getText()); _prefs.put("blocks", _blocksTF.getText()); _prefs.put("hardware", _hardwareTF.getText()); _prefs.put("os", _osTF.getText()); _prefs.put("priority", _priorTF.getText()); _prefs.put("severity", _severTF.getText()); _prefs.put("keywords", _keywordsTF.getText()); } private void setDefaultActions(JRootPane rootPane) { KeyStroke strokeEsc = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); //KeyStroke strokeEnter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0); InputMap inputMap = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); inputMap.put(strokeEsc, "ESCAPE"); //inputMap.put(strokeEnter, "ENTER"); rootPane.getActionMap().put("ESCAPE", _cancelAction); //rootPane.getActionMap().put("ENTER", _okAction); } private JPanel createMainPanel() { JPanel p = new JPanel(new BorderLayout(0, 12)); _tasksTable = new JTable(_tasksModel = new TasksModel()) { public Component prepareRenderer(TableCellRenderer renderer, int row, int column) { Component c = super.prepareRenderer(renderer, row, column); return c; } }; _tasksTable.getColumnModel().getColumn(INDEX_ENABLED).setMaxWidth(25); _tasksTable.getColumnModel().getColumn(INDEX_ENABLED).setMinWidth(25); _tasksTable.getColumnModel().getColumn(INDEX_COLOR).setMaxWidth(50); MouseListener popupListener = new MouseAdapter(){ void showPopup(MouseEvent e) { JPopupMenu pm = new JPopupMenu(); pm.add(new AbstractAction("Select All") { public void actionPerformed(ActionEvent e) { selectAll(true); }}); pm.add(new AbstractAction("Unselect All") { public void actionPerformed(ActionEvent e) { selectAll(false); }}); pm.show((Component)e.getSource(), e.getX(), e.getY()); } public void mouseClicked(MouseEvent e) { if(e.isPopupTrigger()) { showPopup(e); } } public void mousePressed(MouseEvent e) { if(e.isPopupTrigger()) { showPopup(e); } } public void mouseReleased(MouseEvent e) { if(e.isPopupTrigger()) { showPopup(e); } } }; _tasksTable.addMouseListener(popupListener); JScrollPane sp = new JScrollPane(_tasksTable); sp.addMouseListener(popupListener); sp.setPreferredSize(new Dimension(700,200)); JPanel contentP = new JPanel(); p.add(sp, BorderLayout.CENTER); _logArea = new JTextPane(); ((JTextPane)_logArea).setContentType("text/html"); _logArea.setEditable(false); _logArea.setText("<html>Log:"); String domain=""; try { domain = InetAddress.getLocalHost().getCanonicalHostName(); } catch (UnknownHostException e) { e.printStackTrace(); } _baseUrlTF = new JTextField(_prefs.get("baseUrl", "http://")); _userTF = new JTextField(_prefs.get("user", System.getProperty("user.name") + "@" + domain)); _passwdTF = new JPasswordField(""); _productTF = new JTextField(_prefs.get("product", "")); _versionTF = new JTextField(_prefs.get("version", "")); _componentTF = new JTextField(_prefs.get("component", "")); _blocksTF = new JTextField(_prefs.get("blocks", "")); _hardwareTF = new JTextField(_prefs.get("hardware", "All")); _osTF = new JTextField(_prefs.get("os", "All")); _priorTF = new JTextField(_prefs.get("priority", "P2")); _severTF = new JTextField(_prefs.get("severity", "enhancement")); _keywordsTF = new JTextField(_prefs.get("keywords", "")); // layout components contentP.setLayout(new GridBagLayout()); GridBagConstraints gc = new GridBagConstraints(); // add labels gc.gridx = 0; gc.gridy = -1; gc.fill = GridBagConstraints.NONE; gc.insets.bottom = 5; gc.anchor = GridBagConstraints.EAST; // gc.gridy++; contentP.add(new JLabel("Bugzilla URL:"), gc); gc.gridy++; contentP.add(new JLabel("User:"), gc); gc.gridy++; contentP.add(new JLabel("Password:"), gc); gc.gridy++; gc.gridwidth = 4; gc.insets.top = 8; gc.insets.bottom = 8; gc.fill = GridBagConstraints.HORIZONTAL; contentP.add(new JSeparator(JSeparator.HORIZONTAL), gc); gc.insets.top = 0; gc.insets.bottom = 4; gc.gridwidth = 1; gc.fill = GridBagConstraints.NONE; gc.gridy++; contentP.add(new JLabel("Product:"), gc); gc.gridy++; contentP.add(new JLabel("Version:"), gc); gc.gridy++; contentP.add(new JLabel("Component:"), gc); gc.gridy++; contentP.add(new JLabel("Blocks:"), gc); gc.gridx += 2; gc.gridy = 3; gc.insets.left = 10; gc.gridy++; contentP.add(new JLabel("Hardware:"), gc); gc.gridy++; contentP.add(new JLabel("OS:"), gc); gc.gridy++; contentP.add(new JLabel("Priority:"), gc); gc.gridy++; contentP.add(new JLabel("Severity:"), gc); gc.gridy++; contentP.add(new JLabel("Keywords:"), gc); // add edit fields gc.gridx = 1; gc.gridy = -1; gc.fill = GridBagConstraints.HORIZONTAL; gc.weightx = 1; gc.insets.left = 8; // gc.gridwidth = 3; gc.gridy++; contentP.add(_baseUrlTF, gc); gc.gridy++; contentP.add(_userTF, gc); gc.gridy++; contentP.add(_passwdTF, gc); gc.gridwidth = 1; gc.gridy++; gc.gridy++; contentP.add(_productTF, gc); gc.gridy++; contentP.add(_versionTF, gc); gc.gridy++; contentP.add(_componentTF, gc); gc.gridy++; contentP.add(_blocksTF, gc); gc.gridx += 2; gc.gridy = 3; gc.gridy++; contentP.add(_hardwareTF, gc); gc.gridy++; contentP.add(_osTF, gc); gc.gridy++; contentP.add(_priorTF, gc); gc.gridy++; contentP.add(_severTF, gc); gc.gridy++; contentP.add(_keywordsTF, gc); p.add(contentP, BorderLayout.NORTH); JScrollPane logSP = new JScrollPane(_logArea); logSP.setPreferredSize(new Dimension(100,100)); logSP.setMaximumSize(new Dimension(100,100)); p.add(logSP, BorderLayout.SOUTH); return p; } private void createActions() { _cancelAction = new AbstractAction("Cancel") { public void actionPerformed(ActionEvent e) { dispose(); } }; _okAction = new AbstractAction("Submit") { public void actionPerformed(ActionEvent e) { submit(); } }; } private void constructData() { SimpleDateFormat statusWhiteboardFormat = new SimpleDateFormat("ddMMyy"); _tasksData = new Object[_tasks.length][]; for (int i = 0; i < _tasks.length; i++) { TaskImpl task = _tasks[i]; long startTime = task.getStartTime() * Utils.MILLISECONDS_PER_DAY; // in bugzilla we want the end date to be the // last day the worker will work on the task, // not the day after (mathematically) long endTime = (Utils.countFinishTime(task.getStartTime(), task .getEffort(), task.getWorkload()) -1) * Utils.MILLISECONDS_PER_DAY; _tasksData[i] = new Object[COL_NAMES.length]; _tasksData[i][INDEX_ENABLED] = Boolean.valueOf(true); if(_tasks[i].getBugId() != null) { _tasksData[i][INDEX_BUG_ID] = _tasks[i].getBugId(); } ColorLabel taskColLabel = _tasks[i].getColorLabel(); if(taskColLabel == null) { _tasksData[i][INDEX_COLOR] = ColorLabel.COLOR_LABELS[0]._icon; } else { _tasksData[i][INDEX_COLOR] = taskColLabel._icon; } _tasksData[i][INDEX_NAME] = task.getName(); _tasksData[i][INDEX_MAN] = task.getMan().getName(); _tasksData[i][INDEX_HOURS] = Integer.valueOf( (int) ((double) task.getEffort() * 8d)); _tasksData[i][INDEX_REMAINS] = Integer.valueOf( (int) ((double) (task.getEffort()-task.getWorkedTime()) * 8d)); if(((Integer)_tasksData[i][INDEX_REMAINS]) < 0) { _tasksData[i][INDEX_REMAINS] = 0; } _tasksData[i][INDEX_STATUS_WHITEBOARD] = statusWhiteboardFormat.format(new Date(startTime)) + "-" + statusWhiteboardFormat.format(new Date(endTime)); } } private void selectAll(boolean value) { for(int i = 0; i < _tasksData.length; i++) { _tasksData[i][INDEX_ENABLED] = value; } ((TasksModel)_tasksTable.getModel()).fireTableDataChanged(); } class TasksModel extends AbstractTableModel { public String getColumnName(int column) { return COL_NAMES[column]; } public Class<?> getColumnClass(int col) { return COL_CLASSES[col]; } public int getColumnCount() { return COL_NAMES.length; } public int getRowCount() { return _tasksData.length; } public boolean isCellEditable(int rowIndex, int columnIndex) { return true; } public Object getValueAt(int rowIndex, int columnIndex) { return _tasksData[rowIndex][columnIndex]; } public void setValueAt(Object aValue, int rowIndex, int columnIndex) { _tasksData[rowIndex][columnIndex] = aValue; } } private void submit() { _okAction.setEnabled(false); new Thread() { public void run() { try { int success = 0; int fails = 0; for (int i = 0; BugzillaExportDialog.this.isShowing() && i < _tasks.length; i++) { if (((Boolean) _tasksData[i][INDEX_ENABLED]).booleanValue()) { final int row = i; // select the task row SwingUtilities.invokeAndWait(new Runnable(){ public void run() { _tasksTable.getSelectionModel().setSelectionInterval(row, row); _tasksTable.scrollRectToVisible(_tasksTable.getCellRect(row, 0, true)); } }); if(submitTask(i, _tasksData[i], _tasks[i])) { success++; // uncheck the task row SwingUtilities.invokeAndWait(new Runnable(){ public void run() { _tasksData[row][INDEX_ENABLED] = Boolean.FALSE; ((TasksModel)_tasksTable.getModel()).fireTableDataChanged(); } }); } else { fails++; } } } if(success > 0) { logMsg("Submitted " + success + " tasks"); // bug IDs should be saved, setting project as dirty. _changeListener.stateChanged(new ChangeEvent(this)); } if(fails > 0) { logMsg("Failed to submit " + fails + " tasks"); } } catch(InterruptedException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } finally { _okAction.setEnabled(true); } } }.start(); } private boolean submitTask(int index, Object[] taskData, TaskImpl task) { BugzillaSubmitter bs = new BugzillaSubmitter(); Map<String, String> taskProps = new HashMap<String, String>(); String bugId = task.getBugId(); try { if(bugId == null || bugId.trim().length() == 0) { // new task taskProps.put(BugzillaSubmitter.ESTIMATED_TIME, String.valueOf((Integer) taskData[INDEX_HOURS])); taskProps.put(BugzillaSubmitter.REMAINING_TIME, String.valueOf((Integer) taskData[INDEX_REMAINS])); taskProps.put(BugzillaSubmitter.BLOCKS, _blocksTF.getText()); taskProps.put(BugzillaSubmitter.DESCRIPTION, ""); // required by bugzilla v. 2.2, (3.0 doesn't) taskProps.put(BugzillaSubmitter.KEYWORDS, _keywordsTF.getText()); } else { // copy BLOCKS and DEPENDS ON from the actual state in bugzilla. bugId = bugId.trim(); Map<String, Map<String, String>> result = bs.query(_baseUrlTF.getText(), _userTF.getText(), _passwdTF.getText(), new String[] {bugId}); Map<String, String> bugFields = result.get(bugId); String dependson = bugFields.get(BugzillaSubmitter.DEPENDSON); if(dependson != null) { taskProps.put(BugzillaSubmitter.DEPENDSON, dependson); } String blocks = bugFields.get(BugzillaSubmitter.BLOCKS); if(blocks != null) { taskProps.put(BugzillaSubmitter.BLOCKS, blocks); } } taskProps.put(BugzillaSubmitter.SEVERITY, _severTF.getText()); taskProps.put(BugzillaSubmitter.PRIORITY, _priorTF.getText()); taskProps.put(BugzillaSubmitter.HARDWARE, _hardwareTF.getText()); taskProps.put(BugzillaSubmitter.OS, _osTF.getText()); taskProps.put(BugzillaSubmitter.VERSION, _versionTF.getText()); taskProps.put(BugzillaSubmitter.COMPONENT, _componentTF.getText()); taskProps.put(BugzillaSubmitter.PRODUCT, _productTF.getText()); taskProps.put(BugzillaSubmitter.SUMMARY, (String) taskData[INDEX_NAME]); taskProps.put(BugzillaSubmitter.ASSIGNED_TO, (String) taskData[INDEX_MAN]); taskProps.put(BugzillaSubmitter.STATUS_WHITEBOARD, (String) taskData[INDEX_STATUS_WHITEBOARD]); // this is needed by GMC Bugzilla. Hope it will not brake other Bugzillas. taskProps.put("target_milestone", "---"); taskProps.put("bug_file_loc", ""); taskProps.put("longdesclength", "1"); if(bugId != null && bugId.trim().length() > 0) { logMsg("- Changing bug #" + bugId + " for task '" + task.getName() + "'\n"); logMsg(" Submitting " + taskProps); bs.change(_baseUrlTF.getText(), _userTF.getText(), _passwdTF.getText(), bugId, taskProps); logMsg(" OK"); } else { logMsg("- Creating bug #" + bugId + " for task '" + task.getName() + "'\n"); logMsg(" Submitting " + taskProps); bugId = bs.submit(_baseUrlTF.getText(), _userTF.getText(), _passwdTF.getText(), taskProps); task.setBugId(bugId); _tasksData[index][INDEX_BUG_ID] = bugId; _tasksModel.fireTableCellUpdated(index, INDEX_BUG_ID); logMsg(" OK"); } return true; } catch (Exception e) { e.printStackTrace(); String msg; if(e instanceof IOException || e.getMessage() == null) { msg = e.toString(); } else { msg = e.getMessage(); } logMsg("<font color=\"#ff0000\"><b>- Error submitting task '" + task.getName() + "'</b><br>\n     " + msg + "</font>\n"); return false; } } private void logMsg(String msg) { HTMLEditorKit kit = (HTMLEditorKit) _logArea.getEditorKit(); Document doc = _logArea.getDocument(); StringReader reader = new StringReader(msg); try { kit.read(reader, doc, doc.getLength()); } catch (IOException e) { // NOTHING TO DO } catch (BadLocationException e) { // NOTHING TO DO } int len = doc.getLength(); _logArea.setSelectionStart(len); _logArea.setSelectionEnd(len); _logArea.setPreferredSize(new Dimension(100,100)); _logArea.setMaximumSize(new Dimension(100,100)); } public static void openDialog(JFrame owner, TaskImpl[] tasks, ChangeListener changeListener) { BugzillaExportDialog d = new BugzillaExportDialog(owner, tasks, changeListener); d.pack(); d.setLocationRelativeTo(owner); d.setVisible(true); } }