package org.openlca.app.editors.processes;
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.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.forms.IManagedForm;
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.Event;
import org.openlca.app.M;
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.Controls;
import org.openlca.app.util.Error;
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.viewers.combo.AllocationMethodViewer;
import org.openlca.app.viewers.table.modify.ModifySupport;
import org.openlca.app.viewers.table.modify.TextCellModifier;
import org.openlca.core.model.AllocationFactor;
import org.openlca.core.model.AllocationMethod;
import org.openlca.core.model.Exchange;
import org.openlca.core.model.FlowType;
import org.openlca.core.model.Process;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.eventbus.Subscribe;
class AllocationPage extends FormPage {
private Logger log = LoggerFactory.getLogger(getClass());
private ProcessEditor editor;
private FormToolkit tk;
private TableViewer factorViewer;
private CausalFactorTable causalFactorTable;
public AllocationPage(ProcessEditor editor) {
super(editor, "process.AllocationPage", M.Allocation);
this.editor = editor;
editor.getEventBus().register(this);
editor.onSaved(() -> setTableInputs());
}
static Double parseFactor(String text) {
try {
double val = Double.parseDouble(text);
if (val < -0.0001 || val > 1.0001) {
Error.showBox(M.InvalidAllocationFactor,
M.InvalidAllocationFactorMessage);
return null;
}
return val;
} catch (Exception e) {
Error.showBox(M.InvalidNumber, text + " "
+ M.IsNotValidNumber);
return null;
}
}
@Subscribe
public void handleExchangesChange(Event event) {
if (!event.match(editor.EXCHANGES_CHANGED))
return;
log.trace("update allocation page");
AllocationSync.updateFactors(process());
setTableInputs();
}
private void setTableInputs() {
if (factorViewer != null)
factorViewer.setInput(Processes.getOutputProducts(process()));
if (causalFactorTable != null)
causalFactorTable.refresh();
}
@Override
protected void createFormContent(IManagedForm managedForm) {
ScrolledForm form = UI.formHeader(managedForm, M.Allocation);
tk = managedForm.getToolkit();
Composite body = UI.formBody(form, tk);
Composite composite = UI.formComposite(body, tk);
createDefaultCombo(composite);
createCalcButton(composite);
createPhysicalEconomicSection(body);
createCausalSection(body);
form.reflow(true);
causalFactorTable.setInitialInput();
}
private void createDefaultCombo(Composite composite) {
UI.formLabel(composite, tk, M.DefaultMethod);
AllocationMethod[] methods = { AllocationMethod.NONE,
AllocationMethod.CAUSAL, AllocationMethod.ECONOMIC,
AllocationMethod.PHYSICAL, };
AllocationMethodViewer viewer = new AllocationMethodViewer(composite,
methods);
AllocationMethod selected = process().getDefaultAllocationMethod();
if (selected == null)
selected = AllocationMethod.NONE;
viewer.select(selected);
viewer.addSelectionChangedListener((selection) -> {
process().setDefaultAllocationMethod(selection);
editor.setDirty(true);
});
}
private void createCalcButton(Composite comp) {
UI.filler(comp, tk);
Button button = tk.createButton(comp, M.CalculateDefaultValues, SWT.NONE);
button.setImage(Icon.RUN.get());
Controls.onSelect(button, e -> {
AllocationSync.calculateDefaults(process());
factorViewer.refresh();
causalFactorTable.refresh();
editor.setDirty(true);
});
}
private void createPhysicalEconomicSection(Composite body) {
Section section = UI.section(body, tk,
M.PhysicalAndEconomicAllocation);
Composite composite = UI.sectionClient(section, tk);
UI.gridLayout(composite, 1);
String[] colNames = { M.Product, M.Physical,
M.Economic };
factorViewer = Tables.createViewer(composite, colNames);
Tables.bindColumnWidths(factorViewer, 0.3, 0.3, 0.3);
factorViewer.setLabelProvider(new FactorLabel());
factorViewer.setInput(Processes.getOutputProducts(process()));
ModifySupport<Exchange> modifySupport = new ModifySupport<>(
factorViewer);
modifySupport.bind(M.Physical, new ValueModifier(
AllocationMethod.PHYSICAL));
modifySupport.bind(M.Economic, new ValueModifier(
AllocationMethod.ECONOMIC));
Action copy = TableClipboard.onCopy(factorViewer);
Actions.bind(factorViewer, copy);
factorViewer.getTable().getColumns()[1].setAlignment(SWT.RIGHT);
factorViewer.getTable().getColumns()[2].setAlignment(SWT.RIGHT);
}
private void createCausalSection(Composite body) {
Section section = UI.section(body, tk, M.CausalAllocation);
UI.gridData(section, true, true);
causalFactorTable = new CausalFactorTable(editor);
causalFactorTable.render(section, tk);
}
private String productText(Exchange exchange) {
String text = Labels.getDisplayName(exchange.getFlow());
text += " (" + Numbers.format(exchange.getAmountValue(), 2) + " "
+ exchange.getUnit().getName() + ")";
return text;
}
private Process process() {
return editor.getModel();
}
private AllocationFactor getFactor(Exchange exchange,
AllocationMethod method) {
if (exchange == null || method == null)
return null;
AllocationFactor factor = null;
for (AllocationFactor f : process().getAllocationFactors()) {
if (f.getAllocationType() == method
&& f.getProductId() == exchange.getFlow().getId()) {
factor = f;
break;
}
}
return factor;
}
private String getFactorLabel(Exchange exchange, AllocationMethod method) {
AllocationFactor factor = getFactor(exchange, method);
if (factor == null)
return Double.toString(1);
else
return Double.toString(factor.getValue());
}
private class FactorLabel extends LabelProvider implements
ITableLabelProvider {
@Override
public String getColumnText(Object element, int col) {
if (!(element instanceof Exchange))
return null;
Exchange exchange = (Exchange) element;
switch (col) {
case 0:
return productText(exchange);
case 1:
return getFactorLabel(exchange, AllocationMethod.PHYSICAL);
case 2:
return getFactorLabel(exchange, AllocationMethod.ECONOMIC);
default:
return null;
}
}
@Override
public Image getColumnImage(Object element, int col) {
if (col == 0)
return Images.get(FlowType.PRODUCT_FLOW);
return null;
}
}
private class ValueModifier extends TextCellModifier<Exchange> {
private AllocationMethod method;
public ValueModifier(AllocationMethod method) {
this.method = method;
}
@Override
protected String getText(Exchange exchange) {
return getFactorLabel(exchange, method);
}
@Override
protected void setText(Exchange exchange, String text) {
Double val = parseFactor(text);
if (val == null)
return;
AllocationFactor factor = getFactor(exchange, method);
if (factor == null) {
factor = new AllocationFactor();
factor.setAllocationType(method);
factor.setProductId(exchange.getFlow().getId());
process().getAllocationFactors().add(factor);
}
factor.setValue(val);
editor.setDirty(true);
}
@Override
public boolean canModify(Exchange element) {
return Processes.getOutputProducts(process()).size() > 1;
}
}
}