<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed  under the  License is distributed on an "AS IS" BASIS,
 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
 * implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 *
 */
package org.apache.jmeter.visualizers;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;

import org.apache.jmeter.gui.action.ActionNames;
import org.apache.jmeter.gui.action.ActionRouter;
import org.apache.jmeter.gui.action.SaveGraphics;
import org.apache.jmeter.gui.util.FilePanel;
import org.apache.jmeter.gui.util.VerticalPanel;
import org.apache.jmeter.samplers.Clearable;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.gui.AbstractVisualizer;
import org.apache.jmeter.visualizers.utils.Colors;
import org.apache.jorphan.gui.JLabeledTextField;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;

public class RespTimeGraphVisualizer extends AbstractVisualizer implements ActionListener, Clearable {

    private static final long serialVersionUID = 280L;

    private static final Logger log = LoggingManager.getLoggerForClass();

    private final Font FONT_SMALL = new Font("SansSerif", Font.PLAIN, 10);

    private final Border MARGIN = new EmptyBorder(0, 5, 0, 5);
    
    /**
     * Lock used to protect list update
     */
    private final transient Object lock = new Object();
    /**
     * Lock used to protect refresh interval
     */
    private final transient Object lockInterval = new Object();

    private final String yAxisLabel = JMeterUtils.getResString("aggregate_graph_response_time");//$NON-NLS-1$

    private final String yAxisTitle = JMeterUtils.getResString("aggregate_graph_ms"); //$NON-NLS-1$

    private RespTimeGraphChart graphPanel = null;

    private JPanel settingsPane = null;

    private final JTabbedPane tabbedGraph = new JTabbedPane(JTabbedPane.TOP);
    
    private boolean saveGraphToFile = false;

    private final int defaultWidth = 400;

    private final int defaultHeight = 300;
    
    private final static int INTERVAL_DEFAULT = 10000; // in milli-seconds // TODO: properties?
    
    private int intervalValue = INTERVAL_DEFAULT;
    
    private final JLabeledTextField intervalField =
            new JLabeledTextField(JMeterUtils.getResString("graph_resp_time_interval_label"), 7); //$NON-NLS-1$

    private final JButton intervalButton = new JButton(JMeterUtils.getResString("graph_resp_time_interval_reload")); // $NON-NLS-1$

    private final JButton displayButton =
            new JButton(JMeterUtils.getResString("aggregate_graph_display")); //$NON-NLS-1$
    
    private JButton saveGraph =
            new JButton(JMeterUtils.getResString("aggregate_graph_save")); //$NON-NLS-1$

    private final JCheckBox samplerSelection = new JCheckBox(JMeterUtils.getResString("graph_resp_time_series_selection"), false); //$NON-NLS-1$

    private final JTextField samplerMatchLabel = new JTextField();

    private final JButton reloadButton = new JButton(JMeterUtils.getResString("aggregate_graph_reload_data")); // $NON-NLS-1$

    private final JCheckBox caseChkBox = new JCheckBox(JMeterUtils.getResString("search_text_chkbox_case"), false); // $NON-NLS-1$

    private final JCheckBox regexpChkBox = new JCheckBox(JMeterUtils.getResString("search_text_chkbox_regexp"), true); // $NON-NLS-1$

    private final JComboBox titleFontNameList = new JComboBox(StatGraphProperties.getFontNameMap().keySet().toArray());

    private final JComboBox titleFontSizeList = new JComboBox(StatGraphProperties.fontSize);

    private final JComboBox titleFontStyleList = new JComboBox(StatGraphProperties.getFontStyleMap().keySet().toArray());

    private final JComboBox fontNameList = new JComboBox(StatGraphProperties.getFontNameMap().keySet().toArray());

    private final JComboBox fontSizeList = new JComboBox(StatGraphProperties.fontSize);

    private final JComboBox fontStyleList = new JComboBox(StatGraphProperties.getFontStyleMap().keySet().toArray());

    private final JComboBox legendPlacementList = new JComboBox(StatGraphProperties.getPlacementNameMap().keySet().toArray());
    
    private final JComboBox pointShapeLine = new JComboBox(StatGraphProperties.getPointShapeMap().keySet().toArray());

    private final JComboBox strokeWidthList = new JComboBox(StatGraphProperties.strokeWidth);

    private final JCheckBox numberShowGrouping = new JCheckBox(JMeterUtils.getResString("aggregate_graph_number_grouping"), true); // Default checked // $NON-NLS-1$

    private final JButton syncWithName =
            new JButton(JMeterUtils.getResString("aggregate_graph_sync_with_name"));  //$NON-NLS-1$

    private final JLabeledTextField graphTitle =
            new JLabeledTextField(JMeterUtils.getResString("graph_resp_time_title_label")); //$NON-NLS-1$

    private final JLabeledTextField xAxisTimeFormat =
            new JLabeledTextField(JMeterUtils.getResString("graph_resp_time_xaxis_time_format"), 10); //$NON-NLS-1$ $NON-NLS-2$

    private final JLabeledTextField maxValueYAxisLabel =
            new JLabeledTextField(JMeterUtils.getResString("aggregate_graph_yaxis_max_value"), 8); //$NON-NLS-1$

    /**
     * checkbox for use dynamic graph size
     */
    private final JCheckBox dynamicGraphSize = new JCheckBox(JMeterUtils.getResString("aggregate_graph_dynamic_size")); // $NON-NLS-1$

    private final JLabeledTextField graphWidth =
            new JLabeledTextField(JMeterUtils.getResString("aggregate_graph_width"), 6); //$NON-NLS-1$
    private final JLabeledTextField graphHeight =
            new JLabeledTextField(JMeterUtils.getResString("aggregate_graph_height"), 6); //$NON-NLS-1$

    private int minStartTime = Integer.MAX_VALUE;

    private int maxStartTime = Integer.MIN_VALUE;

    private final HashMap<String, RespTimeGraphLineBean> seriesNames = new HashMap<String, RespTimeGraphLineBean>();

    private final LinkedHashMap<String, LinkedHashMap<Integer, Long>> pList = new LinkedHashMap<String, LinkedHashMap<Integer, Long>>();

    private int durationTest = 0;
    
    private int colorIdx = 0;

    private final List<Color> listColors = Colors.getColors();

    private ArrayList<SampleResult> internalList = new ArrayList<SampleResult>();

    public RespTimeGraphVisualizer() {
        init();
    }

