Bundle-Activator: org.simantics.district.network.ui.internal.Activator
Export-Package: org.simantics.district.network.ui,
org.simantics.district.network.ui.adapters,
+ org.simantics.district.network.ui.breakdown,
org.simantics.district.network.ui.function
Require-Bundle: org.eclipse.e4.ui.model.workbench;bundle-version="1.1.100.v20150407-1430",
org.eclipse.swt,
org.eclipse.e4.core.di,
org.eclipse.e4.ui.di,
org.eclipse.e4.core.commands,
- org.eclipse.e4.core.contexts
+ org.eclipse.e4.core.contexts,
+ org.eclipse.jface,
+ org.simantics.scl.osgi
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: javax.inject;version="1.0.0"
+Import-Package: javax.annotation;version="1.0.0";resolution:=optional,
+ javax.inject;version="1.0.0"
Bundle-ActivationPolicy: lazy
id="org.simantics.district.network.ui.diagrameditor">
</editor>
</extension>
+ <extension
+ point="org.eclipse.ui.views">
+ <category
+ id="org.simantics.district" name="District Network">
+ </category>
+ <e4view
+ category="org.simantics.district"
+ class="org.simantics.district.network.ui.breakdown.DistrictNetworkBreakdownPart"
+ icon="platform:/plugin/com.famfamfam.silk/icons/table.png"
+ id="org.simantics.district.breakdown"
+ name="District Network Breakdown"
+ restorable="true">
+ </e4view>
+ </extension>
<extension
point="org.eclipse.ui.preferencePages">
<page
priority="300"
class="org.simantics.district.network.ui.OpenDiagramFromConfigurationAdapter">
</adapterClass>
+ <adapterClass
+ groupId="org.simantics.diagramEditor.group"
+ priority="300"
+ class="org.simantics.district.network.ui.OpenDiagramFromNetworkElementAdapter">
+ </adapterClass>
</extension>
<extension
point="org.simantics.scl.reflection.binding">
--- /dev/null
+package org.simantics.district.network.ui;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.function.Consumer;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.IEditorPart;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.request.UnaryRead;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.request.PossibleModel;
+import org.simantics.db.layer0.variable.RVI;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.db.layer0.variable.Variables;
+import org.simantics.district.network.DistrictNetworkUtil;
+import org.simantics.g2d.canvas.ICanvasContext;
+import org.simantics.g2d.diagram.DiagramHints;
+import org.simantics.layer0.Layer0;
+import org.simantics.modeling.ModelingResources;
+import org.simantics.modeling.actions.NavigateToTarget;
+import org.simantics.scl.runtime.tuple.Tuple4;
+import org.simantics.ui.selection.WorkbenchSelectionUtils;
+import org.simantics.utils.threads.ThreadUtils;
+import org.simantics.utils.ui.ISelectionUtils;
+
+/**
+ * @author Tuukka Lehtonen
+ * @since 1.35.0
+ */
+public class DistrictNetworkUIUtil {
+
+ public static Resource getInputResource(ReadGraph graph, Object input) throws DatabaseException {
+ if (input instanceof Resource) {
+ return (Resource) input;
+ } else if (input instanceof Variable) {
+ return ((Variable) input).getPossibleRepresents(graph);
+ } else if (input instanceof ISelection) {
+ return ISelectionUtils.filterSingleSelection((ISelection) input, Resource.class);
+ } else {
+ return WorkbenchSelectionUtils.getPossibleResource(graph, input);
+ }
+ }
+
+ public static class GetInputResource extends UnaryRead<Object, Resource> {
+
+ public GetInputResource(Object input) {
+ super(input);
+ }
+
+ @Override
+ public Resource perform(ReadGraph graph) throws DatabaseException {
+ return getInputResource(graph, parameter);
+ }
+
+ }
+
+ public static class Input extends Tuple4 {
+ public Input(Resource model, Resource diagram, Resource element, RVI diagramCompositeRvi) {
+ super(model, diagram, element, diagramCompositeRvi);
+ }
+ public Resource model() {
+ return (Resource) get(0);
+ }
+ public Resource diagram() {
+ return (Resource) get(1);
+ }
+ public Resource element() {
+ return (Resource) get(2);
+ }
+ public RVI rvi() {
+ return (RVI) get(3);
+ }
+ }
+
+ public static class FindMappedDNElement extends UnaryRead<Object, Input> {
+
+ public FindMappedDNElement(Object parameter) {
+ super(parameter);
+ }
+
+ @Override
+ public Input perform(ReadGraph graph) throws DatabaseException {
+ Resource e = DistrictNetworkUtil.getMappedDNElement(graph,
+ DistrictNetworkUtil.getDiagramElement(graph,
+ getInputResource(graph, parameter)));
+ return e != null ? graph.syncRequest(new ElementToInput(e)) : null;
+ }
+
+ }
+
+ public static class FindMappedComponent extends UnaryRead<Object, Input> {
+
+ public FindMappedComponent(Object parameter) {
+ super(parameter);
+ }
+
+ @Override
+ public Input perform(ReadGraph graph) throws DatabaseException {
+ Resource e = DistrictNetworkUtil.getMappedElement(graph,
+ getInputResource(graph, parameter));
+ return e != null ? graph.syncRequest(new ElementToInput(e)) : null;
+ }
+
+ }
+
+ public static class ElementToInput extends UnaryRead<Resource, Input> {
+
+ public ElementToInput(Resource element) {
+ super(element);
+ }
+
+ @Override
+ public Input perform(ReadGraph graph) throws DatabaseException {
+ Layer0 L0 = Layer0.getInstance(graph);
+ Resource diagram = graph.getPossibleObject(parameter, L0.PartOf);
+ if (diagram == null)
+ return null;
+
+ Resource model = graph.syncRequest(new PossibleModel(diagram));
+ if (model == null)
+ return null;
+
+ RVI rvi = getDiagramCompositeRvi(graph, diagram);
+ if (rvi == null)
+ return null;
+
+ return new Input(model, diagram, parameter, rvi);
+ }
+
+ private static RVI getDiagramCompositeRvi(ReadGraph graph, Resource diagram) throws DatabaseException {
+ ModelingResources MOD = ModelingResources.getInstance(graph);
+ Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);
+ if (composite == null)
+ return null;
+ Variable v = Variables.getPossibleVariable(graph, composite);
+ return v != null ? v.getPossibleRVI(graph) : null;
+ }
+
+ }
+
+ public static Consumer<IEditorPart> editorActivationCallback(final Collection<? extends Object> selectedObjects) {
+ return part -> {
+ final ICanvasContext openedCanvas = (ICanvasContext) part.getAdapter(ICanvasContext.class);
+ assert openedCanvas != null;
+ // CanvasContext-wide denial of initial zoom-to-fit on diagram open.
+ openedCanvas.getDefaultHintContext().setHint(DiagramHints.KEY_INITIAL_ZOOM_TO_FIT, Boolean.FALSE);
+ ThreadUtils.asyncExec(openedCanvas.getThreadAccess(),
+ NavigateToTarget.elementSelectorZoomer(openedCanvas, selectedObjects, false));
+ };
+ }
+
+ public static void openEditorWithSelection(String editorId, Input input, Object... selection) {
+ NavigateToTarget.editorActivator(
+ editorId,
+ input.diagram(),
+ input.model(),
+ input.rvi(),
+ DistrictNetworkUIUtil.editorActivationCallback(Arrays.asList(selection)))
+ .run();
+ }
+
+ public static void openDNDiagramEditorWithSelection(Input input, Object... selection) {
+ openEditorWithSelection(DistrictDiagramEditor.ID, input, selection);
+ }
+
+}
--- /dev/null
+package org.simantics.district.network.ui;
+
+import org.simantics.Simantics;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.Session;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.district.network.ontology.DistrictNetworkResource;
+import org.simantics.ui.workbench.editor.AbstractResourceEditorAdapter;
+import org.simantics.utils.ui.BundleUtils;
+
+/**
+ * @author Tuukka Lehtonen
+ * @since 1.35.0
+ */
+public class OpenDiagramFromNetworkElementAdapter extends AbstractResourceEditorAdapter {
+
+ public OpenDiagramFromNetworkElementAdapter() {
+ super("District Network Diagram",
+ BundleUtils.getImageDescriptorFromPlugin("com.famfamfam.silk", "icons/map.png"));
+ }
+
+ @Override
+ public boolean canHandle(ReadGraph graph, Object input) throws DatabaseException {
+ DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+ Resource r = DistrictNetworkUIUtil.getInputResource(graph, input);
+ return r != null
+ && graph.isInstanceOf(r, DN.Element)
+ && graph.getPossibleURI(r) != null;
+ }
+
+ @Override
+ public void openEditor(Object input) throws Exception {
+ Session s = Simantics.getSession();
+ Resource dhElement = s.syncRequest(new DistrictNetworkUIUtil.GetInputResource(input));
+ DistrictNetworkUIUtil.Input in = dhElement != null ? s.syncRequest(new DistrictNetworkUIUtil.ElementToInput(dhElement)) : null;
+ if (in != null)
+ DistrictNetworkUIUtil.openDNDiagramEditorWithSelection(in, in.element());
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package org.simantics.district.network.ui.breakdown;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.layout.TreeColumnLayout;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.LocalResourceManager;
+import org.eclipse.jface.resource.ResourceManager;
+import org.eclipse.jface.viewers.CellLabelProvider;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ColumnWeightData;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.TreeViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.TreeColumn;
+import org.simantics.NameLabelMode;
+import org.simantics.NameLabelUtil;
+import org.simantics.ObjectIdentitySchedulingRule;
+import org.simantics.Simantics;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.procedure.adapter.DisposableListener;
+import org.simantics.db.common.request.UnaryRead;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.adapter.Instances;
+import org.simantics.db.layer0.request.ActiveModels;
+import org.simantics.district.network.ontology.DistrictNetworkResource;
+import org.simantics.district.network.ui.DistrictNetworkUIUtil;
+import org.simantics.district.network.ui.breakdown.Input.NetworkDiagram;
+import org.simantics.district.network.ui.breakdown.Input.NetworkDiagrams;
+import org.simantics.district.network.ui.breakdown.Input.Subgraph;
+import org.simantics.district.network.ui.internal.Activator;
+import org.simantics.modeling.ModelingResources;
+import org.simantics.utils.strings.AlphanumComparator;
+import org.simantics.utils.strings.StringUtils;
+import org.simantics.utils.ui.BundleUtils;
+import org.simantics.utils.ui.ISelectionUtils;
+import org.simantics.utils.ui.SWTUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Tuukka Lehtonen
+ * @since 1.35.0
+ */
+public class DistrictNetworkBreakdownPanel extends Composite {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DistrictNetworkBreakdownPanel.class);
+
+ private DisposableListener<NetworkDiagrams> inputListener;
+ //private NetworkDiagrams currentInput;
+ private ConcurrentMap<NetworkDiagram, Subgraph[]> subgraphs = new ConcurrentHashMap<>();
+
+ private ResourceManager rm;
+ private TreeViewer tree;
+
+ private TreeComparator treeComparator;
+
+ private ImageDescriptor mapImg;
+
+ public DistrictNetworkBreakdownPanel(Composite parent, int style) {
+ super(parent, style);
+ this.rm = new LocalResourceManager(JFaceResources.getResources(), this);
+
+ this.mapImg = BundleUtils.getImageDescriptorFromPlugin("com.famfamfam.silk", "icons/map.png");
+
+ addDisposeListener(e -> DistrictNetworkBreakdownPanel.this.widgetDisposed());
+ createUI(parent);
+ trackInput();
+ }
+
+ protected void widgetDisposed() {
+ if (inputListener != null)
+ inputListener.dispose();
+ }
+
+ private void createUI(Composite parent) {
+ GridLayoutFactory.fillDefaults().applyTo(this);
+
+ Composite treeParent = new Composite(this, SWT.NONE);
+ TreeColumnLayout treeLayout = new TreeColumnLayout();
+ treeParent.setLayout(treeLayout);
+ GridDataFactory.fillDefaults().grab(true, true).span(1, 1).applyTo(treeParent);
+
+ treeComparator = new TreeComparator();
+
+ tree = new TreeViewer(treeParent, SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER | SWT.FULL_SELECTION);
+ tree.setUseHashlookup(true);
+ tree.getTree().setHeaderVisible(true);
+ tree.setContentProvider(new ContentProvider());
+ tree.setComparator(treeComparator);
+ createColumns(treeLayout);
+
+ tree.getTree().addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if (e.keyCode == SWT.F5) {
+ e.doit = false;
+ refreshSelection();
+ }
+ }
+ });
+ tree.getTree().addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseDoubleClick(MouseEvent e) {
+ new ShowSubnetworkAction(selectedSubgraphs()).run();
+ }
+ });
+
+ MenuManager menuManager = new MenuManager("District Network Breakdown Actions", "#DistrictNetworkBreakdownPopup");
+ menuManager.setRemoveAllWhenShown(true);
+ Menu menu = menuManager.createContextMenu(tree.getTree());
+ tree.getTree().setMenu(menu);
+ menuManager.addMenuListener(new IMenuListener() {
+ @Override
+ public void menuAboutToShow(IMenuManager manager) {
+ List<Subgraph> sel = selectedSubgraphs();
+ if (sel.size() > 0 && fromSameDiagram(sel)) {
+ manager.add(new ShowSubnetworkAction(sel));
+ }
+ //manager.add(new DeleteAction());
+ }
+
+ private boolean fromSameDiagram(List<Subgraph> list) {
+ NetworkDiagram d = null;
+ for (Subgraph sg : list) {
+ if (d == null)
+ d = sg.parent;
+ else if (!d.equals(sg.parent))
+ return false;
+ }
+ return true;
+ }
+ });
+ }
+
+ protected void refreshSelection() {
+ for (Object obj : tree.getStructuredSelection().toArray()) {
+ if (obj instanceof NetworkDiagram) {
+ subgraphs.remove(obj);
+ tree.refresh(obj);
+ }
+ }
+ }
+
+ protected void scheduleRefresh(NetworkDiagram diagram) {
+ SWTUtils.asyncExec(tree.getTree(), () -> {
+ if (!tree.getTree().isDisposed())
+ tree.refresh(diagram);
+ });
+ }
+
+ private void createColumns(TreeColumnLayout layout) {
+ TreeViewerColumn nameCol = createColumn(0, layout, "Diagram", "Diagram / Subgraph Number", new NameLabeler(), 1, 100, SWT.LEFT);
+ createColumn(1, layout, "Nodes", "Node Count of Subnetwork", new NodeCountLabeler(), 0, 100, SWT.LEFT);
+ createColumn(2, layout, "Edges", "Edge Count of Subnetwork", new EdgeCountLabeler(), 0, 100, SWT.LEFT);
+ setSortColumn(nameCol.getColumn(), 1);
+ }
+
+ private TreeViewerColumn createColumn(int index, TreeColumnLayout layout, String text, String tooltip,
+ CellLabelProvider labelProvider, int weight, int minimumWidth, int style) {
+ TreeViewerColumn column = new TreeViewerColumn(tree, style);
+ column.getColumn().setText(text);
+ column.getColumn().setToolTipText(StringUtils.safeString(tooltip));
+ column.getColumn().setResizable(true);
+ column.getColumn().setMoveable(true);
+ column.getColumn().addSelectionListener(getSelectionAdapter(column.getColumn(), index));
+ column.setLabelProvider(labelProvider);
+ layout.setColumnData(column.getColumn(), new ColumnWeightData(weight, minimumWidth));
+ return column;
+ }
+
+ private List<Subgraph> selectedSubgraphs() {
+ return ISelectionUtils.filterSelection(tree.getStructuredSelection(), Subgraph.class);
+ }
+
+ public class TreeComparator extends ViewerComparator {
+ private static final int DESCENDING = 1;
+
+ private int propertyIndex = -1;
+ private int direction = 0;
+
+ public int getDirection() {
+ return direction == 1 ? SWT.DOWN : SWT.UP;
+ }
+
+ public void setColumn(int column) {
+ if (column == this.propertyIndex) {
+ // Same column as last sort; toggle the direction
+ direction = 1 - direction;
+ } else {
+ // New column; do an ascending sort
+ this.propertyIndex = column;
+ direction = 0;
+ }
+ }
+
+ @Override
+ public int compare(Viewer viewer, Object e1, Object e2) {
+ int rc = 0;
+ if (e1 instanceof NetworkDiagram) {
+ NetworkDiagram nd1 = (NetworkDiagram) e1;
+ NetworkDiagram nd2 = (NetworkDiagram) e2;
+ rc = AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(nd1.name, nd2.name);
+ } else if (e1 instanceof Subgraph) {
+ Subgraph sg1 = (Subgraph) e1;
+ Subgraph sg2 = (Subgraph) e2;
+ switch (propertyIndex) {
+ case 0:
+ break;
+ case 1:
+ rc = Integer.compare(sg1.vertices.size(), sg2.vertices.size());
+ break;
+ case 2:
+ rc = Integer.compare(sg1.edges.size(), sg2.edges.size());
+ break;
+ default:
+ rc = 0;
+ }
+ }
+ if (direction == DESCENDING)
+ rc = -rc;
+ return rc;
+ }
+ }
+
+ private void setSortColumn(TreeColumn column, int index) {
+ treeComparator.setColumn(index);
+ int dir = treeComparator.getDirection();
+ tree.getTree().setSortDirection(dir);
+ tree.getTree().setSortColumn(column);
+ }
+
+ private SelectionListener getSelectionAdapter(TreeColumn column, int index) {
+ return new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setSortColumn(column, index);
+ tree.refresh(true);
+ }
+ };
+ }
+
+ private Runnable setInput(Input.NetworkDiagrams input) {
+ return () -> {
+ if (!tree.getTree().isDisposed())
+ tree.setInput(input);
+ };
+ }
+
+ private void configureInput(Input.NetworkDiagrams input) {
+ SWTUtils.asyncExec(DistrictNetworkBreakdownPanel.this, setInput(input));
+ }
+
+ private static final String PENDING = "Pending...";
+ private static final Subgraph[] PENDING_RESULT = {};
+
+ 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) {
+ return ((NetworkDiagrams) inputElement).diagrams.toArray();
+ }
+
+ @Override
+ public Object getParent(Object element) {
+ if (element instanceof Subgraph)
+ return ((Subgraph) element).parent;
+ return null;
+ }
+
+ @Override
+ public Object[] getChildren(Object parentElement) {
+ if (parentElement instanceof NetworkDiagram) {
+ NetworkDiagram nd = (NetworkDiagram) parentElement;
+ Subgraph[] sgs = subgraphs.get(nd);
+ if (sgs != null)
+ return sgs;
+ subgraphs.put(nd, PENDING_RESULT);
+ new CalculateSubgraphs(nd).schedule();
+ return new Object[] { PENDING };
+ }
+ return new Object[0];
+ }
+
+ @Override
+ public boolean hasChildren(Object element) {
+ if (element instanceof NetworkDiagram) {
+ NetworkDiagram nd = (NetworkDiagram) element;
+ Subgraph[] sgs = subgraphs.get(nd);
+ if (sgs != null)
+ return sgs.length > 0;
+ // We don't know yet so there might be children
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private class NameLabeler extends ColumnLabelProvider {
+ @Override
+ public String getText(Object element) {
+ if (element instanceof NetworkDiagram) {
+ return ((Input.NetworkDiagram) element).name;
+ } else if (element instanceof Subgraph) {
+ return "" + ((Subgraph) element).index;
+ }
+ return element.toString();
+ }
+ @Override
+ public Image getImage(Object element) {
+ if (element instanceof NetworkDiagram) {
+ return (Image) rm.get(mapImg);
+ }
+ return null;
+ }
+ }
+
+ private class NodeCountLabeler extends ColumnLabelProvider {
+ @Override
+ public String getText(Object element) {
+ if (element instanceof Subgraph) {
+ Subgraph sg = (Subgraph) element;
+ return Integer.toString(sg.vertices.size());
+ }
+ return "";
+ }
+ }
+
+ private class EdgeCountLabeler extends ColumnLabelProvider {
+ @Override
+ public String getText(Object element) {
+ if (element instanceof Subgraph) {
+ Subgraph sg = (Subgraph) element;
+ return Integer.toString(sg.edges.size());
+ }
+ return "";
+ }
+ }
+
+ private void trackInput() {
+ this.inputListener = new DisposableListener<NetworkDiagrams>() {
+ @Override
+ public void execute(NetworkDiagrams input) {
+ //currentInput = input;
+ configureInput(input);
+ }
+ @Override
+ public void exception(Throwable t) {
+ LOGGER.error("Problems resolving active models", t);
+ }
+ };
+ Simantics.getSession().asyncRequest(new ResolveInput(Simantics.getProjectResource()), inputListener);
+ }
+
+ private static class ResolveInput extends UnaryRead<Resource, NetworkDiagrams> {
+
+ public ResolveInput(Resource parameter) {
+ super(parameter);
+ }
+
+ @Override
+ public NetworkDiagrams perform(ReadGraph graph) throws DatabaseException {
+ DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+ ModelingResources MOD = ModelingResources.getInstance(graph);
+ Instances index = graph.getPossibleAdapter(DN.Diagram, Instances.class);
+ NetworkDiagrams result = new NetworkDiagrams();
+
+ for (Resource model : graph.syncRequest(new ActiveModels(parameter))) {
+ for (Resource diagram : index.find(graph, model)) {
+ Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);
+ String name = NameLabelUtil.modalName(graph, composite != null ? composite : diagram, NameLabelMode.NAME_AND_LABEL);
+ result.diagrams.add(new NetworkDiagram(name, diagram));
+ }
+ }
+
+ return result;
+ }
+
+ }
+
+ private class CalculateSubgraphs extends Job {
+
+ private NetworkDiagram diagram;
+
+ public CalculateSubgraphs(NetworkDiagram diagram) {
+ super("Calculate subgraphs");
+ this.diagram = diagram;
+ setUser(true);
+ setRule(new ObjectIdentitySchedulingRule(diagram));
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ SubgraphProvider[] sgps = Activator.getInstance().getSubgraphProviders();
+ SubMonitor mon = SubMonitor.convert(monitor, NLS.bind("Calculating district network breakdown for {0}", diagram.name), sgps.length);
+ List<Subgraph> result = new ArrayList<>();
+ for (SubgraphProvider sgp : sgps)
+ for (Subgraph sg : sgp.getProvider(mon.split(1), diagram))
+ result.add(sg);
+ subgraphs.put(diagram, result.toArray(new Subgraph[result.size()]));
+ scheduleRefresh(diagram);
+ return Status.OK_STATUS;
+ } finally {
+ monitor.done();
+ }
+ }
+
+ }
+
+ private static class ShowSubnetworkAction extends Action {
+
+ private final List<Subgraph> subgraphs;
+
+ public ShowSubnetworkAction(List<Subgraph> subgraphs) {
+ super("Show Subnetwork on Diagram");
+ this.subgraphs = subgraphs;
+ }
+
+ @Override
+ public void run() {
+ try {
+ openDiagram(subgraphs);
+ } catch (DatabaseException e) {
+ LOGGER.error("Failed to show selected subnetwork", e);
+ }
+ }
+
+ }
+
+ public static boolean openDiagram(List<Subgraph> subgraphs) throws DatabaseException {
+ if (subgraphs.isEmpty())
+ return false;
+ Subgraph subgraph = subgraphs.get(0);
+ Resource dhElement = subgraph.vertices.size() > 0
+ ? subgraph.vertices.get(0)
+ : subgraph.edges.size() > 0 ? subgraph.edges.get(0) : null;
+ DistrictNetworkUIUtil.Input in = dhElement != null
+ ? Simantics.getSession().syncRequest(new DistrictNetworkUIUtil.ElementToInput(dhElement)) : null;
+
+ if (in != null) {
+ List<Resource> selection = new ArrayList<>();
+ subgraphs.forEach(sg -> {
+ sg.vertices.forEach(selection::add);
+ sg.edges.forEach(selection::add);
+ });
+ DistrictNetworkUIUtil.openDNDiagramEditorWithSelection(in, selection.toArray());
+ return true;
+ }
+ return false;
+ }
+
+}
--- /dev/null
+package org.simantics.district.network.ui.breakdown;
+
+import javax.annotation.PostConstruct;
+
+import org.eclipse.e4.ui.di.Focus;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * @author Tuukka Lehtonen
+ * @since 1.35.0
+ */
+public class DistrictNetworkBreakdownPart {
+
+ public static final String ID = "org.simantics.district.breakdown"; //$NON-NLS-1$
+
+ private DistrictNetworkBreakdownPanel panel;
+
+ @PostConstruct
+ public void createPartControl(Composite parent) {
+ panel = new DistrictNetworkBreakdownPanel(parent, SWT.NONE);
+ }
+
+ @Focus
+ public void setFocus() {
+ panel.setFocus();
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package org.simantics.district.network.ui.breakdown;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.simantics.databoard.util.Bean;
+import org.simantics.db.Resource;
+
+/**
+ * @author Tuukka Lehtonen
+ * @since 1.35.0
+ */
+public class Input {
+
+ public static class NetworkDiagrams extends Bean {
+ public List<NetworkDiagram> diagrams = new ArrayList<>();
+ }
+
+ public static class NetworkDiagram extends Bean {
+ public String name;
+ public Resource diagram;
+
+ public NetworkDiagram(String name, Resource diagram) {
+ this.name = name;
+ this.diagram = diagram;
+ }
+ }
+
+ public static class Subgraph {
+ public NetworkDiagram parent;
+ public int index;
+ public List<Resource> vertices;
+ public List<Resource> edges;
+
+ public Subgraph(NetworkDiagram parent, int index, List<Resource> vertices, List<Resource> edges) {
+ this.parent = parent;
+ this.index = index;
+ this.vertices = vertices;
+ this.edges = edges;
+ }
+ }
+
+}
--- /dev/null
+package org.simantics.district.network.ui.breakdown;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.simantics.district.network.ui.breakdown.Input.NetworkDiagram;
+import org.simantics.district.network.ui.breakdown.Input.Subgraph;
+
+/**
+ * @author Tuukka Lehtonen
+ * @since 1.35.0
+ */
+public interface SubgraphProvider {
+
+ /**
+ * @param monitor
+ * @param diagram
+ * @return
+ */
+ Subgraph[] getProvider(IProgressMonitor monitor, NetworkDiagram diagram);
+
+}
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+import org.simantics.district.network.ui.breakdown.SubgraphProvider;
public class Activator implements BundleActivator {
public static final String PLUGIN_ID = "org.simantics.district.network.ui";
+ private static Activator instance;
private static BundleContext context;
+ private ServiceTracker<SubgraphProvider, SubgraphProvider> subgraphProviderTracker;
@Override
public void start(BundleContext context) throws Exception {
+ Activator.instance = this;
Activator.context = context;
+
+ subgraphProviderTracker = new ServiceTracker<>(context, SubgraphProvider.class.getName(), null);
+ subgraphProviderTracker.open();
}
@Override
public void stop(BundleContext context) throws Exception {
+ subgraphProviderTracker.close();
+
+ Activator.instance = null;
Activator.context = null;
}
-
+
+ public static Activator getInstance() {
+ return instance;
+ }
+
public static BundleContext getContext() {
return context;
}
+ public SubgraphProvider[] getSubgraphProviders() {
+ return subgraphProviderTracker.getServices(new SubgraphProvider[0]);
+ }
+
}
-type Subgraph = [([Resource], [Resource])]
-data Subgraphs = Subgraphs Resource Subgraph
+import "Simantics/Model"
+import "http://www.simantics.org/DistrictNetwork-1.0" as DN
+
+import "Comparator"
+@private
+importJava "org.simantics.utils.strings.AlphanumComparator" where
+ @JavaName CASE_INSENSITIVE_COMPARATOR
+ alphanumericComparator :: Comparator String
+
+type Subgraph = ([Resource], [Resource])
+type Subgraphs = (Resource, [Subgraph])
+type ElementFilter = (Resource -> <ReadGraph> Boolean)
@private
-floodFill :: Resource -> <ReadGraph, Proc> Subgraph
-floodFill fromVertex = do
+floodFill :: ElementFilter -> Resource -> <ReadGraph, Proc> Subgraph
+floodFill edgeFilter fromVertex = do
processVertex fromVertex
(MSet.toList vertices, MSet.toList edges)
where
for starts processEdgeStart
for ends processEdgeEnd
else ()
- processEdgeStart edge = if MSet.add edges edge then for (edge # DN.HasEndVertex) processVertex else ()
- processEdgeEnd edge = if MSet.add edges edge then for (edge # DN.HasStartVertex) processVertex else ()
+ processEdgeStart edge = if MSet.add edges edge && edgeFilter edge then
+ for (edge # DN.HasEndVertex) processVertex
+ else ()
+ processEdgeEnd edge = if MSet.add edges edge && edgeFilter edge then
+ for (edge # DN.HasStartVertex) processVertex
+ else ()
@private
-findDisconnectedSubnetworksFromDiagram :: Resource -> <ReadGraph, Proc> Subgraphs
-findDisconnectedSubnetworksFromDiagram networkDiagram = let
+findDisconnectedSubnetworksFromDiagram :: ElementFilter -> Resource -> <ReadGraph, Proc> Subgraphs
+findDisconnectedSubnetworksFromDiagram edgeFilter networkDiagram = let
all = networkDiagram # L0.ConsistsOf
vertices = filter (flip isInstanceOf DN.Vertex) all
edges = filter (flip isInstanceOf DN.Edge) all
loop Nothing = ()
loop (Just vertex) = do
- subgraph = floodFill vertex
+ subgraph = floodFill edgeFilter vertex
MList.add result subgraph
(vs, es) = subgraph
MSet.removeAll vertexSet vs
loop (take1 vertexSet)
else ()
in do
- print "Total number of vertices: \(length vertices)"
- print "Total number of edges: \(length edges)"
+ //print "Total number of vertices: \(length vertices)"
+ //print "Total number of edges: \(length edges)"
loop (take1 vertexSet)
if MSet.size edgeSet > 0 then
MList.add result ([], MSet.toList edgeSet)
else ()
- print "Found \(MList.size result) disconnected sub-networks"
- Subgraphs networkDiagram (MList.freeze result)
+ //print "Found \(MList.size result) disconnected sub-networks"
+ (networkDiagram, (MList.freeze result))
"""
Finds disconnected district subnetworks from the provided district network diagram.
The input can be either the network diagram resource or the configuration composite
resource of the network diagram.
-See [reportDisconnectedSubnetworks](#reportDisconnectedSubnetworks) for reporting
+See [reportDisconnectedSubnetworks](#reportDisconnectedSubnetworks) for reporting the
+result by printing.
"""
-findDisconnectedSubnetworks :: Resource -> <ReadGraph, Proc> Subgraphs
-findDisconnectedSubnetworks networkDiagramOrComposite = findDisconnectedSubnetworksFromDiagram (toDiagram networkDiagramOrComposite)
+findDisconnectedSubnetworks :: ElementFilter -> Resource -> <ReadGraph, Proc> Subgraphs
+findDisconnectedSubnetworks edgeFilter networkDiagramOrComposite = findDisconnectedSubnetworksFromDiagram edgeFilter (toDiagram networkDiagramOrComposite)
where
toDiagram d = if isInstanceOf d DN.Diagram then d
else match (possibleObject d MOD.CompositeToDiagram) with
Just dia -> toDiagram dia
Nothing -> fail "Provided diagram is not a district network diagram or configuration composite: \(possibleUriOf d)"
-import "Comparator"
-@private
-importJava "org.simantics.utils.strings.AlphanumComparator" where
- @JavaName CASE_INSENSITIVE_COMPARATOR
- alphanumericComparator :: Comparator String
-
reportDisconnectedSubnetworks :: Integer -> Subgraphs -> <ReadGraph, Proc> ()
-reportDisconnectedSubnetworks vertexThreshold (Subgraphs diagram subgraphs) = do
+reportDisconnectedSubnetworks vertexThreshold (diagram, subgraphs) = do
print "## Disconnected sub-network analysis of district network \(relativeUri diagram)"
print "* Detailed reporting vertex count threshold is <= \(vertexThreshold)"
for subgraphs reportGraph
reportShort vs es = do
reportSubgraphTitle vs es
print "* Details not reported because vertex count exceeds threshold"
+ mapFirst (\s -> do print "* v0: \(s)"; Just s)
+ (sortStrings (map showVertex vs))
+ print "* ..."
reportFull vs es = do
reportSubgraphTitle vs es
forI (sortStrings (map showVertex vs)) (\i s -> print "* v\(i): \(s)")
import org.simantics.db.WriteGraph;
import org.simantics.db.common.utils.OrderedSetUtils;
import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.request.PossibleVariable;
+import org.simantics.db.layer0.variable.Variable;
import org.simantics.diagram.stubs.DiagramResource;
import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
import org.simantics.diagram.synchronization.graph.layer.GraphLayer;
import org.simantics.diagram.synchronization.graph.layer.IGraphLayerUtil;
import org.simantics.district.network.ontology.DistrictNetworkResource;
import org.simantics.layer0.Layer0;
+import org.simantics.modeling.ModelingResources;
import org.simantics.operation.Layer0X;
public class DistrictNetworkUtil {
graph.claimLiteral(diagram, DIA.HasModCount, ++l, Bindings.LONG);
return name;
}
+
+ public static Resource getDiagramElement(ReadGraph graph, Resource component) throws DatabaseException {
+ if (component == null)
+ return null;
+ DiagramResource DIA = DiagramResource.getInstance(graph);
+ if (graph.isInstanceOf(component, DIA.Element))
+ return component;
+ ModelingResources MOD = ModelingResources.getInstance(graph);
+ Resource element = graph.getPossibleObject(component, MOD.ComponentToElement);
+ return element != null && graph.isInstanceOf(element, DIA.Element) ? element : null;
+ }
+
+ public static Resource getMappedElement(ReadGraph graph, Resource element) throws DatabaseException {
+ if (element == null)
+ return null;
+ DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+ return graph.getPossibleObject(element, DN.MappedComponent);
+ }
+
+ public static Resource getMappedComponent(ReadGraph graph, Resource element) throws DatabaseException {
+ if (element == null)
+ return null;
+ Resource mappedElement = getMappedElement(graph, element);
+ if (mappedElement == null)
+ return null;
+ ModelingResources MOD = ModelingResources.getInstance(graph);
+ return graph.getPossibleObject(mappedElement, MOD.ElementToComponent);
+ }
+
+ public static Resource getMappedDNElement(ReadGraph graph, Resource element) throws DatabaseException {
+ if (element == null)
+ return null;
+ DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+ return graph.getPossibleObject(element, DN.MappedFromElement);
+ }
+
+ public static Variable toMappedConfigurationModule(ReadGraph graph, Resource input) throws DatabaseException {
+ if (input == null)
+ return null;
+
+ DistrictNetworkResource DN = DistrictNetworkResource.getInstance(graph);
+ if (graph.isInstanceOf(input, DN.Element)) {
+ Resource mappedElement = getMappedElement(graph, input);
+ if (mappedElement == null)
+ return null;
+
+ ModelingResources MOD = ModelingResources.getInstance(graph);
+ Resource mappedComponent = graph.getPossibleObject(mappedElement, MOD.ElementToComponent);
+ if (mappedComponent == null)
+ return null;
+
+ return graph.syncRequest(new PossibleVariable(mappedComponent));
+ }
+ return null;
+ }
+
}