/******************************************************************************* * Copyright (c) 2007, 2018 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.diagram.adapter; import java.util.ArrayList; import java.util.Collection; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.procedure.adapter.ProcedureAdapter; import org.simantics.db.common.utils.OrderedSetUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.request.AsyncRead; import org.simantics.diagram.content.ConnectionPartData; import org.simantics.diagram.content.ConnectionPartRequest; import org.simantics.diagram.content.DiagramContents; import org.simantics.diagram.content.EdgeResource; import org.simantics.diagram.content.RouteGraphConnectionPartData; import org.simantics.diagram.content.RouteGraphConnectionPartRequest; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.diagram.synchronization.ErrorHandler; import org.simantics.g2d.canvas.ICanvasContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import gnu.trove.list.array.TIntArrayList; import gnu.trove.map.hash.THashMap; import gnu.trove.procedure.TIntProcedure; import gnu.trove.set.hash.THashSet; /** * @author Tuukka Lehtonen */ public class DiagramContentRequest extends BaseRequest { private static final Logger LOGGER = LoggerFactory.getLogger(DiagramContentRequest.class); int previousElementCount = 32; ErrorHandler errorHandler; public DiagramContentRequest(ICanvasContext canvas, Resource resource, ErrorHandler errorHandler) { super(canvas, resource); this.errorHandler = errorHandler; } @Override public DiagramContents perform(ReadGraph g) throws DatabaseException { final DiagramResource DIA = DiagramResource.getInstance(g); // These help loading result.elements in the correct order. final AtomicInteger index = new AtomicInteger(); final TIntArrayList unrecognizedElementIndices = new TIntArrayList(); if (!g.hasStatement(data)) { LOGGER.warn("Most likely diagram is being removed and therefore ordered list can not be found for {}", data); return new DiagramContents(); // or null, I don't know } Collection components = OrderedSetUtils.toList(g, data); DiagramContents res = g.syncRequest((AsyncRead)(graph, procedure) -> { DiagramContents result = new DiagramContents(); procedure.execute(graph, result); result.elements = new ArrayList(previousElementCount); result.nodeSet = new THashSet(); result.connectionSet = new THashSet(); result.connectionSegments = new THashSet(); result.branchPoints = new THashSet(); result.routeGraphConnectionSet = new THashSet(); result.routeLinks = new THashSet(); result.routeLines = new THashSet(); result.routePoints = new THashSet(); result.partToConnection = new THashMap(); for (Resource component : components) { // Must add the elements to the result set here in order to // keep their order the same as in the ordered set. final int elementIndex = index.getAndIncrement(); result.elements.add(component); graph.asyncRequest(new org.simantics.db.common.primitiverequest.Types(component), new ProcedureAdapter>() { @Override public void execute(Set types) { if (types.contains(DIA.Connection)) { if (types.contains(DIA.RouteGraphConnection)) { graph.asyncRequest( new RouteGraphConnectionPartRequest(errorHandler, DIA, component), new ProcedureAdapter() { @Override public void execute(RouteGraphConnectionPartData partData) { synchronized (result) { for (EdgeResource link : partData.links) { result.routeLinks.add(link); result.partToConnection.put(link, component); result.connectionToParts.add(component, link); } for (Resource line : partData.routeLines) { result.routeLines.add(line); result.connectionToParts.add(component, line); result.partToConnection.put(line, component); } for (Resource point : partData.routePoints) { result.routePoints.add(point); result.connectionToParts.add(component, point); result.partToConnection.put(point, component); } } } }); synchronized (result.routeGraphConnectionSet) { result.routeGraphConnectionSet.add(component); } } else { graph.asyncRequest( new ConnectionPartRequest(errorHandler, DIA, component), new ProcedureAdapter() { @Override public void execute(ConnectionPartData partData) { synchronized (result) { for (EdgeResource er : partData.edges) { result.connectionSegments.add(er); result.partToConnection.put(er, component); result.connectionToParts.add(component, er); } for (Resource bp : partData.branchPoints) { result.branchPoints.add(bp); result.connectionToParts.add(component, bp); result.partToConnection.put(bp, component); } } } }); synchronized (result.connectionSet) { result.connectionSet.add(component); } } } else if (types.contains(DIA.Element)) { synchronized (result.nodeSet) { result.nodeSet.add(component); } } else { synchronized (unrecognizedElementIndices) { // Unrecognized element, mark it to be // removed after everything is processed. unrecognizedElementIndices.add(elementIndex); } } } }); } }); // Remove elements that were not recognized in descending order. unrecognizedElementIndices.sort(); unrecognizedElementIndices.forEachDescending(new TIntProcedure() { @Override public boolean execute(int index) { res.elements.remove(index); return true; } }); // Help successive request executions by remembering the previous // element count. This will relieve some ArrayList reallocation // strain down the road. previousElementCount = res.elements.size(); return res; } }