    public void add(final SampleResult sampleResult) {
        final String sampleLabel = sampleResult.getSampleLabel();
        synchronized (lockInterval) {
            // Internal light list to permit play with interval and label filter without a reading file results
            SampleResult srTemp = new SampleResult(sampleResult.getStartTime(), sampleResult.getTime());
            srTemp.setSampleLabel(sampleLabel);
            internalList.add(srTemp);
        }

        Matcher matcher = null;
        // Sampler selection
        if (samplerSelection.isSelected() && samplerMatchLabel.getText() != null
                && samplerMatchLabel.getText().length() > 0) {
            Pattern pattern = createPattern(samplerMatchLabel.getText());
            matcher = pattern.matcher(sampleLabel);
        }
        if ((matcher == null) || (matcher.find())) {
            final long startTimeMS = sampleResult.getStartTime();
            final int startTimeInterval = (int) startTimeMS / intervalValue;
            JMeterUtils.runSafe(new Runnable() {
                public void run() {
                    synchronized (lock) {
                        // Use for x-axis scale
                        if (startTimeInterval < minStartTime) {
                            minStartTime = startTimeInterval;
                        } else if (startTimeInterval > maxStartTime) {
                            maxStartTime = startTimeInterval;
                        }
                        // Generate x-axis label and associated color
                        if (!seriesNames.containsKey(sampleLabel)) {
                            seriesNames.put(sampleLabel, 
                                    new RespTimeGraphLineBean(sampleLabel, listColors.get(colorIdx++)));
                            // reset colors index
                            if (colorIdx >= listColors.size()) {
                                colorIdx = 0;
                            }
                        }
                        // List of value by sampler
                        if (pList.containsKey(sampleLabel)) {
                            LinkedHashMap<Integer, Long> subList = pList.get(sampleLabel);
                            long respTime = sampleResult.getTime();
                            if (subList.containsKey(startTimeInterval)) {
                                respTime = (subList.get(startTimeInterval) + respTime) / 2;
                            }
                            subList.put(startTimeInterval, respTime);
                        } else {
                            LinkedHashMap<Integer, Long> newSubList = new LinkedHashMap<Integer, Long>();
                            newSubList.put(startTimeInterval, sampleResult.getTime());
                            pList.put(sampleLabel, newSubList);
                        }
                    }
                }
            });
        }
    }

    public void makeGraph() {
        Dimension size = graphPanel.getSize();
        // canvas size
        int width = (int) size.getWidth();
        int height = (int) size.getHeight();
        if (!dynamicGraphSize.isSelected()) {
            String wstr = graphWidth.getText();
            String hstr = graphHeight.getText();
            if (wstr.length() != 0) {
                width = Integer.parseInt(wstr);
            }
            if (hstr.length() != 0) {
                height = Integer.parseInt(hstr);
            }
        }

        String yAxisStr = maxValueYAxisLabel.getText();
        int maxYAxisScale = yAxisStr.length() == 0 ? 0 : Integer.parseInt(yAxisStr);

        graphPanel.setData(this.getData());
        graphPanel.setTitle(graphTitle.getText());
        graphPanel.setMaxYAxisScale(maxYAxisScale);

        graphPanel.setYAxisLabels(this.yAxisLabel);
        graphPanel.setYAxisTitle(this.yAxisTitle);
        graphPanel.setXAxisLabels(getXAxisLabels());
        graphPanel.setLegendLabels(getLegendLabels());
        graphPanel.setColor(getLinesColors());
        graphPanel.setShowGrouping(numberShowGrouping.isSelected());
        graphPanel.setLegendPlacement(StatGraphProperties.getPlacementNameMap()
                .get(legendPlacementList.getSelectedItem()).intValue());
        graphPanel.setPointShape(StatGraphProperties.getPointShapeMap().get(pointShapeLine.getSelectedItem()));
        graphPanel.setStrokeWidth(Float.parseFloat((String) strokeWidthList.getSelectedItem()));

        graphPanel.setTitleFont(new Font(StatGraphProperties.getFontNameMap().get(titleFontNameList.getSelectedItem()),
                StatGraphProperties.getFontStyleMap().get(titleFontStyleList.getSelectedItem()).intValue(),
                Integer.parseInt((String) titleFontSizeList.getSelectedItem())));
        graphPanel.setLegendFont(new Font(StatGraphProperties.getFontNameMap().get(fontNameList.getSelectedItem()),
                StatGraphProperties.getFontStyleMap().get(fontStyleList.getSelectedItem()).intValue(),
                Integer.parseInt((String) fontSizeList.getSelectedItem())));

        graphPanel.setHeight(height);
        graphPanel.setWidth(width);
        // Draw the graph
        graphPanel.repaint();
    }

    /**
     * Generate the data for the jChart API
     * @return array of array of data to draw
     */
    public double[][] getData() {
        int size = pList.size();
        int max = durationTest;

        double[][] data = new double[size][max];

        double nanLast = 0;
        double nanBegin = 0;
        ArrayList<Double> nanList = new ArrayList<Double>();
        Set<String> pointsKeys = pList.keySet();
        int s = 0;
        for (String pointKey : pointsKeys) {
            LinkedHashMap<Integer, Long> subList = pList.get(pointKey);

            int idx = 0;
            while (idx < durationTest) {
                int keyShift = minStartTime + idx;
                if (subList.containsKey(keyShift)) {
                    nanLast = subList.get(keyShift);

                    data[s][idx] = nanLast;

                    // Calculate intermediate values (if needed)
                    int nlsize = nanList.size();
                    if (nlsize > 0) {
                        double valPrev = nanBegin;
                        for (int cnt = 0; cnt < nlsize; cnt++) {
                            int pos = idx - (nlsize - cnt);
                            if (pos < 0) { pos = 0; }
                            valPrev = (valPrev + ((nanLast - nanBegin) / (nlsize + 2)));
                            data[s][pos] = valPrev;
                        }
                        nanList.clear();
                    }
                } else {
                    nanList.add(Double.NaN);
                    nanBegin = nanLast;
                    data[s][idx] = Double.NaN;
                }
                // log.debug("data["+s+"]["+idx+"]: " + data[s][idx]);
                idx++;
            }
            s++;
        }
        return data;
    }

    public String getLabelResource() {
        return "graph_resp_time_title"; // $NON-NLS-1$
    }

    public void clearData() {
        synchronized (lock) {
            internalList.clear();
            seriesNames.clear();
            pList.clear();
            minStartTime = Integer.MAX_VALUE;
            maxStartTime = Integer.MIN_VALUE;
            durationTest = 0;
            colorIdx = 0;
        }
    }

    /**
     * Initialize the GUI.
     */
    private void init() {
        this.setLayout(new BorderLayout());

        // MAIN PANEL
        JPanel mainPanel = new JPanel();
        Border margin = new EmptyBorder(10, 10, 5, 10);
        Border margin2 = new EmptyBorder(10, 10, 5, 10);

        mainPanel.setBorder(margin);
        mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
        mainPanel.add(makeTitlePanel());

        settingsPane = new VerticalPanel();
        settingsPane.setBorder(margin2);

        graphPanel = new RespTimeGraphChart();
        graphPanel.setPreferredSize(new Dimension(defaultWidth, defaultHeight));

        settingsPane.add(createGraphActionsPane());
        settingsPane.add(createGraphSettingsPane());
        settingsPane.add(createGraphTitlePane());
        settingsPane.add(createLinePane());
        settingsPane.add(createGraphDimensionPane());
        JPanel axisPane = new JPanel(new BorderLayout());
        axisPane.add(createGraphXAxisPane(), BorderLayout.WEST);
        axisPane.add(createGraphYAxisPane(), BorderLayout.CENTER);
        settingsPane.add(axisPane);
        settingsPane.add(createLegendPane());

        tabbedGraph.addTab(JMeterUtils.getResString("aggregate_graph_tab_settings"), settingsPane); //$NON-NLS-1$
        tabbedGraph.addTab(JMeterUtils.getResString("aggregate_graph_tab_graph"), graphPanel); //$NON-NLS-1$

        this.add(mainPanel, BorderLayout.NORTH);
        this.add(tabbedGraph, BorderLayout.CENTER);

    }

