From 3ee9e2ace4bcbb09ad21c51c2881d6cfe664fe16 Mon Sep 17 00:00:00 2001 From: Philip Arickx Date: Wed, 12 Apr 2017 20:11:03 +0200 Subject: [PATCH 1/3] FIX : Improve readability Add preferences dialog for formatting the displayed and exported numbers, with choices for thousands and decimal separators etc. Ensure the preferences are respected in all rendered fields. --- .../controller/AbstractViewController.java | 38 +- .../controller/FlatDiffViewController.java | 2 +- .../javafx/controller/FlatViewController.java | 4 +- .../controller/ProfileRootController.java | 54 ++- .../javafx/controller/RootController.java | 11 + .../javafx/controller/TreeViewController.java | 2 +- .../ConfigurationDialogController.java | 68 ++++ .../FormattingConfigurationController.java | 207 +++++++++++ .../javafx/model/ApplicationContext.java | 202 +++++++++- .../model/configuration/Configuration.java | 41 +++ .../FormattingConfiguration.java | 345 ++++++++++++++++++ .../ports/javafx/util/ContextMenuUtil.java | 2 +- .../ports/javafx/util/ConversionUtil.java | 46 +++ .../ports/javafx/util/RenderUtil.java | 40 ++ .../ports/javafx/util/report/ReportUtil.java | 65 ++-- .../ports/javafx/view/FlameGraphCanvas.java | 5 + .../ports/javafx/view/Rendering.java | 8 - .../javafx/view/cell/CountTableCell.java | 55 --- .../javafx/view/cell/CountTreeTableCell.java | 54 --- .../view/cell/GraphicalShareTableCell.java | 23 ++ .../cell/GraphicalShareTreeTableCell.java | 23 +- .../javafx/view/cell/NumberTableCell.java | 52 +++ .../javafx/view/cell/NumberTreeTableCell.java | 52 +++ .../javafx/view/cell/PercentageTableCell.java | 56 --- .../view/cell/PercentageTreeTableCell.java | 56 --- .../ports/javafx/view/cell/TimeTableCell.java | 56 --- .../javafx/view/cell/TimeTreeTableCell.java | 55 --- .../honest_profiler/ports/javafx/css/HPUI.css | 6 +- .../javafx/fxml/FilterCreationDialog.fxml | 4 + .../ports/javafx/fxml/FilterDialog.fxml | 4 + .../ports/javafx/fxml/Root.fxml | 12 +- .../configuration/ConfigurationDialog.fxml | 48 +++ .../FormattingConfiguration.fxml | 114 ++++++ .../javafx/i18n/HPUIBundle_en.properties | 20 +- 34 files changed, 1383 insertions(+), 447 deletions(-) create mode 100644 src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/configuration/ConfigurationDialogController.java create mode 100644 src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/configuration/FormattingConfigurationController.java create mode 100644 src/main/java/com/insightfullogic/honest_profiler/ports/javafx/model/configuration/Configuration.java create mode 100644 src/main/java/com/insightfullogic/honest_profiler/ports/javafx/model/configuration/FormattingConfiguration.java create mode 100644 src/main/java/com/insightfullogic/honest_profiler/ports/javafx/util/RenderUtil.java delete mode 100644 src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/CountTableCell.java delete mode 100644 src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/CountTreeTableCell.java create mode 100644 src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/NumberTableCell.java create mode 100644 src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/NumberTreeTableCell.java delete mode 100644 src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/PercentageTableCell.java delete mode 100644 src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/PercentageTreeTableCell.java delete mode 100644 src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/TimeTableCell.java delete mode 100644 src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/TimeTreeTableCell.java create mode 100644 src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/configuration/ConfigurationDialog.fxml create mode 100644 src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/configuration/FormattingConfiguration.fxml diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/AbstractViewController.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/AbstractViewController.java index 8e2f6df27..8c127dbd7 100644 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/AbstractViewController.java +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/AbstractViewController.java @@ -18,12 +18,8 @@ import com.insightfullogic.honest_profiler.ports.javafx.controller.filter.FilterDialogController; import com.insightfullogic.honest_profiler.ports.javafx.model.ApplicationContext; import com.insightfullogic.honest_profiler.ports.javafx.model.ProfileContext; -import com.insightfullogic.honest_profiler.ports.javafx.view.cell.CountTableCell; -import com.insightfullogic.honest_profiler.ports.javafx.view.cell.CountTreeTableCell; -import com.insightfullogic.honest_profiler.ports.javafx.view.cell.PercentageTableCell; -import com.insightfullogic.honest_profiler.ports.javafx.view.cell.PercentageTreeTableCell; -import com.insightfullogic.honest_profiler.ports.javafx.view.cell.TimeTableCell; -import com.insightfullogic.honest_profiler.ports.javafx.view.cell.TimeTreeTableCell; +import com.insightfullogic.honest_profiler.ports.javafx.view.cell.NumberTableCell; +import com.insightfullogic.honest_profiler.ports.javafx.view.cell.NumberTreeTableCell; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; @@ -150,6 +146,8 @@ protected void initialize(Label threadGroupingLabel, ChoiceBox t public void setApplicationContext(ApplicationContext applicationContext) { super.setApplicationContext(applicationContext); + applicationContext.getConfiguration() + .addListener((property, oldValue, newValue) -> refresh()); // If the subclass doesn't need filter management, no UI control should have been passed on in the initialize() // method. @@ -264,7 +262,7 @@ protected void cfgPctCol(TableColumn column, String propertyName, ProfileContext profileContext, String title) { column.setCellValueFactory(new PropertyValueFactory<>(propertyName)); - column.setCellFactory(col -> new PercentageTableCell<>(null)); + column.setCellFactory(col -> new NumberTableCell<>(appCtx()::displayPercent, null)); setColumnHeader(column, title, profileContext); } @@ -281,7 +279,8 @@ protected void cfgPctDiffCol(TableColumn column, String propertyN String title) { column.setCellValueFactory(new PropertyValueFactory<>(propertyName)); - column.setCellFactory(col -> new PercentageTableCell<>(doubleDiffStyler)); + column.setCellFactory( + col -> new NumberTableCell<>(appCtx()::displayPercent, doubleDiffStyler)); setColumnHeader(column, title, null); } @@ -299,7 +298,7 @@ protected void cfgNrCol(TableColumn column, String propertyName, ProfileContext profileContext, String title) { column.setCellValueFactory(new PropertyValueFactory<>(propertyName)); - column.setCellFactory(col -> new CountTableCell<>(null)); + column.setCellFactory(col -> new NumberTableCell<>(appCtx()::displayIntegral, null)); setColumnHeader(column, title, profileContext); } @@ -316,7 +315,7 @@ protected void cfgNrDiffCol(TableColumn column, String propertyNa String title) { column.setCellValueFactory(new PropertyValueFactory<>(propertyName)); - column.setCellFactory(col -> new CountTableCell<>(intDiffStyler)); + column.setCellFactory(col -> new NumberTableCell<>(appCtx()::displayIntegral, intDiffStyler)); setColumnHeader(column, title, null); } @@ -334,7 +333,7 @@ protected void cfgTimeCol(TableColumn column, String propertyName ProfileContext profileContext, String title) { column.setCellValueFactory(new PropertyValueFactory<>(propertyName)); - column.setCellFactory(col -> new TimeTableCell<>(null)); + column.setCellFactory(col -> new NumberTableCell<>(appCtx()::displayTime, null)); setColumnHeader(column, title, profileContext); } @@ -351,7 +350,7 @@ protected void cfgTimeDiffCol(TableColumn column, String property String title) { column.setCellValueFactory(new PropertyValueFactory<>(propertyName)); - column.setCellFactory(col -> new TimeTableCell<>(longDiffStyler)); + column.setCellFactory(col -> new NumberTableCell<>(appCtx()::displayTime, longDiffStyler)); setColumnHeader(column, title, null); } @@ -371,7 +370,7 @@ protected void cfgPctCol(TreeTableColumn column, String propertyN ProfileContext profileContext, String title) { column.setCellValueFactory(new TreeItemPropertyValueFactory<>(propertyName)); - column.setCellFactory(col -> new PercentageTreeTableCell<>(null)); + column.setCellFactory(col -> new NumberTreeTableCell<>(appCtx()::displayPercent, null)); setColumnHeader(column, title, profileContext); } @@ -388,7 +387,8 @@ protected void cfgPctDiffCol(TreeTableColumn column, String prope String title) { column.setCellValueFactory(new TreeItemPropertyValueFactory<>(propertyName)); - column.setCellFactory(col -> new PercentageTreeTableCell<>(doubleDiffStyler)); + column.setCellFactory( + col -> new NumberTreeTableCell<>(appCtx()::displayPercent, doubleDiffStyler)); setColumnHeader(column, title, null); } @@ -406,7 +406,7 @@ protected void cfgNrCol(TreeTableColumn column, String propertyNa ProfileContext profileContext, String title) { column.setCellValueFactory(new TreeItemPropertyValueFactory<>(propertyName)); - column.setCellFactory(col -> new CountTreeTableCell<>(null)); + column.setCellFactory(col -> new NumberTreeTableCell<>(appCtx()::displayIntegral, null)); setColumnHeader(column, title, profileContext); } @@ -423,7 +423,8 @@ protected void cfgNrDiffCol(TreeTableColumn column, String proper String title) { column.setCellValueFactory(new TreeItemPropertyValueFactory<>(propertyName)); - column.setCellFactory(col -> new CountTreeTableCell<>(intDiffStyler)); + column + .setCellFactory(col -> new NumberTreeTableCell<>(appCtx()::displayIntegral, intDiffStyler)); setColumnHeader(column, title, null); } @@ -441,7 +442,7 @@ protected void cfgTimeCol(TreeTableColumn column, String property ProfileContext profileContext, String title) { column.setCellValueFactory(new TreeItemPropertyValueFactory<>(propertyName)); - column.setCellFactory(col -> new TimeTreeTableCell<>(null)); + column.setCellFactory(col -> new NumberTreeTableCell<>(appCtx()::displayTime, null)); setColumnHeader(column, title, profileContext); } @@ -458,7 +459,8 @@ protected void cfgTimeDiffCol(TreeTableColumn column, String prop String title) { column.setCellValueFactory(new TreeItemPropertyValueFactory<>(propertyName)); - column.setCellFactory(col -> new TimeTreeTableCell<>(longDiffStyler)); + column.setCellFactory( + col -> new NumberTreeTableCell<>(appCtx()::displayTime, longDiffStyler)); setColumnHeader(column, title, null); } diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/FlatDiffViewController.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/FlatDiffViewController.java index eb75b16f1..27f7d5193 100644 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/FlatDiffViewController.java +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/FlatDiffViewController.java @@ -177,7 +177,7 @@ protected void initializeHandlers() appCtx(), exportButton.getScene().getWindow(), "flat_diff_profile.csv", - out -> writeFlatProfileDiffCsv(out, diff.getData(), ReportUtil.Mode.CSV) + out -> writeFlatProfileDiffCsv(appCtx(), out, diff.getData(), ReportUtil.Mode.CSV) )); } diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/FlatViewController.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/FlatViewController.java index 40df75d1e..11448d5c2 100644 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/FlatViewController.java +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/FlatViewController.java @@ -154,7 +154,7 @@ protected void initializeHandlers() appCtx(), exportButton.getScene().getWindow(), "flat_profile.csv", - out -> writeFlatProfileCsv(out, flatProfile, ReportUtil.Mode.CSV) + out -> writeFlatProfileCsv(appCtx(), out, flatProfile, ReportUtil.Mode.CSV) )); } @@ -187,7 +187,7 @@ protected void initializeTable() method.setCellFactory(col -> new MethodNameTableCell()); selfTimeGraphical.setCellValueFactory(new PropertyValueFactory<>("selfCntPct")); - selfTimeGraphical.setCellFactory(col -> new GraphicalShareTableCell<>()); + selfTimeGraphical.setCellFactory(col -> new GraphicalShareTableCell<>(appCtx())); cfgPctCol(selfCntPct, "selfCntPct", prfCtx(), COLUMN_SELF_CNT_PCT); cfgPctCol(totalCntPct, "totalCntPct", prfCtx(), COLUMN_TOTAL_CNT_PCT); diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/ProfileRootController.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/ProfileRootController.java index 7648ca0b6..e13b735b4 100644 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/ProfileRootController.java +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/ProfileRootController.java @@ -53,6 +53,7 @@ import java.util.List; import java.util.Map; +import com.insightfullogic.honest_profiler.core.aggregation.AggregationProfile; import com.insightfullogic.honest_profiler.ports.javafx.ViewType; import com.insightfullogic.honest_profiler.ports.javafx.model.ApplicationContext; import com.insightfullogic.honest_profiler.ports.javafx.model.ProfileContext; @@ -177,20 +178,16 @@ public void setProfileContext(ProfileContext prCtx) flameController.setProfileContext(prCtx); flameController.bind(prCtx.flameGraphProperty(), FLAME_EXTRACTOR); - // Bind the profile sample count display - prCtx.profileProperty().addListener( - (property, oldValue, newValue) -> profileSampleCount.setText( - newValue == null ? null : getText( - CONTENT_LABEL_PROFILESAMPLECOUNT, - newValue.getGlobalData().getTotalCnt()))); + // Bind the profile sample count display so it changes when new Profiles come in + prCtx.profileProperty() + .addListener((property, oldValue, newValue) -> updateSampleCount(newValue)); - if (prCtx.getProfile() != null) - { - profileSampleCount.setText( - getText( - CONTENT_LABEL_PROFILESAMPLECOUNT, - prCtx.getProfile().getGlobalData().getTotalCnt())); - } + // Bind the profile sample count display so it changes when the display preferences change + appCtx().getConfiguration() + .addListener((property, oldValue, newValue) -> updateSampleCount(prCtx)); + + // Display the initial sample count + updateSampleCount(prCtx); // Configure the View choice viewChoice.setConverter(getStringConverterForType(ViewType.class)); @@ -202,6 +199,37 @@ public void setProfileContext(ProfileContext prCtx) freezeButton.setDisable(prCtx.getMode() != LIVE); } + // Sample Count Display + + /** + * Update the sample count based on the {@link ProfileContext}. + * + * @param profileContext the {@link ProfileContext} + */ + private void updateSampleCount(ProfileContext profileContext) + { + updateSampleCount(profileContext == null ? null : profileContext.getProfile()); + } + + /** + * Update the sample count for the {@link AggregationProfile}. + * + * @param profile the {@link AggregationProfile} + */ + private void updateSampleCount(AggregationProfile profile) + { + + if (profile == null) + { + profileSampleCount.setText(null); + return; + } + + profileSampleCount.setText(getText( + CONTENT_LABEL_PROFILESAMPLECOUNT, + appCtx().displayIntegral(profile.getGlobalData().getTotalCnt()))); + } + // View Methods /** diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/RootController.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/RootController.java index e15a2c3a7..654a6b324 100644 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/RootController.java +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/RootController.java @@ -1,6 +1,7 @@ package com.insightfullogic.honest_profiler.ports.javafx.controller; import static com.insightfullogic.honest_profiler.ports.javafx.model.ProfileContext.ProfileMode.LOG; +import static com.insightfullogic.honest_profiler.ports.javafx.model.configuration.Configuration.DEFAULT_CONFIGURATION; import static com.insightfullogic.honest_profiler.ports.javafx.util.DialogUtil.selectLogFile; import static com.insightfullogic.honest_profiler.ports.javafx.util.DialogUtil.showErrorDialog; import static com.insightfullogic.honest_profiler.ports.javafx.util.DialogUtil.showExceptionDialog; @@ -35,6 +36,7 @@ import com.insightfullogic.honest_profiler.core.MachineListener; import com.insightfullogic.honest_profiler.core.sources.VirtualMachine; import com.insightfullogic.honest_profiler.ports.javafx.UserInterfaceConfigurationException; +import com.insightfullogic.honest_profiler.ports.javafx.controller.configuration.ConfigurationDialogController; import com.insightfullogic.honest_profiler.ports.javafx.model.ApplicationContext; import com.insightfullogic.honest_profiler.ports.javafx.model.ProfileContext; import com.insightfullogic.honest_profiler.ports.javafx.model.task.InitializeProfileTask; @@ -67,6 +69,8 @@ public class RootController extends AbstractController implements MachineListene @FXML private MenuItem openLiveItem; @FXML + private MenuItem preferencesItem; + @FXML private MenuItem quitItem; @FXML private Menu monitorMenu; @@ -74,6 +78,8 @@ public class RootController extends AbstractController implements MachineListene private TabPane profileTabs; @FXML private Label info; + @FXML + private ConfigurationDialogController configurationController; private LocalMachineSource machineSource; @@ -93,6 +99,9 @@ public void initialize() // Monitor running VMs on the local machine. machineSource = new LocalMachineSource(getLogger(getClass()), this); machineSource.start(); + + appCtx().setConfiguration(DEFAULT_CONFIGURATION); + configurationController.readConfiguration(DEFAULT_CONFIGURATION); } // MachineListener Implementation @@ -366,6 +375,8 @@ protected void initializeHandlers() { openLogItem.setOnAction(event -> doWithFile(file -> createNewProfile(file, false))); openLiveItem.setOnAction(event -> doWithFile(file -> createNewProfile(file, true))); + preferencesItem.setOnAction( + event -> appCtx().setConfiguration(configurationController.showAndWait().get())); quitItem.setOnAction(event -> exit()); } } diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/TreeViewController.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/TreeViewController.java index ea79a9400..f0326cf88 100644 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/TreeViewController.java +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/TreeViewController.java @@ -185,7 +185,7 @@ protected void initializeTable() methodColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("key")); percentColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("totalCntPct")); - percentColumn.setCellFactory(param -> new GraphicalShareTreeTableCell()); + percentColumn.setCellFactory(param -> new GraphicalShareTreeTableCell(appCtx())); cfgPctCol(totalCntPct, "totalCntPct", prfCtx(), COLUMN_TOTAL_CNT_PCT); cfgPctCol(selfCntPct, "selfCntPct", prfCtx(), COLUMN_SELF_CNT_PCT); diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/configuration/ConfigurationDialogController.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/configuration/ConfigurationDialogController.java new file mode 100644 index 000000000..e3689b16b --- /dev/null +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/configuration/ConfigurationDialogController.java @@ -0,0 +1,68 @@ +package com.insightfullogic.honest_profiler.ports.javafx.controller.configuration; + +import com.insightfullogic.honest_profiler.ports.javafx.controller.dialog.AbstractDialogController; +import com.insightfullogic.honest_profiler.ports.javafx.model.configuration.Configuration; + +import javafx.fxml.FXML; +import javafx.scene.control.ButtonType; +import javafx.scene.control.Dialog; +import javafx.util.Callback; + +public class ConfigurationDialogController extends AbstractDialogController +{ + // Instance Properties + @FXML + private Dialog dialog; + + @FXML + private FormattingConfigurationController displayFormattingController; + @FXML + private FormattingConfigurationController exportFormattingController; + + // FXML Implementation + + @Override + @FXML + public void initialize() + { + super.initialize(dialog); + } + + // Configuration Management Methods + + public void readConfiguration(Configuration configuration) + { + displayFormattingController + .readConfiguration(configuration.getDisplayFormattingConfiguration()); + exportFormattingController + .readConfiguration(configuration.getExportFormattingConfiguration()); + } + + // AbstractDialogController Implementation + + @Override + public Callback createResultHandler() + { + return button -> new Configuration( + displayFormattingController.getConfiguration(), + exportFormattingController.getConfiguration()); + } + + @Override + public void reset() + { + // NOOP + } + + @Override + protected void initializeInfoText() + { + // NOOP + } + + @Override + protected void initializeHandlers() + { + // NOOP + } +} diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/configuration/FormattingConfigurationController.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/configuration/FormattingConfigurationController.java new file mode 100644 index 000000000..9f693c9b3 --- /dev/null +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/configuration/FormattingConfigurationController.java @@ -0,0 +1,207 @@ +package com.insightfullogic.honest_profiler.ports.javafx.controller.configuration; + +import static com.insightfullogic.honest_profiler.ports.javafx.model.configuration.FormattingConfiguration.ALLOWED_UNITS; +import static java.time.temporal.ChronoUnit.NANOS; +import static java.util.Arrays.asList; +import static javafx.beans.binding.Bindings.and; +import static javafx.beans.binding.Bindings.or; + +import java.time.temporal.ChronoUnit; +import java.util.List; + +import com.insightfullogic.honest_profiler.ports.javafx.controller.AbstractController; +import com.insightfullogic.honest_profiler.ports.javafx.model.configuration.FormattingConfiguration; + +import javafx.fxml.FXML; +import javafx.scene.control.CheckBox; +import javafx.scene.control.ChoiceBox; +import javafx.scene.control.RadioButton; +import javafx.scene.control.ToggleGroup; + +public class FormattingConfigurationController extends AbstractController +{ + // Class Properties + + private static final List ALLOWED_SEPARATORS = asList( + new Character(','), + new Character('.')); + + // Instance Properties + + @FXML + private CheckBox showThousandsSeparator; + @FXML + private ChoiceBox thousandsSeparator; + @FXML + private ChoiceBox fractionDigitsNumber; + @FXML + private ChoiceBox decimalSeparator; + @FXML + private RadioButton showAsPercentage; + @FXML + private ToggleGroup percentDisplay; + @FXML + private CheckBox showPercentSign; + @FXML + private CheckBox spaceBeforePercentSign; + @FXML + private RadioButton showAsFraction; + @FXML + private ChoiceBox timeUnit; + @FXML + private ChoiceBox fractionDigitsTime; + @FXML + private CheckBox showTimeUnits; + + @Override + @FXML + public void initialize() + { + super.initialize(); + + populateChoices(); + } + + // Instance Accessors + + /** + * Returns a {@link FormattingConfiguration} reflecting the currently selected settings. + * + * @return a {@link FormattingConfiguration} reflecting the currently selected settings + */ + public FormattingConfiguration getConfiguration() + { + return new FormattingConfiguration( + showThousandsSeparator.isSelected(), + thousandsSeparator.getValue(), + decimalSeparator.getValue(), + fractionDigitsNumber.getValue(), + fractionDigitsTime.getValue(), + showAsPercentage.isSelected(), + showPercentSign.isSelected(), + spaceBeforePercentSign.isSelected(), + timeUnit.getValue(), + showTimeUnits.isSelected()); + } + + // AbstractController Implementation + + @Override + protected void initializeInfoText() + { + // TODO Auto-generated method stub + } + + @Override + protected void initializeHandlers() + { + // Thousands separator is irrelevant if no thousands separator is shown. + thousandsSeparator.disableProperty().bind(showThousandsSeparator.selectedProperty().not()); + + // Decimal separator is irrelevant if no fractional numbers are shown. + decimalSeparator.disableProperty().bind( + and( + fractionDigitsNumber.getSelectionModel().selectedItemProperty().isEqualTo(0), + fractionDigitsTime.getSelectionModel().selectedItemProperty().isEqualTo(0))); + + // If a new thousands separator is selected, switch the decimal separator. The logic here exploits the fact that + // in the current code, only 2 separators are selectable, and that they are added to the choice boxen in the + // same order. + thousandsSeparator.getSelectionModel().selectedIndexProperty().addListener( + (property, oldValue, newValue) -> decimalSeparator.getSelectionModel() + .select(1 - newValue.intValue())); + + // If a new decimal separator is selected, switch the thousands separator. The logic here exploits the fact that + // in the current code, only 2 separators are selectable, and that they are added to the choice boxen in the + // same order. + decimalSeparator.getSelectionModel().selectedIndexProperty().addListener( + (property, oldValue, newValue) -> thousandsSeparator.getSelectionModel() + .select(1 - newValue.intValue())); + + // Show percent sign is irrelevant if percentages are displayed as fractions. + showPercentSign.disableProperty().bind(showAsPercentage.selectedProperty().not()); + + // Space before percent sign is irrelevant if no percent sign is displayed in the first place. + spaceBeforePercentSign.disableProperty().bind(or( + showAsPercentage.selectedProperty().not(), + showPercentSign.selectedProperty().not())); + + // Fraction digits for time periods are irrelevant when showing nanoseconds, since this is the smallest time + // unit recorded. + fractionDigitsTime.disableProperty() + .bind(timeUnit.getSelectionModel().selectedItemProperty().isEqualTo(NANOS)); + } + + // Configuration Management Methods + + /** + * Updates the selections in the UI based on the provided {@link FormattingConfiguration}. + * + * @param configuration the {@link FormattingConfiguration} whose settings should be reflected in the UI + */ + public void readConfiguration(FormattingConfiguration configuration) + { + showThousandsSeparator.setSelected(configuration.isShowThousandsSeparator()); + thousandsSeparator.getSelectionModel().select(configuration.getThousandsSeparator()); + decimalSeparator.getSelectionModel().select(configuration.getDecimalSeparator()); + fractionDigitsNumber.getSelectionModel().select(configuration.getFractionDigitsNumber()); + fractionDigitsTime.getSelectionModel().select(configuration.getFractionDigitsTime()); + showAsPercentage.setSelected(configuration.isShowAsPercentage()); + showPercentSign.setSelected(configuration.isShowPercentSign()); + spaceBeforePercentSign.setSelected(configuration.isSpaceBeforePercentSign()); + timeUnit.getSelectionModel().select(configuration.getTimeUnit()); + showTimeUnits.setSelected(configuration.isShowTimeUnits()); + } + + // Population Methods + + /** + * Populate all {@link ChoiceBox}es. + */ + private void populateChoices() + { + populateSeparator(thousandsSeparator); + populateSeparator(decimalSeparator); + pupulateTimeUnit(); + populateFractionDigits(fractionDigitsNumber); + populateFractionDigits(fractionDigitsTime); + } + + /** + * Populates a {@link ChoiceBox} with the allowed separator characters. + * + * @param choiceBox the {@link ChoiceBox} to be populated + */ + private void populateSeparator(ChoiceBox choiceBox) + { + for (Character separator : ALLOWED_SEPARATORS) + { + choiceBox.getItems().add(separator); + } + } + + /** + * Populates the Time Unit {@link ChoiceBox} with the allowed {@link ChronoUnit}s. + */ + private void pupulateTimeUnit() + { + for (ChronoUnit unit : ALLOWED_UNITS) + { + timeUnit.getItems().add(unit); + } + } + + /** + * Populates a {@link ChoiceBox} with the allowed numbers of fraction digits. + * + * @param choiceBox the {@link ChoiceBox} to be populated + */ + private void populateFractionDigits(ChoiceBox choiceBox) + { + // Why 0-6 ? 1 ms == 1.000 µs == 1.000.000 ns. + for (int i = 0; i < 7; i++) + { + choiceBox.getItems().add(i); + } + } +} diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/model/ApplicationContext.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/model/ApplicationContext.java index 416a83959..4cace5ce7 100644 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/model/ApplicationContext.java +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/model/ApplicationContext.java @@ -1,5 +1,6 @@ package com.insightfullogic.honest_profiler.ports.javafx.model; +import static com.insightfullogic.honest_profiler.ports.javafx.util.ConversionUtil.convert; import static com.insightfullogic.honest_profiler.ports.javafx.util.ResourceUtil.format; import static com.insightfullogic.honest_profiler.ports.javafx.util.ResourceUtil.getDefaultBundle; import static com.insightfullogic.honest_profiler.ports.javafx.util.ResourceUtil.getDefaultLocale; @@ -7,16 +8,22 @@ import static java.util.stream.Collectors.toList; import java.io.File; +import java.text.DecimalFormat; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; import java.util.concurrent.ExecutorService; +import java.util.function.Function; import com.insightfullogic.honest_profiler.ports.javafx.controller.RootController; +import com.insightfullogic.honest_profiler.ports.javafx.model.configuration.Configuration; +import com.insightfullogic.honest_profiler.ports.javafx.model.configuration.FormattingConfiguration; +import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; +import javafx.beans.value.ObservableObjectValue; import javafx.beans.value.ObservableStringValue; import javafx.concurrent.Task; import javafx.scene.control.Tab; @@ -46,6 +53,23 @@ public final class ApplicationContext // - Task Execution private ExecutorService executorService = newCachedThreadPool(); + // - Application Configuration + private SimpleObjectProperty configuration; + + // - Display Formatting + private DecimalFormat integerDisplayFormat; + private DecimalFormat numberDisplayFormat; + private DecimalFormat percentDisplayFormat; + private DecimalFormat timeDisplayFormat; + private Function timeDisplayConverter; + + // - Export Formatting + private DecimalFormat integerExportFormat; + private DecimalFormat numberExportFormat; + private DecimalFormat percentExportFormat; + private DecimalFormat timeExportFormat; + private Function timeExportConverter; + // Instance Constructors /** @@ -62,6 +86,7 @@ public ApplicationContext(RootController rootController) this.rootController = rootController; nameToContextMap = new HashMap(); pathToContextMap = new HashMap(); + configuration = new SimpleObjectProperty<>(); } // Instance Accessors @@ -79,6 +104,67 @@ public Integer getContextIdByPath(File file) return ctx == null ? null : ctx.getId(); } + /** + * Returns the {@link ProfileContext} with the specified name. + *

+ * + * @param name the name of the {@link ProfileContext} + * @return the corresponding {@link ProfileContext} + */ + public ProfileContext getProfileContext(String name) + { + return nameToContextMap.get(name); + } + + /** + * Registers a {@link ProfileContext} with this ApplicationContext, making it available as shared state. + *

+ * + * @param context the {@link ProfileContext} to be registered + */ + public void registerProfileContext(ProfileContext context) + { + nameToContextMap.put(context.getName(), context); + pathToContextMap.put(context.getFile().getAbsolutePath(), context); + } + + /** + * Returns a list of the names of all known {@link ProfileContext}s. + *

+ * + * @return a list of the names of all known {@link ProfileContext}s + */ + public List getOpenProfileNames() + { + return nameToContextMap.keySet().stream().sorted().collect(toList()); + } + + public ObservableObjectValue getConfiguration() + { + return configuration; + } + + public void setConfiguration(Configuration configuration) + { + FormattingConfiguration displayFmtCfg = configuration.getDisplayFormattingConfiguration(); + this.integerDisplayFormat = displayFmtCfg.getIntegerFormatter(); + this.numberDisplayFormat = displayFmtCfg.getNumberFormatter(); + this.percentDisplayFormat = displayFmtCfg.getPercentFormatter(); + this.timeDisplayFormat = displayFmtCfg.getTimeFormatter(); + this.timeDisplayConverter = nanos -> convert(displayFmtCfg.getTimeUnit(), nanos); + + FormattingConfiguration exportFmtCfg = configuration.getExportFormattingConfiguration(); + this.integerExportFormat = exportFmtCfg.getIntegerFormatter(); + this.numberExportFormat = exportFmtCfg.getNumberFormatter(); + this.percentExportFormat = exportFmtCfg.getPercentFormatter(); + this.timeExportFormat = exportFmtCfg.getTimeFormatter(); + this.timeExportConverter = nanos -> convert(exportFmtCfg.getTimeUnit(), nanos); + + this.configuration.set(configuration); + } + + // I18N-related Methods + /** * Returns the internationalized String stored in the application {@link ResourceBundle} for the specified key based * on the current {@link Locale}. @@ -107,6 +193,8 @@ public String textFor(String key, Object... args) return format(currentLocale, currentBundle, key, args); } + // InfoBar Methods + /** * Set the text in the InfoBar as per {@link #textFor(String)}. *

@@ -130,6 +218,16 @@ public void setInfoFromBundle(String key, Object... args) info.set(textFor(key, args)); } + /** + * Sets the text in the InfoBar. + * + * @param text the raw text + */ + public void setRawInfo(String text) + { + info.set(text); + } + /** * Clears the text in the InfoBar. */ @@ -149,41 +247,103 @@ public ObservableStringValue getInfo() return info; } + // Formatting-related Methods + /** - * Returns the {@link ProfileContext} with the specified name. - *

