package org.openlca.app.editors.graphical.search;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.CheckboxCellEditor;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Table;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.openlca.app.M;
import org.openlca.app.db.Database;
import org.openlca.app.editors.graphical.model.ExchangeNode;
import org.openlca.app.editors.graphical.model.ProductSystemNode;
import org.openlca.app.util.Tuple;
import org.openlca.app.util.UI;
import org.openlca.app.util.tables.Tables;
import org.openlca.core.database.NativeSql;
import org.openlca.core.database.ProcessDao;
import org.openlca.core.model.Exchange;
import org.openlca.core.model.ProcessLink;
import org.openlca.core.model.descriptors.ProcessDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ConnectionDialog extends Dialog {
interface LABELS {
String NAME = M.Name;
String CREATE = M.Add;
String CONNECT = M.Connect;
String EXISTS = M.AlreadyPresent;
String CONNECTED = M.AlreadyConnected;
String[] ALL = new String[] { NAME, CREATE, CONNECT, EXISTS, CONNECTED };
}
private static final Logger log = LoggerFactory.getLogger(ConnectionDialog.class);
private final Exchange exchange;
private final long processId;
private final Set<Long> existingProcesses;
private final MutableProcessLinkSearchMap linkSearch;
private final boolean selectProvider;
private final List<AvailableConnection> availableConnections = new ArrayList<>();
private final boolean isConnected;
TableViewer viewer;
public ConnectionDialog(ExchangeNode exchangeNode) {
super(UI.shell());
setShellStyle(SWT.BORDER | SWT.TITLE);
setBlockOnOpen(true);
exchange = exchangeNode.exchange;
processId = exchangeNode.parent().process.getId();
ProductSystemNode systemNode = exchangeNode.parent().parent();
existingProcesses = systemNode.getProductSystem().getProcesses();
linkSearch = systemNode.linkSearch;
selectProvider = exchangeNode.exchange.isInput();
boolean foundLink = false;
for (ProcessLink link : linkSearch.getIncomingLinks(processId))
if (link.flowId == exchange.getFlow().getId())
foundLink = true;
isConnected = foundLink;
}
@Override
protected Control createButtonBar(Composite parent) {
Control c = super.createButtonBar(parent);
c.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
parent.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
return c;
}
@Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
}
@Override
protected Control createDialogArea(Composite parent) {
Composite container = (Composite) super.createDialogArea(parent);
container.setLayout(new FillLayout());
FormToolkit toolkit = new FormToolkit(Display.getCurrent());
Section selectObjectSection = toolkit.createSection(container, ExpandableComposite.TITLE_BAR
| ExpandableComposite.FOCUS_TITLE);
selectObjectSection.setText(selectProvider ? M.SelectProviders : M.SelectRecipients);
Composite composite = toolkit.createComposite(selectObjectSection, SWT.NONE);
UI.gridLayout(composite, 1);
selectObjectSection.setClient(composite);
toolkit.adapt(composite);
viewer = Tables.createViewer(composite, LABELS.ALL);
viewer.setLabelProvider(new ConnectionLabelProvider(this));
Table table = viewer.getTable();
createInternalModel();
if (availableConnections.size() > 0)
viewer.setInput(availableConnections.toArray(new AvailableConnection[availableConnections.size()]));
CellEditor[] editors = new CellEditor[5];
editors[1] = new CheckboxCellEditor(table);
editors[2] = new CheckboxCellEditor(table);
viewer.setColumnProperties(LABELS.ALL);
viewer.setCellModifier(new ConnectionCellModifier(this));
viewer.setCellEditors(editors);
toolkit.paintBordersFor(composite);
return container;
}
private void createInternalModel() {
availableConnections.clear();
long flowId = exchange.getFlow().getId();
String query = "SELECT id, f_owner FROM tbl_exchanges "
+ "WHERE f_flow = " + flowId + " AND is_input = " + (selectProvider ? 0 : 1);
List<Tuple<Long, Long>> exchanges = new ArrayList<>();
Set<Long> processIds = new HashSet<>();
try {
NativeSql.on(Database.get()).query(query, (rs) -> {
long id = rs.getLong("id");
long pId = rs.getLong("f_owner");
exchanges.add(new Tuple<>(id, pId));
processIds.add(pId);
return true;
});
} catch (Exception e) {
log.error("Error loading available connections", e);
}
List<ProcessDescriptor> processes = new ProcessDao(Database.get()).getDescriptors(processIds);
Map<Long, ProcessDescriptor> idToProcess = new HashMap<>();
for (ProcessDescriptor process : processes)
idToProcess.put(process.getId(), process);
for (Tuple<Long, Long> exchange : exchanges) {
ProcessDescriptor process = idToProcess.get(exchange.second);
boolean existing = existingProcesses.contains(process.getId());
boolean connected = false;
if (existing)
connected = isAlreadyConnected(process);
availableConnections.add(new AvailableConnection(process, exchange.first, existing, connected));
}
}
private boolean isAlreadyConnected(ProcessDescriptor process) {
if (selectProvider)
return isAlreadyProvider(process);
return isAlreadyReceiver(process);
}
private boolean isAlreadyProvider(ProcessDescriptor process) {
for (ProcessLink link : linkSearch.getOutgoingLinks(process.getId())) {
if (link.processId != processId)
continue;
if (link.flowId != exchange.getFlow().getId())
continue;
return true;
}
return false;
}
private boolean isAlreadyReceiver(ProcessDescriptor process) {
for (ProcessLink link : linkSearch.getIncomingLinks(process.getId())) {
if (link.providerId != processId)
continue;
if (link.exchangeId != exchange.getId())
continue;
return true;
}
return false;
}
@Override
protected Point getInitialSize() {
return new Point(800, 500);
}
boolean canBeConnected(AvailableConnection connectable) {
if (isConnected)
return false;
if (connectable.alreadyConnected)
return false;
if (selectProvider && userHasSelectedProvider())
return false;
if (!selectProvider && hasProvider(connectable))
return false;
return true;
}
private boolean hasProvider(AvailableConnection process) {
for (ProcessLink link : linkSearch.getIncomingLinks(process.process.getId()))
if (link.exchangeId == exchange.getId())
return true;
return false;
}
private boolean userHasSelectedProvider() {
for (AvailableConnection connectable : availableConnections)
if (connectable.alreadyConnected || connectable.connect)
return true;
return false;
}
public List<ProcessDescriptor> toCreate() {
List<ProcessDescriptor> toCreate = new ArrayList<>();
for (AvailableConnection connectable : availableConnections)
if (connectable.create)
toCreate.add(connectable.process);
return toCreate;
}
public List<Tuple<ProcessDescriptor, Long>> toConnect() {
List<Tuple<ProcessDescriptor, Long>> toConnect = new ArrayList<>();
for (AvailableConnection connectable : availableConnections)
if (connectable.connect)
toConnect.add(new Tuple<>(connectable.process, connectable.exchangeId));
return toConnect;
}
class AvailableConnection {
final ProcessDescriptor process;
final long exchangeId;
final boolean alreadyExisting;
final boolean alreadyConnected;
boolean connect;
boolean create;
AvailableConnection(ProcessDescriptor process, long exchangeId, boolean existing, boolean connected) {
this.process = process;
this.exchangeId = exchangeId;
this.alreadyExisting = existing;
this.alreadyConnected = connected;
}
@Override
public boolean equals(final Object arg0) {
if (!(arg0 instanceof AvailableConnection))
return false;
AvailableConnection other = (AvailableConnection) arg0;
if (other.process == null)
return process == null;
return Objects.equals(other.process, process) && exchangeId == other.exchangeId;
}
}
}