    @Override
    public JComponent getPrintableComponent() {
        if (saveGraphToFile == true) {
            saveGraphToFile = false;
            graphPanel.setBounds(graphPanel.getLocation().x,graphPanel.getLocation().y,
                    graphPanel.width,graphPanel.height);
            return graphPanel;
        }
        return this;
    }

    private JPanel createGraphActionsPane() {
        JPanel buttonPanel = new JPanel(new BorderLayout());
        JPanel displayPane = new JPanel();
        displayPane.add(displayButton);
        displayButton.addActionListener(this);
        buttonPanel.add(displayPane, BorderLayout.WEST);

        JPanel savePane = new JPanel();
        savePane.add(saveGraph);
        saveGraph.addActionListener(this);
        syncWithName.addActionListener(this);
        buttonPanel.add(savePane, BorderLayout.EAST);

        return buttonPanel;
    }

    public String[] getXAxisLabels() {
        SimpleDateFormat formatter = new SimpleDateFormat(xAxisTimeFormat.getText()); //$NON-NLS-1$ 
        String[] xAxisLabels = new String[durationTest];
        for (int j = 0; j < durationTest; j++) {
            xAxisLabels[j] = formatter.format(new Date((minStartTime + j) * intervalValue));
        }
        return xAxisLabels;
    }

    private String[] getLegendLabels() {
        String[] legends = new String[seriesNames.size()];
        int i = 0;
        for (String key : seriesNames.keySet()) {
            RespTimeGraphLineBean val = seriesNames.get(key);
            legends[i] = val.getLabel();
            i++;
        }
        return legends;
    }

    private Color[] getLinesColors() {
        Color[] linesColors = new Color[seriesNames.size()];
        int i = 0;
        for (String key : seriesNames.keySet()) {
            RespTimeGraphLineBean val = seriesNames.get(key);
            linesColors[i] = val.getLineColor();
            i++;
        }
        return linesColors;
    }

    public void actionPerformed(ActionEvent event) {
        final Object eventSource = event.getSource();
        if (eventSource == displayButton) {
            actionMakeGraph();
        } else if (eventSource == saveGraph) {
            saveGraphToFile = true;
            try {
                ActionRouter.getInstance().getAction(
                        ActionNames.SAVE_GRAPHICS,SaveGraphics.class.getName()).doAction(
                                new ActionEvent(this,1,ActionNames.SAVE_GRAPHICS));
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        } else if (eventSource == syncWithName) {
            graphTitle.setText(namePanel.getName());
        } else if (eventSource == dynamicGraphSize) {
            // if use dynamic graph size is checked, we disable the dimension fields
            if (dynamicGraphSize.isSelected()) {
                graphWidth.setEnabled(false);
                graphHeight.setEnabled(false);
            } else {
                graphWidth.setEnabled(true);
                graphHeight.setEnabled(true);
            }
        } else if (eventSource == samplerSelection) {
            if (samplerSelection.isSelected()) {
                samplerMatchLabel.setEnabled(true);
                reloadButton.setEnabled(true);
                caseChkBox.setEnabled(true);
                regexpChkBox.setEnabled(true);
            } else {
                samplerMatchLabel.setEnabled(false);
                reloadButton.setEnabled(false);
                caseChkBox.setEnabled(false);
                regexpChkBox.setEnabled(false);
            }
        } else if (eventSource == reloadButton || eventSource == intervalButton) {
            if (eventSource == intervalButton) {
                intervalValue = Integer.parseInt(intervalField.getText());
            }
            if (getFile() != null && getFile().length() > 0) {
                clearData();
                FilePanel filePanel = (FilePanel) getFilePanel();
                filePanel.actionPerformed(event);
            } else {
                if (internalList.size() >= 2) {
                    synchronized (lockInterval) {
                        @SuppressWarnings("unchecked")
                        ArrayList<SampleResult> tempList = (ArrayList<SampleResult>) internalList.clone();
                        this.clearData();
                        for (SampleResult sr : tempList) {
                            this.add(sr);
                        }
                    }
                }
            }
        } 

    }
    
    private void actionMakeGraph() {
        String msgErr = null;
        // Calculate the test duration. Needs to xAxis Labels and getData.
        durationTest = maxStartTime - minStartTime;
        if (seriesNames.size() <= 0) {
            msgErr = JMeterUtils.getResString("aggregate_graph_no_values_to_graph"); // $NON-NLS-1$
        } else   if (durationTest < 1) {
            msgErr = JMeterUtils.getResString("graph_resp_time_not_enough_data"); // $NON-NLS-1$
        }
        if (msgErr == null) {
            makeGraph();
            tabbedGraph.setSelectedIndex(1);
        } else {
            JOptionPane.showMessageDialog(null, msgErr, msgErr, JOptionPane.WARNING_MESSAGE);
        }
    }

    private JComponent createLabelCombo(String label, JComboBox comboBox) {
        JPanel labelCombo = new JPanel();
        labelCombo.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        JLabel caption = new JLabel(label);
        caption.setBorder(MARGIN);
        labelCombo.add(caption);
        labelCombo.add(comboBox);
        return labelCombo;
    }

    private JPanel createGraphSettingsPane() {
        JPanel settingsPane = new JPanel(new BorderLayout());
        settingsPane.setBorder(BorderFactory.createTitledBorder(
                BorderFactory.createEtchedBorder(),
                JMeterUtils.getResString("graph_resp_time_settings_pane"))); // $NON-NLS-1$
        
        JPanel intervalPane = new JPanel();
        intervalPane.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        intervalField.setText(String.valueOf(INTERVAL_DEFAULT));
        intervalPane.add(intervalField);
        
        // Button
        intervalButton.setFont(FONT_SMALL);
        intervalButton.addActionListener(this);
        intervalPane.add(intervalButton);

        settingsPane.add(intervalPane, BorderLayout.NORTH);
        settingsPane.add(createGraphSelectionSubPane(), BorderLayout.SOUTH);

        return settingsPane;
    }

    private JPanel createGraphSelectionSubPane() {
        // Search field
        JPanel searchPanel = new JPanel();
        searchPanel.setLayout(new BoxLayout(searchPanel, BoxLayout.X_AXIS));
        searchPanel.setBorder(BorderFactory.createEmptyBorder(3, 0, 3, 0));

        searchPanel.add(samplerSelection);
        samplerMatchLabel.setEnabled(false);
        reloadButton.setEnabled(false);
        caseChkBox.setEnabled(false);
        regexpChkBox.setEnabled(false);
        samplerSelection.addActionListener(this);

        searchPanel.add(samplerMatchLabel);
        searchPanel.add(Box.createRigidArea(new Dimension(5,0)));

        // Button
        reloadButton.setFont(FONT_SMALL);
        reloadButton.addActionListener(this);
        searchPanel.add(reloadButton);

        // checkboxes
        caseChkBox.setFont(FONT_SMALL);
        searchPanel.add(caseChkBox);
        regexpChkBox.setFont(FONT_SMALL);
        searchPanel.add(regexpChkBox);

        return searchPanel;
    }

    private JPanel createGraphTitlePane() {
        JPanel titleNamePane = new JPanel(new BorderLayout());
        syncWithName.setFont(FONT_SMALL);
        titleNamePane.add(graphTitle, BorderLayout.CENTER);
        titleNamePane.add(syncWithName, BorderLayout.EAST);

        JPanel titleStylePane = new JPanel();
        titleStylePane.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 5));
        titleStylePane.add(createLabelCombo(JMeterUtils.getResString("aggregate_graph_font"), //$NON-NLS-1$
                titleFontNameList));
        titleFontNameList.setSelectedIndex(0); // default: sans serif
        titleStylePane.add(createLabelCombo(JMeterUtils.getResString("aggregate_graph_size"), //$NON-NLS-1$
                titleFontSizeList));
        titleFontSizeList.setSelectedItem(StatGraphProperties.fontSize[6]); // default: 16
        titleStylePane.add(createLabelCombo(JMeterUtils.getResString("aggregate_graph_style"), //$NON-NLS-1$
                titleFontStyleList));
        titleFontStyleList.setSelectedItem(JMeterUtils.getResString("fontstyle.bold")); // default: bold

