diff options
Diffstat (limited to 'systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/launcher/SystemTapScriptGraphOptionsTab.java')
-rw-r--r-- | systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/launcher/SystemTapScriptGraphOptionsTab.java | 939 |
1 files changed, 729 insertions, 210 deletions
diff --git a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/launcher/SystemTapScriptGraphOptionsTab.java b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/launcher/SystemTapScriptGraphOptionsTab.java index bc4542f5ff..ed4142607e 100644 --- a/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/launcher/SystemTapScriptGraphOptionsTab.java +++ b/systemtap/org.eclipse.linuxtools.systemtap.ui.ide/src/org/eclipse/linuxtools/internal/systemtap/ui/ide/launcher/SystemTapScriptGraphOptionsTab.java @@ -12,6 +12,7 @@ package org.eclipse.linuxtools.internal.systemtap.ui.ide.launcher; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -24,6 +25,7 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; +import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.wizard.WizardDialog; import org.eclipse.linuxtools.internal.systemtap.ui.ide.IDEPlugin; import org.eclipse.linuxtools.systemtap.graphingapi.core.datasets.IDataSet; @@ -46,6 +48,7 @@ import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Group; @@ -60,14 +63,23 @@ import org.eclipse.ui.plugin.AbstractUIPlugin; public class SystemTapScriptGraphOptionsTab extends AbstractLaunchConfigurationTab { + /** + * The maximum number of regular expressions that can be stored in a configuration. + */ + static final int MAX_NUMBER_OF_REGEXS = 20; + + // Note: any non-private String key with a trailing underscore is to be appended with an integer when looking up values. static final String RUN_WITH_CHART = "runWithChart"; //$NON-NLS-1$ - static final String NUMBER_OF_COLUMNS = "numberOfColumns"; //$NON-NLS-1$ + static final String NUMBER_OF_REGEXS = "numberOfRegexs"; //$NON-NLS-1$ + static final String NUMBER_OF_COLUMNS = "numberOfColumns_"; //$NON-NLS-1$ static final String REGEX_BOX = "regexBox_"; //$NON-NLS-1$ - static final String NUMBER_OF_EXTRAS = "numberOfExtras"; //$NON-NLS-1$ + static final String NUMBER_OF_EXTRAS = "numberOfExtras_"; //$NON-NLS-1$ static final String EXTRA_BOX = "extraBox_"; //$NON-NLS-1$ - static final String REGULARE_EXPRESSION = "regularExpression"; //$NON-NLS-1$ - static final String SAMPLE_OUTPUT = "sampleOutput"; //$NON-NLS-1$ + static final String REGULAR_EXPRESSION = "regularExpression_"; //$NON-NLS-1$ + static final String SAMPLE_OUTPUT = "sampleOutput_"; //$NON-NLS-1$ + // Note: all graph-related keys point to 2D lists (regular expression & graph number), + // except for GRAPH_Y_SERIES (which is a 3D list). private static final String NUMBER_OF_GRAPHS = "numberOfGraphs"; //$NON-NLS-1$ private static final String GRAPH_TITLE = "graphTitle"; //$NON-NLS-1$ private static final String GRAPH_KEY = "graphKey"; //$NON-NLS-1$ @@ -78,9 +90,26 @@ public class SystemTapScriptGraphOptionsTab extends protected Pattern pattern; protected Matcher matcher; - private ModifyListener regExListener = new ModifyListener() { + private ModifyListener regexListener = new ModifyListener() { @Override public void modifyText(ModifyEvent event) { + if (!textListenersEnabled || regularExpressionCombo.getSelectionIndex() != -1) { + return; + } + regularExpressionCombo.setItem(selectedRegex, regularExpressionCombo.getText()); + regularExpressionCombo.select(selectedRegex); + refreshRegexRows(); + updateLaunchConfigurationDialog(); + } + }; + + private ModifyListener sampleOutputListener = new ModifyListener() { + @Override + public void modifyText(ModifyEvent event) { + if (!textListenersEnabled) { + return; + } + outputList.set(selectedRegex, sampleOutputText.getText()); refreshRegexRows(); updateLaunchConfigurationDialog(); } @@ -89,13 +118,32 @@ public class SystemTapScriptGraphOptionsTab extends private ModifyListener columnNameListener = new ModifyListener() { @Override public void modifyText(ModifyEvent event) { + if (!textListenersEnabled) { + return; + } + + ArrayList<String> columnNames = new ArrayList<String>(); + Control[] children = textFieldsComposite.getChildren(); + for (int i = 0; i < numberOfVisibleColumns; i++) { + columnNames.add(((Text)children[i*2]).getText()); + } + columnNamesList.set(selectedRegex, columnNames); updateLaunchConfigurationDialog(); } }; - private Text regularExpressionText; + private Combo regularExpressionCombo; + private Button removeRegexButton; + private Text sampleOutputText; private Composite textFieldsComposite; + /** + * This value controls whether or not the ModifyListeners associated with + * the Texts will perform when dispatched. Sometimes the listeners should + * be disabled to prevent needless/unsafe operations. + */ + private boolean textListenersEnabled = true; + private ScrolledComposite regexTextScrolledComposite; private Group outputParsingGroup; private Button runWithChartCheckButton; @@ -104,64 +152,190 @@ public class SystemTapScriptGraphOptionsTab extends private Button addGraphButton, duplicateGraphButton, editGraphButton, removeGraphButton; private TableItem selectedTableItem; private Group graphsGroup; - private Text sampleOutputText; private int numberOfVisibleColumns = 0; private boolean graphingEnabled = true; - private String regexErrorMessage; - private Stack<String> cachedNames = new Stack<String>(); - private List<TableItem> badGraphs = new LinkedList<TableItem>(); - private int oldNumColumns; - private int oldNumExtras; - public static IDataSetParser createDatasetParser(ILaunchConfiguration configuration) { + /** + * A list of error messages, each entry corresponding to an entered regular expression. + */ + private List<String> regexErrorMessages = new ArrayList<String>(); + + /** + * The index of the selected regular expression. + */ + private int selectedRegex = -1; + + /** + * A list containing the user-defined sample outputs associated with the regex of every index. + */ + private List<String> outputList = new ArrayList<String>(); + + /** + * A name is given to each group captured by a regular expression. This stack contains + * the names of all of a regex's groups that have been deleted, so each name may be + * restored (without having to retype it) when a group is added again. + */ + private Stack<String> cachedNames; + + /** + * A list of cachedNames stacks, containing one entry for each regular expression stored. + */ + private List<Stack<String>> cachedNamesList = new ArrayList<Stack<String>>(); + + /** + * A two-dimensional list that holds references to the names given to each regular expression's captured groups. + */ + private List<ArrayList<String>> columnNamesList = new ArrayList<ArrayList<String>>(); + + /** + * A list holding the data of every graph for the selected regular expression. + */ + private List<GraphData> graphsData = new LinkedList<GraphData>(); + + /** + * A list of graphsData lists. This is needed because each regular expression has its own set of graphs. + */ + private List<LinkedList<GraphData>> graphsDataList = new ArrayList<LinkedList<GraphData>>(); + + /** + * A list of GraphDatas that rely on series information that has been deleted from their relying regex. + */ + private List<GraphData> badGraphs = new LinkedList<GraphData>(); + + /** + * Returns the list of the names given to reach regular expression. + * @param configuration + * @return + */ + public static ArrayList<String> createDatasetNames(ILaunchConfiguration configuration) { try { - return new LineParser("^" + configuration.getAttribute(REGULARE_EXPRESSION, "") + "$"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + int numberOfRegexs = configuration.getAttribute(NUMBER_OF_REGEXS, 0); + ArrayList<String> names = new ArrayList<String>(numberOfRegexs); + for (int r = 0; r < numberOfRegexs; r++) { + names.add(MessageFormat.format(Messages.SystemTapScriptGraphOptionsTab_graphSetTitleBase, r + 1)); + } + return names; } catch (CoreException e) { ExceptionErrorDialog.openError(Messages.SystemTapScriptGraphOptionsTab_0, e); } return null; } - public static IDataSet createDataset(ILaunchConfiguration configuration) { - int n; + /** + * Creates a list of parsers, one for each regular expression created, that will be used + * to parse the output of a running script. + * @param configuration The desired run configuration. + * @return A list of parsers. + */ + public static ArrayList<IDataSetParser> createDatasetParsers(ILaunchConfiguration configuration) { try { - n = configuration.getAttribute(NUMBER_OF_COLUMNS, 0); - ArrayList<String> labels = new ArrayList<String>(n); + int numberOfRegexs = configuration.getAttribute(NUMBER_OF_REGEXS, 0); + ArrayList<IDataSetParser> parsers = new ArrayList<IDataSetParser>(numberOfRegexs); + for (int r = 0; r < numberOfRegexs; r++) { + parsers.add(new LineParser("^" + configuration.getAttribute(REGULAR_EXPRESSION + r, "") + "$")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + return parsers; + } catch (CoreException e) { + ExceptionErrorDialog.openError(Messages.SystemTapScriptGraphOptionsTab_0, e); + } + return null; + } - for (int i = 0; i < n; i++) { - String text = configuration.getAttribute(REGEX_BOX + i, (String) null); - labels.add(text); + /** + * Creates a data set corresponding to the titles given to each output column + * from each of a run configuration's regular expressions. + * @param configuration + * @return + */ + public static ArrayList<IDataSet> createDataset(ILaunchConfiguration configuration) { + try { + int numberOfRegexs = configuration.getAttribute(NUMBER_OF_REGEXS, 0); + ArrayList<IDataSet> datasets = new ArrayList<IDataSet>(numberOfRegexs); + + for (int r = 0; r < numberOfRegexs; r++) { + int numberOfColumns = configuration.getAttribute(NUMBER_OF_COLUMNS + r, 0); + ArrayList<String> labels = new ArrayList<String>(numberOfColumns); + + for (int c = 0; c < numberOfColumns; c++) { + labels.add(configuration.getAttribute(get2DConfigData(REGEX_BOX, r, c), "")); //$NON-NLS-1$ + } + datasets.add(DataSetFactory.createDataSet(RowDataSet.ID, labels.toArray(new String[] {}))); } - return DataSetFactory.createDataSet(RowDataSet.ID, labels.toArray(new String[] {})); + return datasets; } catch (CoreException e) { ExceptionErrorDialog.openError(Messages.SystemTapScriptGraphOptionsTab_1, e); } return null; } - public static LinkedList<GraphData> createGraphsFromConfiguration (ILaunchConfiguration configuration) throws CoreException { - LinkedList<GraphData> graphs = new LinkedList<GraphData>(); - int n = configuration.getAttribute(NUMBER_OF_GRAPHS, 0); - for (int i = 0; i < n; i++) { - GraphData graphData = new GraphData(); - graphData.title = configuration.getAttribute (GRAPH_TITLE + i, ""); //$NON-NLS-1$ - - graphData.key = configuration.getAttribute(GRAPH_KEY + i, ""); //$NON-NLS-1$ - graphData.xSeries = configuration.getAttribute(GRAPH_X_SERIES + i, 0); - graphData.graphID = configuration.getAttribute(GRAPH_ID + i, ""); //$NON-NLS-1$ + /** + * Creates graph data corresponding to the graphs that will plot a script's parsed output data. + * @param configuration The desired run configuration. + * @return A data set. + */ + public static ArrayList<LinkedList<GraphData>> createGraphsFromConfiguration (ILaunchConfiguration configuration) + throws CoreException { + // Restrict number of regexs to at least one, so at least + // one inner list will exist in the return value. + int numberOfRegexs = Math.max(configuration.getAttribute(NUMBER_OF_REGEXS, 1), 1); + ArrayList<LinkedList<GraphData>> graphsList = new ArrayList<LinkedList<GraphData>>(numberOfRegexs); + + for (int r = 0; r < numberOfRegexs; r++) { + int numberOfGraphs = configuration.getAttribute(NUMBER_OF_GRAPHS + r, 0); + LinkedList<GraphData> graphs = new LinkedList<GraphData>(); + for (int i = 0; i < numberOfGraphs; i++) { + GraphData graphData = new GraphData(); + graphData.title = configuration.getAttribute(get2DConfigData(GRAPH_TITLE, r, i), (String) null); + + graphData.key = configuration.getAttribute(get2DConfigData(GRAPH_KEY, r, i), (String) null); + graphData.xSeries = configuration.getAttribute(get2DConfigData(GRAPH_X_SERIES, r, i), 0); + graphData.graphID = configuration.getAttribute(get2DConfigData(GRAPH_ID, r, i), (String) null); + + int ySeriesLength = configuration.getAttribute(get2DConfigData(GRAPH_Y_SERIES_LENGTH, r, i), 0); + if (ySeriesLength == 0) { + graphData.ySeries = null; + } else { + int[] ySeries = new int[ySeriesLength]; + for (int j = 0; j < ySeriesLength; j++) { + ySeries[j] = configuration.getAttribute(get2DConfigData(GRAPH_Y_SERIES, r, i + "_" + j), 0); //$NON-NLS-1$ + } + graphData.ySeries = ySeries; + } - int ySeriesLength = configuration.getAttribute(GRAPH_Y_SERIES_LENGTH + i, 0); - int[] ySeries = new int[ySeriesLength]; - for (int j = 0; j < ySeriesLength; j++) { - ySeries[j] = configuration.getAttribute(GRAPH_Y_SERIES + i + "_" + j, 0); //$NON-NLS-1$ + graphs.add(graphData); } - graphData.ySeries = ySeries; - - graphs.add(graphData); + graphsList.add(graphs); } - return graphs; + return graphsList; + } + + /** + * Returns the key associated with the i'th data item of the r'th regular expression. + * @param configDataName The type of data to access from the configuration. + * @param r The index of the regular expression. + * @param i The index of the data item to access. + */ + private static String get2DConfigData(String configDataName, int r, int i) { + return configDataName + r + "_" + i; //$NON-NLS-1$ + } + + /** + * Returns the key associated with the data item of the r'th regular expression, tagged by string s. + * @param configDataName The type of data to access from the configuration. + * @param r The index of the regular expression. + * @param s The string to put at the end of the key. + */ + private static String get2DConfigData(String configDataName, int r, String s) { + return configDataName + r + "_" + s; //$NON-NLS-1$ + } + + /** + * Returns the total number of regular expressions of the current configuration. + */ + private int getNumberOfRegexs() { + return outputList.size(); } @Override @@ -194,7 +368,6 @@ public class SystemTapScriptGraphOptionsTab extends this.createColumnSelector(outputParsingGroup); this.graphsGroup = new Group(top, SWT.SHADOW_ETCHED_IN); - graphsGroup.setText(Messages.SystemTapScriptGraphOptionsTab_graphsTitle); graphsGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); createGraphCreateArea(graphsGroup); @@ -210,42 +383,118 @@ public class SystemTapScriptGraphOptionsTab extends GridLayout twoColumns = new GridLayout(); twoColumns.numColumns = 2; + GridLayout threeColumns = new GridLayout(); + threeColumns.numColumns = 3; + + Composite regexButtonLayout = new Composite(parent, SWT.NONE); + regexButtonLayout.setLayout(threeColumns); + regexButtonLayout.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + + Label selectedRegexLabel = new Label(regexButtonLayout, SWT.NONE); + selectedRegexLabel.setText(Messages.SystemTapScriptGraphOptionsTab_regexLabel); + selectedRegexLabel.setToolTipText(Messages.SystemTapScriptGraphOptionsTab_regexTooltip); + regularExpressionCombo = new Combo(regexButtonLayout, SWT.DROP_DOWN); + regularExpressionCombo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + regularExpressionCombo.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + int selected = regularExpressionCombo.getSelectionIndex(); + if (selected == selectedRegex) { + return; + } + + // If deselecting an empty regular expression, delete it automatically. + if (regularExpressionCombo.getItem(selectedRegex).equals("") //$NON-NLS-1$ + && graphsDataList.get(selectedRegex).size() == 0 + && outputList.get(selectedRegex).equals("")) { //$NON-NLS-1$ + + // If the deselected regex is the last one in the combo, just quit. + // Otherwise, the deleted blank entry would be replaced by another blank entry. + if (selected == regularExpressionCombo.getItemCount() - 1) { + regularExpressionCombo.select(selectedRegex); // To keep the text blank. + return; + } + removeRegex(false); + if (selected > selectedRegex) { + selected--; + } + } + + // When selecting the "Add New Regex" item in the combo (which is always the last item), + // update all appropriate values to make room for a new regular expression. + if (selected == regularExpressionCombo.getItemCount() - 1 && getNumberOfRegexs() < MAX_NUMBER_OF_REGEXS) { + outputList.add(""); //$NON-NLS-1$ + regexErrorMessages.add(null); + columnNamesList.add(new ArrayList<String>()); + cachedNamesList.add(new Stack<String>()); + graphsDataList.add(new LinkedList<GraphData>()); + + // Remove "Add New Regex" from the selected combo item; make it blank. + regularExpressionCombo.setItem(selected, ""); //$NON-NLS-1$ + regularExpressionCombo.select(selected); + updateRegexSelection(selected, false); + updateLaunchConfigurationDialog(); + + // Enable the "remove" button if only one item was present before. + // (Don't do this _every_ time something is added.) + if (getNumberOfRegexs() == 2) { + removeRegexButton.setEnabled(true); + } + if (getNumberOfRegexs() < MAX_NUMBER_OF_REGEXS) { + regularExpressionCombo.add(Messages.SystemTapScriptGraphOptionsTab_regexAddNew); + } + } else { + updateRegexSelection(selected, false); + } + } + }); + regularExpressionCombo.addModifyListener(regexListener); + + removeRegexButton = new Button(regexButtonLayout, SWT.PUSH); + removeRegexButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false)); + removeRegexButton.setText(Messages.SystemTapScriptGraphOptionsTab_regexRemove); + removeRegexButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + IWorkbench workbench = PlatformUI.getWorkbench(); + MessageDialog dialog = new MessageDialog(workbench + .getActiveWorkbenchWindow().getShell(), Messages.SystemTapScriptGraphOptionsTab_removeRegexTitle, null, + MessageFormat.format(Messages.SystemTapScriptGraphOptionsTab_removeRegexAsk, + regularExpressionCombo.getItem(selectedRegex)), + MessageDialog.QUESTION, new String[]{"Yes", "No"}, 0); //$NON-NLS-1$ //$NON-NLS-2$ + int result = dialog.open(); + if (result == 0) { //Yes + removeRegex(true); + } + } + }); + Composite regexSummaryComposite = new Composite(parent, SWT.NONE); regexSummaryComposite.setLayout(twoColumns); regexSummaryComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - Label regularExpressionLabel = new Label(regexSummaryComposite, SWT.NONE); - regularExpressionLabel.setText(Messages.ParsingWizardPage_RegularExpression + ":"); //$NON-NLS-1$ - regularExpressionLabel.setToolTipText(Messages.SystemTapScriptGraphOptionsTab_regexTooltip); - regularExpressionText = new Text(regexSummaryComposite, SWT.BORDER); - regularExpressionText.setToolTipText(Messages.SystemTapScriptGraphOptionsTab_regexTooltip); - regularExpressionText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - regularExpressionText.addModifyListener(regExListener); - Label sampleOutputLabel = new Label(regexSummaryComposite, SWT.NONE); sampleOutputLabel.setText(Messages.SystemTapScriptGraphOptionsTab_sampleOutputLabel); sampleOutputLabel.setToolTipText(Messages.SystemTapScriptGraphOptionsTab_sampleOutputTooltip); this.sampleOutputText = new Text(regexSummaryComposite, SWT.BORDER); this.sampleOutputText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - this.sampleOutputText.addModifyListener(regExListener); + this.sampleOutputText.addModifyListener(sampleOutputListener); sampleOutputText.setToolTipText(Messages.SystemTapScriptGraphOptionsTab_sampleOutputTooltip); - GridLayout threeColumnLayout = new GridLayout(); - threeColumnLayout.numColumns = 3; - threeColumnLayout.makeColumnsEqualWidth = true; Composite expressionTableLabels = new Composite(parent, SWT.NONE); - expressionTableLabels.setLayout(threeColumnLayout); + expressionTableLabels.setLayout(twoColumns); expressionTableLabels.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); Label label = new Label(expressionTableLabels, SWT.NONE); - label.setText(Messages.ParsingWizardPage_Title); - label.setAlignment(SWT.CENTER); + label.setText(Messages.SystemTapScriptGraphOptionsTab_columnTitle); + label.setAlignment(SWT.LEFT); GridData data = new GridData(SWT.FILL, SWT.FILL, false, false); data.widthHint = 200; label.setLayoutData(data); label = new Label(expressionTableLabels, SWT.NONE); + label.setAlignment(SWT.CENTER); label.setText(Messages.SystemTapScriptGraphOptionsTab_extractedValueLabel); label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); @@ -260,20 +509,10 @@ public class SystemTapScriptGraphOptionsTab extends regexTextScrolledComposite.setContent(textFieldsComposite); regexTextScrolledComposite.setExpandHorizontal(true); regexTextScrolledComposite.setExpandVertical(false); - - refreshRegexRows(); } - private IDataSet getDataset() { - Control[] textBoxes = this.textFieldsComposite.getChildren(); - int numberOfColumns = textBoxes.length/2; - ArrayList<String> labels = new ArrayList<String>(numberOfColumns); - - for (int i = 0; i < numberOfColumns; i++) { - String text = ((Text)textBoxes[i*2]).getText(); - labels.add(text); - } - return DataSetFactory.createDataSet(RowDataSet.ID, labels.toArray(new String[] {})); + private IDataSet getCurrentDataset() { + return DataSetFactory.createDataSet(RowDataSet.ID, columnNamesList.get(selectedRegex).toArray(new String[] {})); } private void createGraphCreateArea(Composite comp){ @@ -332,7 +571,7 @@ public class SystemTapScriptGraphOptionsTab extends addGraphButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { - SelectGraphAndSeriesWizard wizard = new SelectGraphAndSeriesWizard(getDataset(), null); + SelectGraphAndSeriesWizard wizard = new SelectGraphAndSeriesWizard(getCurrentDataset(), null); IWorkbench workbench = PlatformUI.getWorkbench(); wizard.init(workbench, null); WizardDialog dialog = new WizardDialog(workbench @@ -344,9 +583,8 @@ public class SystemTapScriptGraphOptionsTab extends if (null != gd) { TableItem item = new TableItem(graphsTable, SWT.NONE); - item.setText(GraphFactory.getGraphName(gd.graphID) + ":" //$NON-NLS-1$ - + gd.title); - item.setData(gd); + graphsData.add(gd); + setUpGraphTableItem(item, gd, false); updateLaunchConfigurationDialog(); } } @@ -356,15 +594,15 @@ public class SystemTapScriptGraphOptionsTab extends duplicateGraphButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { - GraphData gd = (GraphData) selectedTableItem.getData(); + GraphData gd = ((GraphData) selectedTableItem.getData()).getCopy(); TableItem item = new TableItem(graphsTable, SWT.NONE); - item.setText(GraphFactory.getGraphName(gd.graphID) + ":" //$NON-NLS-1$ - + gd.title); - item.setData(gd); - if (badGraphs.contains(selectedTableItem)) { - badGraphs.add(item); - markGraphTableItem(item, true); + graphsData.add(gd); + if (badGraphs.contains(selectedTableItem.getData())) { + badGraphs.add(gd); + setUpGraphTableItem(item, gd, true); + } else { + setUpGraphTableItem(item, gd, false); } updateLaunchConfigurationDialog(); } @@ -376,7 +614,7 @@ public class SystemTapScriptGraphOptionsTab extends editGraphButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { - SelectGraphAndSeriesWizard wizard = new SelectGraphAndSeriesWizard(getDataset(), + SelectGraphAndSeriesWizard wizard = new SelectGraphAndSeriesWizard(getCurrentDataset(), (GraphData) selectedTableItem.getData()); IWorkbench workbench = PlatformUI.getWorkbench(); wizard.init(workbench, null); @@ -386,14 +624,14 @@ public class SystemTapScriptGraphOptionsTab extends dialog.open(); GraphData gd = wizard.getGraphData(); - - if (null != gd) { - selectedTableItem.setText(GraphFactory.getGraphName(gd.graphID) + ":" //$NON-NLS-1$ - + gd.title); - selectedTableItem.setData(gd); - if (badGraphs.contains(selectedTableItem)){ - findBadGraphs(selectedTableItem); - } + if (null == gd) { + return; + } + GraphData old_gd = (GraphData) selectedTableItem.getData(); + if (!gd.equals(old_gd)) { + badGraphs.remove(old_gd); + setUpGraphTableItem(selectedTableItem, gd, false); + graphsData.set(graphsTable.indexOf(selectedTableItem), gd); updateLaunchConfigurationDialog(); } } @@ -403,7 +641,9 @@ public class SystemTapScriptGraphOptionsTab extends removeGraphButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { - badGraphs.remove(selectedTableItem); + GraphData gd = (GraphData) selectedTableItem.getData(); + graphsData.remove(gd); + badGraphs.remove(gd); selectedTableItem.dispose(); setSelectionControlsEnabled(false); updateLaunchConfigurationDialog(); @@ -411,117 +651,284 @@ public class SystemTapScriptGraphOptionsTab extends }); } + private void removeRegex(boolean autoSelect) { + int removedRegex = selectedRegex; + if (autoSelect) { + // The current selection is to be removed, so select something else that will be available. + regularExpressionCombo.select(selectedRegex != 0 ? selectedRegex - 1 : 1); + updateRegexSelection(regularExpressionCombo.getSelectionIndex(), false); + } + + regularExpressionCombo.remove(removedRegex); + outputList.remove(removedRegex); + regexErrorMessages.remove(removedRegex); + columnNamesList.remove(removedRegex); + cachedNamesList.remove(removedRegex); + graphsDataList.remove(removedRegex); + + if (autoSelect) { + // Make sure the index of the selection is accurate. + selectedRegex = regularExpressionCombo.getSelectionIndex(); + } + + // Re-add the "Add New Regex" entry if it is missing. + if (getNumberOfRegexs() == MAX_NUMBER_OF_REGEXS - 1) { + regularExpressionCombo.add(Messages.SystemTapScriptGraphOptionsTab_regexAddNew); + } + + // Disable the "remove" button if only one selection is left; never want zero items. + if (getNumberOfRegexs() == 1) { + removeRegexButton.setEnabled(false); + } + updateLaunchConfigurationDialog(); + } + + /** + * This handles UI & list updating whenever a different regular expression is selected. + * @param newSelection The index of the regex to be selected. + * @param force If true, the UI will update even if the index of the selected regex did not change. + */ + private void updateRegexSelection(int newSelection, boolean force) { + // Quit if the selection didn't change anything, or if the selection is invalid (-1). + if (newSelection == -1 || (!force && selectedRegex == newSelection)) { + return; + } + selectedRegex = newSelection; + + boolean textListenersDisabled = !textListenersEnabled; + if (!textListenersDisabled) + textListenersEnabled = false; + + sampleOutputText.setText(outputList.get(selectedRegex)); + cachedNames = cachedNamesList.get(selectedRegex); + + // Update the number of columns and their titles here, and not in refreshRegexRows, + // using the list of saved active names instead of a cachedNames stack. + ArrayList<String> columnNames = columnNamesList.get(selectedRegex); + int desiredNumberOfColumns = columnNames.size(); + // Remove all columns to easily update them all immediately afterwards. + while (numberOfVisibleColumns > 0) { + removeColumn(false); + } + while (numberOfVisibleColumns < desiredNumberOfColumns) { + addColumn(columnNames.get(numberOfVisibleColumns)); + } + + refreshRegexRows(); + + // Now, only display graphs that are associated with the selected regex. + graphsData = graphsDataList.get(selectedRegex); + graphsTable.removeAll(); + selectedTableItem = null; + setSelectionControlsEnabled(false); + + for (GraphData gd : graphsData) { + TableItem item = new TableItem(graphsTable, SWT.NONE); + setUpGraphTableItem(item, gd, badGraphs.contains(gd)); + } + graphsGroup.setText(MessageFormat.format(Messages.SystemTapScriptGraphOptionsTab_graphSetTitleBase, + selectedRegex + 1)); + + if (!textListenersDisabled) + textListenersEnabled = true; + } + private void refreshRegexRows() { try{ - pattern = Pattern.compile(regularExpressionText.getText()); + pattern = Pattern.compile(regularExpressionCombo.getText()); matcher = pattern.matcher(sampleOutputText.getText()); - this.regexErrorMessage = ""; //$NON-NLS-1$ + regexErrorMessages.set(selectedRegex, null); }catch (PatternSyntaxException e){ - this.regexErrorMessage = e.getMessage(); + regexErrorMessages.set(selectedRegex, e.getMessage()); return; } - if (regularExpressionText.getText().contains("()")){ //$NON-NLS-1$ - this.regexErrorMessage = Messages.SystemTapScriptGraphOptionsTab_6; + regexErrorMessages.set(selectedRegex, checkRegex(regularExpressionCombo.getText())); + if (regexErrorMessages.get(selectedRegex) != null) { return; } - int desiredNumberOfColumns = matcher.groupCount(); + int desiredNumberOfColumns = matcher.groupCount(); while (numberOfVisibleColumns < desiredNumberOfColumns){ - addColumn(); + addColumn(null); } while (numberOfVisibleColumns > desiredNumberOfColumns){ - removeColumn(); + removeColumn(true); } // Set values Control[] children = textFieldsComposite.getChildren(); for (int i = 0; i < numberOfVisibleColumns; i++) { - if (!matcher.matches()){ - ((Label)children[i*2+1]).setText(""); //$NON-NLS-1$ + String sampleOutputResults; + if (matcher.matches()) { + sampleOutputResults = matcher.group(i+1); + } + else if (sampleOutputText.getText().length() == 0){ + sampleOutputResults = Messages.SystemTapScriptGraphOptionsTab_sampleOutputIsEmpty; } else { - ((Label)children[i*2+1]).setText(" " +matcher.group(i+1)); //$NON-NLS-1$ + sampleOutputResults = Messages.SystemTapScriptGraphOptionsTab_sampleOutputNoMatch; } + ((Label)children[i*2+1]).setText(" " + sampleOutputResults); //$NON-NLS-1$ } + // May only add/edit graphs if there is output data being captured. + addGraphButton.setEnabled(numberOfVisibleColumns > 0); + if (selectedTableItem != null) { + editGraphButton.setEnabled(numberOfVisibleColumns > 0); + } + + regexErrorMessages.set(selectedRegex, findBadGraphs(selectedRegex)); + } + + /** + * Checks if a provided regular expression is valid. + * @param regex The regular expression to check for validity. + * @return <code>null</code> if the regular expression is valid, or an error message. + */ + private String checkRegex(String regex) { + //TODO may add more invalid regexs here, each with its own error message. + if (regex.contains("()")){ //$NON-NLS-1$ + return Messages.SystemTapScriptGraphOptionsTab_6; + } + return null; } - private void addColumn(){ + /** + * Adds one column to the list of the currently-selected regex's columns. + * This creates an extra Text in which the name of the column may be entered, + * and a corresponding Label containing sample expected output. + * @param nameToAdd If non-null, the name of the newly-created column will + * match this String. If null, the column will be given a name recovered from + * the active stack of cached names, or a default name if one doesn't exist. + */ + private void addColumn(String nameToAdd) { Text text = new Text(textFieldsComposite, SWT.BORDER); GridData data = new GridData(SWT.FILL, SWT.FILL, false, false); data.minimumWidth = 200; data.widthHint = 200; text.setLayoutData(data); - if (cachedNames.size() > 0) { - text.setText(cachedNames.pop()); - } + + numberOfVisibleColumns++; text.addModifyListener(columnNameListener); + if (nameToAdd == null) { + // Restore a deleted name by popping from the stack. + if (cachedNames.size() > 0) { + text.setText(cachedNames.pop()); + } else { + text.setText(MessageFormat.format(Messages.SystemTapScriptGraphOptionsTab_defaultColumnTitleBase, + numberOfVisibleColumns)); + } + } else { + text.setText(nameToAdd); + } Label label = new Label(textFieldsComposite, SWT.BORDER); label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); - this.numberOfVisibleColumns++; - findBadGraphs(); - textFieldsComposite.layout(); textFieldsComposite.pack(); } - private void removeColumn(){ + /** + * Removes a column from the currently-selected regex, and removes its + * corresponding Text & Label from the UI. + * @param saveNames Set to <code>true</code> if the contents of removed + * columns are to be saved in a stack for later use. + */ + private void removeColumn(Boolean saveNames) { Control[] children = textFieldsComposite.getChildren(); int i = this.numberOfVisibleColumns*2 -1; - cachedNames.push(((Text)children[i-1]).getText()); + + if (saveNames) { + // Push the removed name on a stack. + String name = ((Text)children[i-1]).getText(); + if (name != null && name != "") { //$NON-NLS-1$ + cachedNames.push(name); + } + columnNamesList.get(selectedRegex).remove(numberOfVisibleColumns - 1); + } + children[i].dispose(); children[i-1].dispose(); this.numberOfVisibleColumns--; - findBadGraphs(); textFieldsComposite.layout(); textFieldsComposite.pack(); } - private void findBadGraphs(){ - findBadGraphs(null); - } - - private void findBadGraphs(TableItem itemToCheck){ - TableItem[] items = {itemToCheck}; - if (itemToCheck == null) { - items = graphsTable.getItems(); - } - if (items.length == 0){ - badGraphs.clear(); - return; - } - for (TableItem item : items){ - GraphData gd = (GraphData) item.getData(); + /** + * Marks all graphs belonging to the indicated regular expression that are + * missing column data, or unmarks graphs that aren't missing data. + * @param regex The index of the regular expression to check for invalid graphs. + * @return An appropriate error message if an invalid graph is found, or if the + * selected regular expression parses nothing. + */ + private String findBadGraphs(int regex) { + boolean foundBad = false; + int numberOfColumns = columnNamesList.get(regex).size(); + for (GraphData gd : graphsDataList.get(regex)) { boolean removed = false; - if (gd.xSeries >= this.numberOfVisibleColumns){ + if (gd.xSeries >= numberOfColumns) { removed = true; } - for (int s = 0; s < gd.ySeries.length && !removed; s++){ - if (gd.ySeries[s] >= this.numberOfVisibleColumns){ + for (int s = 0; s < gd.ySeries.length && !removed; s++) { + if (gd.ySeries[s] >= numberOfColumns) { removed = true; } } - - if (removed){ - if (!badGraphs.contains(item)){ - badGraphs.add(item); - markGraphTableItem(item, true); + if (removed) { + foundBad = true; + if (!badGraphs.contains(gd)) { + badGraphs.add(gd); + setUpGraphTableItem(findGraphTableItem(gd), null, true); } - }else if (badGraphs.contains(item)){ - badGraphs.remove(item); - markGraphTableItem(item, false); + } else if (badGraphs.contains(gd)) { + badGraphs.remove(gd); + setUpGraphTableItem(findGraphTableItem(gd), null, false); } } + if (numberOfColumns == 0) { + return Messages.SystemTapScriptGraphOptionsTab_9; + } + if (foundBad) { + return Messages.SystemTapScriptGraphOptionsTab_8; + } + return null; } - private void markGraphTableItem(TableItem item, boolean bad){ + private TableItem findGraphTableItem(GraphData gd) { + for (TableItem item : graphsTable.getItems()) { + if (item.getData().equals(gd)) { + return item; + } + } + return null; + } + + /** + * Sets up a given {@link TableItem} with the proper title & appearance based on + * its graph data & (in)valid status. + * @param item The {@link TableItem} to set up. + * @param gd The {@link GraphData} that the item will hold. Set to <code>null</code> + * to preserve the item's existing data. + * @param bad <code>true</code> if the item should appear as invalid, <code>false</code> otherwise. + */ + private void setUpGraphTableItem(TableItem item, GraphData gd, boolean bad) { + // Include a null check to avoid accidentally marking non-visible items. + if (item == null) { + return; + } + if (gd != null) { + item.setData(gd); + } else { + gd = (GraphData) item.getData(); + } item.setForeground(item.getDisplay().getSystemColor(bad ? SWT.COLOR_RED : SWT.COLOR_BLACK)); + item.setText(GraphFactory.getGraphName(gd.graphID) + ":" + gd.title //$NON-NLS-1$ + + (bad ? " " + Messages.SystemTapScriptGraphOptionsTab_invalidGraph : "")); //$NON-NLS-1$ //$NON-NLS-2$ } public boolean canFlipToNextPage() { @@ -531,54 +938,85 @@ public class SystemTapScriptGraphOptionsTab extends @Override public void setDefaults(ILaunchConfigurationWorkingCopy configuration) { configuration.setAttribute(RUN_WITH_CHART, false); - configuration.setAttribute(NUMBER_OF_COLUMNS, 0); - configuration.setAttribute(NUMBER_OF_GRAPHS, 0); - configuration.setAttribute(NUMBER_OF_GRAPHS, 0); - configuration.setAttribute(REGULARE_EXPRESSION, ""); //$NON-NLS-1$ - configuration.setAttribute(SAMPLE_OUTPUT, ""); //$NON-NLS-1$ + configuration.setAttribute(NUMBER_OF_REGEXS, 1); + configuration.setAttribute(NUMBER_OF_COLUMNS + 0, 0); + configuration.setAttribute(NUMBER_OF_EXTRAS + 0, 0); + configuration.setAttribute(REGULAR_EXPRESSION + 0, ""); //$NON-NLS-1$ + configuration.setAttribute(SAMPLE_OUTPUT + 0, ""); //$NON-NLS-1$ + configuration.setAttribute(NUMBER_OF_GRAPHS + 0, 0); } @Override public void initializeFrom(ILaunchConfiguration configuration) { try { + textListenersEnabled = false; + boolean chart = configuration.getAttribute(RUN_WITH_CHART, false); setGraphingEnabled(chart); this.runWithChartCheckButton.setSelection(chart); - regularExpressionText.setText(configuration.getAttribute(REGULARE_EXPRESSION, "")); //$NON-NLS-1$ - sampleOutputText.setText(configuration.getAttribute(SAMPLE_OUTPUT, "")); //$NON-NLS-1$ + // Reset lists & settings to keep things idempotent. + regularExpressionCombo.removeAll(); + outputList.clear(); + regexErrorMessages.clear(); + columnNamesList.clear(); + cachedNamesList.clear(); + graphsTable.removeAll(); + badGraphs.clear(); + + // There should always be at least one regular expression (a blank one still counts). + // If configuration's number of regexs is zero, it is outdated. + int numberOfRegexs = Math.max(configuration.getAttribute(NUMBER_OF_REGEXS, 1), 1); - oldNumColumns = configuration.getAttribute(NUMBER_OF_COLUMNS, 0); - Control[] textBoxes = this.textFieldsComposite.getChildren(); + // Only allow removing regexs if there are more than one. + removeRegexButton.setEnabled(numberOfRegexs > 1); - for (int i = 0; i < oldNumColumns && i*2 < textBoxes.length; i++) { - String text = configuration.getAttribute(REGEX_BOX+i, (String)null); - if (text != null) { - ((Text)textBoxes[i*2]).setText(text); + for (int r = 0; r < numberOfRegexs; r++) { + // Save all of the configuration's regular expressions & sample outputs in a list. + regularExpressionCombo.add(configuration.getAttribute(REGULAR_EXPRESSION + r, "")); //$NON-NLS-1$ + outputList.add(configuration.getAttribute(SAMPLE_OUTPUT + r, "")); //$NON-NLS-1$ + + // Save each regex's list of group names. + int numberOfColumns = configuration.getAttribute(NUMBER_OF_COLUMNS + r, 0); + ArrayList<String> namelist = new ArrayList<String>(numberOfColumns); + for (int i = 0; i < numberOfColumns; i++) { + namelist.add(configuration.getAttribute(get2DConfigData(REGEX_BOX, r, i), (String)null)); } - } + columnNamesList.add(namelist); + + //Reclaim missing column data that was required for existing graphs at the time of the previous "apply". + int numberOfExtras = configuration.getAttribute(NUMBER_OF_EXTRAS + r, 0); + Stack<String> oldnames = new Stack<String>(); + for (int i = 0; i < numberOfExtras; i++) { + oldnames.push(configuration.getAttribute(get2DConfigData(EXTRA_BOX, r, i), (String)null)); + } + cachedNamesList.add(oldnames); - cachedNames.clear(); - oldNumExtras = configuration.getAttribute(NUMBER_OF_EXTRAS, 0); - for (int i = oldNumExtras-1; i >= 0; i--) { - cachedNames.push(configuration.getAttribute(EXTRA_BOX+i, "")); //$NON-NLS-1$ + regexErrorMessages.add(null); + } + if (getNumberOfRegexs() < MAX_NUMBER_OF_REGEXS) { + regularExpressionCombo.add(Messages.SystemTapScriptGraphOptionsTab_regexAddNew); } + // When possible, preserve the selection on subsequent initializations, for user convenience. + int defaultSelectedRegex = 0 <= selectedRegex && selectedRegex < numberOfRegexs ? selectedRegex : 0; + regularExpressionCombo.select(defaultSelectedRegex); + // Add graphs - graphsTable.removeAll(); - badGraphs.clear(); - LinkedList<GraphData> graphs = createGraphsFromConfiguration(configuration); - for (GraphData graphData : graphs) { + graphsDataList = createGraphsFromConfiguration(configuration); + graphsData = graphsDataList.get(defaultSelectedRegex); + for (GraphData graphData : graphsData) { TableItem item = new TableItem(graphsTable, SWT.NONE); - item.setText(GraphFactory.getGraphName(graphData.graphID) + ":" //$NON-NLS-1$ - + graphData.title); - item.setForeground(item.getDisplay().getSystemColor(SWT.COLOR_BLACK)); - item.setData(graphData); - findBadGraphs(item); + setUpGraphTableItem(item, graphData, true); } + updateRegexSelection(0, true); // Handles all remaining updates. + checkAllOtherErrors(); + } catch (CoreException e) { ExceptionErrorDialog.openError(Messages.SystemTapScriptGraphOptionsTab_5, e); + } finally { + textListenersEnabled = true; } } @@ -586,51 +1024,134 @@ public class SystemTapScriptGraphOptionsTab extends public void performApply(ILaunchConfigurationWorkingCopy configuration) { configuration.setAttribute(RUN_WITH_CHART, this.runWithChartCheckButton.getSelection()); - configuration.setAttribute(REGULARE_EXPRESSION, regularExpressionText.getText()); - configuration.setAttribute(SAMPLE_OUTPUT, sampleOutputText.getText()); - - Control[] textBoxes = this.textFieldsComposite.getChildren(); - int numberOfColumns = textBoxes.length/2; - configuration.setAttribute(NUMBER_OF_COLUMNS, numberOfColumns); + int numberOfRegexs = getNumberOfRegexs(); + for (int r = 0; r < numberOfRegexs; r++) { + // Save data sets, and clear removed ones. + configuration.setAttribute(REGULAR_EXPRESSION + r, regularExpressionCombo.getItem(r)); + configuration.setAttribute(SAMPLE_OUTPUT + r, outputList.get(r)); - for (int i = 0; i < numberOfColumns; i++) { - String text = ((Text)textBoxes[i*2]).getText(); - configuration.setAttribute(REGEX_BOX+i, text); + ArrayList<String> columnNames = columnNamesList.get(r); + int numberOfColumns = columnNames.size(); + for (int i = 0; i < numberOfColumns; i++) { + configuration.setAttribute(get2DConfigData(REGEX_BOX, r, i), columnNames.get(i)); + } + cleanUpConfigurationItem(configuration, NUMBER_OF_COLUMNS, REGEX_BOX, r, numberOfColumns); + configuration.setAttribute(NUMBER_OF_COLUMNS + r, numberOfColumns); + + // If the current regex has graphs with missing data, store all cached names + // in the configuration so that they will be easily restorable for next time. + Stack<String> extranames = cachedNamesList.get(r); + int numberOfExtras = findBadGraphs(r) == null ? 0 : extranames.size(); + for (int i = 0; i < numberOfExtras; i++) { + configuration.setAttribute(get2DConfigData(EXTRA_BOX, r, i), extranames.get(i)); + } + cleanUpConfigurationItem(configuration, NUMBER_OF_EXTRAS, EXTRA_BOX, r, numberOfExtras); + configuration.setAttribute(NUMBER_OF_EXTRAS + r, numberOfExtras); + + // Save new graphs, and clear removed ones. + LinkedList<GraphData> list = graphsDataList.get(r); + int numberOfGraphs = list.size(); + for (int i = 0; i < numberOfGraphs; i++) { + GraphData graphData = list.get(i); + configuration.setAttribute(get2DConfigData(GRAPH_TITLE, r, i), graphData.title); + configuration.setAttribute(get2DConfigData(GRAPH_KEY, r, i), graphData.key); + configuration.setAttribute(get2DConfigData(GRAPH_X_SERIES, r, i), graphData.xSeries); + configuration.setAttribute(get2DConfigData(GRAPH_ID, r, i), graphData.graphID); + + int ySeriesLength = graphData.ySeries.length; + for (int j = 0; j < ySeriesLength; j++) { + configuration.setAttribute(get2DConfigData(GRAPH_Y_SERIES, r, i + "_" + j), //$NON-NLS-1$ + graphData.ySeries[j]); + } + cleanUpConfigurationGraphYSeries(configuration, r, i, ySeriesLength); + configuration.setAttribute(get2DConfigData(GRAPH_Y_SERIES_LENGTH, r, i), ySeriesLength); + } + cleanUpConfigurationGraphs(configuration, r, numberOfGraphs); + configuration.setAttribute(NUMBER_OF_GRAPHS + r, numberOfGraphs); } - //clear what's unused - for (int i = numberOfColumns; i < oldNumColumns; i++) { - configuration.setAttribute(REGEX_BOX+i, (String) null); + cleanUpConfiguration(configuration, numberOfRegexs); + configuration.setAttribute(NUMBER_OF_REGEXS, numberOfRegexs); + } + + /** + * Removes all configuration attributes associated with deleted regular expressions. + * @param configuration The configuration to remove attributes from. + * @param numberOfRegexs The number of regex-related properties to exist in the + * configuration after cleanup. + */ + private void cleanUpConfiguration(ILaunchConfigurationWorkingCopy configuration, int numberOfRegexs) { + int oldNumberOfRegexs = 0; + try { + oldNumberOfRegexs = configuration.getAttribute(NUMBER_OF_REGEXS, 0); + } catch (CoreException e) {} + for (int r = numberOfRegexs; r < oldNumberOfRegexs; r++) { + configuration.removeAttribute(REGULAR_EXPRESSION + r); + configuration.removeAttribute(SAMPLE_OUTPUT + r); + + cleanUpConfigurationItem(configuration, NUMBER_OF_COLUMNS, REGEX_BOX, r, 0); + configuration.removeAttribute(NUMBER_OF_COLUMNS + r); + + cleanUpConfigurationItem(configuration, NUMBER_OF_COLUMNS, EXTRA_BOX, r, 0); + configuration.removeAttribute(NUMBER_OF_EXTRAS + r); + + cleanUpConfigurationGraphs(configuration, r, 0); + configuration.removeAttribute(NUMBER_OF_GRAPHS + r); } - oldNumColumns = numberOfColumns; + } - int numberOfExtras = (badGraphs.size() == 0) ? 0 : cachedNames.size(); - configuration.setAttribute(NUMBER_OF_EXTRAS, numberOfExtras); - for (int i = 0; i < numberOfExtras; i++) { - configuration.setAttribute(EXTRA_BOX+i, cachedNames.get(i)); + private void cleanUpConfigurationGraphs(ILaunchConfigurationWorkingCopy configuration, int regex, int newNumberOfGraphs) { + int oldNumberOfGraphs = 0; + try { + oldNumberOfGraphs = configuration.getAttribute(NUMBER_OF_GRAPHS + regex, 0); + } catch (CoreException e) {} + for (int i = newNumberOfGraphs; i < oldNumberOfGraphs; i++) { + configuration.removeAttribute(get2DConfigData(GRAPH_TITLE, regex, i)); + configuration.removeAttribute(get2DConfigData(GRAPH_KEY, regex, i)); + configuration.removeAttribute(get2DConfigData(GRAPH_X_SERIES, regex, i)); + configuration.removeAttribute(get2DConfigData(GRAPH_ID, regex, i)); + + cleanUpConfigurationGraphYSeries(configuration, regex, i, 0); + configuration.removeAttribute(get2DConfigData(GRAPH_Y_SERIES_LENGTH, regex, i)); } - for (int i = numberOfExtras; i < oldNumExtras; i++) { - configuration.setAttribute(EXTRA_BOX+i, (String) null); + } + + private void cleanUpConfigurationItem(ILaunchConfigurationWorkingCopy configuration, String counter, String property, int regex, int newNumberOfItems) { + int oldNumberOfItems = 0; + try { + oldNumberOfItems = configuration.getAttribute(counter + regex, 0); + } catch (CoreException e) {} + for (int i = newNumberOfItems; i < oldNumberOfItems; i++) { + configuration.removeAttribute(get2DConfigData(property, regex, i)); } - oldNumExtras = numberOfExtras; + } - // Save graphs. - TableItem[] list = this.graphsTable.getItems(); - configuration.setAttribute(NUMBER_OF_GRAPHS, list.length); - for (int i = 0; i < list.length; i++) { - GraphData graphData = (GraphData)list[i].getData(); - configuration.setAttribute(GRAPH_TITLE + i, graphData.title); + private void cleanUpConfigurationGraphYSeries(ILaunchConfigurationWorkingCopy configuration, int regex, int graph, int newLength) { + int oldYSeriesLength = 0; + try { + oldYSeriesLength = configuration.getAttribute(get2DConfigData(GRAPH_Y_SERIES_LENGTH, regex, graph), 0); + } catch (CoreException e) {} + for (int i = newLength; i < oldYSeriesLength; i++) { + configuration.removeAttribute(get2DConfigData(GRAPH_Y_SERIES, regex, graph + "_" + i)); //$NON-NLS-1$ + } + } - configuration.setAttribute(GRAPH_KEY + i, graphData.key); - configuration.setAttribute(GRAPH_X_SERIES + i, graphData.xSeries); - configuration.setAttribute(GRAPH_ID + i, graphData.graphID); + /** + * Checks all regular expressions for errors, except for the currently-selected + * expression (as it should be checked by {@link #refreshRegexRows}). + */ + private void checkAllOtherErrors() { + for (int i = 0, n = getNumberOfRegexs(); i < n; i++) { + if (i == selectedRegex) { + continue; + } - configuration.setAttribute(GRAPH_Y_SERIES_LENGTH + i, graphData.ySeries.length); - for (int j = 0; j < graphData.ySeries.length; j++) { - configuration.setAttribute(GRAPH_Y_SERIES + i + "_" + j, graphData.ySeries[j]); //$NON-NLS-1$ + String error = findBadGraphs(i); + if (error == null) { + error = checkRegex(regularExpressionCombo.getItem(i)); } - } - addGraphButton.setEnabled(numberOfColumns > 0); + regexErrorMessages.set(i, error); + } } @Override @@ -642,17 +1163,12 @@ public class SystemTapScriptGraphOptionsTab extends return true; } - if (!this.regexErrorMessage.equals("")){ //$NON-NLS-1$ - setErrorMessage(regexErrorMessage); - return false; - } - if (this.numberOfVisibleColumns == 0){ - setErrorMessage(Messages.SystemTapScriptGraphOptionsTab_9); - return false; - } - if (badGraphs.size() > 0){ - setErrorMessage(Messages.SystemTapScriptGraphOptionsTab_8); - return false; + for (int i = 0, n = getNumberOfRegexs(); i < n; i++) { + String regexErrorMessage = regexErrorMessages.get(i); + if (regexErrorMessage != null){ + setErrorMessage(String.format("Expression \"%s\": %s", regularExpressionCombo.getItems()[i], regexErrorMessage)); //$NON-NLS-1$ + return false; + } } return true; @@ -678,6 +1194,9 @@ public class SystemTapScriptGraphOptionsTab extends this.setControlEnabled(graphsGroup, enabled); // Disable buttons that rely on a selected graph if no graph is selected. this.setSelectionControlsEnabled(selectedTableItem != null); + this.addGraphButton.setEnabled(enabled && numberOfVisibleColumns > 0); + this.editGraphButton.setEnabled(enabled && numberOfVisibleColumns > 0); + this.removeRegexButton.setEnabled(enabled && getNumberOfRegexs() > 1); updateLaunchConfigurationDialog(); } @@ -697,7 +1216,7 @@ public class SystemTapScriptGraphOptionsTab extends */ private void setSelectionControlsEnabled(boolean enabled) { duplicateGraphButton.setEnabled(enabled); - editGraphButton.setEnabled(enabled); + editGraphButton.setEnabled(enabled && numberOfVisibleColumns > 0); removeGraphButton.setEnabled(enabled); } } |