package org.openlca.app.components;
import java.sql.Connection;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.FormDialog;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.openlca.app.M;
import org.openlca.app.db.Cache;
import org.openlca.app.db.Database;
import org.openlca.app.rcp.images.Images;
import org.openlca.app.util.Labels;
import org.openlca.app.util.UI;
import org.openlca.app.util.viewers.Viewers;
import org.openlca.core.database.EntityCache;
import org.openlca.core.database.IDatabase;
import org.openlca.core.model.ModelType;
import org.openlca.core.model.ParameterRedef;
import org.openlca.core.model.descriptors.BaseDescriptor;
import org.openlca.core.model.descriptors.ImpactMethodDescriptor;
import org.openlca.core.model.descriptors.ProcessDescriptor;
import org.openlca.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A dialog for the creation of parameter redefinitions from existing global or
* process parameters.
*/
public class ParameterRedefDialog extends FormDialog {
private TreeModel model;
private Text filterText;
private TreeViewer viewer;
private IStructuredSelection selection;
/**
* Opens the dialog which contains all global parameters and parameters of
* processes and LCIA methods which IDs are contained in the set of valid
* contexts.
*/
public static List<ParameterRedef> select(Set<Long> validContexts) {
TreeModel model = loadModel(Database.get(), Cache.getEntityCache(),
validContexts);
ParameterRedefDialog dialog = new ParameterRedefDialog(UI.shell(),
model);
if (dialog.open() != OK)
return Collections.emptyList();
else
return dialog.getSelection();
}
private static TreeModel loadModel(IDatabase database, EntityCache cache,
Set<Long> validContexts) {
try (Connection con = database.createConnection()) {
List<ParameterRedef> parameters = new ArrayList<>();
String query = "select * from tbl_parameters where is_input_param = 1";
ResultSet results = con.createStatement().executeQuery(query);
while (results.next()) {
ParameterRedef redef = fetchRedef(results);
if (redef.getContextId() == null
|| validContexts.contains(redef.getContextId()))
parameters.add(redef);
}
results.close();
return buildModel(parameters, cache);
} catch (Exception e) {
Logger log = LoggerFactory.getLogger(ParameterRedefDialog.class);
log.error("Failed to load parameter from database");
return new TreeModel();
}
}
private static ParameterRedef fetchRedef(ResultSet results)
throws Exception {
ParameterRedef redef = new ParameterRedef();
redef.setName(results.getString("name"));
redef.setValue(results.getDouble("value"));
long modelId = results.getLong("f_owner");
if (!results.wasNull())
redef.setContextId(modelId);
return redef;
}
private static TreeModel buildModel(List<ParameterRedef> parameters,
EntityCache cache) {
TreeModel model = new TreeModel();
HashMap<Long, ModelNode> createdNodes = new HashMap<>();
for (ParameterRedef redef : parameters) {
ParameterNode paramNode = new ParameterNode();
paramNode.parameter = redef;
Long modelId = redef.getContextId();
if (modelId == null)
model.globalParameters.add(paramNode);
else {
ModelNode node = createdNodes.get(modelId);
if (node == null) {
node = new ModelNode();
node.model = getModel(modelId, cache);
createdNodes.put(modelId, node);
}
if (node.model != null)
redef.setContextType(node.model.getModelType());
paramNode.modelNode = node;
node.parameters.add(paramNode);
}
}
model.modelNodes.addAll(createdNodes.values());
return model;
}
private static BaseDescriptor getModel(long modelId, EntityCache cache) {
BaseDescriptor model = cache.get(ImpactMethodDescriptor.class, modelId);
if (model != null)
return model;
else
return cache.get(ProcessDescriptor.class, modelId);
}
private ParameterRedefDialog(Shell shell, TreeModel model) {
super(shell);
this.model = model;
setBlockOnOpen(true);
}
@Override
protected Point getInitialSize() {
// TODO: calculate from screen size
return new Point(600, 600);
}
private List<ParameterRedef> getSelection() {
List<Object> list = Viewers.getAll(selection);
if (list == null)
return Collections.emptyList();
List<ParameterRedef> selection = new ArrayList<>();
for (Object element : list) {
if (element instanceof ParameterNode) {
ParameterNode node = (ParameterNode) element;
selection.add(node.parameter);
}
}
return selection;
}
@Override
protected Point getInitialLocation(Point initialSize) {
Point loc = super.getInitialLocation(initialSize);
int marginTop = (getParentShell().getSize().y - initialSize.y) / 3;
if (marginTop < 0)
marginTop = 0;
return new Point(loc.x, loc.y + marginTop);
}
@Override
protected void createFormContent(IManagedForm mform) {
FormToolkit toolkit = mform.getToolkit();
UI.formHeader(mform, M.SearchParameters);
Composite body = UI.formBody(mform.getForm(), mform.getToolkit());
UI.gridLayout(body, 1);
Label filterLabel = UI.formLabel(body, toolkit, M.Filter);
filterLabel.setFont(UI.boldFont());
filterText = UI.formText(body, SWT.SEARCH);
filterText.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
viewer.refresh();
}
});
Section section = UI.section(body, toolkit, M.Parameters);
UI.gridData(section, true, true);
Composite composite = UI.sectionClient(section, toolkit);
UI.gridLayout(composite, 1);
createViewer(composite);
}
private void createViewer(Composite composite) {
viewer = new TreeViewer(composite);
UI.gridData(viewer.getTree(), true, true);
viewer.setContentProvider(new ContentProvider());
viewer.setLabelProvider(new LabelProvider());
viewer.setInput(model);
viewer.addFilter(new ViewerFilter() {
@Override
public boolean select(Viewer viewer, Object parentElement,
Object element) {
return filterNode(parentElement, element);
}
});
viewer.setSorter(new ViewerSorter() {
@Override
public int compare(Viewer viewer, Object e1, Object e2) {
return compareNodes(e1, e2);
}
});
viewer.addSelectionChangedListener((event) -> {
selection = (IStructuredSelection) event.getSelection();
});
}
private int compareNodes(Object e1, Object e2) {
if (e1 instanceof ParameterNode) {
ParameterNode node1 = (ParameterNode) e1;
if (e2 instanceof ParameterNode) {
ParameterNode node2 = (ParameterNode) e2;
return Strings.compare(node1.parameter.getName(),
node2.parameter.getName());
}
return -1; // global parameters before processes
}
if (e1 instanceof ModelNode) {
ModelNode node1 = (ModelNode) e1;
if (e2 instanceof ModelNode) {
ModelNode node2 = (ModelNode) e2;
return Strings.compare(node1.model.getName(),
node2.model.getName());
}
return 1; // process nodes after global parameters
}
return 0;
}
private boolean filterNode(Object parent, Object element) {
if (element instanceof TreeModel)
return true;
String text = filterText.getText();
if (text == null || text.trim().isEmpty())
return true;
String term = text.trim();
if (element instanceof ParameterNode)
return filterParameter(element, term);
if (element instanceof ModelNode)
return filterProcess(element, term);
return false;
}
private boolean filterParameter(Object element, String term) {
ParameterNode node = (ParameterNode) element;
if (node.modelNode != null && filterNode(null, node.modelNode))
return true;
return contains(node, term);
}
private boolean filterProcess(Object element, String term) {
ModelNode node = (ModelNode) element;
BaseDescriptor model = node.model;
if (StringUtils.containsIgnoreCase(model.getName(), term))
return true;
for (ParameterNode param : node.parameters)
if (contains(param, term))
return true;
return false;
}
private boolean contains(ParameterNode node, String term) {
ParameterRedef redef = node.parameter;
return StringUtils.containsIgnoreCase(redef.getName(), term);
}
private static class TreeModel {
private List<ModelNode> modelNodes = new ArrayList<>();
private List<ParameterNode> globalParameters = new ArrayList<>();
}
private static class ModelNode {
private BaseDescriptor model;
private List<ParameterNode> parameters = new ArrayList<>();
}
private static class ParameterNode {
private ParameterRedef parameter;
private ModelNode modelNode;
}
private class ContentProvider implements ITreeContentProvider {
@Override
public void dispose() {
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
@Override
public Object[] getElements(Object inputElement) {
if (!(inputElement instanceof TreeModel))
return new Object[0];
TreeModel model = (TreeModel) inputElement;
List<Object> elements = new ArrayList<>();
elements.addAll(model.globalParameters);
elements.addAll(model.modelNodes);
return elements.toArray();
}
@Override
public Object[] getChildren(Object parentElement) {
if (!(parentElement instanceof ModelNode))
return null;
ModelNode node = (ModelNode) parentElement;
return node.parameters.toArray();
}
@Override
public Object getParent(Object element) {
if (!(element instanceof ParameterNode))
return null;
ParameterNode node = (ParameterNode) element;
return node.modelNode;
}
@Override
public boolean hasChildren(Object element) {
return element instanceof ModelNode;
}
}
private class LabelProvider extends org.eclipse.jface.viewers.LabelProvider {
@Override
public Image getImage(Object element) {
if (element instanceof ParameterNode)
return Images.get(ModelType.PARAMETER);
if (element instanceof ModelNode) {
ModelNode node = (ModelNode) element;
return Images.get(node.model);
}
return null;
}
@Override
public String getText(Object element) {
if (element instanceof ModelNode) {
ModelNode node = (ModelNode) element;
return Labels.getDisplayName(node.model);
}
if (element instanceof ParameterNode) {
ParameterNode node = (ParameterNode) element;
return node.parameter.getName();
}
return null;
}
}
}