        JPanel titlePane = new JPanel(new BorderLayout());
        titlePane.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
                JMeterUtils.getResString("aggregate_graph_title_group"))); // $NON-NLS-1$
        titlePane.add(titleNamePane, BorderLayout.NORTH);
        titlePane.add(titleStylePane, BorderLayout.SOUTH);
        return titlePane;
    }

    private JPanel createLinePane() {       
        JPanel lineStylePane = new JPanel();
        lineStylePane.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        lineStylePane.setBorder(BorderFactory.createTitledBorder(
                BorderFactory.createEtchedBorder(),
                JMeterUtils.getResString("graph_resp_time_settings_line"))); // $NON-NLS-1$
        lineStylePane.add(createLabelCombo(JMeterUtils.getResString("graph_resp_time_stroke_width"), //$NON-NLS-1$
                strokeWidthList));
        strokeWidthList.setSelectedItem(StatGraphProperties.strokeWidth[4]); // default: 3.0f
        lineStylePane.add(createLabelCombo(JMeterUtils.getResString("graph_resp_time_shape_label"), //$NON-NLS-1$
                pointShapeLine));
        pointShapeLine.setSelectedIndex(0); // default: circle
        return lineStylePane;
    }

    private JPanel createGraphDimensionPane() {
        JPanel dimensionPane = new JPanel();
        dimensionPane.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        dimensionPane.setBorder(BorderFactory.createTitledBorder(
                BorderFactory.createEtchedBorder(),
                JMeterUtils.getResString("aggregate_graph_dimension"))); // $NON-NLS-1$

        dimensionPane.add(dynamicGraphSize);
        dynamicGraphSize.setSelected(true); // default option
        graphWidth.setEnabled(false);
        graphHeight.setEnabled(false);
        dynamicGraphSize.addActionListener(this);
        dimensionPane.add(Box.createRigidArea(new Dimension(10,0)));
        dimensionPane.add(graphWidth);
        dimensionPane.add(Box.createRigidArea(new Dimension(5,0)));
        dimensionPane.add(graphHeight);
        return dimensionPane;
    }

    /**
     * Create pane for X Axis options
     * @return X Axis pane
     */
    private JPanel createGraphXAxisPane() {
        JPanel xAxisPane = new JPanel();
        xAxisPane.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        xAxisPane.setBorder(BorderFactory.createTitledBorder(
                BorderFactory.createEtchedBorder(),
                JMeterUtils.getResString("aggregate_graph_xaxis_group"))); // $NON-NLS-1$
        xAxisTimeFormat.setText("HH:mm:ss"); // $NON-NLS-1$
        xAxisPane.add(xAxisTimeFormat);
        return xAxisPane;
    }

    /**
     * Create pane for Y Axis options
     * @return Y Axis pane
     */
    private JPanel createGraphYAxisPane() {
        JPanel yAxisPane = new JPanel();
        yAxisPane.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        yAxisPane.setBorder(BorderFactory.createTitledBorder(
                BorderFactory.createEtchedBorder(),
                JMeterUtils.getResString("aggregate_graph_yaxis_group"))); // $NON-NLS-1$
        yAxisPane.add(maxValueYAxisLabel);

        yAxisPane.add(numberShowGrouping);
        return yAxisPane;
    }

    /**
     * Create pane for legend settings
     * @return Legend pane
     */
    private JPanel createLegendPane() {
        JPanel legendPanel = new JPanel();
        legendPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        legendPanel.setBorder(BorderFactory.createTitledBorder(
                BorderFactory.createEtchedBorder(),
                JMeterUtils.getResString("aggregate_graph_legend"))); // $NON-NLS-1$

        legendPanel.add(createLabelCombo(JMeterUtils.getResString("aggregate_graph_legend_placement"), //$NON-NLS-1$
                legendPlacementList));
        legendPlacementList.setSelectedItem(JMeterUtils.getResString("aggregate_graph_legend.placement.bottom")); // default: bottom
        legendPanel.add(createLabelCombo(JMeterUtils.getResString("aggregate_graph_font"), //$NON-NLS-1$
                fontNameList));
        fontNameList.setSelectedIndex(0); // default: sans serif
        legendPanel.add(createLabelCombo(JMeterUtils.getResString("aggregate_graph_size"), //$NON-NLS-1$
                fontSizeList));
        fontSizeList.setSelectedItem(StatGraphProperties.fontSize[2]); // default: 10
        legendPanel.add(createLabelCombo(JMeterUtils.getResString("aggregate_graph_style"), //$NON-NLS-1$
                fontStyleList));
        fontStyleList.setSelectedItem(JMeterUtils.getResString("fontstyle.normal")); // default: normal

        return legendPanel;
    }

    /**
     * @param textToFind
     * @return pattern ready to search
     */
    private Pattern createPattern(String textToFind) {
        String textToFindQ = Pattern.quote(textToFind);
        if (regexpChkBox.isSelected()) {
            textToFindQ = textToFind;
        }
        Pattern pattern = null;
        try {
            if (caseChkBox.isSelected()) {
                pattern = Pattern.compile(textToFindQ);
            } else {
                pattern = Pattern.compile(textToFindQ, Pattern.CASE_INSENSITIVE);
            }
        } catch (PatternSyntaxException pse) {
            return null;
        }
        return pattern;
    }
}
<commit_msg>Improves the memory usage by internal list (map now) which keep all results to allow the "reload data" action or "apply interval" action Bugzilla Id: 53718<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed  under the  License is distributed on an "AS IS" BASIS,
 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
 * implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 *
 */
