package org.openlca.app.editors.parameters;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.function.Supplier;
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.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.openlca.app.M;
import org.openlca.app.components.UncertaintyCellEditor;
import org.openlca.app.editors.ModelEditor;
import org.openlca.app.util.Actions;
import org.openlca.app.util.Error;
import org.openlca.app.rcp.images.Images;
import org.openlca.app.util.Question;
import org.openlca.app.util.UI;
import org.openlca.app.util.UncertaintyLabel;
import org.openlca.app.util.Warning;
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.app.viewers.table.modify.field.DoubleModifier;
import org.openlca.app.viewers.table.modify.field.StringModifier;
import org.openlca.core.model.CategorizedEntity;
import org.openlca.core.model.ModelType;
import org.openlca.core.model.Parameter;
import org.openlca.core.model.ParameterScope;
import org.openlca.util.Strings;
/**
* A section with a table for parameters in processes, LCIA methods, and global
* parameters. It is possible to create two kinds of tables with this class: for
* input parameters with the columns name, value, and description, or for
* dependent parameters with the columns name, formula, value, and description.
*/
public class ParameterSection {
private TableViewer viewer;
private final String NAME = M.Name;
private final String VALUE = M.Value;
private final String FORMULA = M.Formula;
private final String UNCERTAINTY = M.Uncertainty;
private final String DESCRIPTION = M.Description;
private final String EXTERNAL_SOURCE = M.ExternalSource;
private boolean forInputParameters = true;
private ParameterChangeSupport support;
private ModelEditor<? extends CategorizedEntity> editor;
private Supplier<List<Parameter>> supplier;
private ParameterScope scope;
private SourceHandler sourceHandler;
public static ParameterSection forInputParameters(
ModelEditor<? extends CategorizedEntity> editor,
ParameterChangeSupport support, Composite body,
FormToolkit toolkit, SourceHandler sourceHandler) {
return new ParameterSection(editor, support, body, toolkit,
sourceHandler, true);
}
public static ParameterSection forInputParameters(
ModelEditor<? extends CategorizedEntity> editor,
ParameterChangeSupport support, Composite body, FormToolkit toolkit) {
return new ParameterSection(editor, support, body, toolkit, null, true);
}
public static ParameterSection forDependentParameters(
ModelEditor<? extends CategorizedEntity> editor,
ParameterChangeSupport support, Composite body, FormToolkit toolkit) {
return new ParameterSection(editor, support, body, toolkit, null, false);
}
private ParameterSection(ModelEditor<? extends CategorizedEntity> editor,
ParameterChangeSupport support, Composite body,
FormToolkit toolkit, SourceHandler sourceHandler,
boolean forInputParameters) {
this.forInputParameters = forInputParameters;
this.sourceHandler = sourceHandler;
this.editor = editor;
this.support = support;
String[] props = getProperties();
createComponents(body, toolkit, props);
createCellModifiers();
addDoubleClickHandler();
support.afterEvaluation(this::setInput);
editor.onSaved(this::setInput);
}
private String[] getProperties() {
if (forInputParameters)
if (sourceHandler != null)
return new String[] { NAME, VALUE, UNCERTAINTY, DESCRIPTION,
EXTERNAL_SOURCE };
else
return new String[] { NAME, VALUE, UNCERTAINTY, DESCRIPTION };
else {
if (sourceHandler != null)
return new String[] { NAME, FORMULA, VALUE, DESCRIPTION,
EXTERNAL_SOURCE };
else
return new String[] { NAME, FORMULA, VALUE, DESCRIPTION };
}
}
public void setSupplier(Supplier<List<Parameter>> supplier,
ParameterScope scope) {
this.supplier = supplier;
this.scope = scope;
fillInitialInput();
}
private void addDoubleClickHandler() {
Tables.onDoubleClick(viewer, (event) -> {
TableItem item = Tables.getItem(viewer, event);
if (item == null)
onAdd();
});
}
private void createComponents(Composite body, FormToolkit toolkit,
String[] properties) {
String title = forInputParameters ? M.InputParameters
: M.DependentParameters;
Section section = UI.section(body, toolkit, title);
UI.gridData(section, true, true);
Composite parent = UI.sectionClient(section, toolkit);
viewer = Tables.createViewer(parent, properties);
ParameterLabelProvider label = new ParameterLabelProvider();
viewer.setLabelProvider(label);
addSorters(viewer, label);
bindColumnWidths(viewer);
bindActions(section);
int col = forInputParameters ? 1 : 2;
viewer.getTable().getColumns()[col].setAlignment(SWT.RIGHT);
}
private void bindColumnWidths(TableViewer viewer) {
if (sourceHandler != null)
Tables.bindColumnWidths(viewer, 0.25, 0.25, 0.15, 0.15, 0.2);
else
Tables.bindColumnWidths(viewer, 0.3, 0.3, 0.2, 0.2);
}
private void addSorters(TableViewer table, ParameterLabelProvider label) {
if (forInputParameters) {
Viewers.sortByLabels(table, label, 0, 2, 3);
Viewers.sortByDouble(table, (Parameter p) -> p.getValue(), 1);
} else {
Viewers.sortByLabels(table, label, 0, 1, 3);
Viewers.sortByDouble(table, (Parameter p) -> p.getValue(), 2);
}
}
private void bindActions(Section section) {
Action addAction = Actions.onAdd(() -> onAdd());
Action removeAction = Actions.onRemove(() -> onRemove());
Action copy = TableClipboard.onCopy(viewer);
Action paste = TableClipboard.onPaste(viewer, this::onPaste);
Actions.bind(section, addAction, removeAction);
Actions.bind(viewer, addAction, removeAction, copy, paste);
Tables.onDeletePressed(viewer, (e) -> onRemove());
}
private void createCellModifiers() {
ModifySupport<Parameter> ms = new ModifySupport<>(viewer);
ms.bind(NAME, new NameModifier());
ms.bind(DESCRIPTION, new StringModifier<>(editor, "description"));
if (forInputParameters) {
ms.bind(VALUE, new DoubleModifier<>(editor, "value",
(elem) -> support.evaluate()));
ms.bind(UNCERTAINTY, new UncertaintyCellEditor(viewer.getTable(),
editor));
} else
ms.bind(FORMULA, new StringModifier<>(editor, "formula",
(elem) -> support.evaluate()));
if (sourceHandler != null)
ms.bind(EXTERNAL_SOURCE, new ExternalSourceModifier());
}
private void fillInitialInput() {
if (supplier == null)
return;
Collections.sort(supplier.get(),
(o1, o2) -> Strings.compare(o1.getName(), o2.getName()));
setInput();
}
private void setInput() {
if (supplier == null)
return;
List<Parameter> input = new ArrayList<>();
for (Parameter param : supplier.get()) {
if (param.isInputParameter() == forInputParameters)
input.add(param);
}
viewer.setInput(input);
}
private void onAdd() {
if (supplier == null)
return;
List<Parameter> params = supplier.get();
int count = params.size();
String name = "p_" + count++;
while (exists(name))
name = "p_" + count++;
Parameter p = new Parameter();
p.setRefId(UUID.randomUUID().toString());
p.setName(name);
p.setScope(scope);
p.setInputParameter(forInputParameters);
p.setValue(1.0);
if (!forInputParameters)
p.setFormula("1.0");
params.add(p);
setInput();
editor.setDirty(true);
}
private boolean exists(String name) {
for (Parameter parameter : supplier.get()) {
if (name == null && parameter.getName() == null)
return true;
if (name == null || parameter.getName() == null)
continue;
if (name.toLowerCase().equals(parameter.getName().toLowerCase()))
return true;
}
return false;
}
private void onRemove() {
if (supplier == null)
return;
List<Parameter> params = supplier.get();
List<Parameter> selection = Viewers.getAllSelected(viewer);
for (Parameter parameter : selection) {
params.remove(parameter);
}
setInput();
editor.setDirty(true);
support.evaluate();
}
private void onPaste(String text) {
if (supplier == null)
return;
List<Parameter> params = forInputParameters ? Clipboard
.readInputParams(text) : Clipboard.readCalculatedParams(text);
boolean skipped = false;
for (Parameter param : params) {
String name = param.getName();
if (!Parameter.isValidName(name) || exists(name)) {
skipped = true;
continue;
}
param.setScope(scope);
supplier.get().add(param);
}
if (skipped)
Warning.showBox(M.SomeParametersWereNotAdded);
setInput();
editor.setDirty(true);
support.evaluate();
}
private class ParameterLabelProvider extends LabelProvider implements
ITableLabelProvider {
@Override
public Image getColumnImage(Object element, int col) {
if (col != 0 || !(element instanceof Parameter))
return null;
Parameter parameter = (Parameter) element;
if (parameter.getExternalSource() == null)
return null;
// currently the only external sources are shape files
return Images.get(ModelType.IMPACT_METHOD);
}
@Override
public String getColumnText(Object element, int columnIndex) {
if (!(element instanceof Parameter))
return null;
Parameter parameter = (Parameter) element;
switch (columnIndex) {
case 0:
return parameter.getName();
case 1:
if (forInputParameters)
return Double.toString(parameter.getValue());
else
return parameter.getFormula();
case 2:
if (forInputParameters)
return UncertaintyLabel.get(parameter.getUncertainty());
else
return Double.toString(parameter.getValue());
case 3:
return parameter.getDescription();
case 4:
return parameter.getExternalSource();
default:
return null;
}
}
}
private class NameModifier extends TextCellModifier<Parameter> {
@Override
protected String getText(Parameter param) {
return param.getName();
}
@Override
protected void setText(Parameter param, String text) {
if (text == null)
return;
if (Objects.equals(text, param.getName()))
return;
String name = text.trim();
if (!Parameter.isValidName(name)) {
Error.showBox(M.InvalidParameterName, name + " "
+ M.IsNotValidParameterName);
return;
}
if (exists(name)) {
Error.showBox(M.InvalidParameterName,
M.ParameterWithSameNameExists);
return;
}
param.setName(name);
editor.setDirty(true);
support.evaluate();
}
}
private class ExternalSourceModifier extends
ComboBoxCellModifier<Parameter, String> {
@Override
protected String[] getItems(Parameter element) {
return sourceHandler.getSources(element);
}
@Override
protected String getItem(Parameter element) {
return element.getExternalSource();
}
@Override
protected String getText(String value) {
return value;
}
@Override
protected void setItem(Parameter element, String item) {
if (!Question.ask(M.ExternalSourceChange, M.RecalculateQuestion))
return;
element.setExternalSource(item);
sourceHandler.sourceChanged(element, item);
}
}
}