package org.openlca.app.results.contributions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jface.viewers.BaseLabelProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.editor.FormEditor;
import org.eclipse.ui.forms.editor.FormPage;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.forms.widgets.Section;
import org.openlca.app.M;
import org.openlca.app.components.ContributionImage;
import org.openlca.app.db.Cache;
import org.openlca.app.util.Actions;
import org.openlca.app.util.Controls;
import org.openlca.app.util.Labels;
import org.openlca.app.util.Numbers;
import org.openlca.app.util.UI;
import org.openlca.app.util.tables.TableClipboard;
import org.openlca.app.util.tables.Tables;
import org.openlca.app.util.viewers.Viewers;
import org.openlca.app.viewers.combo.ProcessViewer;
import org.openlca.core.database.EntityCache;
import org.openlca.core.matrix.FlowIndex;
import org.openlca.core.model.descriptors.FlowDescriptor;
import org.openlca.core.model.descriptors.ImpactCategoryDescriptor;
import org.openlca.core.model.descriptors.ProcessDescriptor;
import org.openlca.core.results.FullResultProvider;
/**
* Shows the single and upstream results of the processes in an analysis result.
*/
public class ProcessResultPage extends FormPage {
private EntityCache cache = Cache.getEntityCache();
private Map<Long, ProcessDescriptor> processDescriptors = new HashMap<>();
private FullResultProvider result;
private ResultProvider flowResult;
private ResultProvider impactResult;
private FormToolkit toolkit;
private ProcessViewer flowProcessViewer;
private ProcessViewer impactProcessCombo;
private TableViewer inputTable;
private TableViewer outputTable;
private TableViewer impactTable;
private Spinner flowSpinner;
private Spinner impactSpinner;
private ContributionImage image = new ContributionImage(
Display.getCurrent());
private double flowCutOff = 0.01;
private double impactCutOff = 0.01;
private final static String[] EXCHANGE_COLUMN_LABELS = {
M.Contribution,
M.Flow, M.UpstreamInclDirect, M.Direct,
M.Unit };
private final static String[] IMPACT_COLUMN_LABELS = {
M.Contribution,
M.ImpactCategory, M.UpstreamInclDirect,
M.Direct, M.Unit };
public ProcessResultPage(FormEditor editor, FullResultProvider result) {
super(editor, ProcessResultPage.class.getName(),
M.ProcessResults);
this.result = result;
for (ProcessDescriptor desc : result.getProcessDescriptors())
processDescriptors.put(desc.getId(), desc);
this.flowResult = new ResultProvider(result);
this.impactResult = new ResultProvider(result);
}
@Override
public void dispose() {
image.dispose();
super.dispose();
}
@Override
protected void createFormContent(IManagedForm managedForm) {
toolkit = managedForm.getToolkit();
ScrolledForm form = UI.formHeader(managedForm, M.ProcessResults);
Composite body = UI.formBody(form, toolkit);
createFlowSection(body);
if (result.hasImpactResults())
createImpactSection(body);
form.reflow(true);
setInputs();
}
private void setInputs() {
fillFlows(inputTable);
fillFlows(outputTable);
long refProcessId = result.result.productIndex.getRefFlow().getFirst();
ProcessDescriptor p = processDescriptors.get(refProcessId);
flowProcessViewer.select(p);
if (result.hasImpactResults()) {
impactProcessCombo.select(p);
impactTable.setInput(result.getImpactDescriptors());
}
}
private void fillFlows(TableViewer table) {
boolean input = table == inputTable;
FlowIndex idx = result.result.flowIndex;
List<FlowDescriptor> list = new ArrayList<>();
for (FlowDescriptor f : result.getFlowDescriptors()) {
if (idx.isInput(f.getId()) == input)
list.add(f);
}
Collections.sort(list);
table.setInput(list);
}
private void createFlowSection(Composite parent) {
Section section = UI.section(parent, toolkit, M.FlowContributionsToProcessResults);
UI.gridData(section, true, true);
Composite composite = toolkit.createComposite(section);
section.setClient(composite);
UI.gridLayout(composite, 1);
Composite container = new Composite(composite, SWT.NONE);
UI.gridData(container, true, false);
UI.gridLayout(container, 5);
UI.formLabel(container, toolkit, M.Process);
flowProcessViewer = new ProcessViewer(container, cache);
flowProcessViewer.setInput(result.getProcessDescriptors());
flowProcessViewer.addSelectionChangedListener((selection) -> {
flowResult.setProcess(selection);
inputTable.refresh();
outputTable.refresh();
});
UI.formLabel(container, toolkit, M.Cutoff);
flowSpinner = new Spinner(container, SWT.BORDER);
flowSpinner.setValues(1, 0, 10000, 2, 1, 100);
toolkit.adapt(flowSpinner);
toolkit.createLabel(container, "%");
Controls.onSelect(flowSpinner, (e) -> {
flowCutOff = flowSpinner.getSelection();
inputTable.refresh();
outputTable.refresh();
});
Composite resultContainer = new Composite(composite, SWT.NONE);
resultContainer.setLayout(new GridLayout(2, true));
UI.gridData(resultContainer, true, true);
UI.formLabel(resultContainer, M.Inputs);
UI.formLabel(resultContainer, M.Outputs);
inputTable = createFlowTable(resultContainer);
outputTable = createFlowTable(resultContainer);
}
private TableViewer createFlowTable(Composite parent) {
FlowLabel label = new FlowLabel();
TableViewer table = Tables.createViewer(parent, EXCHANGE_COLUMN_LABELS, label);
decorateResultViewer(table);
Viewers.sortByLabels(table, label, 1, 4);
Viewers.sortByDouble(table, (FlowDescriptor f) -> flowResult.getUpstreamContribution(f), 0);
Viewers.sortByDouble(table, (FlowDescriptor f) -> flowResult.getUpstreamTotal(f), 2);
Viewers.sortByDouble(table, (FlowDescriptor f) -> flowResult.getDirectResult(f), 3);
Actions.bind(table, TableClipboard.onCopy(table));
table.getTable().getColumns()[2].setAlignment(SWT.RIGHT);
table.getTable().getColumns()[3].setAlignment(SWT.RIGHT);
return table;
}
private void createImpactSection(Composite parent) {
Section section = UI.section(parent, toolkit,
M.ImpactAssessmentResults);
UI.gridData(section, true, true);
Composite composite = toolkit.createComposite(section);
section.setClient(composite);
UI.gridLayout(composite, 1);
Composite container = new Composite(composite, SWT.NONE);
UI.gridLayout(container, 5);
UI.gridData(container, true, false);
UI.formLabel(container, toolkit, M.Process);
impactProcessCombo = new ProcessViewer(container, cache);
impactProcessCombo.setInput(result.getProcessDescriptors());
impactProcessCombo.addSelectionChangedListener((selection) -> {
impactResult.setProcess(selection);
impactTable.refresh();
});
UI.formLabel(container, toolkit, M.Cutoff);
impactSpinner = new Spinner(container, SWT.BORDER);
impactSpinner.setValues(1, 0, 10000, 2, 1, 100);
toolkit.adapt(impactSpinner);
toolkit.createLabel(container, "%");
Controls.onSelect(impactSpinner, (e) -> {
impactCutOff = impactSpinner.getSelection();
impactTable.refresh();
});
impactTable = createImpactTable(composite);
}
private TableViewer createImpactTable(Composite composite) {
ImpactLabel label = new ImpactLabel();
TableViewer table = Tables.createViewer(composite, IMPACT_COLUMN_LABELS, label);
decorateResultViewer(table);
Viewers.sortByLabels(table, label, 1, 4);
Viewers.sortByDouble(table, (ImpactCategoryDescriptor i) -> impactResult.getUpstreamContribution(i), 0);
Viewers.sortByDouble(table, (ImpactCategoryDescriptor i) -> impactResult.getUpstreamTotal(i), 2);
Viewers.sortByDouble(table, (ImpactCategoryDescriptor i) -> impactResult.getDirectResult(i), 3);
Actions.bind(table, TableClipboard.onCopy(table));
table.getTable().getColumns()[2].setAlignment(SWT.RIGHT);
table.getTable().getColumns()[3].setAlignment(SWT.RIGHT);
return table;
}
private void decorateResultViewer(TableViewer table) {
table.setFilters(new ViewerFilter[] { new CutOffFilter() });
UI.gridData(table.getTable(), true, true);
Tables.bindColumnWidths(table.getTable(), 0.20, 0.30, 0.20, 0.20, 0.10);
}
private class FlowLabel extends BaseLabelProvider implements
ITableLabelProvider {
@Override
public Image getColumnImage(Object o, int col) {
if (!(o instanceof FlowDescriptor) || col != 0)
return null;
FlowDescriptor flow = (FlowDescriptor) o;
double c = flowResult.getUpstreamContribution(flow);
return image.getForTable(c);
}
@Override
public String getColumnText(Object o, int col) {
if (!(o instanceof FlowDescriptor))
return null;
FlowDescriptor flow = (FlowDescriptor) o;
switch (col) {
case 0:
return Numbers.percent(
flowResult.getUpstreamContribution(flow));
case 1:
return getFlowLabel(flow);
case 2:
return Numbers
.format(flowResult.getUpstreamTotal(flow));
case 3:
return Numbers.format(flowResult.getDirectResult(flow));
case 4:
return Labels.getRefUnit(flow, cache);
default:
return null;
}
}
private String getFlowLabel(FlowDescriptor flow) {
String val = flow.getName();
if (flow.getCategory() == null)
return val;
return val + " (" + Labels.getShortCategory(flow, cache) + ")";
}
}
private class ImpactLabel extends BaseLabelProvider implements
ITableLabelProvider {
@Override
public Image getColumnImage(Object o, int col) {
if (!(o instanceof ImpactCategoryDescriptor))
return null;
if (col != 0)
return null;
ImpactCategoryDescriptor d = (ImpactCategoryDescriptor) o;
return image.getForTable(impactResult.getUpstreamContribution(d));
}
@Override
public String getColumnText(Object o, int col) {
if (!(o instanceof ImpactCategoryDescriptor))
return null;
ImpactCategoryDescriptor d = (ImpactCategoryDescriptor) o;
switch (col) {
case 0:
return Numbers.percent(impactResult.getUpstreamContribution(d));
case 1:
return d.getName();
case 2:
return Numbers.format(impactResult.getUpstreamTotal(d));
case 3:
return Numbers.format(impactResult.getDirectResult(d));
case 4:
return d.getReferenceUnit();
}
return null;
}
}
private class CutOffFilter extends ViewerFilter {
@Override
public boolean select(Viewer viewer, Object parent, Object o) {
if (!(o instanceof FlowDescriptor
|| o instanceof ImpactCategoryDescriptor))
return false;
boolean forFlow = o instanceof FlowDescriptor;
double cutoff = forFlow ? flowCutOff : impactCutOff;
if (cutoff == 0)
return true;
double c = 0;
if (forFlow)
c = flowResult
.getUpstreamContribution((FlowDescriptor) o);
else
c = impactResult
.getUpstreamContribution((ImpactCategoryDescriptor) o);
return c * 100 > cutoff;
}
}
private class ResultProvider {
private ProcessDescriptor process;
private FullResultProvider result;
public ResultProvider(FullResultProvider result) {
long refProcessId = result.result.productIndex.getRefFlow().getFirst();
this.process = cache.get(ProcessDescriptor.class, refProcessId);
this.result = result;
}
public void setProcess(ProcessDescriptor process) {
this.process = process;
}
private double getUpstreamContribution(FlowDescriptor flow) {
if (process == null || flow == null)
return 0;
double total = result.getTotalFlowResult(flow).value;
if (total == 0)
return 0;
double val = result.getUpstreamFlowResult(process, flow).value;
double c = val / Math.abs(total);
return c > 1 ? 1 : c;
}
private double getDirectResult(FlowDescriptor flow) {
if (process == null || flow == null)
return 0;
return result.getSingleFlowResult(process, flow).value;
}
private double getUpstreamTotal(FlowDescriptor flow) {
if (process == null || flow == null)
return 0;
return result.getUpstreamFlowResult(process, flow).value;
}
private double getUpstreamContribution(ImpactCategoryDescriptor d) {
if (process == null || d == null)
return 0;
double total = result.getTotalImpactResult(d).value;
if (total == 0)
return 0;
double val = result.getUpstreamImpactResult(process, d).value;
double c = val / Math.abs(total);
return c > 1 ? 1 : c;
}
private double getDirectResult(ImpactCategoryDescriptor category) {
if (process == null || category == null)
return 0;
return result.getSingleImpactResult(process, category).value;
}
private double getUpstreamTotal(ImpactCategoryDescriptor category) {
if (process == null || category == null)
return 0;
return result.getUpstreamImpactResult(process, category).value;
}
}
}