package org.apache.jmeter.visualizers;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;

import org.apache.jmeter.gui.action.ActionNames;
import org.apache.jmeter.gui.action.ActionRouter;
import org.apache.jmeter.gui.action.SaveGraphics;
import org.apache.jmeter.gui.util.FilePanel;
import org.apache.jmeter.gui.util.VerticalPanel;
import org.apache.jmeter.samplers.Clearable;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.visualizers.gui.AbstractVisualizer;
import org.apache.jmeter.visualizers.utils.Colors;
import org.apache.jorphan.gui.JLabeledTextField;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;

public class RespTimeGraphVisualizer extends AbstractVisualizer implements ActionListener, Clearable {

    private static final long serialVersionUID = 280L;

    private static final Logger log = LoggingManager.getLoggerForClass();

    private final Font FONT_SMALL = new Font("SansSerif", Font.PLAIN, 10);

    private final Border MARGIN = new EmptyBorder(0, 5, 0, 5);
    
    /**
     * Lock used to protect list update
     */
    private final transient Object lock = new Object();
    /**
     * Lock used to protect refresh interval
     */
    private final transient Object lockInterval = new Object();

    private final String yAxisLabel = JMeterUtils.getResString("aggregate_graph_response_time");//$NON-NLS-1$

    private final String yAxisTitle = JMeterUtils.getResString("aggregate_graph_ms"); //$NON-NLS-1$

    private RespTimeGraphChart graphPanel = null;

    private JPanel settingsPane = null;

    private final JTabbedPane tabbedGraph = new JTabbedPane(JTabbedPane.TOP);
    
    private boolean saveGraphToFile = false;

    private final int defaultWidth = 400;

    private final int defaultHeight = 300;
    
    private final static int INTERVAL_DEFAULT = 10000; // in milli-seconds // TODO: properties?
    
    private int intervalValue = INTERVAL_DEFAULT;
    
    private final JLabeledTextField intervalField =
            new JLabeledTextField(JMeterUtils.getResString("graph_resp_time_interval_label"), 7); //$NON-NLS-1$

    private final JButton intervalButton = new JButton(JMeterUtils.getResString("graph_resp_time_interval_reload")); // $NON-NLS-1$

    private final JButton displayButton =
            new JButton(JMeterUtils.getResString("aggregate_graph_display")); //$NON-NLS-1$
    
    private JButton saveGraph =
            new JButton(JMeterUtils.getResString("aggregate_graph_save")); //$NON-NLS-1$

    private final JCheckBox samplerSelection = new JCheckBox(JMeterUtils.getResString("graph_resp_time_series_selection"), false); //$NON-NLS-1$

    private final JTextField samplerMatchLabel = new JTextField();

    private final JButton reloadButton = new JButton(JMeterUtils.getResString("aggregate_graph_reload_data")); // $NON-NLS-1$

    private final JCheckBox caseChkBox = new JCheckBox(JMeterUtils.getResString("search_text_chkbox_case"), false); // $NON-NLS-1$

    private final JCheckBox regexpChkBox = new JCheckBox(JMeterUtils.getResString("search_text_chkbox_regexp"), true); // $NON-NLS-1$

    private final JComboBox titleFontNameList = new JComboBox(StatGraphProperties.getFontNameMap().keySet().toArray());

    private final JComboBox titleFontSizeList = new JComboBox(StatGraphProperties.fontSize);

    private final JComboBox titleFontStyleList = new JComboBox(StatGraphProperties.getFontStyleMap().keySet().toArray());

    private final JComboBox fontNameList = new JComboBox(StatGraphProperties.getFontNameMap().keySet().toArray());

    private final JComboBox fontSizeList = new JComboBox(StatGraphProperties.fontSize);

    private final JComboBox fontStyleList = new JComboBox(StatGraphProperties.getFontStyleMap().keySet().toArray());

    private final JComboBox legendPlacementList = new JComboBox(StatGraphProperties.getPlacementNameMap().keySet().toArray());
    
    private final JComboBox pointShapeLine = new JComboBox(StatGraphProperties.getPointShapeMap().keySet().toArray());

    private final JComboBox strokeWidthList = new JComboBox(StatGraphProperties.strokeWidth);

    private final JCheckBox numberShowGrouping = new JCheckBox(JMeterUtils.getResString("aggregate_graph_number_grouping"), true); // Default checked // $NON-NLS-1$

    private final JButton syncWithName =
            new JButton(JMeterUtils.getResString("aggregate_graph_sync_with_name"));  //$NON-NLS-1$

    private final JLabeledTextField graphTitle =
            new JLabeledTextField(JMeterUtils.getResString("graph_resp_time_title_label")); //$NON-NLS-1$

    private final JLabeledTextField xAxisTimeFormat =
            new JLabeledTextField(JMeterUtils.getResString("graph_resp_time_xaxis_time_format"), 10); //$NON-NLS-1$ $NON-NLS-2$

    private final JLabeledTextField maxValueYAxisLabel =
            new JLabeledTextField(JMeterUtils.getResString("aggregate_graph_yaxis_max_value"), 8); //$NON-NLS-1$

    /**
     * checkbox for use dynamic graph size
     */
    private final JCheckBox dynamicGraphSize = new JCheckBox(JMeterUtils.getResString("aggregate_graph_dynamic_size")); // $NON-NLS-1$

    private final JLabeledTextField graphWidth =
            new JLabeledTextField(JMeterUtils.getResString("aggregate_graph_width"), 6); //$NON-NLS-1$
    private final JLabeledTextField graphHeight =
            new JLabeledTextField(JMeterUtils.getResString("aggregate_graph_height"), 6); //$NON-NLS-1$

    private int minStartTime = Integer.MAX_VALUE;

    private int maxStartTime = Integer.MIN_VALUE;

    private final HashMap<String, RespTimeGraphLineBean> seriesNames = new HashMap<String, RespTimeGraphLineBean>();

    private final LinkedHashMap<String, LinkedHashMap<Integer, Long>> pList = new LinkedHashMap<String, LinkedHashMap<Integer, Long>>();

    private int durationTest = 0;
    
    private int colorIdx = 0;

    private final List<Color> listColors = Colors.getColors();

    private HashMap<Long, Long> internalMap = new HashMap<Long, Long>(); // internal list of all results

    public RespTimeGraphVisualizer() {
        init();
    }

