package org.openlca.app.editors.processes.exchanges;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.eclipse.jface.action.Action;
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.widgets.Composite;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.openlca.app.App;
import org.openlca.app.M;
import org.openlca.app.components.ModelSelectionDialog;
import org.openlca.app.components.UncertaintyCellEditor;
import org.openlca.app.db.Cache;
import org.openlca.app.db.Database;
import org.openlca.app.editors.processes.ProcessEditor;
import org.openlca.app.rcp.images.Icon;
import org.openlca.app.util.Actions;
import org.openlca.app.util.Error;
import org.openlca.app.util.Labels;
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.table.modify.CheckBoxCellModifier;
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.EntityCache;
import org.openlca.core.database.FlowDao;
import org.openlca.core.database.IDatabase;
import org.openlca.core.model.Exchange;
import org.openlca.core.model.Flow;
import org.openlca.core.model.FlowPropertyFactor;
import org.openlca.core.model.FlowType;
import org.openlca.core.model.ModelType;
import org.openlca.core.model.Process;
import org.openlca.core.model.Unit;
import org.openlca.core.model.descriptors.BaseDescriptor;
import org.openlca.core.model.descriptors.FlowDescriptor;
import org.openlca.core.model.descriptors.ProcessDescriptor;
import org.openlca.util.Strings;
/**
* The table for the display and editing of inputs or outputs of process
* exchanges. Avoided products are inputs that are shown on the output site in
* this table.
*
*/
class ExchangeTable {
private final boolean forInputs;
private final ProcessEditor editor;
private IDatabase database = Database.get();
private EntityCache cache = Cache.getEntityCache();
private final String FLOW = M.Flow;
private final String CATEGORY = M.Category;
private final String AMOUNT = M.Amount;
private final String UNIT = M.Unit;
private final String COSTS;
private final String PEDIGREE = M.DataQualityEntry;
private final String DEFAULT_PROVIDER = M.DefaultProvider;
private final String UNCERTAINTY = M.Uncertainty;
private final String DESCRIPTION = M.Description;
private final String AVOIDED_PRODUCT = M.AvoidedProduct;
private TableViewer viewer;
private ExchangeLabel label;
public static ExchangeTable forInputs(Section section, FormToolkit toolkit,
ProcessEditor editor) {
ExchangeTable table = new ExchangeTable(true, editor);
table.render(section, toolkit);
return table;
}
public static ExchangeTable forOutputs(Section section,
FormToolkit toolkit, ProcessEditor editor) {
ExchangeTable table = new ExchangeTable(false, editor);
table.render(section, toolkit);
return table;
}
private ExchangeTable(boolean forInputs, ProcessEditor editor) {
this.forInputs = forInputs;
this.COSTS = forInputs ? M.Costs : M.CostsRevenues;
this.editor = editor;
editor.getParameterSupport().afterEvaluation(() -> viewer.refresh());
}
private void render(Section section, FormToolkit toolkit) {
Composite composite = UI.sectionClient(section, toolkit);
UI.gridLayout(composite, 1);
viewer = Tables.createViewer(composite, getColumns());
label = new ExchangeLabel(editor, forInputs);
viewer.setLabelProvider(label.asColumnLabel());
bindModifiers();
Tables.addDropSupport(viewer, this::add);
viewer.addFilter(new Filter());
bindActions(section, viewer);
bindDoubleClick(viewer);
Tables.bindColumnWidths(viewer, 0.2, 0.15, 0.1, 0.09, 0.09, 0.09, 0.09,
0.09, 0.05);
Viewers.sortByLabels(viewer, label, 0, 1, 3, 4, 5, 6, 7, 8);
Viewers.sortByDouble(viewer, (Exchange e) -> e.getAmountValue(), 2);
viewer.getTable().getColumns()[2].setAlignment(SWT.RIGHT);
viewer.getTable().getColumns()[4].setAlignment(SWT.RIGHT);
}
void setInput(Process process) {
viewer.setInput(process.getExchanges());
}
private void bindModifiers() {
ModifySupport<Exchange> ms = new ModifySupport<>(viewer);
ms.bind(AMOUNT, new AmountModifier());
ms.bind(UNIT, new UnitCell(editor));
ms.bind(COSTS, new CostCellEditor(viewer, editor));
ms.bind(PEDIGREE, new DataQualityCellEditor(viewer, editor));
ms.bind(UNCERTAINTY, new UncertaintyCellEditor(viewer.getTable(),
editor));
ms.bind(DESCRIPTION, new CommentEditor(viewer, editor));
if (forInputs)
ms.bind(DEFAULT_PROVIDER, new ProviderModifier());
if (!forInputs)
ms.bind(AVOIDED_PRODUCT, new AvoidedProductModifier());
}
private void bindActions(Section section, final TableViewer viewer) {
Action add = Actions.onAdd(() -> onAdd());
Action remove = Actions.onRemove(() -> onRemove());
Action formulaSwitch = new FormulaSwitchAction();
Action clipboard = TableClipboard.onCopy(viewer);
Actions.bind(section, add, remove, formulaSwitch);
Actions.bind(viewer, add, remove, clipboard);
Tables.onDeletePressed(viewer, e -> onRemove());
}
private void bindDoubleClick(TableViewer table) {
Tables.onDoubleClick(table, e -> {
TableItem item = Tables.getItem(table, e);
if (item == null) {
onAdd();
return;
}
Exchange exchange = Viewers.getFirstSelected(table);
if (exchange != null && exchange.getFlow() != null)
App.openEditor(exchange.getFlow());
});
}
private String[] getColumns() {
if (forInputs)
return new String[] { FLOW, CATEGORY, AMOUNT, UNIT, COSTS,
UNCERTAINTY, DEFAULT_PROVIDER, PEDIGREE, DESCRIPTION };
else
return new String[] { FLOW, CATEGORY, AMOUNT, UNIT, COSTS,
UNCERTAINTY, AVOIDED_PRODUCT, PEDIGREE, DESCRIPTION };
}
private void onAdd() {
BaseDescriptor[] descriptors = ModelSelectionDialog
.multiSelect(ModelType.FLOW);
if (descriptors != null)
add(Arrays.asList(descriptors));
}
private void onRemove() {
Process process = editor.getModel();
List<Exchange> selection = Viewers.getAllSelected(viewer);
if (!Exchanges.canRemove(process, selection))
return;
selection.forEach(e -> process.getExchanges().remove(e));
viewer.setInput(process.getExchanges());
editor.setDirty(true);
editor.postEvent(editor.EXCHANGES_CHANGED, this);
}
private void add(List<BaseDescriptor> descriptors) {
if (descriptors.isEmpty())
return;
Process process = editor.getModel();
for (BaseDescriptor descriptor : descriptors) {
if (!(descriptor instanceof FlowDescriptor))
continue;
Exchange e = new Exchange();
FlowDao flowDao = new FlowDao(Database.get());
Flow flow = flowDao.getForId(descriptor.getId());
e.setFlow(flow);
e.setFlowPropertyFactor(flow.getReferenceFactor());
Unit unit = getUnit(flow.getReferenceFactor());
e.setUnit(unit);
e.setAmountValue(1.0);
e.setInput(forInputs);
process.getExchanges().add(e);
}
viewer.setInput(process.getExchanges());
editor.setDirty(true);
editor.postEvent(editor.EXCHANGES_CHANGED, this);
}
private Unit getUnit(FlowPropertyFactor factor) {
if (factor == null)
return null;
if (factor.getFlowProperty() == null)
return null;
if (factor.getFlowProperty().getUnitGroup() == null)
return null;
return factor.getFlowProperty().getUnitGroup().getReferenceUnit();
}
private class AmountModifier extends TextCellModifier<Exchange> {
@Override
protected String getText(Exchange e) {
if (e.getAmountFormula() == null)
return Double.toString(e.getAmountValue());
return e.getAmountFormula();
}
@Override
protected void setText(Exchange exchange, String text) {
try {
double value = Double.parseDouble(text);
exchange.setAmountFormula(null);
exchange.setAmountValue(value);
editor.setDirty(true);
} catch (NumberFormatException e) {
try {
exchange.setAmountFormula(text);
editor.setDirty(true);
editor.getParameterSupport().evaluate();
} catch (Exception ex) {
Error.showBox(M.InvalidFormula, text + " "
+ M.IsInvalidFormula);
}
}
}
}
private class ProviderModifier extends
ComboBoxCellModifier<Exchange, ProcessDescriptor> {
@Override
public boolean canModify(Exchange e) {
return e.isInput() && e.getFlow() != null
&& e.getFlow().getFlowType() == FlowType.PRODUCT_FLOW;
}
@Override
protected ProcessDescriptor[] getItems(Exchange e) {
if (e.getFlow() == null)
return new ProcessDescriptor[0];
FlowDao dao = new FlowDao(database);
Set<Long> providerIds = dao.getProviders(e.getFlow().getId());
Collection<ProcessDescriptor> list = cache.getAll(
ProcessDescriptor.class, providerIds).values();
ProcessDescriptor[] providers = list.toArray(
new ProcessDescriptor[list.size()]);
Arrays.sort(providers, (p1, p2) -> Strings.compare(
Labels.getDisplayName(p1), Labels.getDisplayName(p2)));
return providers;
}
@Override
protected ProcessDescriptor getItem(Exchange e) {
if (e.getDefaultProviderId() == 0)
return null;
return cache.get(ProcessDescriptor.class, e.getDefaultProviderId());
}
@Override
protected String getText(ProcessDescriptor d) {
if (d == null)
return M.None;
return Labels.getDisplayName(d);
}
@Override
protected void setItem(Exchange e, ProcessDescriptor d) {
if (d == null)
e.setDefaultProviderId(0);
else
e.setDefaultProviderId(d.getId());
editor.setDirty(true);
}
}
private class AvoidedProductModifier extends CheckBoxCellModifier<Exchange> {
@Override
public boolean canModify(Exchange e) {
Process process = editor.getModel();
if (Objects.equals(process.getQuantitativeReference(), e))
return false;
if (e.getFlow() == null)
return false;
if (e.getFlow().getFlowType() != FlowType.PRODUCT_FLOW)
return false;
return true;
}
@Override
protected boolean isChecked(Exchange e) {
return e.isAvoidedProduct();
}
@Override
protected void setChecked(Exchange e, boolean value) {
if (e.isAvoidedProduct() == value)
return;
e.setAvoidedProduct(value);
e.setInput(value);
editor.setDirty(true);
}
}
private class Filter extends ViewerFilter {
@Override
public boolean select(Viewer viewer, Object parent, Object obj) {
if (!(obj instanceof Exchange))
return false;
Exchange e = (Exchange) obj;
if (e.isAvoidedProduct())
return !forInputs;
else
return e.isInput() == forInputs;
}
}
private class FormulaSwitchAction extends Action {
private boolean showFormulas = true;
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);
}
label.showFormulas = showFormulas;
viewer.refresh();
}
}
}