- * - * @param name the name of the {@link ProfileContext} - * @return the corresponding {@link ProfileContext} + * Return a {@link String} representing the number, formatted using the configured settings, discarding any fraction + * digits. + * + * @param number the {@link Number} to be displayed + * @return a {@link String} representing the number, formatted using the configured settings */ - public ProfileContext getProfileContext(String name) + public String displayIntegral(Number number) { - return nameToContextMap.get(name); + return integerDisplayFormat.format(number.longValue()); } /** - * Registers a {@link ProfileContext} with this ApplicationContext, making it available as shared state. - *

- * - * @param context the {@link ProfileContext} to be registered + * Return a {@link String} representing the number, formatted using the configured settings. + * + * @param number the {@link Number} to be displayed + * @return a {@link String} representing the number, formatted using the configured settings */ - public void registerProfileContext(ProfileContext context) + public String displayNumber(Number number) { - nameToContextMap.put(context.getName(), context); - pathToContextMap.put(context.getFile().getAbsolutePath(), context); + return numberDisplayFormat.format(number); } /** - * Returns a list of the names of all known {@link ProfileContext}s. - *

- * - * @return a list of the names of all known {@link ProfileContext}s + * Return a {@link String} representing the percentage, formatted using the configured settings. + * + * @param number the percentage to be displayed + * @return a {@link String} representing the percentage, formatted using the configured settings */ - public List getOpenProfileNames() + public String displayPercent(Number number) { - return nameToContextMap.keySet().stream().sorted().collect(toList()); + return percentDisplayFormat.format(number); } + /** + * Return a {@link String} representing the amount of time, formatted and converted using the configured settings. + * + * @param nanos the amount of time, in nanoseconds, to be displayed + * @return a {@link String} representing the amount of time, formatted using the configured settings + */ + public String displayTime(Number nanos) + { + return timeDisplayFormat.format(timeDisplayConverter.apply(nanos.longValue())); + } + + /** + * Return a {@link String} representing the number, formatted using the configured settings, discarding any fraction + * digits, for exporting to a file. + * + * @param number the {@link Number} to be exported + * @return a {@link String} representing the number, formatted using the configured settings + */ + public String exportIntegral(Number number) + { + return integerExportFormat.format(number.longValue()); + } + + /** + * Return a {@link String} representing the number, formatted using the configured settings, for exporting to a + * file. + * + * @param number the {@link Number} to be exported + * @return a {@link String} representing the number, formatted using the configured settings + */ + public String exportNumber(Number number) + { + return numberExportFormat.format(number); + } + + /** + * Return a {@link String} representing the percentage, formatted using the configured settings, for exporting to a + * file. + * + * @param number the percentage to be exported + * @return a {@link String} representing the percentage, formatted using the configured settings + */ + public String exportPercent(Number number) + { + return percentExportFormat.format(number); + } + + /** + * Return a {@link String} representing the amount of time, formatted and converted using the configured settings, + * for exporting to a file. + * + * @param nanos the amount of time, in nanoseconds, to be exported + * @return a {@link String} representing the amount of time, formatted using the configured settings + */ + public String exportTime(Number nanos) + { + return timeExportFormat.format(timeExportConverter.apply(nanos.longValue())); + } + + // Task-related Methods + /** * Executes a task on a background worker thread. *