    public void add(final SampleResult sampleResult) {
        final String sampleLabel = sampleResult.getSampleLabel();
        // Make a internal list of all results to allow reload data with filter or interval
        synchronized (lockInterval) {
            internalMap.put(sampleResult.getStartTime(), sampleResult.getTime());
        }

        Matcher matcher = null;
        // Sampler selection
        if (samplerSelection.isSelected() && samplerMatchLabel.getText() != null
                && samplerMatchLabel.getText().length() > 0) {
            Pattern pattern = createPattern(samplerMatchLabel.getText());
            matcher = pattern.matcher(sampleLabel);
        }
        if ((matcher == null) || (matcher.find())) {
            final long startTimeMS = sampleResult.getStartTime();
            final int startTimeInterval = (int) startTimeMS / intervalValue;
            JMeterUtils.runSafe(new Runnable() {
                public void run() {
                    synchronized (lock) {
                        // Use for x-axis scale
                        if (startTimeInterval < minStartTime) {
                            minStartTime = startTimeInterval;
                        } else if (startTimeInterval > maxStartTime) {
                            maxStartTime = startTimeInterval;
                        }
                        // Generate x-axis label and associated color
                        if (!seriesNames.containsKey(sampleLabel)) {
                            seriesNames.put(sampleLabel, 
                                    new RespTimeGraphLineBean(sampleLabel, listColors.get(colorIdx++)));
                            // reset colors index
                            if (colorIdx >= listColors.size()) {
                                colorIdx = 0;
                            }
                        }
                        // List of value by sampler
                        if (pList.containsKey(sampleLabel)) {
                            LinkedHashMap<Integer, Long> subList = pList.get(sampleLabel);
                            long respTime = sampleResult.getTime();
                            if (subList.containsKey(startTimeInterval)) {
                                respTime = (subList.get(startTimeInterval) + respTime) / 2;
                            }
                            subList.put(startTimeInterval, respTime);
                        } else {
                            LinkedHashMap<Integer, Long> newSubList = new LinkedHashMap<Integer, Long>();
                            newSubList.put(startTimeInterval, sampleResult.getTime());
                            pList.put(sampleLabel, newSubList);
                        }
                    }
                }
            });
        }
    }

    public void makeGraph() {
        Dimension size = graphPanel.getSize();
        // canvas size
        int width = (int) size.getWidth();
        int height = (int) size.getHeight();
        if (!dynamicGraphSize.isSelected()) {
            String wstr = graphWidth.getText();
            String hstr = graphHeight.getText();
            if (wstr.length() != 0) {
                width = Integer.parseInt(wstr);
            }
            if (hstr.length() != 0) {
                height = Integer.parseInt(hstr);
            }
        }

        String yAxisStr = maxValueYAxisLabel.getText();
        int maxYAxisScale = yAxisStr.length() == 0 ? 0 : Integer.parseInt(yAxisStr);

        graphPanel.setData(this.getData());
        graphPanel.setTitle(graphTitle.getText());
        graphPanel.setMaxYAxisScale(maxYAxisScale);

        graphPanel.setYAxisLabels(this.yAxisLabel);
        graphPanel.setYAxisTitle(this.yAxisTitle);
        graphPanel.setXAxisLabels(getXAxisLabels());
        graphPanel.setLegendLabels(getLegendLabels());
        graphPanel.setColor(getLinesColors());
        graphPanel.setShowGrouping(numberShowGrouping.isSelected());
        graphPanel.setLegendPlacement(StatGraphProperties.getPlacementNameMap()
                .get(legendPlacementList.getSelectedItem()).intValue());
        graphPanel.setPointShape(StatGraphProperties.getPointShapeMap().get(pointShapeLine.getSelectedItem()));
        graphPanel.setStrokeWidth(Float.parseFloat((String) strokeWidthList.getSelectedItem()));

        graphPanel.setTitleFont(new Font(StatGraphProperties.getFontNameMap().get(titleFontNameList.getSelectedItem()),
                StatGraphProperties.getFontStyleMap().get(titleFontStyleList.getSelectedItem()).intValue(),
                Integer.parseInt((String) titleFontSizeList.getSelectedItem())));
        graphPanel.setLegendFont(new Font(StatGraphProperties.getFontNameMap().get(fontNameList.getSelectedItem()),
                StatGraphProperties.getFontStyleMap().get(fontStyleList.getSelectedItem()).intValue(),
                Integer.parseInt((String) fontSizeList.getSelectedItem())));

        graphPanel.setHeight(height);
        graphPanel.setWidth(width);
        // Draw the graph
        graphPanel.repaint();
    }

    /**
     * Generate the data for the jChart API
     * @return array of array of data to draw
     */
    public double[][] getData() {
        int size = pList.size();
        int max = durationTest;

        double[][] data = new double[size][max];

        double nanLast = 0;
        double nanBegin = 0;
        ArrayList<Double> nanList = new ArrayList<Double>();
        Set<String> pointsKeys = pList.keySet();
        int s = 0;
        for (String pointKey : pointsKeys) {
            LinkedHashMap<Integer, Long> subList = pList.get(pointKey);

            int idx = 0;
            while (idx < durationTest) {
                int keyShift = minStartTime + idx;
                if (subList.containsKey(keyShift)) {
                    nanLast = subList.get(keyShift);

                    data[s][idx] = nanLast;

                    // Calculate intermediate values (if needed)
                    int nlsize = nanList.size();
                    if (nlsize > 0) {
                        double valPrev = nanBegin;
                        for (int cnt = 0; cnt < nlsize; cnt++) {
                            int pos = idx - (nlsize - cnt);
                            if (pos < 0) { pos = 0; }
                            valPrev = (valPrev + ((nanLast - nanBegin) / (nlsize + 2)));
                            data[s][pos] = valPrev;
                        }
                        nanList.clear();
                    }
                } else {
                    nanList.add(Double.NaN);
                    nanBegin = nanLast;
                    data[s][idx] = Double.NaN;
                }
                // log.debug("data["+s+"]["+idx+"]: " + data[s][idx]);
                idx++;
            }
            s++;
        }
        return data;
    }

    public String getLabelResource() {
        return "graph_resp_time_title"; // $NON-NLS-1$
    }

    public void clearData() {
        synchronized (lock) {
            internalMap.clear();
            seriesNames.clear();
            pList.clear();
            minStartTime = Integer.MAX_VALUE;
            maxStartTime = Integer.MIN_VALUE;
            durationTest = 0;
            colorIdx = 0;
        }
    }

    /**
     * Initialize the GUI.
     */
    private void init() {
        this.setLayout(new BorderLayout());

        // MAIN PANEL
        JPanel mainPanel = new JPanel();
        Border margin = new EmptyBorder(10, 10, 5, 10);
        Border margin2 = new EmptyBorder(10, 10, 5, 10);

        mainPanel.setBorder(margin);
        mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
        mainPanel.add(makeTitlePanel());

        settingsPane = new VerticalPanel();
        settingsPane.setBorder(margin2);

        graphPanel = new RespTimeGraphChart();
        graphPanel.setPreferredSize(new Dimension(defaultWidth, defaultHeight));

        settingsPane.add(createGraphActionsPane());
        settingsPane.add(createGraphSettingsPane());
        settingsPane.add(createGraphTitlePane());
        settingsPane.add(createLinePane());
        settingsPane.add(createGraphDimensionPane());
        JPanel axisPane = new JPanel(new BorderLayout());
        axisPane.add(createGraphXAxisPane(), BorderLayout.WEST);
        axisPane.add(createGraphYAxisPane(), BorderLayout.CENTER);
        settingsPane.add(axisPane);
        settingsPane.add(createLegendPane());

        tabbedGraph.addTab(JMeterUtils.getResString("aggregate_graph_tab_settings"), settingsPane); //$NON-NLS-1$
        tabbedGraph.addTab(JMeterUtils.getResString("aggregate_graph_tab_graph"), graphPanel); //$NON-NLS-1$

        this.add(mainPanel, BorderLayout.NORTH);
        this.add(tabbedGraph, BorderLayout.CENTER);

    }

