package org.openlca.app.editors.lcia_methods; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; import org.eclipse.jface.action.Action; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.TableItem; import org.eclipse.ui.forms.widgets.Section; import org.openlca.app.M; import org.openlca.app.components.ModelSelectionDialog; import org.openlca.app.components.UncertaintyCellEditor; import org.openlca.app.db.Database; 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.Error; import org.openlca.app.util.Labels; import org.openlca.app.util.UncertaintyLabel; 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.table.modify.ComboBoxCellModifier; import org.openlca.app.viewers.table.modify.ModifySupport; import org.openlca.app.viewers.table.modify.TextCellModifier; import org.openlca.core.database.IDatabase; import org.openlca.core.model.Flow; import org.openlca.core.model.FlowProperty; import org.openlca.core.model.FlowPropertyFactor; import org.openlca.core.model.ImpactCategory; import org.openlca.core.model.ImpactFactor; import org.openlca.core.model.ModelType; import org.openlca.core.model.Unit; import org.openlca.core.model.descriptors.BaseDescriptor; import org.openlca.io.CategoryPath; import org.openlca.util.Strings; class ImpactFactorTable { private final String FLOW = M.Flow; private final String CATEGORY = M.Category; private final String FLOW_PROPERTY = M.FlowProperty; private final String UNIT = M.Unit; private final String FACTOR = M.Factor; private final String UNCERTAINTY = M.Uncertainty; private boolean showFormulas = true; private IDatabase database = Database.get(); private ImpactMethodEditor editor; private ImpactCategory category; private TableViewer viewer; public ImpactFactorTable(ImpactMethodEditor editor) { this.editor = editor; editor.getParameterSupport().afterEvaluation(this::refresh); } void refresh() { viewer.refresh(); } public void render(Composite parent, Section section) { viewer = Tables.createViewer(parent, new String[] { FLOW, CATEGORY, FLOW_PROPERTY, FACTOR, UNIT, UNCERTAINTY }); FactorLabelProvider label = new FactorLabelProvider(); Viewers.sortByLabels(viewer, label, 0, 1, 2, 4, 5); Viewers.sortByDouble(viewer, (ImpactFactor f) -> f.getValue(), 3); viewer.setLabelProvider(label); Tables.bindColumnWidths(viewer, 0.2, 0.2, 0.15, 0.15, 0.15, 0.15); ModifySupport<ImpactFactor> support = new ModifySupport<>(viewer); support.bind(FLOW_PROPERTY, new FlowPropertyModifier()); support.bind(UNIT, new UnitModifier()); support.bind(FACTOR, new ValueModifier()); support.bind(UNCERTAINTY, new UncertaintyCellEditor(viewer.getTable(), editor)); bindActions(viewer, section); viewer.getTable().getColumns()[3].setAlignment(SWT.RIGHT); } void setImpactCategory(ImpactCategory impact, boolean sort) { if (impact == null) { viewer.setInput(Collections.emptyList()); this.category = null; return; } this.category = impact; List<ImpactFactor> factors = impact.getImpactFactors(); if (sort) sortFactors(factors); viewer.setInput(factors); } private void sortFactors(List<ImpactFactor> factors) { Collections.sort(factors, (o1, o2) -> { Flow f1 = o1.getFlow(); Flow f2 = o2.getFlow(); int c = Strings.compare(f1.getName(), f2.getName()); if (c != 0) return c; String cat1 = CategoryPath.getShort(f1.getCategory()); String cat2 = CategoryPath.getShort(f2.getCategory()); return Strings.compare(cat1, cat2); }); } private void bindActions(TableViewer viewer, Section section) { Action add = Actions.onAdd(this::onAdd); Action remove = Actions.onRemove(this::onRemove); Action formulaSwitch = new FormulaSwitchAction(); Action copy = TableClipboard.onCopy(viewer); Actions.bind(section, add, remove, formulaSwitch); Actions.bind(viewer, add, remove, copy); Tables.onDeletePressed(viewer, (e) -> onRemove()); Tables.addDropSupport(viewer, (descriptors) -> createFactors(descriptors)); Tables.onDoubleClick(viewer, (event) -> { TableItem item = Tables.getItem(viewer, event); if (item == null) onAdd(); }); } private void onAdd() { if (category == null) return; BaseDescriptor[] descriptors = ModelSelectionDialog.multiSelect((ModelType.FLOW)); if (descriptors != null) createFactors(Arrays.asList(descriptors)); } private void createFactors(List<BaseDescriptor> descriptors) { if (descriptors == null || descriptors.isEmpty()) return; for (BaseDescriptor descriptor : descriptors) { if (descriptors == null || descriptor.getModelType() != ModelType.FLOW) continue; Flow flow = database.createDao(Flow.class).getForId(descriptor.getId()); ImpactFactor factor = new ImpactFactor(); factor.setFlow(flow); factor.setFlowPropertyFactor(flow.getReferenceFactor()); factor.setUnit(flow.getReferenceFactor().getFlowProperty().getUnitGroup().getReferenceUnit()); factor.setValue(1d); category.getImpactFactors().add(factor); } viewer.setInput(category.getImpactFactors()); editor.setDirty(true); } private void onRemove() { if (category == null) return; List<ImpactFactor> factors = Viewers.getAllSelected(viewer); for (ImpactFactor factor : factors) category.getImpactFactors().remove(factor); viewer.setInput(category.getImpactFactors()); editor.setDirty(true); } private class FactorLabelProvider extends LabelProvider implements ITableLabelProvider { @Override public Image getColumnImage(Object o, int column) { if (column != 0) return null; if (!(o instanceof ImpactFactor)) return null; ImpactFactor f = (ImpactFactor) o; return Images.get(f.getFlow()); } @Override public String getColumnText(Object o, int col) { if (!(o instanceof ImpactFactor)) return null; ImpactFactor f = (ImpactFactor) o; switch (col) { case 0: return Labels.getDisplayName(f.getFlow()); case 1: return CategoryPath.getShort(f.getFlow().getCategory()); case 2: if (f.getFlowPropertyFactor() == null) return null; return Labels.getDisplayName(f.getFlowPropertyFactor().getFlowProperty()); case 3: if (f.getFormula() == null || !showFormulas) return Double.toString(f.getValue()); else return f.getFormula(); case 4: return getFactorUnit(f); case 5: return UncertaintyLabel.get(f.getUncertainty()); default: return null; } } private String getFactorUnit(ImpactFactor factor) { if (factor.getUnit() == null || category == null) return null; String impactUnit = category.getReferenceUnit(); if (Strings.notEmpty(impactUnit)) return impactUnit + "/" + factor.getUnit().getName(); else return "1/" + factor.getUnit().getName(); } } private class FlowPropertyModifier extends ComboBoxCellModifier<ImpactFactor, FlowProperty> { @Override protected FlowProperty[] getItems(ImpactFactor element) { List<FlowProperty> items = new ArrayList<>(); for (FlowPropertyFactor factor : element.getFlow().getFlowPropertyFactors()) items.add(factor.getFlowProperty()); return items.toArray(new FlowProperty[items.size()]); } @Override protected FlowProperty getItem(ImpactFactor element) { if (element.getFlowPropertyFactor() == null) return null; return element.getFlowPropertyFactor().getFlowProperty(); } @Override protected String getText(FlowProperty value) { return value.getName(); } @Override protected void setItem(ImpactFactor element, FlowProperty item) { if (element.getFlowPropertyFactor() == null || !Objects.equals(item, element.getFlowPropertyFactor().getFlowProperty())) { FlowPropertyFactor factor = element.getFlow().getFactor(item); element.setFlowPropertyFactor(factor); editor.setDirty(true); } } } private class UnitModifier extends ComboBoxCellModifier<ImpactFactor, Unit> { @Override protected Unit[] getItems(ImpactFactor element) { if (element.getFlowPropertyFactor() == null) return new Unit[0]; if (element.getFlowPropertyFactor().getFlowProperty() == null) return new Unit[0]; if (element.getFlowPropertyFactor().getFlowProperty().getUnitGroup() == null) return new Unit[0]; List<Unit> items = new ArrayList<>(); for (Unit unit : element.getFlowPropertyFactor().getFlowProperty().getUnitGroup().getUnits()) items.add(unit); return items.toArray(new Unit[items.size()]); } @Override protected Unit getItem(ImpactFactor element) { return element.getUnit(); } @Override protected String getText(Unit value) { return value.getName(); } @Override protected void setItem(ImpactFactor element, Unit item) { if (!Objects.equals(item, element.getUnit())) { element.setUnit(item); editor.setDirty(true); } } } private class ValueModifier extends TextCellModifier<ImpactFactor> { @Override protected String getText(ImpactFactor factor) { if (factor.getFormula() == null) return Double.toString(factor.getValue()); else return factor.getFormula(); } @Override protected void setText(ImpactFactor factor, String text) { try { double value = Double.parseDouble(text); if (value == factor.getValue() && factor.getFormula() == null) return; // nothing changed factor.setValue(value); factor.setFormula(null); editor.setDirty(true); } catch (NumberFormatException e) { try { factor.setFormula(text); editor.setDirty(true); editor.getParameterSupport().evaluate(); } catch (Exception ex) { Error.showBox(M.InvalidFormula, text + " " + M.IsInvalidFormula); } } } } private class FormulaSwitchAction extends Action { public FormulaSwitchAction() { setImageDescriptor(Icon.NUMBER.descriptor()); setText(M.ShowValues); } @Override public void run() { showFormulas = !showFormulas; if (showFormulas) { setImageDescriptor(Icon.NUMBER.descriptor()); setText(M.ShowValues); } else { setImageDescriptor(Icon.FORMULA.descriptor()); setText(M.ShowFormulas); } viewer.refresh(); } } }