Package pspdash

Source Code of pspdash.DefectDialog

// PSP Dashboard - Data Automation Tool for PSP-like processes
// Copyright (C) 2003 Software Process Dashboard Initiative
//
// This program 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 2
// of the License, or (at your option) any later version.
//
// This program 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, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// The author(s) may be contacted at:
// OO-ALC/TISHD
// Attn: PSP Dashboard Group
// 6137 Wardleigh Road
// Hill AFB, UT 84056-5843
//
// E-Mail POC:  processdash-devel@lists.sourceforge.net


package pspdash;

import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.awt.Dimension;
import java.awt.BorderLayout;
import java.awt.event.*;
import java.text.NumberFormat;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.border.BevelBorder;
import javax.swing.border.EmptyBorder;
import java.util.*;

import pspdash.data.ListData;
import pspdash.data.StringData;

public class DefectDialog extends JDialog
    implements ActionListener, DocumentListener, WindowListener
{
    PSPDashboard parent;
    String defectFilename;
    PropertyKey defectPath;
    DefectLog defectLog = null;
    Timer stopwatch = new Timer(false);
    JButton defectTimerButton, OKButton, CancelButton, fixDefectButton;
    Date date = null;
    String defectNumber = null;
    JLabel number;
    JTextField fix_defect;
    DecimalField fix_time;
    JTextArea description;
    JComboBox defect_type, phase_injected, phase_removed;
    boolean isDirty = false, autoCreated = false;

    /** A stack of the defect dialogs that have been interrupted. */
    private static Stack interruptedDialogs = new Stack();
    /** The defect dialog which was timing most recently. */
    private static DefectDialog activeDialog = null;
    /** A list of all open defect dialogs. */
    private static Hashtable defectDialogs = new Hashtable();
    /** A timer object for refreshing the fix time field. */
    private javax.swing.Timer activeRefreshTimer = null;

    Resources resources = Resources.getDashBundle("pspdash.Defects");


    DefectDialog(PSPDashboard dash, String defectFilename,
                 PropertyKey defectPath) {
        this(dash, defectFilename, defectPath, true);
    }

    DefectDialog(PSPDashboard dash, String defectFilename,
                 PropertyKey defectPath, boolean guessDefaults) {
        super(dash);
        setTitle(resources.getString("Defect_Dialog_Window_Title"));
        PCSH.enableHelpKey(this, "EnteringDefects");

        parent = dash;
        this.defectFilename = defectFilename;
        this.defectPath = defectPath;
        defectLog = new DefectLog(defectFilename, defectPath.path(),
                                  dash.data, dash);
        date = new Date();

        JPanel panel = new JPanel();
        GridBagLayout layout = new GridBagLayout();
        GridBagConstraints g = new GridBagConstraints();
        JComponent c;
        Insets bottom_margin = new Insets(1, 1, 8, 1);
        Insets small_margin = new Insets(1, 1, 1, 1);
        panel.setBorder(new EmptyBorder(5,5,5,5));
        panel.setLayout(layout);

                                // first row
        g.gridy = 0;   g.insets = small_margin; g.anchor = g.WEST;

        number = new JLabel();
        g.gridx = 0;   layout.setConstraints(number, g);
        panel.add(number);

        c = new JLabel(resources.getString("Fix_Defect_Label"));
        g.gridx = 1;   g.gridwidth = 2; layout.setConstraints(c, g);
        panel.add(c);

                                // second row
        g.gridy = 1;   g.insets = bottom_margin;   g.anchor = g.NORTHWEST;
        g.gridwidth = 1;
        defect_type = DefectTypeStandard.get
            (defectPath.path(), dash.data).getAsComboBox();
        defect_type.insertItemAt(resources.getString("Defect_Type_Prompt"), 0);
        defect_type.setMaximumRowCount(20);
        defect_type.setSelectedIndex(0);
        defect_type.addActionListener(this);

        g.gridx = 0;   layout.setConstraints(defect_type, g);
        panel.add(defect_type);

        fix_defect = new JTextField(5);
        fix_defect.getDocument().addDocumentListener(this);
        fix_defect.setMinimumSize(fix_defect.getPreferredSize());
        g.gridx = 1;   layout.setConstraints(fix_defect, g);
        panel.add(fix_defect);

        fixDefectButton = new JButton
            (new ImageIcon(getClass().getResource("defect.gif")));
        fixDefectButton.addActionListener(this);
        g.gridx = 2;   layout.setConstraints(fixDefectButton, g);
        panel.add(fixDefectButton);

                                // third row
        g.gridy = 2;   g.insets = small_margin;   g.anchor = g.WEST;

        c = new JLabel(resources.getString("Injected_Label"));
        g.gridx = 0;   layout.setConstraints(c, g);
        panel.add(c);

        c = new JLabel(resources.getString("Removed_Label"));
        g.gridx = 1;   g.gridwidth = 2;   layout.setConstraints(c, g);
        panel.add(c);

                                // fourth row
        g.gridy = 3;   g.insets = bottom_margin;   g.anchor = g.NORTHWEST;
        g.gridwidth = 1;


        int prefixLength = defectPath.path().length() + 1;
        String defaultRemovalPhase = null;
        if (guessDefaults) {
            String phasePath = parent.currentPhase.path();
            if (phasePath.length() > prefixLength)
                defaultRemovalPhase = phasePath.substring(prefixLength);
        }

        phase_removed = phaseComboBox(defectPath, defaultRemovalPhase);

        String defaultInjectionPhase = null;
        if (guessDefaults && defaultRemovalPhase != null)
            defaultInjectionPhase =
                guessInjectionPhase(phase_removed, defaultRemovalPhase);
        phase_injected = phaseComboBox(defectPath, defaultInjectionPhase);

        phase_injected.insertItemAt("Before Development", 0);
        phase_injected.addActionListener(this);
        g.gridx = 0;   layout.setConstraints(phase_injected, g);
        panel.add(phase_injected);

        phase_removed.addItem("After Development");
        phase_removed.addActionListener(this);
        g.gridx = 1; g.gridwidth =2; layout.setConstraints(phase_removed, g);
        panel.add(phase_removed);

                                // fifth row
        g.gridy = 4;   g.insets = small_margin;   g.anchor = g.WEST;
        g.gridwidth = 1;

        c = new JLabel(resources.getString("Fix_Time_Label"));
        g.gridx = 0;   layout.setConstraints(c, g);
        panel.add(c);

                                // sixth row
        g.gridy = 5;   g.insets = bottom_margin;

        NumberFormat nf = NumberFormat.getNumberInstance();
        nf.setMaximumFractionDigits(1);
        fix_time = new DecimalField(0.0, 10, nf);
        fix_time.setMinimumSize(fix_time.getPreferredSize());
        g.anchor = g.NORTHWEST;   layout.setConstraints(fix_time, g);
        panel.add(fix_time);
        fix_time.getDocument().addDocumentListener(this);

        defectTimerButton = new JButton
            (resources.getString("Start_Fixing_Button"));
        defectTimerButton.addActionListener(this);
        g.gridx = 1;   g.gridwidth = 2;   g.anchor = g.NORTH;
        layout.setConstraints(defectTimerButton, g);
        panel.add(defectTimerButton);

                                // seventh row
        g.gridy = 6;   g.insets = small_margin;   g.anchor = g.WEST;
        g.gridwidth = 1;
        c = new JLabel(resources.getString("Description_Label"));
        g.gridx = 0;   layout.setConstraints(c, g);
        panel.add(c);

                                // eighth row
        g.gridy = 7;   g.insets = bottom_margin;  g.fill = g.BOTH;
        description = new JTextArea();
        description.getDocument().addDocumentListener(this);
        description.setLineWrap(true);
        description.setWrapStyleWord(true);

        JScrollPane scroller = new
          JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                      JScrollPane.HORIZONTAL_SCROLLBAR_NEVER) {
              public Dimension getPreferredSize() {
                  return new Dimension(100, 100);
              }
          };
        scroller.getViewport().add(description);

        JPanel textWrapper = new JPanel(new BorderLayout());
//      textWrapper.setAlignmentX(LEFT_ALIGNMENT);
        textWrapper.setBorder(new BevelBorder(BevelBorder.LOWERED));
        textWrapper.add("Center", scroller);

        g.gridwidth = 3;   layout.setConstraints(textWrapper, g);
        panel.add(textWrapper);
        g.gridwidth = 1;

                                // ninth row
        g.gridy = 8;  g.insets = small_margin;
        g.anchor = g.CENTER;   g.fill = g.NONE;

        OKButton = new JButton(resources.getString("OK"));
        OKButton.addActionListener(this);
        g.gridx = 0;   layout.setConstraints(OKButton, g);
        panel.add(OKButton);

        CancelButton = new JButton(resources.getString("Cancel"));
        CancelButton.addActionListener(this);
        g.gridx = 1; g.gridwidth = 2; layout.setConstraints(CancelButton, g);
        panel.add(CancelButton);

        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        addWindowListener(this);

        getContentPane().add(panel);
        pack();
        panel.setMinimumSize(panel.getPreferredSize());
        show();

        if ("true".equalsIgnoreCase(Settings.getVal("defectDialog.autostart")))
          startTimingDefect();
        setDirty(false);
    }

    private DefectDialog(PSPDashboard dash, String defectFilename,
                         PropertyKey defectPath, Defect defect) {
      this(dash, defectFilename, defectPath, false);
      stopTimingDefect();
      setValues(defect);
      setDirty(false);
    }

    public static DefectDialog getDialogForDefect
        (PSPDashboard dash, String defectFilename,
         PropertyKey defectPath, Defect defect)
    {
        DefectDialog result = null;

        String comparisonKey = defectFilename + defect.number;
        result = (DefectDialog) defectDialogs.get(comparisonKey);
        if (result != null && result.isDisplayable()) return result;

        result = new DefectDialog(dash, defectFilename, defectPath, defect);
        result.saveDialogInCache();

        return result;
    }

    private String comparisonKey() {
        return defectLog.defectLogFilename + defectNumber;
    }
    private void saveDialogInCache() {
        defectDialogs.put(comparisonKey(), this);
    }

    public void setDirty(boolean dirty) {
        isDirty = dirty;
        //OKButton.setEnabled(dirty);
    }

    public void save() {
      refreshFixTimeFromStopwatch();

      Defect d = new Defect();
      d.date = date;
      d.number = defectNumber;
      d.defect_type = (String)defect_type.getSelectedItem();
      d.phase_injected = (String)phase_injected.getSelectedItem();
      d.phase_removed = (String)phase_removed.getSelectedItem();
      d.fix_time = fix_time.getText();
      d.fix_defect = fix_defect.getText();
      d.description = description.getText();

      defectLog.writeDefect(d);

      defectNumber = d.number;
      number.setText(formatDefectNum(d.number));
      setDirty(false);
    }
    private String formatDefectNum(String number) {
        return resources.format("Defect_Number_FMT", number);
    }

    public void startTimingDefect() {
      stopwatch.start();
      defectTimerButton.setText(resources.getString("Stop_Fixing_Button"));
      if (activeRefreshTimer == null)
          activeRefreshTimer = new javax.swing.Timer(6000, this);
      activeRefreshTimer.start();

      if (activeDialog != this) synchronized (interruptedDialogs) {
          if (activeDialog != null && activeDialog.stopwatch.isRunning()) {
              interruptedDialogs.push(activeDialog);
              activeDialog.stopTimingDefect();
          }

          interruptedDialogs.remove(this); // it might not be there, that's OK
          activeDialog = this;
      }
    }

    public void stopTimingDefect() {
      stopwatch.stop();
      defectTimerButton.setText(resources.getString("Start_Fixing_Button"));
      if (activeRefreshTimer != null)
          activeRefreshTimer.stop();

      refreshFixTimeFromStopwatch();
    }

    private void toggleDefect() {
      boolean is_now_running = stopwatch.toggle();

      if (is_now_running)
        startTimingDefect();
      else
        stopTimingDefect();

      setDirty(true);
    }

    private void maybePopDialog() {
        if (activeDialog == this)
            if (interruptedDialogs.empty())
                activeDialog = null;
            else {
                activeDialog = (DefectDialog) interruptedDialogs.pop();
                activeDialog.toFront();

                if (stopwatch.isRunning())
                    activeDialog.startTimingDefect();
            }
    }

    private void maybeDeleteAutocreatedDefect() {
        if (autoCreated)
            defectLog.deleteDefect(defectNumber);
    }

    private void comboSelect(JComboBox cb, String item) {
      int i = cb.getItemCount();
      while (i != 0)
        if (item.equals(cb.getItemAt(--i))) {
          cb.setSelectedIndex(i);
          break;
        }
    }

    private JComboBox phaseComboBox(PropertyKey defectPath,
                                    String selectedChild) {
      JComboBox result = new JComboBox();

      int prefixLength = 0;

      Enumeration leafNames = getInheritedPhaseList(defectPath.path());
      if (leafNames == null) {
          leafNames = parent.getProperties().getLeafNames(defectPath);
          prefixLength = defectPath.path().length() + 1;
      }

      String item = null;
      while (leafNames.hasMoreElements()) {
        item = (String) leafNames.nextElement();
        if (item == null || item.length() <= prefixLength) continue;
        item = item.substring(prefixLength);

        // This is NOT the right way to do this. A better way would be to
        // look at the defect flag of each leaf.  Leaves that wanted to
        // forbid defects could set their flag to false. But this will work...
        if (item.endsWith("Postmortem") || item.endsWith("Reassessment"))
            continue;           // don't add to the list.
        result.addItem(item);
        if (item.equals(selectedChild))
          result.setSelectedItem(item);
      }

      return result;
    }

    protected Enumeration getInheritedPhaseList(String defectPath) {
        Object inheritedPhaseList = parent.data.getInheritableValue
            (defectPath, "Effective_Defect_Phase_List");
        ListData list = null;
        if (inheritedPhaseList instanceof ListData)
            list = (ListData) inheritedPhaseList;
        else if (inheritedPhaseList instanceof StringData)
            list = ((StringData) inheritedPhaseList).asList();

        if (list == null)
            return null;

        Vector result = new Vector();
        for (int i = 0;   i < list.size();   i++)
            result.add(list.get(i).toString());
        return result.elements();
    }



    /** Make an educated guess about which injection phase might correspond
     *  to the given removal phase.
     *
     * Currently, this just guesses the phase immediately preceeding the
     * removal phase.  In the future, it might read a user setting
     */
    private String guessInjectionPhase(JComboBox phases, String removalPhase)
    {
        String result, mappedGuess, onePhase;

        int pos = removalPhase.lastIndexOf('/');
        if (pos == -1)
            mappedGuess = (String) INJ_REM_PAIRS.get(removalPhase);
        else
            mappedGuess =
                (String) INJ_REM_PAIRS.get(removalPhase.substring(pos+1));

        int i = phases.getItemCount();
        while (i-- > 0)
            if (removalPhase.equals(phases.getItemAt(i))) break;

        result = null;
        while (i-- > 0) {
            onePhase = (String) phases.getItemAt(i);
            if (phaseMatches(onePhase, mappedGuess))
                return onePhase;
            if (result == null &&
                !onePhase.endsWith(" Review") &&
                !onePhase.endsWith(" Inspection") &&
                !onePhase.endsWith(" Inspect") &&
                !onePhase.endsWith("Compile") &&
                !onePhase.endsWith("Test"))
                // remember the first non-quality, non-failure phase
                // we find before the removalPhase.
                result = onePhase;
        }
        if (result == null)
            result = removalPhase;

        return result;
    }

    private boolean phaseMatches(String fullName, String phaseName) {
        if (fullName == null || phaseName == null) return false;

        int pos = fullName.lastIndexOf('/');
        if (pos != -1)
            fullName = fullName.substring(pos+1);

        return fullName.equalsIgnoreCase(phaseName);
    }

    private static Map INJ_REM_PAIRS;
    static {
        HashMap phaseMap = new HashMap();
        String userSetting = Settings.getVal("defectDialog.phaseMap");
        if (userSetting != null) {
            StringTokenizer tok = new StringTokenizer(userSetting, "|");
            String phasePair, rem, inj;
            int pos;
            while (tok.hasMoreTokens()) {
                phasePair = tok.nextToken();
                pos = phasePair.indexOf("=>");
                if (pos != -1) {
                    inj = phasePair.substring(0, pos).trim();
                    rem = phasePair.substring(pos+2).trim();
                    phaseMap.put(rem, inj);
                }
            }
        }
        INJ_REM_PAIRS = Collections.unmodifiableMap(phaseMap);
    }

    private void hide_popups() {
        defect_type.hidePopup();
        phase_injected.hidePopup();
        phase_removed.hidePopup();
    }

    private volatile boolean programmaticallyChangingFixTime = false;

    private void fixTimeChanged() {
        if (programmaticallyChangingFixTime) return;
        setDirty(true);
        stopwatch.setElapsed((long) (fix_time.getValue() * 60.0));
    }

    private void refreshFixTimeFromStopwatch() {
        programmaticallyChangingFixTime = true;
        fix_time.setValue(stopwatch.minutesElapsedDouble());
        programmaticallyChangingFixTime = false;
    }

    private void openFixDefectDialog() {
        if (defectNumber == null) {
            save();
            setDirty(true);
            saveDialogInCache();
            autoCreated = true;
        }
        DefectDialog d = new DefectDialog(parent, defectFilename, defectPath);
        d.fix_defect.setText(defectNumber);
        d.setDirty(false);
    }

    public void setValues(Defect d) {
      date = d.date;
      defectNumber = d.number;
      number.setText(formatDefectNum(d.number));
      comboSelect(defect_type, d.defect_type);
      comboSelect(phase_injected, d.phase_injected);
      comboSelect(phase_removed, d.phase_removed);
      fix_time.setText(d.fix_time); // will trigger fixTimeChanged
      fix_defect.setText(d.fix_defect);
      description.setText(d.description);
    }

    public void dispose() {
        hide_popups();
        if (activeRefreshTimer != null) {
            activeRefreshTimer.stop();
            activeRefreshTimer.removeActionListener(this);
            activeRefreshTimer = null;
        }
        interruptedDialogs.remove(this); // it might not be there, that's OK
        defectDialogs.remove(comparisonKey());
        super.dispose();
    }


    /** Check to see if the removal phase is before the injection phase.
     *
     * If they are out of order, display an error message to the user and
     * return false; otherwise return true.
     */
    private boolean checkSequence() {
      if ("false".equalsIgnoreCase
          (Settings.getVal("defectDialog.restrictSequence")))
          return true;

      // Ensure that the user isn't removing a defect before it is
      // injected.
      String injected = (String)phase_injected.getSelectedItem();
      String removed  = (String)phase_removed.getSelectedItem();

      int numOptions = phase_injected.getItemCount();
      String option;
      for (int i = 0;  i < numOptions;  i++) {
          option = (String) phase_injected.getItemAt(i);
          if (option.equalsIgnoreCase(injected))
              return true;
          if (option.equalsIgnoreCase(removed)) {
              JOptionPane.showMessageDialog
                  (this,
                   resources.getStrings("Sequence_Error_Message"),
                   resources.getString("Sequence_Error_Title"),
                   JOptionPane.ERROR_MESSAGE);
              return false;
          }
      }
      // We shouldn't get here...
      return true;
    }


    /** Check to ensure that the user has selected a defect type.
     *
     * If they have not, display an error message to the user and
     * return false; otherwise return true.
     */
    private boolean checkValidType() {
        if (defect_type.getSelectedIndex() > 0)
            return true;

        JOptionPane.showMessageDialog
            (this, resources.getString("Choose_Defect_Type_Message"),
             resources.getString("Choose_Defect_Type_Title"),
             JOptionPane.ERROR_MESSAGE);

        return false;
    }


    /** Check to see if the defect has been modified, prior to a "Cancel"
     * operation.
     *
     * If the defect has not been modified, return true.  If the
     * defect HAS been modified, ask the user if they really want to
     * discard their changes.  If they do, return true; otherwise
     * returns false.
     */
    private boolean checkDirty() {
        return (!isDirty ||
                JOptionPane.showConfirmDialog
                (this, resources.getString("Confirm_Cancel_Message"),
                 resources.getString("Confirm_Cancel_Title"),
                 JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION);
    }

    /** Logic supporting a click of the OK button */
    private void okButtonAction() {
        if (checkSequence() && checkValidType()) {
            maybePopDialog();
            save();
            dispose();
        }
    }

    /** Logic supporting a click of the Cancel button */
    private void cancelButtonAction() {
        if (checkDirty()) {
            maybeDeleteAutocreatedDefect();
            maybePopDialog();
            dispose();
        }
    }

    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == activeRefreshTimer)
            refreshFixTimeFromStopwatch();
        else if (e.getSource() == OKButton)
            okButtonAction();
        else if (e.getSource() == CancelButton)
            cancelButtonAction();
        else if (e.getSource() == defectTimerButton)
            toggleDefect();
        else if (e.getSource() == fixDefectButton)
            openFixDefectDialog();
        else
            // this event must be a notification of a change to one of the
            // JComboBoxes on the form.
            setDirty(true);
    }

    // Implementation of the DocumentListener interface

    private void handleDocumentEvent(DocumentEvent e) {
        if (e.getDocument() == fix_time.getDocument())
            // If the user edited the "Fix Time" field, perform the
            // necessary recalculations.
            fixTimeChanged();

        else
            // The user changed one of the other text fields on the form
            // (for example, the Fix Defect or the Description).
            setDirty(true);
    }

    public void changedUpdate(DocumentEvent e) {}
    public void insertUpdate(DocumentEvent e)  { handleDocumentEvent(e); }
    public void removeUpdate(DocumentEvent e)  { handleDocumentEvent(e); }

    // Implementation of the WindowListener interface

    public void windowOpened(WindowEvent e) {}
    public void windowClosing(WindowEvent e) { cancelButtonAction(); }
    public void windowClosed(WindowEvent e) {}
    public void windowIconified(WindowEvent e) {}
    public void windowDeiconified(WindowEvent e) {}
    public void windowActivated(WindowEvent e) {}
    public void windowDeactivated(WindowEvent e) {}
}
TOP

Related Classes of pspdash.DefectDialog

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.