From a96a1d4c72a409ec853b724c9b1d6bcd0eb26679 Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Fri, 30 Mar 2018 10:23:00 +0300 Subject: [PATCH 1/1] Improved subscription CSV export wizard page initial selection handling Now it supports chart/chart group/chart item selections as well to initialize the subscription item selection. refs #7849 Change-Id: Idbfbd979116b932f0d54958a63b01142401366a6 (cherry picked from commit b6f0f7aa4de4c172a830ef9087749e38185a8edf) --- .../org.simantics.charts/META-INF/MANIFEST.MF | 3 +- .../simantics/charts/ui/CSVExportPage.java | 515 ++++++++++-------- 2 files changed, 292 insertions(+), 226 deletions(-) diff --git a/bundles/org.simantics.charts/META-INF/MANIFEST.MF b/bundles/org.simantics.charts/META-INF/MANIFEST.MF index 3d289e419..2b3484be8 100644 --- a/bundles/org.simantics.charts/META-INF/MANIFEST.MF +++ b/bundles/org.simantics.charts/META-INF/MANIFEST.MF @@ -48,6 +48,7 @@ Require-Bundle: org.eclipse.ui, org.eclipse.e4.ui.bindings;bundle-version="0.11.0", org.eclipse.e4.core.di.annotations, org.eclipse.e4.core.services, - com.fasterxml.jackson.core.jackson-core;bundle-version="2.8.2" + com.fasterxml.jackson.core.jackson-core;bundle-version="2.8.2", + org.slf4j.api Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy diff --git a/bundles/org.simantics.charts/src/org/simantics/charts/ui/CSVExportPage.java b/bundles/org.simantics.charts/src/org/simantics/charts/ui/CSVExportPage.java index 047376dbb..d2a741f1a 100644 --- a/bundles/org.simantics.charts/src/org/simantics/charts/ui/CSVExportPage.java +++ b/bundles/org.simantics.charts/src/org/simantics/charts/ui/CSVExportPage.java @@ -12,14 +12,20 @@ package org.simantics.charts.ui; import java.io.File; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; @@ -45,15 +51,17 @@ import org.eclipse.ui.preferences.ScopedPreferenceStore; import org.simantics.NameLabelUtil; import org.simantics.Simantics; import org.simantics.browsing.ui.common.ColumnKeys; +import org.simantics.charts.ontology.ChartResource; import org.simantics.databoard.Bindings; import org.simantics.databoard.parser.StringEscapeUtils; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.NamedResource; import org.simantics.db.common.request.IsParent; -import org.simantics.db.common.request.UniqueRead; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.SelectionHints; +import org.simantics.db.layer0.request.ProjectModels; +import org.simantics.db.request.Read; import org.simantics.history.csv.ColumnSeparator; import org.simantics.history.csv.DecimalSeparator; import org.simantics.history.csv.ExportInterpolation; @@ -63,9 +71,11 @@ import org.simantics.modeling.ModelingUtils; import org.simantics.modeling.preferences.CSVPreferences; import org.simantics.modeling.ui.modelBrowser2.label.SubscriptionItemLabelRule; import org.simantics.utils.datastructures.Arrays; -import org.simantics.utils.datastructures.Pair; import org.simantics.utils.strings.AlphanumComparator; import org.simantics.utils.ui.ISelectionUtils; +import org.simantics.utils.ui.SWTUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** @@ -73,6 +83,76 @@ import org.simantics.utils.ui.ISelectionUtils; */ public class CSVExportPage extends WizardPage { + private static final Logger LOGGER = LoggerFactory.getLogger(CSVExportPage.class); + + private static class Model { + private static final Comparator COMP = + (o1,o2) -> AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare( + o1.getName(), o2.getName()); + + public final NamedResource model; + public List sortedSubs = new ArrayList<>(); + public Map subs = new HashMap<>(); + public Map chartItemsToSubs = new HashMap<>(); + public Set initiallySelectedSubscriptions = Collections.emptySet(); + + public Model(NamedResource model) { + this.model = model; + } + + public void initialize(ReadGraph graph, Collection inputSelection) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + ModelingResources MOD = ModelingResources.getInstance(graph); + ChartResource CHART = ChartResource.getInstance(graph); + SubscriptionItemLabelRule rule = new SubscriptionItemLabelRule(); + + Resource m = model.getResource(); + String name = graph.getPossibleRelatedValue(m, L0.HasName, Bindings.STRING); + if (name == null) + return; + + name = NameLabelUtil.modalName(graph, m); + for (Resource item : ModelingUtils.searchByTypeShallow(graph, m, MOD.Subscription_Item)) { + String subscriptionLabel = null; + Resource subscription = graph.getPossibleObject(item, L0.PartOf); + if (subscription != null) + subscriptionLabel = graph.getPossibleRelatedValue(subscription, L0.HasLabel, Bindings.STRING); + String label = rule.getLabel(graph, item).get(ColumnKeys.SINGLE); + if (label == null) + continue; + if (subscriptionLabel != null) + label = subscriptionLabel + "/" + label; + subs.put(item, new NamedResource(label, item)); + } + for (Resource cItem : ModelingUtils.searchByTypeShallow(graph, m, CHART.Chart_Item)) { + Resource sItem = graph.getPossibleObject(cItem, CHART.Chart_Item_HasSubscriptionItem); + if (sItem != null && subs.containsKey(sItem)) + chartItemsToSubs.put(cItem, sItem); + } + + sortedSubs = subs.values().stream().sorted(COMP).collect(Collectors.toList()); + initiallySelectedSubscriptions = initiallySelectedSubscriptions(graph, inputSelection); + } + + private Set initiallySelectedSubscriptions(ReadGraph graph, Collection inputSelection) throws DatabaseException { + if (inputSelection == null) + return Collections.emptySet(); + + HashSet result = new HashSet<>(); + + for (Resource i : inputSelection) { + for (NamedResource nr : sortedSubs) + if (graph.syncRequest(new IsParent(i, nr.getResource()))) + result.add(nr); + for (Map.Entry cs : chartItemsToSubs.entrySet()) + if (graph.syncRequest(new IsParent(i, cs.getKey()))) + result.add( subs.get( cs.getValue() ) ); + } + + return result; + } + } + CSVExportPlan exportModel; CCombo model; Table item; @@ -83,20 +163,17 @@ public class CSVExportPage extends WizardPage { CCombo decimalSeparator; CCombo columnSeparator; CCombo sampling; + Group resampling; Text timeStep; Text startTime; Text timeStamps; CCombo samplingMode; Text singlePrecision; Text doublePrecision; - - Group resampling; + Button overwrite; Collection initialSelection; - - List>> models = new ArrayList<>(); - - private Button overwrite; + List models = Collections.emptyList(); ModifyListener m = (e) -> validatePage(); @@ -290,47 +367,21 @@ public class CSVExportPage extends WizardPage { } }); - try { - initializeData(); - } catch (DatabaseException e) { - e.printStackTrace(); - } - model.addSelectionListener(s); - scroller.setMinSize(container.computeSize(SWT.DEFAULT, SWT.DEFAULT)); + initialSelection = ISelectionUtils.getPossibleKeys(exportModel.selection, SelectionHints.KEY_MAIN, Resource.class); + initializeWidgetsFromPreferences(); + initializeData(); + scroller.setMinSize(container.computeSize(SWT.DEFAULT, SWT.DEFAULT)); setControl(scroller); validatePage(); - } - void updateSelectAll(boolean checked, boolean gray, boolean notify) { - if (checked) { - selectAllItems.setText("Select None"); - } else { - selectAllItems.setText("Select All"); - } - if (!notify) - selectAllItems.removeSelectionListener(selectAllItemsListener); - selectAllItems.setGrayed(checked && gray); - selectAllItems.setSelection(checked); - if (!notify) - selectAllItems.addSelectionListener(selectAllItemsListener); - } - - protected int countCheckedItems(Table table) { - int ret = 0; - for (TableItem item : table.getItems()) - ret += item.getChecked() ? 1 : 0; - return ret; - } - - private void initializeData() throws DatabaseException { - + private void initializeWidgetsFromPreferences() { // Write preferences to formatter IPreferenceStore csvnode = new ScopedPreferenceStore( InstanceScope.INSTANCE, CSVPreferences.P_NODE ); - + Double startTime = csvnode.getDouble(CSVPreferences.P_CSV_START_TIME); Double timeStep = csvnode.getDouble(CSVPreferences.P_CSV_TIME_STEP); String decimalSeparator = csvnode.getString(CSVPreferences.P_CSV_DECIMAL_SEPARATOR); @@ -340,231 +391,246 @@ public class CSVExportPage extends WizardPage { int timeDigits = csvnode.getInt(CSVPreferences.P_CSV_TIME_DIGITS); int floatDigits = csvnode.getInt(CSVPreferences.P_CSV_FLOAT_DIGITS); int doubleDigits = csvnode.getInt(CSVPreferences.P_CSV_DOUBLE_DIGITS); - - initialSelection = ISelectionUtils.getPossibleKeys(exportModel.selection, SelectionHints.KEY_MAIN, Resource.class); - - models = exportModel.sessionContext.getSession().syncRequest(new UniqueRead>>>() { - - @Override - public List>> perform(ReadGraph graph) throws DatabaseException { - Layer0 L0 = Layer0.getInstance(graph); - ModelingResources MOD = ModelingResources.getInstance(graph); - List>> result = new ArrayList<>(); - for(Resource model : graph.syncRequest(new org.simantics.db.layer0.request.ProjectModels(Simantics.getProjectResource()))) { - String name = graph.getPossibleRelatedValue(model, L0.HasName, Bindings.STRING); - if(name == null) continue; - name = NameLabelUtil.modalName(graph, model); - List subs = new ArrayList<>(); - for(Resource item : ModelingUtils.searchByType(graph, model, MOD.Subscription_Item)) { - String subscriptionLabel = null; - Resource subscription = graph.getPossibleObject(item, L0.PartOf); - if(subscription != null) { - subscriptionLabel = graph.getPossibleRelatedValue(subscription, L0.HasLabel, Bindings.STRING); - } - SubscriptionItemLabelRule rule = new SubscriptionItemLabelRule(); - Map map = rule.getLabel(graph, item); - String label = map.get(ColumnKeys.SINGLE); - if(label == null) continue; - if(subscriptionLabel != null) label = subscriptionLabel + "/" + label; - subs.add(new NamedResource(label, item)); - } - subs.sort((o1,o2) -> AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(o1.getName(), o2.getName())); - result.add(new Pair<>(new NamedResource(name, model), subs)); - } - return result; - } - - }); - - Set selected = exportModel.sessionContext.getSession().syncRequest(new UniqueRead>() { - - @Override - public Set perform(ReadGraph graph) throws DatabaseException { - - if(initialSelection == null) return Collections.emptySet(); - - HashSet result = new HashSet<>(); - - for(Pair> p : models) { - for(NamedResource nr : p.second) { - for(Resource i : initialSelection) { - if(graph.syncRequest(new IsParent(i, nr.getResource()))) result.add(nr); - } - } - } - return result; - } - - }); - - - for(int i=0;i> p = models.get(i); - model.add(p.first.getName()); - boolean hasInitial = false; - for(NamedResource nr : p.second) { - if(selected.contains(nr)) hasInitial = true; - } - if(hasInitial) { - model.select(i); - item.removeAll(); - exportModel.items.clear(); - int index = 0; - int firstIndex = 0; - for(NamedResource nr : p.second) { - TableItem ti = new TableItem(item, SWT.NONE); - ti.setText(nr.getName()); - ti.setData(nr); - if(selected.contains(nr)) { - exportModel.items.add(nr.getResource()); - ti.setChecked(true); - if(firstIndex == 0) firstIndex = index; - } - index++; - } - item.setTopIndex(firstIndex); - item.setData(p.first); - } - - } - this.decimalSeparator.select(DecimalSeparator.fromPreference(decimalSeparator).ordinal()); this.columnSeparator.select(ColumnSeparator.fromPreference(columnSeparator).ordinal()); - - if(resample) { - this.sampling.select(1); - } else { - this.sampling.select(0); - } - + + this.sampling.select(resample ? 1 : 0); this.samplingMode.select(ExportInterpolation.fromPreference(samplingModePreference).index()); - + this.startTime.setText("" + startTime); this.timeStep.setText("" + timeStep); this.timeStamps.setText("" + timeDigits); this.singlePrecision.setText("" + floatDigits); this.doublePrecision.setText("" + doubleDigits); - - for (String path : exportModel.recentLocations) { + + for (String path : exportModel.recentLocations) exportLocation.add(path); - } if (exportLocation.getItemCount() > 0) exportLocation.select(0); - + } + + private void initializeData() { + try { + getContainer().run(true, true, monitor -> { + try { + initializeModelData(monitor); + SWTUtils.asyncExec(model, () -> { + if (!model.isDisposed()) + initializeModelAndItemSelection(); + }); + } catch (DatabaseException e) { + throw new InvocationTargetException(e); + } finally { + monitor.done(); + } + }); + } catch (InvocationTargetException e) { + setErrorMessage(e.getMessage()); + LOGGER.error("Failed to initialized model data for wizard.", e.getCause()); + } catch (InterruptedException e) { + setErrorMessage(e.getMessage()); + LOGGER.error("Interrupted wizard model data initialization.", e); + } + } + + private void initializeModelData(IProgressMonitor monitor) throws DatabaseException { + models = exportModel.sessionContext.getSession().syncRequest( + (Read>) graph -> readModelData(monitor, graph)); + } + + private List readModelData(IProgressMonitor monitor, ReadGraph graph) throws DatabaseException { + List result = new ArrayList<>(); + Layer0 L0 = Layer0.getInstance(graph); + Collection models = graph.syncRequest(new ProjectModels(Simantics.getProjectResource())); + SubMonitor mon = SubMonitor.convert(monitor, "Reading model subscriptions", models.size()); + for (Resource model : models) { + String name = graph.getPossibleRelatedValue(model, L0.HasName, Bindings.STRING); + if (name != null) { + name = NameLabelUtil.modalName(graph, model); + mon.subTask(name); + Model m = new Model(new NamedResource(name, model)); + m.initialize(graph, initialSelection); + result.add(m); + } + mon.worked(1); + } + return result; + } + + private void initializeModelAndItemSelection() { + boolean initialSelectionDone = false; + for (int i = 0; i < models.size(); i++) { + Model m = models.get(i); + model.add(m.model.getName()); + if (!initialSelectionDone) { + boolean hasInitialSelection = m.sortedSubs.stream().anyMatch(m.initiallySelectedSubscriptions::contains); + if (hasInitialSelection) { + initializeItemSelectionForModel(m); + initialSelectionDone = true; + } + } + } + } + + private void initializeItemSelectionForModel(Model m) { + int i = models.indexOf(m); + model.select(i); + item.removeAll(); + exportModel.items.clear(); + int index = 0; + int firstIndex = -1; + item.setRedraw(false); + for (NamedResource nr : m.sortedSubs) { + TableItem ti = new TableItem(item, SWT.NONE); + ti.setText(nr.getName()); + ti.setData(nr); + if (m.initiallySelectedSubscriptions.contains(nr)) { + exportModel.items.add(nr.getResource()); + ti.setChecked(true); + if (firstIndex == -1) + firstIndex = index; + } + index++; + } + item.setTopIndex(Math.max(0, firstIndex)); + item.setData(m); + item.setRedraw(true); + + int checked = countCheckedItems(item); + updateSelectAll(checked > 0, checked < item.getItemCount(), false); + } + + private void updateSelectAll(boolean checked, boolean gray, boolean notify) { + if (checked) { + selectAllItems.setText("Select None"); + } else { + selectAllItems.setText("Select All"); + } + if (!notify) + selectAllItems.removeSelectionListener(selectAllItemsListener); + selectAllItems.setGrayed(checked && gray); + selectAllItems.setSelection(checked); + if (!notify) + selectAllItems.addSelectionListener(selectAllItemsListener); + } + + protected int countCheckedItems(Table table) { + int ret = 0; + for (TableItem item : table.getItems()) + ret += item.getChecked() ? 1 : 0; + return ret; } Integer validInteger(String s) { - try { - return Integer.parseInt(s); - } catch (NumberFormatException e) { - return null; - } + try { + return Integer.parseInt(s); + } catch (NumberFormatException e) { + return null; + } } Double validDouble(String s) { - try { - return Double.parseDouble(s); - } catch (NumberFormatException e) { - return null; - } + try { + return Double.parseDouble(s); + } catch (NumberFormatException e) { + return null; + } } - - Pair> getModel(String name) { - for(Pair> data : models) { - if(data.first.getName().equals(name)) return data; - } - return null; + + Model getModel(String name) { + for (Model m : models) + if (m.model.getName().equals(name)) + return m; + return null; + } + + private void setText(Group g, String text) { + if (!g.getText().equals(text)) + g.setText(text); } - + void validatePage() { + boolean resample = sampling.getText().equals("Resampled"); + if (resample) { + setText(resampling, "Resampling settings"); + timeStep.setEnabled(true); + startTime.setEnabled(true); + samplingMode.setEnabled(true); + } else { + setText(resampling, "Resampling settings (not used with recorded samples)"); + timeStep.setEnabled(false); + startTime.setEnabled(false); + samplingMode.setEnabled(false); + } - boolean resample = sampling.getText().equals("Resampled"); - - if(resample) { - resampling.setText("Resampling settings"); - timeStep.setEnabled(true); - startTime.setEnabled(true); - samplingMode.setEnabled(true); - } else { - resampling.setText("Resampling settings (not used with recorded samples)"); - timeStep.setEnabled(false); - startTime.setEnabled(false); - samplingMode.setEnabled(false); - } - - String selectedModel = model.getText(); - Pair> p = getModel(selectedModel); - if(p != null) { - - Set checked = new HashSet<>(); - - NamedResource existing = (NamedResource)item.getData(); - if(!p.first.equals(existing)) { - - item.removeAll(); - for(NamedResource nr : p.second) { - TableItem ti = new TableItem(item, SWT.NONE); - ti.setText(nr.getName()); - ti.setData(nr); - } - item.setData(p.first); - - } - - for(TableItem ti : item.getItems()) { - if(ti.getChecked()) checked.add(((NamedResource)ti.getData()).getResource()); - } - - exportModel.items = checked; - - } - - Double validStartTime = validDouble(startTime.getText()); - Double validStepSize = validDouble(timeStep.getText()); - - if(resample) { - - if(validStartTime == null) { + String selectedModel = model.getText(); + Model m = getModel(selectedModel); + if (m != null) { + Model existing = (Model) item.getData(); + if (!m.equals(existing)) { + item.setRedraw(false); + item.removeAll(); + for (NamedResource sub : m.sortedSubs) { + TableItem ti = new TableItem(item, SWT.NONE); + ti.setText(sub.getName()); + ti.setData(sub); + ti.setChecked(m.initiallySelectedSubscriptions.contains(sub)); + } + item.setData(m); + item.setRedraw(true); + } + + exportModel.items = java.util.Arrays.stream(item.getItems()) + .filter(TableItem::getChecked) + .map(ti -> ((NamedResource) ti.getData()).getResource()) + .collect(Collectors.toSet()); + } + + Double validStartTime = validDouble(startTime.getText()); + Double validStepSize = validDouble(timeStep.getText()); + + if (resample) { + + if (validStartTime == null) { setErrorMessage("Start time must be a number."); setPageComplete(false); return; } - - if(validStepSize == null) { + + if (validStepSize == null) { setErrorMessage("Step size must be a number."); setPageComplete(false); return; } - if(validStepSize <= 0) { + if (validStepSize <= 0) { setErrorMessage("Step size must be greater than 0."); setPageComplete(false); return; } - } else { - - if(exportModel.items.size() > 1) { + } else { + + if (exportModel.items.size() > 1) { setErrorMessage("Recorded samples can only be exported for a single subscription item."); setPageComplete(false); return; - } + } + + } - } - - if(item.getItemCount() == 0) { + if (item.getItemCount() == 0) { setErrorMessage("No subscription items in selected model."); setPageComplete(false); return; - } - + } + if (exportModel.items.isEmpty()) { setErrorMessage("No items selected for export."); setPageComplete(false); return; } - + String exportLoc = exportLocation.getText(); if (exportLoc.isEmpty()) { setErrorMessage("Select output file."); @@ -594,21 +660,21 @@ public class CSVExportPage extends WizardPage { Integer validTimeDigits = validInteger(timeStamps.getText()); - if(validTimeDigits == null) { + if (validTimeDigits == null) { setErrorMessage("Time stamps needs to be an integer number."); setPageComplete(false); return; } Integer validSinglePrecision = validInteger(singlePrecision.getText()); - if(validSinglePrecision == null) { + if (validSinglePrecision == null) { setErrorMessage("Single precision needs to be an integer number."); setPageComplete(false); return; } Integer validDoublePrecision = validInteger(doublePrecision.getText()); - if(validDoublePrecision == null) { + if (validDoublePrecision == null) { setErrorMessage("Double precision needs to be an integer number."); setPageComplete(false); return; @@ -622,15 +688,14 @@ public class CSVExportPage extends WizardPage { exportModel.resample = sampling.getSelectionIndex() == 1; exportModel.samplingMode = ExportInterpolation.fromIndex(samplingMode.getSelectionIndex()); - + exportModel.timeDigits = validTimeDigits; exportModel.floatDigits = validSinglePrecision; exportModel.doubleDigits = validDoublePrecision; - + setErrorMessage(null); - setMessage("Press Finish to export subscription data."); + setMessage("Press Finish to export subscription data for " + exportModel.items.size() + " items."); setPageComplete(true); - } } -- 2.43.2