]> gerrit.simantics Code Review - simantics/district.git/blob - org.simantics.district.route/src/org/simantics/district/route/internal/RouteServiceImpl.java
Improved Routes view functionality
[simantics/district.git] / org.simantics.district.route / src / org / simantics / district / route / internal / RouteServiceImpl.java
1 package org.simantics.district.route.internal;
2
3 import java.io.Closeable;
4 import java.util.ArrayList;
5 import java.util.Collections;
6 import java.util.List;
7 import java.util.concurrent.CompletableFuture;
8
9 import org.eclipse.core.runtime.ListenerList;
10 import org.simantics.Simantics;
11 import org.simantics.db.ReadGraph;
12 import org.simantics.db.Resource;
13 import org.simantics.db.Session;
14 import org.simantics.db.common.procedure.adapter.DisposableListener;
15 import org.simantics.db.common.procedure.adapter.DisposableSyncListener;
16 import org.simantics.db.layer0.request.PossibleActiveModel;
17 import org.simantics.db.management.ISessionContext;
18 import org.simantics.db.management.ISessionContextChangedListener;
19 import org.simantics.db.management.SessionContextChangedEvent;
20 import org.simantics.district.route.Route;
21 import org.simantics.district.route.RouteEvent;
22 import org.simantics.district.route.RouteService;
23 import org.simantics.district.route.RouteServiceListener;
24 import org.simantics.district.route.Router;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 /**
29  * @author Tuukka Lehtonen
30  */
31 public class RouteServiceImpl implements RouteService, ISessionContextChangedListener, Closeable {
32
33     private final Logger LOGGER = LoggerFactory.getLogger(RouteServiceImpl.class); 
34
35     private ListenerList<RouteServiceListener> listeners = new ListenerList<>();
36     private List<Route> routes = new ArrayList<>();
37     private List<Route> unmodifiableRoutes = Collections.unmodifiableList(routes);
38     private List<Router> routers = new ArrayList<>();
39     private List<Router> unmodifiableRouters = Collections.unmodifiableList(routers);
40
41     private class StoreRoutesListener extends DisposableListener<List<RouteImpl>> {
42         @Override
43         public void execute(List<RouteImpl> result) {
44             resetRoutes(result);
45             fireEvent(RouteEvent.TYPE_ROUTE_SOURCE_CHANGED, RouteServiceImpl.this);
46         }
47
48         @Override
49         public void exception(Throwable t) {
50             LOGGER.error("Failed to listen to current route store routes", t);
51         }
52     }
53
54     private class StoreListener extends DisposableSyncListener<Resource> {
55         @Override
56         public void execute(ReadGraph graph, Resource activeModel) {
57             if (activeModel != null) {
58                 StoreRoutesListener srl = storeRoutesListener;
59                 if (srl != null)
60                     srl.dispose();
61                 Simantics.getSession().asyncRequest(
62                         new RoutePersistence.ModelRoutesRequest(activeModel),
63                         storeRoutesListener = new StoreRoutesListener());
64             } else {
65                 resetRoutes(Collections.emptyList());
66                 fireEvent(RouteEvent.TYPE_ROUTE_SOURCE_CHANGED, RouteServiceImpl.this);
67             }
68         }
69
70         @Override
71         public void exception(ReadGraph graph, Throwable t) {
72             LOGGER.error("Failed to listen to current route service storage", t);
73         }
74     }
75
76     private StoreRoutesListener storeRoutesListener;
77     private StoreListener storeListener;
78
79     private synchronized void listenToActiveModels(Session s) {
80         StoreListener sl = storeListener;
81         if (sl != null)
82             sl.dispose();
83         if (s != null) {
84             s.asyncRequest(
85                     new PossibleActiveModel(Simantics.getProjectResource()),
86                     storeListener = new StoreListener());
87         } else {
88             resetRoutes(Collections.emptyList());
89         }
90     }
91
92     void resetRoutes(List<RouteImpl> newRoutes) {
93         routes.clear();
94         newRoutes.forEach(r -> r.routeService(this));
95         routes.addAll(newRoutes);
96     }
97
98     @Override
99     public void sessionContextChanged(SessionContextChangedEvent event) {
100         ISessionContext ctx = event.getNewValue();
101         Session s = ctx != null ? ctx.getSession() : null;
102         listenToActiveModels(s);
103     }
104
105     public RouteServiceImpl() {
106         Session s = Simantics.peekSession();
107         Simantics.getSessionContextProvider().addContextChangedListener(this);
108         if (s != null)
109             listenToActiveModels(s);
110     }
111
112     @Override
113     public void close() {
114         Simantics.getSessionContextProvider().removeContextChangedListener(this);
115     }
116
117     @Override
118     public void addListener(RouteServiceListener l) {
119         listeners.add(l);
120     }
121
122     @Override
123     public void removeListener(RouteServiceListener l) {
124         listeners.remove(l);
125     }
126
127     @Override
128     public Route createRoute(String name, Object backendModelEntity) {
129         Route r = new RouteImpl(name).modelEntity((Resource) backendModelEntity).routeService(this);
130         fireEvent(RouteEvent.TYPE_ROUTE_CREATED, r);
131         return r;
132     }
133
134     @Override
135     public void registerRoute(Route route) {
136         routes.add(route);
137         fireEvent(RouteEvent.TYPE_ROUTE_REGISTERED, route);
138     }
139
140     @Override
141     public void refreshRoute(Route route) {
142         fireEvent(RouteEvent.TYPE_ROUTE_MODIFIED, route);
143     }
144
145     @Override
146     public CompletableFuture<Route> persistRoute(Route route) {
147         fireEvent(RouteEvent.TYPE_ROUTE_PERSISTING, route);
148         CompletableFuture<Route> future = new CompletableFuture<>();
149         new RoutePersistenceJob((RouteImpl) route, RouteEvent.TYPE_ROUTE_PERSISTING, future).schedule();
150         future.thenAccept(r -> fireEvent(RouteEvent.TYPE_ROUTE_PERSISTED, r));
151         return future;
152     }
153
154     @Override
155     public CompletableFuture<Route> discardRoute(Route route) {
156         fireEvent(RouteEvent.TYPE_ROUTE_DISCARDING, route);
157         CompletableFuture<Route> result = new CompletableFuture<>();
158         RouteImpl ri = (RouteImpl) route;
159         result.thenAccept(r -> {
160             routes.remove(route);
161             fireEvent(RouteEvent.TYPE_ROUTE_DISCARDED, route);
162         });
163         if (ri.backend() != null) {
164             new RoutePersistenceJob(ri, RouteEvent.TYPE_ROUTE_DISCARDING, result).schedule();
165         } else {
166             result.complete(route);
167         }
168         return result;
169     }
170
171     @Override
172     public List<Route> listRoutes() {
173         return unmodifiableRoutes;
174     }
175
176     @Override
177     public void registerRouter(Router router) {
178         routers.add(router);
179         fireEvent(RouteEvent.TYPE_ROUTER_REGISTERED, router);
180     }
181
182     @Override
183     public void unregisterRouter(Router router) {
184         routers.add(router);
185         fireEvent(RouteEvent.TYPE_ROUTER_UNREGISTERED, router);
186     }
187
188     public List<Router> routers() {
189         return unmodifiableRouters;
190     }
191
192     void fireEvent(int type, Object obj) {
193         RouteEvent e = new RouteEvent(this, type, obj);
194         LOGGER.info("firing route event {}", e);
195         listeners.forEach(l -> {
196             try {
197                 l.handleEvent(e);
198             } catch (Exception | LinkageError | AssertionError ex) {
199                 LOGGER.error("Failed to invoke RouteListener {}", l, ex);
200             }
201         });
202     }
203
204 }