    @Override
    public JComponent getPrintableComponent() {
        if (saveGraphToFile == true) {
            saveGraphToFile = false;
            graphPanel.setBounds(graphPanel.getLocation().x,graphPanel.getLocation().y,
                    graphPanel.width,graphPanel.height);
            return graphPanel;
        }
        return this;
    }

    private JPanel createGraphActionsPane() {
        JPanel buttonPanel = new JPanel(new BorderLayout());
        JPanel displayPane = new JPanel();
        displayPane.add(displayButton);
        displayButton.addActionListener(this);
        buttonPanel.add(displayPane, BorderLayout.WEST);

        JPanel savePane = new JPanel();
        savePane.add(saveGraph);
        saveGraph.addActionListener(this);
        syncWithName.addActionListener(this);
        buttonPanel.add(savePane, BorderLayout.EAST);

        return buttonPanel;
    }

    public String[] getXAxisLabels() {
        SimpleDateFormat formatter = new SimpleDateFormat(xAxisTimeFormat.getText()); //$NON-NLS-1$ 
        String[] xAxisLabels = new String[durationTest];
        for (int j = 0; j < durationTest; j++) {
            xAxisLabels[j] = formatter.format(new Date((minStartTime + j) * intervalValue));
        }
        return xAxisLabels;
    }

    private String[] getLegendLabels() {
        String[] legends = new String[seriesNames.size()];
        int i = 0;
        for (String key : seriesNames.keySet()) {
            RespTimeGraphLineBean val = seriesNames.get(key);
            legends[i] = val.getLabel();
            i++;
        }
        return legends;
    }

    private Color[] getLinesColors() {
        Color[] linesColors = new Color[seriesNames.size()];
        int i = 0;
        for (String key : seriesNames.keySet()) {
            RespTimeGraphLineBean val = seriesNames.get(key);
            linesColors[i] = val.getLineColor();
            i++;
        }
        return linesColors;
    }

    public void actionPerformed(ActionEvent event) {
        final Object eventSource = event.getSource();
        if (eventSource == displayButton) {
            actionMakeGraph();
        } else if (eventSource == saveGraph) {
            saveGraphToFile = true;
            try {
                ActionRouter.getInstance().getAction(
                        ActionNames.SAVE_GRAPHICS,SaveGraphics.class.getName()).doAction(
                                new ActionEvent(this,1,ActionNames.SAVE_GRAPHICS));
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        } else if (eventSource == syncWithName) {
            graphTitle.setText(namePanel.getName());
        } else if (eventSource == dynamicGraphSize) {
            // if use dynamic graph size is checked, we disable the dimension fields
            if (dynamicGraphSize.isSelected()) {
                graphWidth.setEnabled(false);
                graphHeight.setEnabled(false);
            } else {
                graphWidth.setEnabled(true);
                graphHeight.setEnabled(true);
            }
        } else if (eventSource == samplerSelection) {
            if (samplerSelection.isSelected()) {
                samplerMatchLabel.setEnabled(true);
                reloadButton.setEnabled(true);
                caseChkBox.setEnabled(true);
                regexpChkBox.setEnabled(true);
            } else {
                samplerMatchLabel.setEnabled(false);
                reloadButton.setEnabled(false);
                caseChkBox.setEnabled(false);
                regexpChkBox.setEnabled(false);
            }
        } else if (eventSource == reloadButton || eventSource == intervalButton) {
            if (eventSource == intervalButton) {
                intervalValue = Integer.parseInt(intervalField.getText());
            }
            if (getFile() != null && getFile().length() > 0) {
                clearData();
                FilePanel filePanel = (FilePanel) getFilePanel();
                filePanel.actionPerformed(event);
            } else {
                // Reload data form internal list of results
                if (internalMap.size() >= 2) {
                    synchronized (lockInterval) {
                        @SuppressWarnings("unchecked")
                        HashMap<Long, Long> tempMap = (HashMap<Long, Long>) internalMap.clone();
                        this.clearData();
                        for (Long key : tempMap.keySet()) {
                            this.add(new SampleResult(key, tempMap.get(key)));
                        }
                    }
                }
            }
        } 

    }
    
    private void actionMakeGraph() {
        String msgErr = null;
        // Calculate the test duration. Needs to xAxis Labels and getData.
        durationTest = maxStartTime - minStartTime;
        if (seriesNames.size() <= 0) {
            msgErr = JMeterUtils.getResString("aggregate_graph_no_values_to_graph"); // $NON-NLS-1$
        } else   if (durationTest < 1) {
            msgErr = JMeterUtils.getResString("graph_resp_time_not_enough_data"); // $NON-NLS-1$
        }
        if (msgErr == null) {
            makeGraph();
            tabbedGraph.setSelectedIndex(1);
        } else {
            JOptionPane.showMessageDialog(null, msgErr, msgErr, JOptionPane.WARNING_MESSAGE);
        }
    }

    private JComponent createLabelCombo(String label, JComboBox comboBox) {
        JPanel labelCombo = new JPanel();
        labelCombo.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        JLabel caption = new JLabel(label);
        caption.setBorder(MARGIN);
        labelCombo.add(caption);
        labelCombo.add(comboBox);
        return labelCombo;
    }

    private JPanel createGraphSettingsPane() {
        JPanel settingsPane = new JPanel(new BorderLayout());
        settingsPane.setBorder(BorderFactory.createTitledBorder(
                BorderFactory.createEtchedBorder(),
                JMeterUtils.getResString("graph_resp_time_settings_pane"))); // $NON-NLS-1$
        
        JPanel intervalPane = new JPanel();
        intervalPane.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        intervalField.setText(String.valueOf(INTERVAL_DEFAULT));
        intervalPane.add(intervalField);
        
        // Button
        intervalButton.setFont(FONT_SMALL);
        intervalButton.addActionListener(this);
        intervalPane.add(intervalButton);

        settingsPane.add(intervalPane, BorderLayout.NORTH);
        settingsPane.add(createGraphSelectionSubPane(), BorderLayout.SOUTH);

        return settingsPane;
    }

    private JPanel createGraphSelectionSubPane() {
        // Search field
        JPanel searchPanel = new JPanel();
        searchPanel.setLayout(new BoxLayout(searchPanel, BoxLayout.X_AXIS));
        searchPanel.setBorder(BorderFactory.createEmptyBorder(3, 0, 3, 0));

        searchPanel.add(samplerSelection);
        samplerMatchLabel.setEnabled(false);
        reloadButton.setEnabled(false);
        caseChkBox.setEnabled(false);
        regexpChkBox.setEnabled(false);
        samplerSelection.addActionListener(this);

        searchPanel.add(samplerMatchLabel);
        searchPanel.add(Box.createRigidArea(new Dimension(5,0)));

        // Button
        reloadButton.setFont(FONT_SMALL);
        reloadButton.addActionListener(this);
        searchPanel.add(reloadButton);

        // checkboxes
        caseChkBox.setFont(FONT_SMALL);
        searchPanel.add(caseChkBox);
        regexpChkBox.setFont(FONT_SMALL);
        searchPanel.add(regexpChkBox);

        return searchPanel;
    }

    private JPanel createGraphTitlePane() {
        JPanel titleNamePane = new JPanel(new BorderLayout());
        syncWithName.setFont(FONT_SMALL);
        titleNamePane.add(graphTitle, BorderLayout.CENTER);
        titleNamePane.add(syncWithName, BorderLayout.EAST);

        JPanel titleStylePane = new JPanel();
        titleStylePane.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 5));
        titleStylePane.add(createLabelCombo(JMeterUtils.getResString("aggregate_graph_font"), //$NON-NLS-1$
                titleFontNameList));
        titleFontNameList.setSelectedIndex(0); // default: sans serif
        titleStylePane.add(createLabelCombo(JMeterUtils.getResString("aggregate_graph_size"), //$NON-NLS-1$
                titleFontSizeList));
        titleFontSizeList.setSelectedItem(StatGraphProperties.fontSize[6]); // default: 16
        titleStylePane.add(createLabelCombo(JMeterUtils.getResString("aggregate_graph_style"), //$NON-NLS-1$
                titleFontStyleList));
        titleFontStyleList.setSelectedItem(JMeterUtils.getResString("fontstyle.bold")); // default: bold

