package org.openlca.app.results.contributions; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.viewers.BaseLabelProvider; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerSorter; 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.swt.widgets.Menu; 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.openlca.app.App; import org.openlca.app.M; import org.openlca.app.components.ContributionImage; import org.openlca.app.components.ResultTypeSelection; import org.openlca.app.components.ResultTypeSelection.EventHandler; import org.openlca.app.db.Cache; import org.openlca.app.rcp.images.Icon; import org.openlca.app.rcp.images.Images; import org.openlca.app.util.Actions; import org.openlca.app.util.CostResultDescriptor; import org.openlca.app.util.CostResults; import org.openlca.app.util.Labels; import org.openlca.app.util.Numbers; import org.openlca.app.util.UI; import org.openlca.app.util.trees.Trees; import org.openlca.app.util.viewers.Viewers; import org.openlca.core.database.EntityCache; import org.openlca.core.matrix.LongPair; import org.openlca.core.model.ModelType; import org.openlca.core.model.descriptors.BaseDescriptor; 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; import org.openlca.core.results.UpstreamTree; import org.openlca.core.results.UpstreamTreeNode; public class ContributionTreePage extends FormPage { private EntityCache cache = Cache.getEntityCache(); private Map<Long, ProcessDescriptor> processDescriptors = new HashMap<>(); private FullResultProvider result; private TreeViewer tree; private Object selection; private static final String[] HEADERS = { M.Contribution, M.Process, M.Amount, M.Unit }; public ContributionTreePage(FormEditor editor, FullResultProvider result) { super(editor, "analysis.ContributionTreePage", M.ContributionTree); this.result = result; for (ProcessDescriptor desc : result.getProcessDescriptors()) processDescriptors.put(desc.getId(), desc); Iterator<FlowDescriptor> it = result.getFlowDescriptors().iterator(); if (it.hasNext()) selection = it.next(); } @Override protected void createFormContent(IManagedForm managedForm) { FormToolkit toolkit = managedForm.getToolkit(); ScrolledForm form = UI.formHeader(managedForm, M.ContributionTree); Composite body = UI.formBody(form, toolkit); Composite composite = toolkit.createComposite(body); UI.gridLayout(composite, 2); ResultTypeSelection selector = ResultTypeSelection .on(result, Cache.getEntityCache()) .withEventHandler(new SelectionHandler()) .create(composite, toolkit); Composite treeContainer = toolkit.createComposite(body); UI.gridLayout(treeContainer, 1); UI.gridData(treeContainer, true, true); createTree(toolkit, treeContainer); form.reflow(true); selector.selectWithEvent(selection); } private void createTree(FormToolkit toolkit, Composite treeContainer) { tree = Trees.createViewer(treeContainer, HEADERS, new ContributionLabelProvider()); tree.setAutoExpandLevel(2); tree.getTree().setLinesVisible(false); tree.setContentProvider(new ContributionContentProvider()); tree.setSorter(new ContributionSorter()); toolkit.adapt(tree.getTree(), false, false); toolkit.paintBordersFor(tree.getTree()); createMenu(); Trees.bindColumnWidths(tree.getTree(), 0.20, 0.50, 0.20, 0.10); tree.getTree().getColumns()[2].setAlignment(SWT.RIGHT); } private void createMenu() { MenuManager mm = new MenuManager(); mm.add(new OpenEditorAction()); mm.add(TreeClipboard.onCopy(tree)); mm.add(Actions.create(M.ExpandAll, Icon.EXPAND.descriptor(), () -> { tree.expandAll(); })); Menu menu = mm.createContextMenu(tree.getControl()); tree.getControl().setMenu(menu); } private class SelectionHandler implements EventHandler { @Override public void flowSelected(FlowDescriptor flow) { selection = flow; UpstreamTree model = result.getTree(flow); tree.setInput(model); } @Override public void impactCategorySelected( ImpactCategoryDescriptor impactCategory) { selection = impactCategory; UpstreamTree model = result.getTree(impactCategory); tree.setInput(model); } @Override public void costResultSelected(CostResultDescriptor cost) { selection = cost; UpstreamTree model = result.getCostTree(); if (cost.forAddedValue) CostResults.forAddedValues(model); tree.setInput(model); } } private class ContributionContentProvider implements ITreeContentProvider { @Override public Object[] getChildren(Object parent) { if (!(parent instanceof UpstreamTreeNode)) return null; UpstreamTreeNode node = (UpstreamTreeNode) parent; return node.getChildren().toArray(); } @Override public Object[] getElements(Object inputElement) { if (!(inputElement instanceof UpstreamTree)) return null; return new Object[] { ((UpstreamTree) inputElement).getRoot() }; } @Override public Object getParent(Object element) { return null; } @Override public boolean hasChildren(Object element) { if (!(element instanceof UpstreamTreeNode)) return false; UpstreamTreeNode node = (UpstreamTreeNode) element; return !node.getChildren().isEmpty(); } @Override public void inputChanged(Viewer viewer, Object old, Object newInput) { } @Override public void dispose() { } } private class ContributionLabelProvider extends BaseLabelProvider implements ITableLabelProvider { private ContributionImage image = new ContributionImage( Display.getCurrent()); @Override public void dispose() { image.dispose(); super.dispose(); } @Override public Image getColumnImage(Object element, int columnIndex) { if (!(element instanceof UpstreamTreeNode) || element == null) return null; if (columnIndex != 1) return null; UpstreamTreeNode node = (UpstreamTreeNode) element; return image.getForTable(getContribution(node)); } @Override public String getColumnText(Object element, int columnIndex) { if (!(element instanceof UpstreamTreeNode)) return null; UpstreamTreeNode node = (UpstreamTreeNode) element; switch (columnIndex) { case 0: return Numbers.percent(getContribution(node)); case 1: long processId = node.getProcessProduct().getFirst(); BaseDescriptor d = processDescriptors.get(processId); return Labels.getDisplayName(d); case 2: return Numbers.format(getSingleAmount(node)); case 3: return getUnit(); default: return null; } } private String getUnit() { if (selection instanceof FlowDescriptor) { FlowDescriptor flow = (FlowDescriptor) selection; return Labels.getRefUnit(flow, cache); } else if (selection instanceof ImpactCategoryDescriptor) { ImpactCategoryDescriptor impact = (ImpactCategoryDescriptor) selection; return impact.getReferenceUnit(); } else if (selection instanceof CostResultDescriptor) { return Labels.getReferenceCurrencyCode(); } return null; } private double getTotalAmount() { return ((UpstreamTree) tree.getInput()).getRoot().getAmount(); } private double getContribution(UpstreamTreeNode node) { double singleResult = getSingleAmount(node); if (singleResult == 0) return 0; double referenceResult = Math.abs(getTotalAmount()); if (referenceResult == 0) return 0; double contribution = singleResult / referenceResult; return contribution; } private double getSingleAmount(UpstreamTreeNode node) { return node.getAmount(); } } private class ContributionSorter extends ViewerSorter { @Override public int compare(Viewer viewer, Object e1, Object e2) { if (!(e1 instanceof UpstreamTreeNode && e2 instanceof UpstreamTreeNode) || e1 == null || e2 == null) return 0; UpstreamTreeNode node1 = (UpstreamTreeNode) e1; UpstreamTreeNode node2 = (UpstreamTreeNode) e2; return -1 * Double.compare(node1.getAmount(), node2.getAmount()); } } private class OpenEditorAction extends Action { public OpenEditorAction() { setText(M.Open); setImageDescriptor(Images.descriptor(ModelType.PROCESS)); } @Override public void run() { Object selection = Viewers.getFirstSelected(tree); if (!(selection instanceof UpstreamTreeNode)) return; UpstreamTreeNode node = (UpstreamTreeNode) selection; LongPair processProduct = node.getProcessProduct(); ProcessDescriptor process = processDescriptors.get(processProduct.getFirst()); if (process != null) App.openEditor(process); } } }