package org.openlca.app.editors.graphical.model;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.eclipse.draw2d.ImageFigure;
import org.eclipse.draw2d.MouseEvent;
import org.eclipse.draw2d.MouseListener;
import org.eclipse.gef.commands.Command;
import org.openlca.app.db.Cache;
import org.openlca.app.editors.graphical.command.ExpansionCommand;
import org.openlca.app.editors.graphical.search.MutableProcessLinkSearchMap;
import org.openlca.app.rcp.images.Icon;
import org.openlca.core.model.ProcessLink;
import org.openlca.core.model.descriptors.ProcessDescriptor;
class ProcessExpander extends ImageFigure {
private ProcessNode node;
private Side side;
private boolean expanded;
// isCollapsing is used to prevent endless recursion in collapse()
private boolean isCollapsing;
ProcessExpander(ProcessNode node, Side side) {
this.node = node;
this.side = side;
setImage(Icon.PLUS.get());
setVisible(shouldBeVisible());
addMouseListener(new ExpansionListener());
}
boolean shouldBeVisible() {
MutableProcessLinkSearchMap linkSearch = node.parent().linkSearch;
long processId = node.process.getId();
for (ProcessLink link : linkSearch.getLinks(processId)) {
if (side == Side.LEFT && link.processId == processId)
return true;
if (side == Side.RIGHT && link.providerId == processId)
return true;
}
return false;
}
void expand() {
createNecessaryNodes();
showLinksAndNodes();
setImage(Icon.MINUS.get());
expanded = true;
}
private List<ProcessNode> getNodesToShow() {
List<ProcessNode> nodes = new ArrayList<>();
for (Link link : node.links) {
ProcessNode match = getMatchingNode(link);
if (match == null || nodes.contains(match))
continue;
nodes.add(match);
}
return nodes;
}
private void createNecessaryNodes() {
ProductSystemNode systemNode = node.parent();
MutableProcessLinkSearchMap linkSearch = systemNode.linkSearch;
long processId = node.process.getId();
List<ProcessLink> links = null;
if (side == Side.LEFT)
links = linkSearch.getIncomingLinks(processId);
else
links = linkSearch.getOutgoingLinks(processId);
Map<Long, ProcessDescriptor> map = getLinkedProcesses(links);
for (ProcessLink link : links) {
long linkedProcessId = side == Side.LEFT ? link.providerId : link.processId;
ProcessNode node = systemNode.getProcessNode(linkedProcessId);
if (node == null) {
ProcessDescriptor descriptor = map.get(linkedProcessId);
node = new ProcessNode(descriptor);
systemNode.add(node);
}
ProcessNode sourceNode = side == Side.LEFT ? node : this.node;
ProcessNode targetNode = side == Side.LEFT ? this.node : node;
Link connectionLink = new Link();
connectionLink.sourceNode = sourceNode;
connectionLink.targetNode = targetNode;
connectionLink.processLink = link;
connectionLink.link();
}
}
private Map<Long, ProcessDescriptor> getLinkedProcesses(
List<ProcessLink> links) {
HashSet<Long> processIds = new HashSet<>();
for (ProcessLink link : links)
if (side == Side.LEFT)
processIds.add(link.providerId);
else
processIds.add(link.processId);
return Cache.getEntityCache().getAll(ProcessDescriptor.class, processIds);
}
void collapse(ProcessNode initialNode) {
if (isCollapsing)
return;
isCollapsing = true;
Link[] links = node.links.toArray(
new Link[node.links.size()]);
for (Link link : links) {
ProcessNode thisNode = side == Side.LEFT ? link.targetNode : link.sourceNode;
ProcessNode otherNode = side == Side.LEFT ? link.sourceNode : link.targetNode;
if (!thisNode.equals(node))
continue;
link.unlink();
otherNode.collapseLeft(initialNode);
otherNode.collapseRight(initialNode);
if (otherNode.equals(initialNode))
continue;
if (!otherNode.links.isEmpty())
continue;
node.parent().remove(otherNode);
}
setImage(Icon.PLUS.get());
isCollapsing = false;
expanded = false;
}
private ProcessNode getMatchingNode(Link link) {
ProcessNode source = link.sourceNode;
ProcessNode target = link.targetNode;
if (side == Side.LEFT)
if (target.equals(node))
if (!source.equals(node))
return source;
if (side == Side.RIGHT)
if (source.equals(node))
if (!target.equals(node))
return target;
return null;
}
private void showLinksAndNodes() {
List<ProcessNode> nodes = getNodesToShow();
for (ProcessNode node : nodes) {
node.setVisible(true);
for (Link link : node.links) {
if (!processFiguresVisible(link))
continue;
link.setVisible(true);
}
}
}
private boolean processFiguresVisible(Link link) {
if (!link.sourceNode.isVisible())
return false;
if (!link.targetNode.isVisible())
return false;
return true;
}
void refresh() {
setVisible(shouldBeVisible());
if (expanded)
setImage(Icon.MINUS.get());
else
setImage(Icon.PLUS.get());
}
boolean isExpanded() {
return expanded;
}
void setExpanded(boolean value) {
expanded = value;
}
enum Side {
LEFT, RIGHT;
}
private class ExpansionListener implements MouseListener {
@Override
public void mouseDoubleClicked(MouseEvent me) {
}
@Override
public void mousePressed(MouseEvent me) {
Command command = getCommand();
node.parent().editor.getCommandStack().execute(command);
}
private Command getCommand() {
if (side == Side.LEFT) {
if (expanded)
return ExpansionCommand.collapseLeft(node);
return ExpansionCommand.expandLeft(node);
}
if (expanded)
return ExpansionCommand.collapseRight(node);
return ExpansionCommand.expandRight(node);
}
@Override
public void mouseReleased(MouseEvent me) {
}
}
}