@@ -204,6 +364,8 @@ public void stop() this.executorService.shutdown(); } + // View Creation Methods + /** * Create a {@link Tab} containing the Diff Views for the specified profiles. *

diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/model/configuration/Configuration.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/model/configuration/Configuration.java new file mode 100644 index 000000000..6d89933fc --- /dev/null +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/model/configuration/Configuration.java @@ -0,0 +1,41 @@ +package com.insightfullogic.honest_profiler.ports.javafx.model.configuration; + +import static com.insightfullogic.honest_profiler.ports.javafx.model.configuration.FormattingConfiguration.DEFAULT_DISPLAY_CONFIGURATION; +import static com.insightfullogic.honest_profiler.ports.javafx.model.configuration.FormattingConfiguration.DEFAULT_EXPORT_CONFIGURATION; + +public class Configuration +{ + // Class Properties + + public static final Configuration DEFAULT_CONFIGURATION = new Configuration( + DEFAULT_DISPLAY_CONFIGURATION, + DEFAULT_EXPORT_CONFIGURATION); + + // Instance Properties + + private FormattingConfiguration displayFormattingConfiguration; + private FormattingConfiguration exportFormattingConfiguration; + + // Instance Constructors + + public Configuration(FormattingConfiguration displayFormattingConfiguration, + FormattingConfiguration exportFormattingConfiguration) + { + super(); + + this.displayFormattingConfiguration = displayFormattingConfiguration; + this.exportFormattingConfiguration = exportFormattingConfiguration; + } + + // Instance Accessors + + public FormattingConfiguration getDisplayFormattingConfiguration() + { + return displayFormattingConfiguration; + } + + public FormattingConfiguration getExportFormattingConfiguration() + { + return exportFormattingConfiguration; + } +} diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/model/configuration/FormattingConfiguration.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/model/configuration/FormattingConfiguration.java new file mode 100644 index 000000000..22fb221fa --- /dev/null +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/model/configuration/FormattingConfiguration.java @@ -0,0 +1,345 @@ +package com.insightfullogic.honest_profiler.ports.javafx.model.configuration; + +import static java.time.temporal.ChronoUnit.MICROS; +import static java.time.temporal.ChronoUnit.MILLIS; +import static java.time.temporal.ChronoUnit.NANOS; +import static java.time.temporal.ChronoUnit.SECONDS; +import static java.util.Arrays.asList; +import static java.util.Locale.getDefault; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public class FormattingConfiguration +{ + // Class Properties + + /** + * Only for use when creating the default configuration, to set the default separators to those of the default + * {@link Locale}, to avoid annoying people in other locations than myself. + */ + private static DecimalFormatSymbols DEFAULT_SYMBOLS = new DecimalFormatSymbols(getDefault()); + + public static final List ALLOWED_UNITS = asList(NANOS, MICROS, MILLIS, SECONDS); + + public static final FormattingConfiguration DEFAULT_DISPLAY_CONFIGURATION = new FormattingConfiguration( + true, + DEFAULT_SYMBOLS.getGroupingSeparator(), + DEFAULT_SYMBOLS.getDecimalSeparator(), + 2, + 0, + true, + true, + true, + MILLIS, + false); + + public static final FormattingConfiguration DEFAULT_EXPORT_CONFIGURATION = new FormattingConfiguration( + false, + DEFAULT_SYMBOLS.getGroupingSeparator(), + DEFAULT_SYMBOLS.getDecimalSeparator(), + 2, + 0, + false, + false, + true, + MILLIS, + false); + + private static final Map UNIT_NAMES = new HashMap<>(); + + // Class Constructors + + static + { + UNIT_NAMES.put(NANOS, "ns"); + UNIT_NAMES.put(MICROS, "µs"); + UNIT_NAMES.put(MILLIS, "ms"); + UNIT_NAMES.put(SECONDS, "s"); + } + + // Instance Properties + + private boolean showThousandsSeparator; + private Character thousandsSeparator; + + private Character decimalSeparator; + private int fractionDigitsNumber; + private int fractionDigitsTime; + + private boolean showAsPercentage; + private boolean showPercentSign; + private boolean spaceBeforePercentSign; + + private ChronoUnit timeUnit; + private boolean showTimeUnits; + + // Instance Constructors + + /** + * Constructor specifying all settings. + * + * @param showThousandsSeparator + * @param thousandsSeparator + * @param decimalSeparator + * @param fractionDigitsNumber + * @param fractionDigitsTime + * @param showAsPercentage + * @param showPercentSign + * @param spaceBeforePercentSign + * @param timeUnit + * @param showTimeUnits + */ + public FormattingConfiguration(boolean showThousandsSeparator, + Character thousandsSeparator, + Character decimalSeparator, + int fractionDigitsNumber, + int fractionDigitsTime, + boolean showAsPercentage, + boolean showPercentSign, + boolean spaceBeforePercentSign, + ChronoUnit timeUnit, + boolean showTimeUnits) + { + super(); + + this.showThousandsSeparator = showThousandsSeparator; + this.thousandsSeparator = thousandsSeparator; + this.decimalSeparator = decimalSeparator; + this.fractionDigitsNumber = fractionDigitsNumber; + this.fractionDigitsTime = fractionDigitsTime; + this.showAsPercentage = showAsPercentage; + this.showPercentSign = showPercentSign; + this.spaceBeforePercentSign = spaceBeforePercentSign; + this.timeUnit = timeUnit; + this.showTimeUnits = showTimeUnits; + } + + // Instance Accessors + + /** + * Returns a boolean indicating whether the thousands separator should be displayed. + * + * @return a boolean indicating whether the thousands separator should be displayed + */ + public boolean isShowThousandsSeparator() + { + return showThousandsSeparator; + } + + /** + * Returns the character used as thousands separator. + * + * @return the character used as thousands separator + */ + public Character getThousandsSeparator() + { + return thousandsSeparator; + } + + /** + * Returns the character used as decimal separator. + * + * @return the character used as decimal separator + */ + public Character getDecimalSeparator() + { + return decimalSeparator; + } + + /** + * Returns the number of digits to be displayed after the decimal separator when displaying numbers other than + * integers or time periods. + * + * @return the number of digits to be displayed after the decimal separator + */ + public int getFractionDigitsNumber() + { + return fractionDigitsNumber; + } + + /** + * Returns the number of digits to be displayed after the decimal separator when displaying time periods. + * + * @return the number of digits to be displayed after the decimal separator + */ + public int getFractionDigitsTime() + { + return fractionDigitsTime; + } + + /** + * Returns a boolean indicating whether a percentage should be shown as a percent (100-based) or a fraction (where 1 + * == 100 %). + * + * @return a boolean indicating whether a percentage should be shown as a percent + */ + public boolean isShowAsPercentage() + { + return showAsPercentage; + } + + /** + * Returns a boolean indicating whether the percent sign should be shown in percentages. This setting only is + * applicable if {@link #isShowAsPercentage()} returns true. + * + * @return a boolean indicating whether the percent sign should be shown in percentages + */ + public boolean isShowPercentSign() + { + return showPercentSign; + } + + /** + * Returns a boolean indicating whether the percentage and percent sign should be separated by a space. This setting + * only is applicable if {@link #isShowAsPercentage()} and {@link #isShowPercentSign()} both return true. + * + * @return a boolean indicating whether the percentage and percent sign should be separated by a space + */ + public boolean isSpaceBeforePercentSign() + { + return spaceBeforePercentSign; + } + + /** + * Returns a {@link ChronoUnit} indicating the time units to be used when displaying time periods. + * + * @return a {@link ChronoUnit} indicating the time units to be used when displaying time periods + */ + public ChronoUnit getTimeUnit() + { + return timeUnit; + } + + /** + * Returns a boolean indicating whether the time units should be shown when displaying time periods. + * + * @return a boolean indicating whether the time units should be shown when displaying time periods + */ + public boolean isShowTimeUnits() + { + return showTimeUnits; + } + + // Formatter Generation Methods + + /** + * Returns a {@link DecimalFormat} for formatting integer numbers, based on the configured settings. + * + * @return a {@link DecimalFormat} for formatting integer numbers + */ + public DecimalFormat getIntegerFormatter() + { + return new DecimalFormat(integral(), symbols()); + } + + /** + * Returns a {@link DecimalFormat} for formatting {@link Number}s, based on the configured settings. + * + * @return a {@link DecimalFormat} for formatting {@link Number}s + */ + public DecimalFormat getNumberFormatter() + { + return new DecimalFormat(integral() + fractional(fractionDigitsNumber), symbols()); + } + + /** + * Returns a {@link DecimalFormat} for formatting percentages, based on the configured settings. + * + * @return a {@link DecimalFormat} for formatting percentages + */ + public DecimalFormat getPercentFormatter() + { + return new DecimalFormat( + integral() + fractional(fractionDigitsNumber) + percent(), + symbols()); + } + + /** + * Returns a {@link DecimalFormat} for formatting time periods, based on the configured settings. + * + * @return a {@link DecimalFormat} for formatting time periods + */ + public DecimalFormat getTimeFormatter() + { + return new DecimalFormat(integral() + fractional(fractionDigitsTime) + time(), symbols()); + } + + /** + * Returns the {@link DecimalFormatSymbols} for {@link DecimalFormat} creation, based on the configured settings. + * + * @return the {@link DecimalFormatSymbols} for {@link DecimalFormat} creation + */ + private DecimalFormatSymbols symbols() + { + DecimalFormatSymbols result = new DecimalFormatSymbols(); + result.setDecimalSeparator(decimalSeparator); + result.setGroupingSeparator(thousandsSeparator); + return result; + } + + /** + * Returns the portion of the formatting pattern corresponding to the integer digits. + * + * @return the portion of the formatting pattern corresponding to the integerdigits + */ + private String integral() + { + return showThousandsSeparator ? "#,##0" : "0"; + } + + /** + * Returns the portion of the formatting pattern corresponding to the decimal separator and the fraction digits. + * + * @param fractionDigits the number of fraction digits to be displayed + * @return the portion of the formatting pattern corresponding to the decimal separator and the fraction digits + */ + private String fractional(int fractionDigits) + { + if (fractionDigits == 0) + { + return ""; + } + + StringBuilder result = new StringBuilder("."); + for (int i = 0; i < fractionDigits; i++) + { + result.append("0"); + } + return result.toString(); + } + + /** + * Returns the portion of the formatting pattern corresponding to the percent symbol. + * + * @return the portion of the formatting pattern corresponding to the percent symbol + */ + private String percent() + { + if (!showAsPercentage || !showPercentSign) + { + return ""; + } + return spaceBeforePercentSign ? " %" : "%"; + } + + /** + * Returns the portion of the formatting pattern corresponding to the time unit. + * + * @return the portion of the formatting pattern corresponding to the time unit + */ + private String time() + { + if (!showTimeUnits) + { + return ""; + } + + return " " + UNIT_NAMES.get(timeUnit); + } +} diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/util/ContextMenuUtil.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/util/ContextMenuUtil.java index 462a70d09..c258b306d 100644 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/util/ContextMenuUtil.java +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/util/ContextMenuUtil.java @@ -183,7 +183,7 @@ private static ContextMenu getContextMenu(ApplicationContext appCtx, TreeIte appCtx, menu.getScene().getWindow(), "stack_profile.txt", - out -> writeStack(out, (Node)treeItem.getValue()) + out -> writeStack(appCtx, out, (Node)treeItem.getValue()) )); } return menu; diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/util/ConversionUtil.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/util/ConversionUtil.java index 0054787d1..1a03ef557 100644 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/util/ConversionUtil.java +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/util/ConversionUtil.java @@ -1,7 +1,13 @@ package com.insightfullogic.honest_profiler.ports.javafx.util; +import static java.time.temporal.ChronoUnit.MICROS; +import static java.time.temporal.ChronoUnit.MILLIS; +import static java.time.temporal.ChronoUnit.NANOS; +import static java.time.temporal.ChronoUnit.SECONDS; import static java.util.EnumSet.allOf; +import java.time.temporal.ChronoUnit; + import javafx.util.StringConverter; /** @@ -11,6 +17,9 @@ public final class ConversionUtil { // Class Properties + private static final ChronoUnit[] UNITS = + { NANOS, MICROS, MILLIS, SECONDS }; + private static final long NS_TO_MS = 1000 * 1000; // Class Methods @@ -27,6 +36,43 @@ public static final long toMillis(long nanos) return nanos / NS_TO_MS; } + /** + * Convert the specified number of nanoseconds to the corresponding amount expressed in the specified + * {@link ChronoUnit}. + *

+ * + * @param unit the {@link ChronoUnit} to which the number of nanoseconds will be converted + * @param nanos the number of nanoseconds + * @return the duration value expressed in the specified {@link ChronoUnit} + */ + public static final double convert(ChronoUnit unit, long nanos) + { + double result = nanos; + for (int i = 0; i < UNITS.length; i++) + { + if (UNITS[i] == unit) + { + return result; + } + result /= 1000; + } + + return result; + } + + /** + * Convert the specified number of nanoseconds to the corresponding amount expressed in the specified + * {@link ChronoUnit}, ignoring the fractional part of the result. + *

+ * @param unit the {@link ChronoUnit} to which the number of nanoseconds will be converted + * @param nanos the number of nanoseconds + * @return the duration value expressed in the specified {@link ChronoUnit} + */ + public static final long to(long nanos) + { + return nanos / NS_TO_MS; + } + /** * Generates a {@link StringConverter} for an {@link Enum}, converting between the String representation obtained by * calling {@link Enum#toString()} and the {@link Enum} value. diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/util/RenderUtil.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/util/RenderUtil.java new file mode 100644 index 000000000..ada7b3f3a --- /dev/null +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/util/RenderUtil.java @@ -0,0 +1,40 @@ +package com.insightfullogic.honest_profiler.ports.javafx.util; + +import static java.text.MessageFormat.format; + +import com.insightfullogic.honest_profiler.core.collector.Frame; +import com.insightfullogic.honest_profiler.core.parser.Method; + +public final class RenderUtil +{ + public static String renderPercentage(double percentage) + { + return format("{0,number,0.00 %}", percentage); + } + + public static String renderMethod(Frame method) + { + if (method == null) + { + return "unknown"; + } + + return method.getClassName() + "." + method.getMethodName(); + } + + public static String renderShortMethod(Method method) + { + String className = method.getClassName(); + int index = className.lastIndexOf('.'); + String shortClassName = index == -1 ? className : className.substring(index + 1); + return shortClassName + "." + method.getMethodName(); + } + + /** + * Empty Constructor for utility class. + */ + private RenderUtil() + { + // Empty Constructor for utility class + } +} diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/util/report/ReportUtil.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/util/report/ReportUtil.java index 1620bfae9..03005074a 100644 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/util/report/ReportUtil.java +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/util/report/ReportUtil.java @@ -2,10 +2,8 @@ import static com.insightfullogic.honest_profiler.ports.javafx.util.report.Table.Alignment.LEFT; import static com.insightfullogic.honest_profiler.ports.javafx.util.report.Table.Alignment.RIGHT; -import static java.text.NumberFormat.getPercentInstance; import java.io.PrintWriter; -import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -16,6 +14,7 @@ import com.insightfullogic.honest_profiler.core.aggregation.result.straight.Flat; import com.insightfullogic.honest_profiler.core.aggregation.result.straight.Node; import com.insightfullogic.honest_profiler.core.profiles.ProfileNode; +import com.insightfullogic.honest_profiler.ports.javafx.model.ApplicationContext; /** * Utility class for creating various text reports (either ASCII-art based or CSV) based on profile data. @@ -73,19 +72,8 @@ private PrintWriter end(PrintWriter out) private static String DROP_LAST = new String(new char[] { BOX_UPANDRIGHT, BOX_HORIZONTAL }); - // - Formatter which renders percentages with 2 digits after the dot. - private static NumberFormat FMT_PERCENT; - // Class Constructors - // - Initialize the FMT_PERCENT NumberFormat - static - { - FMT_PERCENT = getPercentInstance(); - FMT_PERCENT.setMinimumFractionDigits(2); - FMT_PERCENT.setMaximumFractionDigits(2); - } - /** * Writes a stack (fragment) with the specified {@link Node} as root to the specified {@link PrintWriter}. Nicely * formatted at that, using droplines. @@ -96,14 +84,14 @@ private PrintWriter end(PrintWriter out) * @param out the {@link PrintWriter} to wite the stack to * @param node the root {@link Node} of the stack (fragment) */ - public static void writeStack(PrintWriter out, Node node) + public static void writeStack(ApplicationContext appCtx, PrintWriter out, Node node) { Table table = new Table(); - table.addColumn("Method", obj -> obj.toString(), LEFT); - table.addColumn("Self Time %", object -> FMT_PERCENT.format(object), RIGHT); - table.addColumn("Total Time %", object -> FMT_PERCENT.format(object), RIGHT); - table.addColumn("Self Sample #", object -> Integer.toString((Integer)object), RIGHT); - table.addColumn("Total Sample #", object -> Integer.toString((Integer)object), RIGHT); + table.addColumn("Method", object -> object.toString(), LEFT); + table.addColumn("Self Time %", object -> appCtx.exportPercent((Double)object), RIGHT); + table.addColumn("Total Time %", object -> appCtx.exportPercent((Double)object), RIGHT); + table.addColumn("Self Sample #", object -> appCtx.exportIntegral((Integer)object), RIGHT); + table.addColumn("Total Sample #", object -> appCtx.exportIntegral((Integer)object), RIGHT); buildStackTable(node, 0, table, new ArrayList<>()); table.print(out); @@ -158,7 +146,8 @@ private static void buildStackTable(Node node, int level, Table table, List entries, Mode mode) + public static void writeFlatProfileCsv(ApplicationContext appCtx, PrintWriter out, + List entries, Mode mode) { mode.start(out); out.print("Key"); @@ -180,14 +169,14 @@ public static void writeFlatProfileCsv(PrintWriter out, List entries, Mod out.print(entry.getKey()); mode.middle(out); - out.printf("%.4f", entry.getSelfCntPct()); + out.print(appCtx.exportPercent(entry.getSelfCntPct())); mode.middle(out); - out.printf("%.4f", entry.getTotalCntPct()); + out.print(appCtx.exportPercent(entry.getTotalCntPct())); mode.middle(out); - out.printf("%d", entry.getSelfCnt()); + out.print(appCtx.exportIntegral(entry.getSelfCnt())); mode.middle(out); - out.printf("%d", entry.getTotalCnt()); + out.print(appCtx.exportIntegral(entry.getTotalCnt())); mode.end(out); }); @@ -203,8 +192,8 @@ public static void writeFlatProfileCsv(PrintWriter out, List entries, Mod * @param entries the data to be written * @param mode the {@link Mode} for formatting the output */ - public static void writeFlatProfileDiffCsv(PrintWriter out, Collection entries, - Mode mode) + public static void writeFlatProfileDiffCsv(ApplicationContext appCtx, PrintWriter out, + Collection entries, Mode mode) { mode.start(out); out.print("Key"); @@ -245,32 +234,32 @@ public static void writeFlatProfileDiffCsv(PrintWriter out, Collection - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - *

- * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - *

- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT - * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - **/ -package com.insightfullogic.honest_profiler.ports.javafx.view.cell; - -import static javafx.geometry.Pos.CENTER_RIGHT; -import static javafx.scene.text.TextAlignment.RIGHT; - -import java.util.function.Function; - -import javafx.scene.control.TableCell; - -public class CountTableCell extends TableCell -{ - private Function styleFunction; - - public CountTableCell(Function styleFunction) - { - super(); - - setTextAlignment(RIGHT); - setAlignment(CENTER_RIGHT); - - this.styleFunction = styleFunction; - } - - @Override - protected void updateItem(Number item, boolean isEmpty) - { - if (isEmpty || item == null) - { - setText(null); - setStyle(null); - return; - } - - setText(item.toString()); - setStyle(styleFunction == null ? null : styleFunction.apply(item)); - } -} diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/CountTreeTableCell.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/CountTreeTableCell.java deleted file mode 100644 index 9a34a9838..000000000 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/CountTreeTableCell.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2014 Richard Warburton (richard.warburton@gmail.com) - *

- * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - *

- * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - *

- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT - * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - **/ -package com.insightfullogic.honest_profiler.ports.javafx.view.cell; - -import static javafx.geometry.Pos.CENTER_RIGHT; -import static javafx.scene.text.TextAlignment.RIGHT; - -import java.util.function.Function; - -import javafx.scene.control.TreeTableCell; - -public class CountTreeTableCell extends TreeTableCell -{ - private Function styleFunction; - - public CountTreeTableCell(Function styleFunction) - { - super(); - setTextAlignment(RIGHT); - setAlignment(CENTER_RIGHT); - - this.styleFunction = styleFunction; - } - - @Override - protected void updateItem(Number number, boolean isEmpty) - { - if (isEmpty || number == null) - { - setText(null); - setStyle(null); - return; - } - - setText(number.toString()); - setStyle(styleFunction == null ? null : styleFunction.apply(number)); - } -} diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/GraphicalShareTableCell.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/GraphicalShareTableCell.java index b63afe4ef..2da98b009 100644 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/GraphicalShareTableCell.java +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/GraphicalShareTableCell.java @@ -20,15 +20,37 @@ import static javafx.scene.paint.Color.DARKRED; +import com.insightfullogic.honest_profiler.ports.javafx.model.ApplicationContext; + import javafx.scene.control.TableCell; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; public class GraphicalShareTableCell extends TableCell { + // Class Properties + private static final double HEIGHT = 8; private static final Color COLOR = DARKRED; + // Instance Properties + + private ApplicationContext appCtx; + + // Instance Constructors + + /** + * Simple Constructor. + * + * @param appCtx the {@link ApplicationContext} for the application + */ + public GraphicalShareTableCell(ApplicationContext appCtx) + { + this.appCtx = appCtx; + } + + // TableCell Implementation + @Override protected void updateItem(Double share, boolean empty) { @@ -41,6 +63,7 @@ protected void updateItem(Double share, boolean empty) return; } + setText(appCtx.displayPercent(share)); setGraphic(new Rectangle(share * getWidth(), HEIGHT, COLOR)); } } diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/GraphicalShareTreeTableCell.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/GraphicalShareTreeTableCell.java index 6baa1f242..968ab740e 100644 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/GraphicalShareTreeTableCell.java +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/GraphicalShareTreeTableCell.java @@ -18,11 +18,11 @@ **/ package com.insightfullogic.honest_profiler.ports.javafx.view.cell; -import static com.insightfullogic.honest_profiler.ports.javafx.view.Rendering.renderPercentage; import static javafx.scene.paint.Color.RED; import static javafx.scene.paint.Color.WHEAT; import com.insightfullogic.honest_profiler.core.aggregation.result.straight.Node; +import com.insightfullogic.honest_profiler.ports.javafx.model.ApplicationContext; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; @@ -31,15 +31,32 @@ public class GraphicalShareTreeTableCell extends TreeTableCell { + // Class Properties + private static final int IMAGE_WIDTH = 50; private static final int IMAGE_HEIGHT = 15; private static final int TEXT_HORIZONTAL_INSET = 10; private static final int TEXT_VERTICAL_INSET = 12; + // Instance Properties + + private ApplicationContext appCtx; + + // Instance Constructors + /** - * Not threadsafe: must be run on JavaFx thread. + * Simple Constructor. + * + * @param appCtx the {@link ApplicationContext} for the application */ + public GraphicalShareTreeTableCell(ApplicationContext appCtx) + { + this.appCtx = appCtx; + } + + // TreeTableCell Implementation + @Override protected void updateItem(Number number, boolean empty) { @@ -64,7 +81,7 @@ protected void updateItem(Number number, boolean empty) Color color = share > 0.5 ? WHEAT : RED; context.setFill(color); - context.fillText(renderPercentage(share), TEXT_HORIZONTAL_INSET, TEXT_VERTICAL_INSET); + context.fillText(appCtx.displayPercent(number), TEXT_HORIZONTAL_INSET, TEXT_VERTICAL_INSET); setGraphic(canvas); } diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/NumberTableCell.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/NumberTableCell.java new file mode 100644 index 000000000..9dc6e89a3 --- /dev/null +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/NumberTableCell.java @@ -0,0 +1,52 @@ +package com.insightfullogic.honest_profiler.ports.javafx.view.cell; + +import static javafx.geometry.Pos.CENTER_RIGHT; +import static javafx.scene.text.TextAlignment.RIGHT; + +import java.util.function.Function; + +import javafx.scene.control.TableCell; + +public class NumberTableCell extends TableCell +{ + // Instance Properties + + private Function displayFunction; + private Function styleFunction; + + // Instance Constructors + + /** + * Simple Constructor. + * + * @param displayFunction a {@link Function} which converts the {@link Number} to be displayed to a {@link String} + * @param styleFunction an optional {@link Function} returning a style based on the displayed {@link Number} + */ + public NumberTableCell(Function displayFunction, + Function styleFunction) + { + super(); + + setTextAlignment(RIGHT); + setAlignment(CENTER_RIGHT); + + this.displayFunction = displayFunction; + this.styleFunction = styleFunction; + } + + // TableCell Implementation + + @Override + protected void updateItem(Number number, boolean isEmpty) + { + if (isEmpty || number == null) + { + setText(null); + setStyle(null); + return; + } + + setText(displayFunction.apply(number)); + setStyle(styleFunction == null ? null : styleFunction.apply(number)); + } +} diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/NumberTreeTableCell.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/NumberTreeTableCell.java new file mode 100644 index 000000000..eebc61b82 --- /dev/null +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/NumberTreeTableCell.java @@ -0,0 +1,52 @@ +package com.insightfullogic.honest_profiler.ports.javafx.view.cell; + +import static javafx.geometry.Pos.CENTER_RIGHT; +import static javafx.scene.text.TextAlignment.RIGHT; + +import java.util.function.Function; + +import javafx.scene.control.TreeTableCell; + +public class NumberTreeTableCell extends TreeTableCell +{ + // Instance Properties + + private Function displayFunction; + private Function styleFunction; + + // Instance Constructors + + /** + * Simple Constructor. + * + * @param displayFunction a {@link Function} which converts the {@link Number} to be displayed to a {@link String} + * @param styleFunction an optional {@link Function} returning a style based on the displayed {@link Number} + */ + public NumberTreeTableCell(Function displayFunction, + Function styleFunction) + { + super(); + + setTextAlignment(RIGHT); + setAlignment(CENTER_RIGHT); + + this.displayFunction = displayFunction; + this.styleFunction = styleFunction; + } + + // TableCell Implementation + + @Override + protected void updateItem(Number number, boolean isEmpty) + { + if (isEmpty || number == null) + { + setText(null); + setStyle(null); + return; + } + + setText(displayFunction.apply(number)); + setStyle(styleFunction == null ? null : styleFunction.apply(number)); + } +} diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/PercentageTableCell.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/PercentageTableCell.java deleted file mode 100644 index 80dd9f854..000000000 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/PercentageTableCell.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2014 Richard Warburton (richard.warburton@gmail.com) - *

- * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - *

- * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - *

- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT - * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - **/ -package com.insightfullogic.honest_profiler.ports.javafx.view.cell; - -import static com.insightfullogic.honest_profiler.ports.javafx.view.Rendering.renderPercentage; -import static javafx.geometry.Pos.CENTER_RIGHT; -import static javafx.scene.text.TextAlignment.RIGHT; - -import java.util.function.Function; - -import javafx.scene.control.TableCell; - -public class PercentageTableCell extends TableCell -{ - private Function styleFunction; - - public PercentageTableCell(Function styleFunction) - { - super(); - - setTextAlignment(RIGHT); - setAlignment(CENTER_RIGHT); - - this.styleFunction = styleFunction; - } - - @Override - protected void updateItem(Number item, boolean isEmpty) - { - if (isEmpty || item == null) - { - setText(null); - setStyle(null); - return; - } - - setText(renderPercentage(item.doubleValue())); - setStyle(this.styleFunction == null ? null : this.styleFunction.apply(item)); - } -} diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/PercentageTreeTableCell.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/PercentageTreeTableCell.java deleted file mode 100644 index a511f34f3..000000000 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/PercentageTreeTableCell.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2014 Richard Warburton (richard.warburton@gmail.com) - *

- * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - *

- * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - *

- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT - * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - **/ -package com.insightfullogic.honest_profiler.ports.javafx.view.cell; - -import static com.insightfullogic.honest_profiler.ports.javafx.view.Rendering.renderPercentage; -import static javafx.geometry.Pos.CENTER_RIGHT; -import static javafx.scene.text.TextAlignment.RIGHT; - -import java.util.function.Function; - -import javafx.scene.control.TreeTableCell; - -public class PercentageTreeTableCell extends TreeTableCell -{ - private Function styleFunction; - - public PercentageTreeTableCell(Function styleFunction) - { - super(); - - setTextAlignment(RIGHT); - setAlignment(CENTER_RIGHT); - - this.styleFunction = styleFunction; - } - - @Override - protected void updateItem(Number number, boolean isEmpty) - { - if (isEmpty || number == null) - { - setText(null); - setStyle(null); - return; - } - - setText(renderPercentage(number.doubleValue())); - setStyle(styleFunction == null ? null : styleFunction.apply(number)); - } -} diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/TimeTableCell.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/TimeTableCell.java deleted file mode 100644 index 82846314e..000000000 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/TimeTableCell.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2014 Richard Warburton (richard.warburton@gmail.com) - *

- * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - *

- * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - *

- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT - * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - **/ -package com.insightfullogic.honest_profiler.ports.javafx.view.cell; - -import static com.insightfullogic.honest_profiler.ports.javafx.util.ConversionUtil.toMillis; -import static javafx.geometry.Pos.CENTER_RIGHT; -import static javafx.scene.text.TextAlignment.RIGHT; - -import java.util.function.Function; - -import javafx.scene.control.TableCell; - -public class TimeTableCell extends TableCell -{ - private Function styleFunction; - - public TimeTableCell(Function styleFunction) - { - super(); - - setTextAlignment(RIGHT); - setAlignment(CENTER_RIGHT); - - this.styleFunction = styleFunction; - } - - @Override - protected void updateItem(Number item, boolean isEmpty) - { - if (isEmpty || item == null) - { - setText(null); - setStyle(null); - return; - } - - setText(Long.toString(toMillis(item.longValue()))); - setStyle(styleFunction == null ? null : styleFunction.apply(item)); - } -} diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/TimeTreeTableCell.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/TimeTreeTableCell.java deleted file mode 100644 index b299dab41..000000000 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/view/cell/TimeTreeTableCell.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) 2014 Richard Warburton (richard.warburton@gmail.com) - *

- * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - *

- * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - *

- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT - * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - **/ -package com.insightfullogic.honest_profiler.ports.javafx.view.cell; - -import static com.insightfullogic.honest_profiler.ports.javafx.util.ConversionUtil.toMillis; -import static javafx.geometry.Pos.CENTER_RIGHT; -import static javafx.scene.text.TextAlignment.RIGHT; - -import java.util.function.Function; - -import javafx.scene.control.TreeTableCell; - -public class TimeTreeTableCell extends TreeTableCell -{ - private Function styleFunction; - - public TimeTreeTableCell(Function styleFunction) - { - super(); - setTextAlignment(RIGHT); - setAlignment(CENTER_RIGHT); - - this.styleFunction = styleFunction; - } - - @Override - protected void updateItem(Number item, boolean isEmpty) - { - if (isEmpty || item == null) - { - setText(null); - setStyle(null); - return; - } - - setText(Long.toString(toMillis(item.longValue()))); - setStyle(styleFunction == null ? null : styleFunction.apply(item)); - } -} diff --git a/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/css/HPUI.css b/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/css/HPUI.css index e712bf8b8..0ddc824f1 100644 --- a/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/css/HPUI.css +++ b/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/css/HPUI.css @@ -30,14 +30,11 @@ .tab-pane:top *.tab-header-area { -fx-background-insets: 0, 0 0 1 0; -fx-padding: 0.416667em 0.166667em 0.0em 0.0em; - /* overridden as 5 2 0 0 */ } .tab-pane:left *.tab-header-area { -fx-background-insets: 0, 0 0 1 0; -fx-padding: 0px 0px 0px 0px; - /* -fx-padding: 0.416667em 0.166667em 0.0em 0.0em;*/ - /* overridden as 5 2 0 0 */ } .tab-pane:left { @@ -45,7 +42,7 @@ } .tab-content-area { - -fx-padding: 0.0em; /* 0 */ + -fx-padding: 0.0em; } .tab:selected { @@ -53,6 +50,5 @@ } .tooltip { -/* -fx-font-size: 0.95em; */ -fx-font-size: 8px; } \ No newline at end of file diff --git a/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/FilterCreationDialog.fxml b/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/FilterCreationDialog.fxml index 4bf49e6f0..577a9565d 100644 --- a/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/FilterCreationDialog.fxml +++ b/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/FilterCreationDialog.fxml @@ -1,5 +1,6 @@ + @@ -15,6 +16,9 @@

+ + + diff --git a/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/FilterDialog.fxml b/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/FilterDialog.fxml index b6fe0d52c..5941fcc59 100644 --- a/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/FilterDialog.fxml +++ b/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/FilterDialog.fxml @@ -1,5 +1,6 @@ + @@ -14,6 +15,9 @@ + + + diff --git a/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/Root.fxml b/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/Root.fxml index bab7d81e1..c150f6c56 100644 --- a/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/Root.fxml +++ b/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/Root.fxml @@ -13,7 +13,13 @@ - + + + + + + + @@ -23,6 +29,7 @@ + @@ -48,7 +55,4 @@ - - - diff --git a/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/configuration/ConfigurationDialog.fxml b/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/configuration/ConfigurationDialog.fxml new file mode 100644 index 000000000..55e430a4a --- /dev/null +++ b/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/configuration/ConfigurationDialog.fxml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/configuration/FormattingConfiguration.fxml b/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/configuration/FormattingConfiguration.fxml new file mode 100644 index 000000000..d66240d47 --- /dev/null +++ b/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/fxml/configuration/FormattingConfiguration.fxml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/i18n/HPUIBundle_en.properties b/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/i18n/HPUIBundle_en.properties index 983940877..d6a3c9ff6 100644 --- a/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/i18n/HPUIBundle_en.properties +++ b/src/main/resources/com/insightfullogic/honest_profiler/ports/javafx/i18n/HPUIBundle_en.properties @@ -3,6 +3,7 @@ menu.root.info=The File menu allows you to open existing log files. The Monitor menu.root.file=File menu.root.file.openLog=Open Log... menu.root.file.monitorLog=Monitor Log... +menu.root.file.preferences=Preferences... menu.root.file.quit=Quit menu.root.monitor=Monitor @@ -50,7 +51,7 @@ input.filterValue.info=Specify the value the filter will use for comparison. label.profileSampleCount.tooltip=Number of samples in the profile label.profileSampleCount.info=Shows the number of samples in the profile. -label.profileSampleCount.content={0,number,integer} samples +label.profileSampleCount.content={0} samples label.base.content=Baseline : label.new.content=New : label.baseSource.tooltip=Baseline profile in the comparison @@ -62,6 +63,21 @@ label.descendants.info=Descendants (Calling) label.exception.content=The exception stacktrace was : label.threadGrouping.info=Threads label.frameGrouping.info=Frames +label.displayFormatting.info=Display Formatting +label.exportFormatting.info=Export Formatting +label.numberFormatting.info=Number Formatting +label.percentFormatting.info=Percentage Formatting +label.timeFormatting.info=Time Formatting +label.showThousandsSeparator.info=Show Thousands Separator +label.thousandsSeparator.info=Thousands Separator Character +label.nrFractionDigits.info=Number of Fraction Digits +label.decimalSeparator.info=Decimal Separator Character +label.showAsPercent.info=Show as Percentage +label.showPercentSign.info=Show Percent Sign +label.spaceBeforePercentSign.info=Add Space Before Percent Sign +label.showAsFraction.info=Show as Fraction +label.timeUnit.info=Time Unit +label.showTimeUnits.info=Show Time Units check.hideErrorThreads.tooltip=Hide thread containing only errors check.hideErrorThreads.info=When checked, all threads which contain only errors will be filtered out. @@ -119,6 +135,8 @@ dialog.err.alreadyOpen.title=Profile Already Open dialog.err.alreadyOpen.header=Profile Already Open dialog.err.alreadyOpen.message=This source is already being profiled in profile {0}. +dialog.configuration.title=Preferences + type.file.hp=HP Log Files type.file.all=All Files From 8d7debbabf53ea6b912e297bb607f9214d2c5bf4 Mon Sep 17 00:00:00 2001 From: PhRX Date: Fri, 2 Feb 2018 13:50:21 +0100 Subject: [PATCH 2/3] Fix merge error --- .../controller/AbstractViewController.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/AbstractViewController.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/AbstractViewController.java index e46f177fd..eb99a4dd7 100644 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/AbstractViewController.java +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/AbstractViewController.java @@ -387,7 +387,7 @@ protected void cfgPctCol(TableColumn column, String propertyName, { column.setCellValueFactory(new PropertyValueFactory<>(propertyName)); column.setCellFactory(col -> new NumberTableCell<>(appCtx()::displayPercent, null)); - setColumnHeader(column, title, profileContext); + configureHeader(column, title, profileContext); } /** @@ -405,7 +405,7 @@ protected void cfgPctDiffCol(TableColumn column, String propertyN column.setCellValueFactory(new PropertyValueFactory<>(propertyName)); column.setCellFactory( col -> new NumberTableCell<>(appCtx()::displayPercent, doubleDiffStyler)); - setColumnHeader(column, title, null); + configureHeader(column, title, null); } /** @@ -423,7 +423,7 @@ protected void cfgNrCol(TableColumn column, String propertyName, { column.setCellValueFactory(new PropertyValueFactory<>(propertyName)); column.setCellFactory(col -> new NumberTableCell<>(appCtx()::displayIntegral, null)); - setColumnHeader(column, title, profileContext); + configureHeader(column, title, profileContext); } /** @@ -440,7 +440,7 @@ protected void cfgNrDiffCol(TableColumn column, String propertyNa { column.setCellValueFactory(new PropertyValueFactory<>(propertyName)); column.setCellFactory(col -> new NumberTableCell<>(appCtx()::displayIntegral, intDiffStyler)); - setColumnHeader(column, title, null); + configureHeader(column, title, null); } /** @@ -458,7 +458,7 @@ protected void cfgTimeCol(TableColumn column, String propertyName { column.setCellValueFactory(new PropertyValueFactory<>(propertyName)); column.setCellFactory(col -> new NumberTableCell<>(appCtx()::displayTime, null)); - setColumnHeader(column, title, profileContext); + configureHeader(column, title, profileContext); } /** @@ -475,7 +475,7 @@ protected void cfgTimeDiffCol(TableColumn column, String property { column.setCellValueFactory(new PropertyValueFactory<>(propertyName)); column.setCellFactory(col -> new NumberTableCell<>(appCtx()::displayTime, longDiffStyler)); - setColumnHeader(column, title, null); + configureHeader(column, title, null); } // UI Helper Methods : TreeTable Column Configuration @@ -495,7 +495,7 @@ protected void cfgPctCol(TreeTableColumn column, String propertyN { column.setCellValueFactory(new TreeItemPropertyValueFactory<>(propertyName)); column.setCellFactory(col -> new NumberTreeTableCell<>(appCtx()::displayPercent, null)); - setColumnHeader(column, title, profileContext); + configureHeader(column, title, profileContext); } /** @@ -513,7 +513,7 @@ protected void cfgPctDiffCol(TreeTableColumn column, String prope column.setCellValueFactory(new TreeItemPropertyValueFactory<>(propertyName)); column.setCellFactory( col -> new NumberTreeTableCell<>(appCtx()::displayPercent, doubleDiffStyler)); - setColumnHeader(column, title, null); + configureHeader(column, title, null); } /** @@ -531,7 +531,7 @@ protected void cfgNrCol(TreeTableColumn column, String propertyNa { column.setCellValueFactory(new TreeItemPropertyValueFactory<>(propertyName)); column.setCellFactory(col -> new NumberTreeTableCell<>(appCtx()::displayIntegral, null)); - setColumnHeader(column, title, profileContext); + configureHeader(column, title, profileContext); } /** @@ -549,7 +549,7 @@ protected void cfgNrDiffCol(TreeTableColumn column, String proper column.setCellValueFactory(new TreeItemPropertyValueFactory<>(propertyName)); column .setCellFactory(col -> new NumberTreeTableCell<>(appCtx()::displayIntegral, intDiffStyler)); - setColumnHeader(column, title, null); + configureHeader(column, title, null); } /** @@ -567,7 +567,7 @@ protected void cfgTimeCol(TreeTableColumn column, String property { column.setCellValueFactory(new TreeItemPropertyValueFactory<>(propertyName)); column.setCellFactory(col -> new NumberTreeTableCell<>(appCtx()::displayTime, null)); - setColumnHeader(column, title, profileContext); + configureHeader(column, title, profileContext); } /** @@ -585,7 +585,7 @@ protected void cfgTimeDiffCol(TreeTableColumn column, String prop column.setCellValueFactory(new TreeItemPropertyValueFactory<>(propertyName)); column.setCellFactory( col -> new NumberTreeTableCell<>(appCtx()::displayTime, longDiffStyler)); - setColumnHeader(column, title, null); + configureHeader(column, title, null); } // Filter-related methods From 065e46bc84e43e120f57e1a2e8ff13728f1b73bc Mon Sep 17 00:00:00 2001 From: Philip Arickx Date: Fri, 2 Feb 2018 14:01:33 +0100 Subject: [PATCH 3/3] Fix merge error, missing import --- .../ports/javafx/controller/AbstractViewController.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/AbstractViewController.java b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/AbstractViewController.java index eb99a4dd7..54131cc18 100644 --- a/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/AbstractViewController.java +++ b/src/main/java/com/insightfullogic/honest_profiler/ports/javafx/controller/AbstractViewController.java @@ -25,6 +25,8 @@ import com.insightfullogic.honest_profiler.ports.javafx.model.ProfileContext; import com.insightfullogic.honest_profiler.ports.javafx.view.cell.NumberTableCell; import com.insightfullogic.honest_profiler.ports.javafx.view.cell.NumberTreeTableCell; +import com.insightfullogic.honest_profiler.ports.javafx.view.menu.ColumnGroupMenuItem; +import com.insightfullogic.honest_profiler.ports.javafx.view.menu.ColumnMenuItem; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty;