]> gerrit.simantics Code Review - simantics/district.git/blobdiff - org.simantics.district.route/src/org/simantics/district/route/internal/RouteServiceImpl.java
Merge "Initial version of the district network Routes view."
[simantics/district.git] / org.simantics.district.route / src / org / simantics / district / route / internal / RouteServiceImpl.java
diff --git a/org.simantics.district.route/src/org/simantics/district/route/internal/RouteServiceImpl.java b/org.simantics.district.route/src/org/simantics/district/route/internal/RouteServiceImpl.java
new file mode 100644 (file)
index 0000000..e37a803
--- /dev/null
@@ -0,0 +1,195 @@
+package org.simantics.district.route.internal;
+
+import java.io.Closeable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import org.eclipse.core.runtime.ListenerList;
+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.db.layer0.request.PossibleActiveModel;
+import org.simantics.db.management.ISessionContext;
+import org.simantics.db.management.ISessionContextChangedListener;
+import org.simantics.db.management.SessionContextChangedEvent;
+import org.simantics.db.procedure.SyncListener;
+import org.simantics.district.route.Route;
+import org.simantics.district.route.RouteEvent;
+import org.simantics.district.route.RouteService;
+import org.simantics.district.route.RouteServiceListener;
+import org.simantics.district.route.Router;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Tuukka Lehtonen
+ */
+public class RouteServiceImpl implements RouteService, ISessionContextChangedListener, Closeable {
+
+    private final Logger LOGGER = LoggerFactory.getLogger(RouteServiceImpl.class); 
+
+    private ListenerList<RouteServiceListener> listeners = new ListenerList<>();
+    private List<Route> routes = new ArrayList<>();
+    private List<Route> unmodifiableRoutes = Collections.unmodifiableList(routes);
+    private List<Router> routers = new ArrayList<>();
+    private List<Router> unmodifiableRouters = Collections.unmodifiableList(routers);
+
+    private class StoreListener implements SyncListener<Resource> {
+        private boolean disposed = false;
+
+        @Override
+        public void execute(ReadGraph graph, Resource activeModel) {
+            try {
+                if (activeModel != null) {
+                    resetRoutes(RoutePersistence.findRoutes(graph, activeModel));
+                } else {
+                    resetRoutes(Collections.emptyList());
+                }
+                fireEvent(RouteEvent.TYPE_ROUTE_SOURCE_CHANGED, this);
+            } catch (DatabaseException e) {
+                LOGGER.error("Failed to read routes from model {}", activeModel, e);
+            }
+        }
+
+        @Override
+        public void exception(ReadGraph graph, Throwable t) {
+            LOGGER.error("Failed to listen to current route service storage", t);
+        }
+
+        public void dispose() {
+            disposed = true;
+        }
+
+        @Override
+        public boolean isDisposed() {
+            return disposed;
+        }
+    }
+
+    private StoreListener storeListener;
+
+    private synchronized void listenToActiveModels(Session s) {
+        StoreListener sl =  storeListener;
+        if (sl != null)
+            sl.dispose();
+        if (s != null) {
+            s.asyncRequest(
+                    new PossibleActiveModel(Simantics.getProjectResource()),
+                    storeListener = new StoreListener());
+        } else {
+            resetRoutes(Collections.emptyList());
+        }
+    }
+
+    void resetRoutes(List<RouteImpl> newRoutes) {
+        routes.clear();
+        newRoutes.forEach(r -> r.routeService(this));
+        routes.addAll(newRoutes);
+    }
+
+    @Override
+    public void sessionContextChanged(SessionContextChangedEvent event) {
+        ISessionContext ctx = event.getNewValue();
+        Session s = ctx != null ? ctx.getSession() : null;
+        listenToActiveModels(s);
+    }
+
+    public RouteServiceImpl() {
+        Session s = Simantics.peekSession();
+        Simantics.getSessionContextProvider().addContextChangedListener(this);
+        if (s != null)
+            listenToActiveModels(s);
+    }
+
+    @Override
+    public void close() {
+        Simantics.getSessionContextProvider().removeContextChangedListener(this);
+    }
+
+    @Override
+    public void addListener(RouteServiceListener l) {
+        listeners.add(l);
+    }
+
+    @Override
+    public void removeListener(RouteServiceListener l) {
+        listeners.remove(l);
+    }
+
+    @Override
+    public Route createRoute(String name, Object backendModelEntity) {
+        Route r = new RouteImpl(name).modelEntity((Resource) backendModelEntity).routeService(this);
+        fireEvent(RouteEvent.TYPE_ROUTE_CREATED, r);
+        return r;
+    }
+
+    @Override
+    public void registerRoute(Route route) {
+        routes.add(route);
+        fireEvent(RouteEvent.TYPE_ROUTE_REGISTERED, route);
+    }
+
+    @Override
+    public CompletableFuture<Route> persistRoute(Route route) {
+        fireEvent(RouteEvent.TYPE_ROUTE_PERSISTING, route);
+        CompletableFuture<Route> future = new CompletableFuture<>();
+        new RoutePersistenceJob((RouteImpl) route, RouteEvent.TYPE_ROUTE_PERSISTING, future).schedule();
+        future.thenAccept(r -> fireEvent(RouteEvent.TYPE_ROUTE_PERSISTED, r));
+        return future;
+    }
+
+    @Override
+    public CompletableFuture<Route> discardRoute(Route route) {
+        fireEvent(RouteEvent.TYPE_ROUTE_DISCARDING, route);
+        CompletableFuture<Route> result = new CompletableFuture<>();
+        RouteImpl ri = (RouteImpl) route;
+        result.thenAccept(r -> {
+            routes.remove(route);
+            fireEvent(RouteEvent.TYPE_ROUTE_DISCARDED, route);
+        });
+        if (ri.backend() != null) {
+            new RoutePersistenceJob(ri, RouteEvent.TYPE_ROUTE_DISCARDING, result).schedule();
+        } else {
+            result.complete(route);
+        }
+        return result;
+    }
+
+    @Override
+    public List<Route> listRoutes() {
+        return unmodifiableRoutes;
+    }
+
+    @Override
+    public void registerRouter(Router router) {
+        routers.add(router);
+        fireEvent(RouteEvent.TYPE_ROUTER_REGISTERED, router);
+    }
+
+    @Override
+    public void unregisterRouter(Router router) {
+        routers.add(router);
+        fireEvent(RouteEvent.TYPE_ROUTER_UNREGISTERED, router);
+    }
+
+    public List<Router> routers() {
+        return unmodifiableRouters;
+    }
+
+    void fireEvent(int type, Object obj) {
+        RouteEvent e = new RouteEvent(this, type, obj);
+        LOGGER.info("firing route event {}", e);
+        listeners.forEach(l -> {
+            try {
+                l.handleEvent(e);
+            } catch (Exception | LinkageError | AssertionError ex) {
+                LOGGER.error("Failed to invoke RouteListener {}", l, ex);
+            }
+        });
+    }
+
+}