package org.openlca.app.results;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
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.rcp.images.Images;
import org.openlca.app.results.ContributionCutoff.CutoffContentProvider;
import org.openlca.app.util.Actions;
import org.openlca.app.util.DQUI;
import org.openlca.app.util.Labels;
import org.openlca.app.util.Numbers;
import org.openlca.app.util.UI;
import org.openlca.app.util.trees.TreeClipboard;
import org.openlca.app.util.trees.Trees;
import org.openlca.app.util.viewers.Viewers;
import org.openlca.core.database.EntityCache;
import org.openlca.core.math.data_quality.DQResult;
import org.openlca.core.model.descriptors.FlowDescriptor;
import org.openlca.core.model.descriptors.ProcessDescriptor;
import org.openlca.core.results.ContributionItem;
import org.openlca.core.results.ContributionResultProvider;
import org.openlca.core.results.FlowResult;
import org.openlca.util.Strings;
/**
* Shows the total inventory result of a quick calculation, analysis result,
* etc.
*/
public class TotalFlowResultPage extends FormPage {
private EntityCache cache = Cache.getEntityCache();
private FormToolkit toolkit;
private ContributionResultProvider<?> result;
private DQResult dqResult;
public TotalFlowResultPage(FormEditor editor, ContributionResultProvider<?> result, DQResult dqResult) {
super(editor, "InventoryPage", M.InventoryResults);
this.result = result;
this.dqResult = dqResult;
}
@Override
protected void createFormContent(IManagedForm mform) {
ScrolledForm form = UI.formHeader(mform, M.InventoryResults);
toolkit = mform.getToolkit();
Composite body = UI.formBody(form, toolkit);
TreeViewer inputTree = createSectionAndViewer(body, true);
TreeViewer outputTree = createSectionAndViewer(body, false);
TotalRequirementsSection reqSection = new TotalRequirementsSection(result, dqResult);
reqSection.create(body, toolkit);
form.reflow(true);
fillTrees(inputTree, outputTree);
reqSection.fill();
}
private void fillTrees(TreeViewer inputTree, TreeViewer outputTree) {
Collection<FlowDescriptor> flows = result.getFlowDescriptors();
List<FlowDescriptor> inFlows = new ArrayList<>();
List<FlowDescriptor> outFlows = new ArrayList<>();
for (FlowDescriptor flow : flows) {
if (result.isInput(flow)) {
inFlows.add(flow);
} else {
outFlows.add(flow);
}
}
Comparator<FlowDescriptor> sorter = (f1, f2) -> Strings.compare(
f1.getName(), f2.getName());
Collections.sort(inFlows, sorter);
Collections.sort(outFlows, sorter);
inputTree.setInput(inFlows);
outputTree.setInput(outFlows);
}
private TreeViewer createSectionAndViewer(Composite parent, boolean forInputs) {
Section section = UI.section(parent, toolkit, forInputs ? M.Inputs : M.Outputs);
UI.gridData(section, true, true);
Composite comp = UI.sectionClient(section, toolkit);
UI.gridLayout(comp, 1);
String[] headers = new String[] { M.Name, M.Category, M.SubCategory, M.Amount, M.Unit };
if (DQUI.displayExchangeQuality(dqResult)) {
headers = DQUI.appendTableHeaders(headers, dqResult.setup.exchangeDqSystem);
}
ContributionCutoff spinner = ContributionCutoff.create(comp, toolkit);
Label label = new Label();
TreeViewer viewer = Trees.createViewer(comp, headers, label);
viewer.setContentProvider(new ContentProvider());
createColumnSorters(viewer, label);
double[] widths = { .4, .2, .2, .15, .05 };
if (DQUI.displayExchangeQuality(dqResult)) {
widths = DQUI.adjustTableWidths(widths, dqResult.setup.exchangeDqSystem);
}
viewer.getTree().getColumns()[3].setAlignment(SWT.RIGHT);
Trees.bindColumnWidths(viewer.getTree(), DQUI.MIN_COL_WIDTH, widths);
Actions.bind(viewer, TreeClipboard.onCopy(viewer));
spinner.register(viewer);
return viewer;
}
private void createColumnSorters(TreeViewer viewer, Label label) {
Viewers.sortByLabels(viewer, label, 0, 1, 2, 4);
Viewers.sortByDouble(viewer, this::getAmount, 3);
if (DQUI.displayExchangeQuality(dqResult)) {
for (int i = 0; i < dqResult.setup.exchangeDqSystem.indicators.size(); i++) {
Viewers.sortByDouble(viewer, label, i + 5);
}
}
}
private class ContentProvider extends ArrayContentProvider implements ITreeContentProvider, CutoffContentProvider {
private double cutoff;
@Override
public Object[] getChildren(Object e) {
if (!(e instanceof FlowDescriptor))
return null;
FlowDescriptor flow = (FlowDescriptor) e;
double cutoffValue = getAmount(flow) * this.cutoff;
return result.getProcessContributions(flow).contributions.stream()
.filter(i -> i.amount != 0)
.filter(i -> Math.abs(i.amount) >= cutoffValue)
.sorted((i1, i2) -> -Double.compare(i1.amount, i2.amount))
.map(i -> new Contribution(i, flow))
.collect(Collectors.toList())
.toArray();
}
@Override
public Object getParent(Object e) {
if (e instanceof Contribution)
return ((Contribution) e).flow;
return null;
}
@Override
public boolean hasChildren(Object e) {
if (e instanceof FlowDescriptor)
return true;
return false;
}
@Override
public void setCutoff(double cutoff) {
this.cutoff = cutoff;
}
}
private class Label extends DQLabelProvider {
private ContributionImage img = new ContributionImage(Display.getCurrent());
Label() {
super(dqResult, dqResult != null ? dqResult.setup.exchangeDqSystem : null, 5);
}
@Override
public void dispose() {
img.dispose();
super.dispose();
}
@Override
public Image getImage(Object obj, int col) {
if (col == 0 && obj instanceof FlowDescriptor)
return Images.get((FlowDescriptor) obj);
if (!(obj instanceof Contribution))
return null;
Contribution c = (Contribution) obj;
if (col == 0)
return Images.get(c.item.item);
if (col == 3)
return img.getForTable(c.item.share);
return null;
}
@Override
public String getText(Object obj, int col) {
if (obj instanceof FlowDescriptor)
return getFlowColumnText((FlowDescriptor) obj, col);
if (obj instanceof Contribution)
return getProcessColumnText((Contribution) obj, col);
return null;
}
private String getFlowColumnText(FlowDescriptor flow, int col) {
Pair<String, String> category = Labels.getCategory(flow, cache);
switch (col) {
case 0:
return Labels.getDisplayName(flow);
case 1:
return category.getLeft();
case 2:
return category.getRight();
case 3:
double v = getAmount(flow);
return Numbers.format(v);
case 4:
return Labels.getRefUnit(flow, cache);
default:
return null;
}
}
private String getProcessColumnText(Contribution item, int col) {
ProcessDescriptor process = item.item.item;
Pair<String, String> category = Labels.getCategory(process, cache);
switch (col) {
case 0:
return Labels.getDisplayName(process);
case 1:
return category.getLeft();
case 2:
return category.getRight();
case 3:
double v = getAmount(item);
return Numbers.format(v);
case 4:
return Labels.getRefUnit(item.flow, cache);
default:
return null;
}
}
@Override
protected double[] getQuality(Object obj) {
if (obj instanceof FlowDescriptor) {
FlowDescriptor flow = (FlowDescriptor) obj;
return dqResult.get(flow);
}
if (obj instanceof Contribution) {
Contribution item = (Contribution) obj;
return dqResult.get(item.item.item, item.flow);
}
return null;
}
}
private double getAmount(Object element) {
if (element instanceof FlowDescriptor) {
FlowResult r = result.getTotalFlowResult((FlowDescriptor) element);
return r == null ? 0 : r.value;
} else if (element instanceof Contribution) {
Contribution item = (Contribution) element;
return item.item.amount;
}
return 0d;
}
private class Contribution {
final ContributionItem<ProcessDescriptor> item;
final FlowDescriptor flow;
private Contribution(ContributionItem<ProcessDescriptor> item, FlowDescriptor flow) {
this.item = item;
this.flow = flow;
}
}
}