        JPanel titlePane = new JPanel(new BorderLayout());
        titlePane.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
                JMeterUtils.getResString("aggregate_graph_title_group"))); // $NON-NLS-1$
        titlePane.add(titleNamePane, BorderLayout.NORTH);
        titlePane.add(titleStylePane, BorderLayout.SOUTH);
        return titlePane;
    }

    private JPanel createLinePane() {       
        JPanel lineStylePane = new JPanel();
        lineStylePane.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        lineStylePane.setBorder(BorderFactory.createTitledBorder(
                BorderFactory.createEtchedBorder(),
                JMeterUtils.getResString("graph_resp_time_settings_line"))); // $NON-NLS-1$
        lineStylePane.add(createLabelCombo(JMeterUtils.getResString("graph_resp_time_stroke_width"), //$NON-NLS-1$
                strokeWidthList));
        strokeWidthList.setSelectedItem(StatGraphProperties.strokeWidth[4]); // default: 3.0f
        lineStylePane.add(createLabelCombo(JMeterUtils.getResString("graph_resp_time_shape_label"), //$NON-NLS-1$
                pointShapeLine));
        pointShapeLine.setSelectedIndex(0); // default: circle
        return lineStylePane;
    }

    private JPanel createGraphDimensionPane() {
        JPanel dimensionPane = new JPanel();
        dimensionPane.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        dimensionPane.setBorder(BorderFactory.createTitledBorder(
                BorderFactory.createEtchedBorder(),
                JMeterUtils.getResString("aggregate_graph_dimension"))); // $NON-NLS-1$

        dimensionPane.add(dynamicGraphSize);
        dynamicGraphSize.setSelected(true); // default option
        graphWidth.setEnabled(false);
        graphHeight.setEnabled(false);
        dynamicGraphSize.addActionListener(this);
        dimensionPane.add(Box.createRigidArea(new Dimension(10,0)));
        dimensionPane.add(graphWidth);
        dimensionPane.add(Box.createRigidArea(new Dimension(5,0)));
        dimensionPane.add(graphHeight);
        return dimensionPane;
    }

    /**
     * Create pane for X Axis options
     * @return X Axis pane
     */
    private JPanel createGraphXAxisPane() {
        JPanel xAxisPane = new JPanel();
        xAxisPane.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        xAxisPane.setBorder(BorderFactory.createTitledBorder(
                BorderFactory.createEtchedBorder(),
                JMeterUtils.getResString("aggregate_graph_xaxis_group"))); // $NON-NLS-1$
        xAxisTimeFormat.setText("HH:mm:ss"); // $NON-NLS-1$
        xAxisPane.add(xAxisTimeFormat);
        return xAxisPane;
    }

    /**
     * Create pane for Y Axis options
     * @return Y Axis pane
     */
    private JPanel createGraphYAxisPane() {
        JPanel yAxisPane = new JPanel();
        yAxisPane.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        yAxisPane.setBorder(BorderFactory.createTitledBorder(
                BorderFactory.createEtchedBorder(),
                JMeterUtils.getResString("aggregate_graph_yaxis_group"))); // $NON-NLS-1$
        yAxisPane.add(maxValueYAxisLabel);

        yAxisPane.add(numberShowGrouping);
        return yAxisPane;
    }

    /**
     * Create pane for legend settings
     * @return Legend pane
     */
    private JPanel createLegendPane() {
        JPanel legendPanel = new JPanel();
        legendPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        legendPanel.setBorder(BorderFactory.createTitledBorder(
                BorderFactory.createEtchedBorder(),
                JMeterUtils.getResString("aggregate_graph_legend"))); // $NON-NLS-1$

        legendPanel.add(createLabelCombo(JMeterUtils.getResString("aggregate_graph_legend_placement"), //$NON-NLS-1$
                legendPlacementList));
        legendPlacementList.setSelectedItem(JMeterUtils.getResString("aggregate_graph_legend.placement.bottom")); // default: bottom
        legendPanel.add(createLabelCombo(JMeterUtils.getResString("aggregate_graph_font"), //$NON-NLS-1$
                fontNameList));
        fontNameList.setSelectedIndex(0); // default: sans serif
        legendPanel.add(createLabelCombo(JMeterUtils.getResString("aggregate_graph_size"), //$NON-NLS-1$
                fontSizeList));
        fontSizeList.setSelectedItem(StatGraphProperties.fontSize[2]); // default: 10
        legendPanel.add(createLabelCombo(JMeterUtils.getResString("aggregate_graph_style"), //$NON-NLS-1$
                fontStyleList));
        fontStyleList.setSelectedItem(JMeterUtils.getResString("fontstyle.normal")); // default: normal

        return legendPanel;
    }

    /**
     * @param textToFind
     * @return pattern ready to search
     */
    private Pattern createPattern(String textToFind) {
        String textToFindQ = Pattern.quote(textToFind);
        if (regexpChkBox.isSelected()) {
            textToFindQ = textToFind;
        }
        Pattern pattern = null;
        try {
            if (caseChkBox.isSelected()) {
                pattern = Pattern.compile(textToFindQ);
            } else {
                pattern = Pattern.compile(textToFindQ, Pattern.CASE_INSENSITIVE);
            }
        } catch (PatternSyntaxException pse) {
            return null;
        }
        return pattern;
    }
}

{'fraction_non_alphanumeric': 0.10209751580041918, 'fraction_numerical': 0.005150367999480089, 'mean_word_length': 2.4987494315598, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 51, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4443530', 'n_tokens_mistral': 17320, 'n_tokens_neox': 16527, 'n_words': 3219}