001: /*
002: * Copyright (C) Jakub Neubauer, 2007
003: *
004: * This file is part of TaskBlocks
005: *
006: * TaskBlocks is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 3 of the License, or
009: * (at your option) any later version.
010: *
011: * TaskBlocks is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program. If not, see <http://www.gnu.org/licenses/>.
018: */
019:
020: package taskblocks.io;
021:
022: import java.awt.BorderLayout;
023: import java.awt.Component;
024: import java.awt.Dimension;
025: import java.awt.GridBagConstraints;
026: import java.awt.GridBagLayout;
027: import java.awt.event.ActionEvent;
028: import java.awt.event.KeyEvent;
029: import java.awt.event.WindowAdapter;
030: import java.awt.event.WindowEvent;
031: import java.io.IOException;
032: import java.io.StringReader;
033: import java.lang.reflect.InvocationTargetException;
034: import java.net.InetAddress;
035: import java.net.UnknownHostException;
036: import java.text.SimpleDateFormat;
037: import java.util.Date;
038: import java.util.HashMap;
039: import java.util.Map;
040: import java.util.prefs.Preferences;
041:
042: import javax.swing.AbstractAction;
043: import javax.swing.BorderFactory;
044: import javax.swing.Box;
045: import javax.swing.Icon;
046: import javax.swing.InputMap;
047: import javax.swing.JButton;
048: import javax.swing.JComponent;
049: import javax.swing.JDialog;
050: import javax.swing.JFrame;
051: import javax.swing.JLabel;
052: import javax.swing.JPanel;
053: import javax.swing.JPasswordField;
054: import javax.swing.JRootPane;
055: import javax.swing.JScrollPane;
056: import javax.swing.JSeparator;
057: import javax.swing.JTable;
058: import javax.swing.JTextField;
059: import javax.swing.JTextPane;
060: import javax.swing.KeyStroke;
061: import javax.swing.SwingUtilities;
062: import javax.swing.table.AbstractTableModel;
063: import javax.swing.table.TableCellRenderer;
064: import javax.swing.text.BadLocationException;
065: import javax.swing.text.Document;
066: import javax.swing.text.html.HTMLEditorKit;
067:
068: import taskblocks.Utils;
069: import taskblocks.bugzilla.BugzillaSubmitter;
070: import taskblocks.modelimpl.ColorLabel;
071: import taskblocks.modelimpl.TaskImpl;
072:
073: public class BugzillaExportDialog extends JDialog {
074:
075: TaskImpl[] _tasks;
076:
077: JTable _tasksTable;
078:
079: Object[][] _tasksData;
080:
081: JButton okB;
082:
083: JButton cancelB;
084:
085: private AbstractAction _okAction;
086:
087: private AbstractAction _cancelAction;
088:
089: JTextField _baseUrlTF;
090:
091: JTextField _userTF;
092:
093: JTextField _passwdTF;
094:
095: JTextField _productTF;
096:
097: JTextField _versionTF;
098:
099: JTextField _componentTF;
100:
101: JTextField _blocksTF;
102:
103: JTextField _hardwareTF;
104:
105: JTextField _osTF;
106:
107: JTextField _priorTF;
108:
109: JTextField _severTF;
110:
111: JTextPane _logArea;
112:
113: JTextField _keywordsTF;
114:
115: Preferences _prefs = Preferences
116: .userNodeForPackage(this .getClass());
117:
118: public BugzillaExportDialog(JFrame owner, TaskImpl[] tasks) {
119: super (owner, "Bugzilla export", true);
120:
121: _tasks = tasks;
122: constructData();
123: createActions();
124:
125: JPanel mainP = new JPanel(new BorderLayout(0, 12));
126: okB = new JButton(_okAction);
127: cancelB = new JButton(_cancelAction);
128: Box butP = Box.createHorizontalBox();
129: butP.add(Box.createHorizontalGlue());
130: butP.add(okB);
131: butP.add(Box.createHorizontalStrut(10));
132: butP.add(cancelB);
133: mainP.add(butP, BorderLayout.SOUTH);
134: getContentPane().add(mainP);
135: JPanel mainPanel = createMainPanel();
136: mainP.add(mainPanel, BorderLayout.CENTER);
137:
138: mainP
139: .setBorder(BorderFactory.createEmptyBorder(12, 12, 12,
140: 12));
141:
142: setDefaultActions(getRootPane());
143: this .addWindowListener(new WindowAdapter() {
144: public void windowClosed(WindowEvent e) {
145: saveTextFields();
146: }
147: });
148: }
149:
150: private void saveTextFields() {
151: _prefs.put("baseUrl", _baseUrlTF.getText());
152: _prefs.put("user", _userTF.getText());
153: _prefs.put("product", _productTF.getText());
154: _prefs.put("version", _versionTF.getText());
155: _prefs.put("component", _componentTF.getText());
156: _prefs.put("blocks", _blocksTF.getText());
157: _prefs.put("hardware", _hardwareTF.getText());
158: _prefs.put("os", _osTF.getText());
159: _prefs.put("priority", _priorTF.getText());
160: _prefs.put("severity", _severTF.getText());
161: _prefs.put("keywords", _keywordsTF.getText());
162: }
163:
164: private void setDefaultActions(JRootPane rootPane) {
165: KeyStroke strokeEsc = KeyStroke.getKeyStroke(
166: KeyEvent.VK_ESCAPE, 0);
167: //KeyStroke strokeEnter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
168:
169: InputMap inputMap = rootPane
170: .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
171: inputMap.put(strokeEsc, "ESCAPE");
172: //inputMap.put(strokeEnter, "ENTER");
173: rootPane.getActionMap().put("ESCAPE", _cancelAction);
174: //rootPane.getActionMap().put("ENTER", _okAction);
175: }
176:
177: private JPanel createMainPanel() {
178: JPanel p = new JPanel(new BorderLayout(0, 12));
179: _tasksTable = new JTable(new TasksModel()) {
180: public Component prepareRenderer(
181: TableCellRenderer renderer, int row, int column) {
182: Component c = super .prepareRenderer(renderer, row,
183: column);
184: return c;
185: }
186: };
187: //_tasksTable.setIntercellSpacing(new Dimension());
188: _tasksTable.getColumnModel().getColumn(0).setMaxWidth(25);
189: _tasksTable.getColumnModel().getColumn(0).setMinWidth(25);
190: _tasksTable.getColumnModel().getColumn(1).setMaxWidth(50);
191: JScrollPane sp = new JScrollPane(_tasksTable);
192: sp.setPreferredSize(new Dimension(700, 200));
193: JPanel contentP = new JPanel();
194: p.add(sp, BorderLayout.CENTER);
195:
196: _logArea = new JTextPane();
197: ((JTextPane) _logArea).setContentType("text/html");
198: _logArea.setEditable(false);
199: _logArea.setText("<html>Log:");
200:
201: String domain = "";
202: try {
203: domain = InetAddress.getLocalHost().getCanonicalHostName();
204: } catch (UnknownHostException e) {
205: e.printStackTrace();
206: }
207: _baseUrlTF = new JTextField(_prefs.get("baseUrl", "http://"));
208: _userTF = new JTextField(_prefs.get("user", System
209: .getProperty("user.name")
210: + "@" + domain));
211: _passwdTF = new JPasswordField("");
212: _productTF = new JTextField(_prefs.get("product", ""));
213: _versionTF = new JTextField(_prefs.get("version", ""));
214: _componentTF = new JTextField(_prefs.get("component", ""));
215: _blocksTF = new JTextField(_prefs.get("blocks", ""));
216: _hardwareTF = new JTextField(_prefs.get("hardware", "All"));
217: _osTF = new JTextField(_prefs.get("os", "All"));
218: _priorTF = new JTextField(_prefs.get("priority", "P2"));
219: _severTF = new JTextField(_prefs.get("severity", "enhancement"));
220: _keywordsTF = new JTextField(_prefs.get("keywords", ""));
221:
222: // layout components
223: contentP.setLayout(new GridBagLayout());
224: GridBagConstraints gc = new GridBagConstraints();
225:
226: // add labels
227: gc.gridx = 0;
228: gc.gridy = -1;
229: gc.fill = GridBagConstraints.NONE;
230: gc.insets.bottom = 5;
231: gc.anchor = GridBagConstraints.EAST;
232: //
233: gc.gridy++;
234: contentP.add(new JLabel("Bugzilla URL:"), gc);
235: gc.gridy++;
236: contentP.add(new JLabel("User:"), gc);
237: gc.gridy++;
238: contentP.add(new JLabel("Password:"), gc);
239:
240: gc.gridy++;
241: gc.gridwidth = 4;
242: gc.insets.top = 8;
243: gc.insets.bottom = 8;
244: gc.fill = GridBagConstraints.HORIZONTAL;
245: contentP.add(new JSeparator(JSeparator.HORIZONTAL), gc);
246: gc.insets.top = 0;
247: gc.insets.bottom = 4;
248: gc.gridwidth = 1;
249: gc.fill = GridBagConstraints.NONE;
250:
251: gc.gridy++;
252: contentP.add(new JLabel("Product:"), gc);
253: gc.gridy++;
254: contentP.add(new JLabel("Version:"), gc);
255: gc.gridy++;
256: contentP.add(new JLabel("Component:"), gc);
257: gc.gridy++;
258: contentP.add(new JLabel("Blocks:"), gc);
259: gc.gridx += 2;
260: gc.gridy = 3;
261: gc.insets.left = 10;
262: gc.gridy++;
263: contentP.add(new JLabel("Hardware:"), gc);
264: gc.gridy++;
265: contentP.add(new JLabel("OS:"), gc);
266: gc.gridy++;
267: contentP.add(new JLabel("Priority:"), gc);
268: gc.gridy++;
269: contentP.add(new JLabel("Severity:"), gc);
270: gc.gridy++;
271: contentP.add(new JLabel("Keywords:"), gc);
272:
273: // add edit fields
274: gc.gridx = 1;
275: gc.gridy = -1;
276: gc.fill = GridBagConstraints.HORIZONTAL;
277: gc.weightx = 1;
278: gc.insets.left = 8;
279: //
280: gc.gridwidth = 3;
281: gc.gridy++;
282: contentP.add(_baseUrlTF, gc);
283: gc.gridy++;
284: contentP.add(_userTF, gc);
285: gc.gridy++;
286: contentP.add(_passwdTF, gc);
287: gc.gridwidth = 1;
288: gc.gridy++;
289: gc.gridy++;
290: contentP.add(_productTF, gc);
291: gc.gridy++;
292: contentP.add(_versionTF, gc);
293: gc.gridy++;
294: contentP.add(_componentTF, gc);
295: gc.gridy++;
296: contentP.add(_blocksTF, gc);
297: gc.gridx += 2;
298: gc.gridy = 3;
299: gc.gridy++;
300: contentP.add(_hardwareTF, gc);
301: gc.gridy++;
302: contentP.add(_osTF, gc);
303: gc.gridy++;
304: contentP.add(_priorTF, gc);
305: gc.gridy++;
306: contentP.add(_severTF, gc);
307: gc.gridy++;
308: contentP.add(_keywordsTF, gc);
309:
310: p.add(contentP, BorderLayout.NORTH);
311: JScrollPane logSP = new JScrollPane(_logArea);
312: logSP.setPreferredSize(new Dimension(100, 100));
313: logSP.setMaximumSize(new Dimension(100, 100));
314: p.add(logSP, BorderLayout.SOUTH);
315: return p;
316: }
317:
318: private void createActions() {
319: _cancelAction = new AbstractAction("Cancel") {
320: public void actionPerformed(ActionEvent e) {
321: dispose();
322: }
323: };
324:
325: _okAction = new AbstractAction("Submit") {
326: public void actionPerformed(ActionEvent e) {
327: submit();
328: }
329: };
330: }
331:
332: private void constructData() {
333:
334: SimpleDateFormat statusWhiteboardFormat = new SimpleDateFormat(
335: "ddMMyy");
336:
337: _tasksData = new Object[_tasks.length][];
338: for (int i = 0; i < _tasks.length; i++) {
339: TaskImpl task = _tasks[i];
340: long startTime = task.getStartTime()
341: * Utils.MILLISECONDS_PER_DAY;
342: long endTime = Utils.countFinishTime(task.getStartTime(),
343: task.getDuration())
344: * Utils.MILLISECONDS_PER_DAY;
345: _tasksData[i] = new Object[6];
346: _tasksData[i][0] = new Boolean(true);
347: ColorLabel taskColLabel = _tasks[i].getColorLabel();
348: if (taskColLabel == null) {
349: _tasksData[i][1] = ColorLabel.COLOR_LABELS[0]._icon;
350: } else {
351: _tasksData[i][1] = taskColLabel._icon;
352: }
353: _tasksData[i][2] = task.getName();
354: _tasksData[i][3] = task.getMan().getName();
355: _tasksData[i][4] = new Integer((int) ((double) task
356: .getDuration() * 8d * 0.8));
357: _tasksData[i][5] = statusWhiteboardFormat.format(new Date(
358: startTime))
359: + "-"
360: + statusWhiteboardFormat.format(new Date(endTime));
361: }
362: }
363:
364: class TasksModel extends AbstractTableModel {
365:
366: String[] COL_NAMES = new String[] { "", "Color", "Summary",
367: "Assignee", "Estimated Time", "Status Whiteboard" };
368:
369: Class[] COL_CLASSES = new Class[] { Boolean.class, Icon.class,
370: String.class, String.class, String.class, String.class };
371:
372: public String getColumnName(int column) {
373: return COL_NAMES[column];
374: }
375:
376: public Class getColumnClass(int col) {
377: return COL_CLASSES[col];
378: }
379:
380: public int getColumnCount() {
381: return COL_NAMES.length;
382: }
383:
384: public int getRowCount() {
385: return _tasksData.length;
386: }
387:
388: public boolean isCellEditable(int rowIndex, int columnIndex) {
389: return true;
390: }
391:
392: public Object getValueAt(int rowIndex, int columnIndex) {
393: return _tasksData[rowIndex][columnIndex];
394: }
395:
396: public void setValueAt(Object aValue, int rowIndex,
397: int columnIndex) {
398: _tasksData[rowIndex][columnIndex] = aValue;
399: }
400:
401: }
402:
403: private void submit() {
404: _okAction.setEnabled(false);
405: new Thread() {
406: public void run() {
407: try {
408: int success = 0;
409: int fails = 0;
410: for (int i = 0; BugzillaExportDialog.this
411: .isShowing()
412: && i < _tasks.length; i++) {
413: if (((Boolean) _tasksData[i][0]).booleanValue()) {
414:
415: final int row = i;
416: SwingUtilities
417: .invokeAndWait(new Runnable() {
418: public void run() {
419: _tasksTable
420: .getSelectionModel()
421: .setSelectionInterval(
422: row, row);
423: _tasksTable
424: .scrollRectToVisible(_tasksTable
425: .getCellRect(
426: row,
427: 0,
428: true));
429: }
430: });
431:
432: if (submitTask(_tasksData[i], _tasks[i])) {
433: success++;
434: SwingUtilities
435: .invokeAndWait(new Runnable() {
436: public void run() {
437: _tasksData[row][0] = Boolean.FALSE;
438: ((TasksModel) _tasksTable
439: .getModel())
440: .fireTableDataChanged();
441: }
442: });
443: } else {
444: fails++;
445: }
446: }
447: }
448: if (success > 0) {
449: logMsg("Submitted " + success + " tasks");
450: }
451: if (fails > 0) {
452: logMsg("Failed to submit " + fails + " tasks");
453: }
454: } catch (InterruptedException e) {
455: e.printStackTrace();
456: } catch (InvocationTargetException e) {
457: e.printStackTrace();
458: } finally {
459: _okAction.setEnabled(true);
460: }
461: }
462: }.start();
463: }
464:
465: private boolean submitTask(Object[] taskData, TaskImpl task) {
466: BugzillaSubmitter bs = new BugzillaSubmitter();
467: Map<String, String> taskProps = new HashMap<String, String>();
468: taskProps.put(BugzillaSubmitter.SUMMARY, (String) taskData[2]);
469: taskProps.put(BugzillaSubmitter.ASSIGNED_TO,
470: (String) taskData[3]);
471: taskProps.put(BugzillaSubmitter.ESTIMATED_TIME, String
472: .valueOf((Integer) taskData[4]));
473: taskProps.put(BugzillaSubmitter.STATUS_WHITEBOARD,
474: (String) taskData[5]);
475:
476: taskProps.put(BugzillaSubmitter.PRODUCT, _productTF.getText());
477: taskProps.put(BugzillaSubmitter.VERSION, _versionTF.getText());
478: taskProps.put(BugzillaSubmitter.COMPONENT, _componentTF
479: .getText());
480: taskProps.put(BugzillaSubmitter.BLOCKS, _blocksTF.getText());
481: taskProps
482: .put(BugzillaSubmitter.HARDWARE, _hardwareTF.getText());
483: taskProps.put(BugzillaSubmitter.OS, _osTF.getText());
484: taskProps.put(BugzillaSubmitter.PRIORITY, _priorTF.getText());
485: taskProps.put(BugzillaSubmitter.SEVERITY, _severTF.getText());
486: taskProps.put(BugzillaSubmitter.DESCRIPTION, ""); // required by bugzilla v. 2.2, (3.0 doesn't)
487: taskProps
488: .put(BugzillaSubmitter.KEYWORDS, _keywordsTF.getText());
489:
490: try {
491: //String bugId = "pokus";
492: String bugId = bs.submit(_baseUrlTF.getText(), _userTF
493: .getText(), _passwdTF.getText(), taskProps);
494: logMsg("- Submitted bug #" + bugId + " for task '"
495: + task.getName() + "'\n");
496: return true;
497: } catch (Exception e) {
498: String msg;
499: if (e instanceof IOException || e.getMessage() == null) {
500: msg = e.toString();
501: } else {
502: msg = e.getMessage();
503: }
504: logMsg("<font color=\"#ff0000\"><b>- Error submitting task '"
505: + task.getName()
506: + "'</b><br>\n "
507: + msg
508: + "</font>\n");
509: return false;
510: }
511: }
512:
513: private void logMsg(String msg) {
514: HTMLEditorKit kit = (HTMLEditorKit) _logArea.getEditorKit();
515: Document doc = _logArea.getDocument();
516: StringReader reader = new StringReader(msg);
517: try {
518: kit.read(reader, doc, doc.getLength());
519: } catch (IOException e) {
520: } catch (BadLocationException e) {
521: }
522: int len = doc.getLength();
523: _logArea.setSelectionStart(len);
524: _logArea.setSelectionEnd(len);
525: _logArea.setPreferredSize(new Dimension(100, 100));
526: _logArea.setMaximumSize(new Dimension(100, 100));
527: }
528:
529: public static void openDialog(JFrame owner, TaskImpl[] tasks) {
530: BugzillaExportDialog d = new BugzillaExportDialog(owner, tasks);
531: d.pack();
532: d.setLocationRelativeTo(owner);
533: d.setVisible(true);
534: }
535:
536: }
|