]> git.argeo.org Git - gpl/argeo-suite.git/blob - swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/SwtArgeoApp.java
d123aa09c1821b447b76d948c8eebe53b8eaf1c0
[gpl/argeo-suite.git] / swt / org.argeo.app.swt / src / org / argeo / app / swt / ux / SwtArgeoApp.java
1 package org.argeo.app.swt.ux;
2
3 import static org.argeo.api.cms.ux.CmsView.CMS_VIEW_UID_PROPERTY;
4
5 import java.lang.ref.WeakReference;
6 import java.util.ArrayList;
7 import java.util.Collections;
8 import java.util.HashMap;
9 import java.util.HashSet;
10 import java.util.Iterator;
11 import java.util.List;
12 import java.util.Locale;
13 import java.util.Map;
14 import java.util.Objects;
15 import java.util.Set;
16 import java.util.Timer;
17 import java.util.TimerTask;
18 import java.util.TreeMap;
19
20 import javax.xml.namespace.QName;
21
22 import org.argeo.api.acr.Content;
23 import org.argeo.api.acr.ContentRepository;
24 import org.argeo.api.acr.spi.ProvidedSession;
25 import org.argeo.api.app.AppUserState;
26 import org.argeo.api.app.EntityConstants;
27 import org.argeo.api.app.EntityName;
28 import org.argeo.api.app.EntityType;
29 import org.argeo.api.app.RankedObject;
30 import org.argeo.api.cms.CmsConstants;
31 import org.argeo.api.cms.CmsEvent;
32 import org.argeo.api.cms.CmsEventSubscriber;
33 import org.argeo.api.cms.CmsLog;
34 import org.argeo.api.cms.CmsSession;
35 import org.argeo.api.cms.ux.CmsTheme;
36 import org.argeo.api.cms.ux.CmsUi;
37 import org.argeo.api.cms.ux.CmsView;
38 import org.argeo.app.ux.AbstractArgeoApp;
39 import org.argeo.app.ux.AppUi;
40 import org.argeo.app.ux.SuiteUxEvent;
41 import org.argeo.cms.LocaleUtils;
42 import org.argeo.cms.Localized;
43 import org.argeo.cms.swt.CmsSwtUtils;
44 import org.argeo.cms.swt.acr.SwtUiProvider;
45 import org.argeo.cms.swt.dialogs.CmsFeedback;
46 import org.argeo.cms.util.LangUtils;
47 import org.argeo.cms.ux.CmsUxUtils;
48 import org.argeo.eclipse.ui.specific.UiContext;
49 import org.eclipse.swt.SWT;
50 import org.eclipse.swt.events.DisposeEvent;
51 import org.eclipse.swt.events.DisposeListener;
52 import org.eclipse.swt.widgets.Composite;
53 import org.osgi.framework.Constants;
54
55 /** The Argeo Suite App. */
56 public class SwtArgeoApp extends AbstractArgeoApp implements CmsEventSubscriber {
57 private final static CmsLog log = CmsLog.getLog(SwtArgeoApp.class);
58
59 public final static String PUBLIC_BASE_PATH_PROPERTY = "publicBasePath";
60 public final static String DEFAULT_UI_NAME_PROPERTY = "defaultUiName";
61 public final static String DEFAULT_THEME_ID_PROPERTY = "defaultThemeId";
62 public final static String DEFAULT_LAYER_PROPERTY = "defaultLayer";
63 public final static String SHARED_PID_PREFIX_PROPERTY = "sharedPidPrefix";
64
65 private final static String LOGIN = "login";
66 private final static String HOME_STATE = "~";
67
68 private String publicBasePath = null;
69
70 private String appPid;
71 private String pidPrefix;
72 private String sharedPidPrefix;
73
74 // private String headerPid;
75 // private String footerPid;
76 // private String leadPanePid;
77 // private String adminLeadPanePid;
78 // private String loginScreenPid;
79
80 private String defaultUiName = "app";
81 private String adminUiName = "admin";
82
83 // FIXME such default names make refactoring more dangerous
84 @Deprecated
85 private String defaultLayerPid = "argeo.suite.ui.dashboardLayer";
86 @Deprecated
87 private String defaultThemeId = "org.argeo.app.theme.default";
88
89 // TODO use QName as key for byType
90 private Map<String, RankedObject<SwtUiProvider>> uiProvidersByPid = Collections.synchronizedMap(new HashMap<>());
91 private Map<String, RankedObject<SwtUiProvider>> uiProvidersByType = Collections.synchronizedMap(new HashMap<>());
92 private Map<String, RankedObject<SwtAppLayer>> layersByPid = Collections.synchronizedSortedMap(new TreeMap<>());
93 private Map<String, RankedObject<SwtAppLayer>> layersByType = Collections.synchronizedSortedMap(new TreeMap<>());
94
95 // private CmsUserManager cmsUserManager;
96
97 // TODO make more optimal or via CmsSession/CmsView
98 private static Timer janitorTimer = new Timer(true);
99 private Map<String, WeakReference<SwtAppUi>> managedUis = new HashMap<>();
100
101 // ACR
102 private ContentRepository contentRepository;
103 private AppUserState appUserState;
104 // JCR
105 // private Repository repository;
106
107 public void start(Map<String, Object> properties) {
108 for (SuiteUxEvent event : SuiteUxEvent.values()) {
109 getCmsContext().getCmsEventBus().addEventSubscriber(event.topic(), this);
110 }
111
112 if (properties.containsKey(DEFAULT_UI_NAME_PROPERTY))
113 defaultUiName = LangUtils.get(properties, DEFAULT_UI_NAME_PROPERTY);
114 if (properties.containsKey(DEFAULT_THEME_ID_PROPERTY))
115 defaultThemeId = LangUtils.get(properties, DEFAULT_THEME_ID_PROPERTY);
116 if (properties.containsKey(DEFAULT_LAYER_PROPERTY))
117 defaultLayerPid = LangUtils.get(properties, DEFAULT_LAYER_PROPERTY);
118 sharedPidPrefix = LangUtils.get(properties, SHARED_PID_PREFIX_PROPERTY);
119 publicBasePath = LangUtils.get(properties, PUBLIC_BASE_PATH_PROPERTY);
120
121 if (properties.containsKey(Constants.SERVICE_PID)) {
122 appPid = properties.get(Constants.SERVICE_PID).toString();
123 int lastDotIndex = appPid.lastIndexOf('.');
124 if (lastDotIndex >= 0) {
125 pidPrefix = appPid.substring(0, lastDotIndex);
126 }
127 } else {
128 // TODO does it make sense to accept that?
129 appPid = "<unknown>";
130 }
131 Objects.requireNonNull(contentRepository, "Content repository must be provided");
132 Objects.requireNonNull(appUserState, "App user state must be provided");
133
134 long janitorPeriod = 60 * 60 * 1000;// 1h
135 // long janitorPeriod = 60 * 1000;// min
136 janitorTimer.schedule(new TimerTask() {
137
138 @Override
139 public void run() {
140 try {
141 // copy Map in order to avoid concurrent modification exception
142 Iterator<Map.Entry<String, WeakReference<SwtAppUi>>> uiRefs = new HashMap<>(managedUis).entrySet()
143 .iterator();
144 refs: while (uiRefs.hasNext()) {
145 Map.Entry<String, WeakReference<SwtAppUi>> entry = uiRefs.next();
146 String uiUuid = entry.getKey();
147 WeakReference<SwtAppUi> uiRef = entry.getValue();
148 SwtAppUi ui = uiRef.get();
149 if (ui == null) {
150 if (log.isTraceEnabled())
151 log.warn("Unreferenced UI " + uiUuid + " in " + appPid + ", removing it");
152 managedUis.remove(uiUuid);
153 continue refs;
154 }
155 if (!ui.isDisposed() && !ui.getDisplay().isDisposed()) {
156 if (ui.isTimedOut()) {
157 if (log.isTraceEnabled())
158 log.trace("Killing timed-out UI " + uiUuid + " in " + appPid);
159 UiContext.killDisplay(ui.getDisplay());
160 }
161 } else {
162 if (log.isTraceEnabled())
163 log.warn("Disposed UI " + uiUuid + " still referenced in " + appPid + ", removing it");
164 managedUis.remove(uiUuid);
165 }
166 }
167 if (log.isTraceEnabled())
168 log.trace(managedUis.size() + " UIs being managed by app " + appPid);
169 } catch (Exception e) {
170 log.error("Could not clean up timed-out UIs", e);
171 }
172 }
173 }, janitorPeriod, janitorPeriod);
174
175 if (log.isDebugEnabled())
176 log.info("Argeo Suite App " + appPid + " started");
177 }
178
179 public void stop(Map<String, Object> properties) {
180 refs: for (WeakReference<SwtAppUi> uiRef : managedUis.values()) {
181 SwtAppUi ui = uiRef.get();
182 if (ui == null)
183 continue refs;
184 if (!ui.isDisposed() && !ui.getDisplay().isDisposed()) {
185 ui.getDisplay().syncExec(() -> ui.dispose());
186 }
187 }
188 managedUis.clear();
189 if (log.isDebugEnabled())
190 log.info("Argeo Suite App stopped");
191
192 }
193
194 @Override
195 public Set<String> getUiNames() {
196 HashSet<String> uiNames = new HashSet<>();
197 uiNames.add(defaultUiName);
198 uiNames.add(adminUiName);
199 return uiNames;
200 }
201
202 @Override
203 public CmsUi initUi(Object parent) {
204 Composite uiParent = (Composite) parent;
205 String uiName = uiParent.getData(UI_NAME_PROPERTY) != null ? uiParent.getData(UI_NAME_PROPERTY).toString()
206 : null;
207 CmsView cmsView = CmsSwtUtils.getCmsView(uiParent);
208 if (cmsView == null)
209 throw new IllegalStateException("No CMS view is registered.");
210 CmsTheme theme = getTheme(uiName);
211 if (theme != null)
212 CmsSwtUtils.registerCmsTheme(uiParent.getShell(), theme);
213 SwtAppUi argeoSuiteUi = new SwtAppUi(uiParent, SWT.INHERIT_DEFAULT);
214 // TODO make timeout configurable
215 argeoSuiteUi.setUiTimeout(6 * 60 * 60 * 1000);// 6 hours
216 // argeoSuiteUi.setUiTimeout(60 * 1000);// 1 min
217 String uid = cmsView.getUid();
218 argeoSuiteUi.addDisposeListener(new CleanUpUi(uid));
219 managedUis.put(uid, new WeakReference<>(argeoSuiteUi));
220 return argeoSuiteUi;
221 }
222
223 @Override
224 public String getThemeId(String uiName) {
225 String themeId = System.getProperty("org.argeo.app.theme.default");
226 if (themeId != null)
227 return themeId;
228 return defaultThemeId;
229 }
230
231 @Override
232 public void refreshUi(CmsUi cmsUi, String state) {
233 try {
234 Content context = null;
235 SwtAppUi ui = (SwtAppUi) cmsUi;
236 ui.updateLastAccess();
237
238 String uiName = Objects.toString(ui.getParent().getData(UI_NAME_PROPERTY), null);
239 if (uiName == null)
240 throw new IllegalStateException("UI name should not be null");
241 CmsView cmsView = CmsSwtUtils.getCmsView(ui);
242
243 ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository, cmsView);
244
245 SwtUiProvider headerUiProvider = findStructuralUiProvider(SwtAppUi.Structural.header.name());
246 SwtUiProvider footerUiProvider = findStructuralUiProvider(SwtAppUi.Structural.footer.name());
247 SwtUiProvider leadPaneUiProvider;
248 if (adminUiName.equals(uiName)) {
249 leadPaneUiProvider = findStructuralUiProvider(SwtAppUi.Structural.adminLeadPane.name());
250 } else {
251 leadPaneUiProvider = findStructuralUiProvider(SwtAppUi.Structural.leadPane.name());
252 }
253
254 Localized appTitle = null;
255 if (headerUiProvider instanceof DefaultHeader) {
256 appTitle = ((DefaultHeader) headerUiProvider).getTitle();
257 }
258 ui.setTitle(appTitle);
259
260 if (cmsView.isAnonymous() && publicBasePath == null) {// internal app, must login
261 ui.logout();
262 ui.setLoginScreen(true);
263 if (headerUiProvider != null)
264 refreshPart(headerUiProvider, ui.getHeader(), context);
265 ui.refreshBelowHeader(false);
266 SwtUiProvider loginScreenUiProvider = findStructuralUiProvider(SwtAppUi.Structural.loginScreen.name());
267 refreshPart(loginScreenUiProvider, ui.getBelowHeader(), context);
268 if (footerUiProvider != null)
269 refreshPart(footerUiProvider, ui.getFooter(), context);
270 ui.layout(true, true);
271 setState(ui, LOGIN);
272 } else {
273 if (LOGIN.equals(state))
274 state = null;
275 if (ui.isLoginScreen()) {
276 ui.setLoginScreen(false);
277 }
278 CmsSession cmsSession = cmsView.getCmsSession();
279 if (ui.getUserDir() == null) {
280 // FIXME NPE on CMSSession when logging in from anonymous
281 if (cmsSession == null || cmsView.isAnonymous()) {
282 assert publicBasePath != null;
283 Content userDir = contentSession
284 .get(Content.ROOT_PATH + CmsConstants.SYS_WORKSPACE + publicBasePath);
285 ui.setUserDir(userDir);
286 } else {
287 Content userDir = appUserState.getOrCreateSessionDir(cmsSession);
288 ui.setUserDir(userDir);
289 // Node userDirNode = jcrContentProvider.doInAdminSession((adminSession) -> {
290 // Node node = SuiteUtils.getOrCreateCmsSessionNode(adminSession, cmsSession);
291 // return node;
292 // });
293 // Content userDir = contentSession
294 // .get(ContentUtils.SLASH + CmsConstants.SYS_WORKSPACE + userDirNode.getPath());
295 // ui.setUserDir(userDir);
296 }
297 }
298 initLocale(cmsSession);
299 context = stateToNode(ui, state);
300 if (context == null)
301 context = ui.getUserDir();
302
303 if (headerUiProvider != null)
304 refreshPart(headerUiProvider, ui.getHeader(), context);
305 ui.refreshBelowHeader(true);
306 for (String key : layersByPid.keySet()) {
307 SwtAppLayer layer = layersByPid.get(key).get();
308 ui.addLayer(key, layer);
309 }
310
311 if (leadPaneUiProvider != null)
312 refreshPart(leadPaneUiProvider, ui.getLeadPane(), context);
313 if (footerUiProvider != null)
314 refreshPart(footerUiProvider, ui.getFooter(), context);
315 ui.layout(true, true);
316 setState(ui, state != null ? state : defaultLayerPid);
317 }
318 } catch (Exception e) {
319 CmsFeedback.error("Unexpected exception", e);
320 }
321 }
322
323 private void initLocale(CmsSession cmsSession) {
324 if (cmsSession == null)
325 return;
326 Locale locale = cmsSession.getLocale();
327 UiContext.setLocale(locale);
328 LocaleUtils.setThreadLocale(locale);
329
330 }
331
332 private void refreshPart(SwtUiProvider uiProvider, Composite part, Content context) {
333 CmsSwtUtils.clear(part);
334 uiProvider.createUiPart(part, context);
335 }
336
337 private SwtUiProvider findStructuralUiProvider(String suffix) {
338 SwtUiProvider res = null;
339 if (pidPrefix != null)
340 res = findUiProvider(pidPrefix + "." + suffix);
341 if (res != null)
342 return res;
343 if (sharedPidPrefix != null)
344 res = findUiProvider(sharedPidPrefix + "." + suffix);
345 return res;
346 }
347
348 private SwtUiProvider findUiProvider(String pid) {
349 if (!uiProvidersByPid.containsKey(pid))
350 return null;
351 return uiProvidersByPid.get(pid).get();
352 }
353
354 private SwtAppLayer findLayer(String pid) {
355 if (!layersByPid.containsKey(pid))
356 return null;
357 return layersByPid.get(pid).get();
358 }
359
360 private List<String> listTypes(Map<String, ? extends Object> byType, Content content) {
361 if (content == null)
362 throw new IllegalArgumentException("A content should be provided");
363 List<String> types = new ArrayList<>();
364 if (content.hasContentClass(EntityType.entity.qName())) {
365 String type = content.attr(EntityName.type.qName());
366 if (type != null && byType.containsKey(type))
367 types.add(type);
368 }
369
370 List<QName> objectClasses = content.getContentClasses();
371 for (QName cc : objectClasses) {
372 String type = cc.getPrefix() + ":" + cc.getLocalPart();
373 if (byType.containsKey(type))
374 types.add(type);
375 }
376 if (types.isEmpty())
377 throw new IllegalArgumentException("No type found for " + content + " (" + objectClasses + ")");
378 return types;
379 }
380
381 private RankedObject<SwtAppLayer> findLayerByType(Content content) {
382 List<String> types = listTypes(layersByType, content);
383 // we assume the types will be ordered by priority
384 // (no possible for LDAP at this stage)
385 for (String type : types) {
386 if (layersByType.containsKey(type))
387 return layersByType.get(type);
388 }
389 throw new IllegalArgumentException("No layer found for " + content + " with type " + types);
390 }
391
392 private RankedObject<SwtUiProvider> findUiProviderByType(Content content) {
393 RankedObject<SwtAppLayer> layerRO = findLayerByType(content);
394 List<String> layerTypes = LangUtils.toStringList(layerRO.getProperties().get(EntityConstants.TYPE));
395 List<String> types = listTypes(uiProvidersByType, content);
396 // layer types are ordered by priority
397 for (String type : layerTypes) {
398 if (types.contains(type) && uiProvidersByType.containsKey(type))
399 return uiProvidersByType.get(type);
400 }
401 throw new IllegalArgumentException("No UI provider found for " + content + " with types " + types);
402 }
403
404 @Override
405 public void setState(CmsUi cmsUi, String state) {
406 AppUi ui = (AppUi) cmsUi;
407 if (state == null)
408 return;
409 if (!state.startsWith("/")) {
410 if (LOGIN.equals(state)) {
411 String appTitle = "";
412 if (ui.getTitle() != null)
413 appTitle = ui.getTitle().lead();
414 ui.getCmsView().stateChanged(state, appTitle);
415 return;
416 }
417 Map<String, Object> properties = new HashMap<>();
418 String layerId = HOME_STATE.equals(state) ? defaultLayerPid : state;
419 properties.put(SuiteUxEvent.LAYER, layerId);
420 properties.put(SuiteUxEvent.CONTENT_PATH, HOME_STATE);
421 ui.getCmsView().sendEvent(SuiteUxEvent.switchLayer.topic(), properties);
422 return;
423 }
424 if (ui.isLoginScreen()) {
425 return;
426 }
427
428 Content node = stateToNode(ui, state);
429 if (node == null) {
430 ui.getCmsView().navigateTo(HOME_STATE);
431 } else {
432 ui.getCmsView().sendEvent(SuiteUxEvent.switchLayer.topic(), SuiteUxEvent.eventProperties(node));
433 ui.getCmsView().sendEvent(SuiteUxEvent.refreshPart.topic(), SuiteUxEvent.eventProperties(node));
434 }
435 }
436
437 // TODO move it to an internal package?
438 private static String nodeToState(Content node) {
439 return node.getPath();
440 }
441
442 private Content stateToNode(CmsUi suiteUi, String state) {
443 if (suiteUi == null)
444 return null;
445 if (state == null || !state.startsWith("/"))
446 return null;
447
448 String path = state;
449
450 ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository,
451 suiteUi.getCmsView());
452 return contentSession.get(path);
453 }
454
455 /*
456 * Events management
457 */
458
459 @Override
460 public void onEvent(String topic, Map<String, Object> event) {
461
462 // Specific UI related events
463 SwtAppUi ui = getRelatedUi(event);
464 if (ui == null)
465 return;
466 ui.updateLastAccess();
467 ui.getCmsView().runAs(() -> {
468 try {
469 String appTitle = "";
470 if (ui.getTitle() != null)
471 appTitle = ui.getTitle().lead();
472
473 if (isTopic(topic, SuiteUxEvent.refreshPart)) {
474 Content content = getContentFromEvent(ui, event);
475 if (content == null)
476 return;
477 SwtUiProvider uiProvider = findUiProviderByType(content).get();
478 SwtAppLayer layer = findLayerByType(content).get();
479 ui.switchToLayer(layer, content);
480 layer.view(uiProvider, ui.getCurrentWorkArea(), content);
481 ui.getCmsView().stateChanged(nodeToState(content),
482 stateTitle(appTitle, CmsUxUtils.getTitle(content)));
483 } else if (isTopic(topic, SuiteUxEvent.openNewPart)) {
484 Content content = getContentFromEvent(ui, event);
485 if (content == null)
486 return;
487 SwtUiProvider uiProvider = findUiProviderByType(content).get();
488 SwtAppLayer layer = findLayerByType(content).get();
489 ui.switchToLayer(layer, content);
490 layer.open(uiProvider, ui.getCurrentWorkArea(), content);
491 ui.getCmsView().stateChanged(nodeToState(content),
492 stateTitle(appTitle, CmsUxUtils.getTitle(content)));
493 } else if (isTopic(topic, SuiteUxEvent.switchLayer)) {
494 String layerId = get(event, SuiteUxEvent.LAYER);
495 if (layerId != null && !"".equals(layerId.trim())) {
496 SwtAppLayer suiteLayer = findLayer(layerId);
497 if (suiteLayer == null)
498 throw new IllegalArgumentException("No layer '" + layerId + "' available.");
499 Localized layerTitle = suiteLayer.getTitle();
500 // FIXME make sure we don't rebuild the work area twice
501 Composite workArea = ui.switchToLayer(layerId, ui.getUserDir());
502 String title = null;
503 if (layerTitle != null)
504 title = layerTitle.lead();
505 Content nodeFromState = getContentFromEvent(ui, event);
506 if (nodeFromState != null && nodeFromState.getPath().equals(ui.getUserDir().getPath())) {
507 // default layer view is forced
508 String state = defaultLayerPid.equals(layerId) ? "~" : layerId;
509 ui.getCmsView().stateChanged(state, stateTitle(appTitle, title));
510 suiteLayer.view(null, workArea, nodeFromState);
511 } else {
512 Content layerCurrentContext = suiteLayer.getCurrentContext(workArea);
513 if (layerCurrentContext != null && !layerCurrentContext.equals(ui.getUserDir())) {
514 // layer was already showing a context so we set the state to it
515 ui.getCmsView().stateChanged(nodeToState(layerCurrentContext),
516 stateTitle(appTitle, CmsUxUtils.getTitle(layerCurrentContext)));
517 } else {
518 // no context was shown
519 ui.getCmsView().stateChanged(layerId, stateTitle(appTitle, title));
520 }
521 }
522 } else {
523 Content content = getContentFromEvent(ui, event);
524 if (content != null) {
525 SwtAppLayer layer = findLayerByType(content).get();
526 ui.switchToLayer(layer, content);
527 }
528 }
529 }
530 } catch (Exception e) {
531 CmsFeedback.error("Cannot handle event " + topic + " " + event, e);
532 // log.error("Cannot handle event " + event, e);
533 }
534 });
535 }
536
537 private String stateTitle(String appTitle, String additionalTitle) {
538 return additionalTitle == null ? appTitle : appTitle + " - " + additionalTitle;
539 }
540
541 private boolean isTopic(String topic, CmsEvent cmsEvent) {
542 Objects.requireNonNull(topic);
543 return topic.equals(cmsEvent.topic());
544 }
545
546 protected Content getContentFromEvent(SwtAppUi ui, Map<String, Object> event) {
547 ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository,
548 ui.getCmsView());
549
550 String path = get(event, SuiteUxEvent.CONTENT_PATH);
551
552 if (path != null && (path.equals(HOME_STATE) || path.equals("")))
553 return ui.getUserDir();
554 Content node;
555 if (path == null) {
556 return null;
557 } else {
558 node = contentSession.get(path);
559 }
560 return node;
561 }
562
563 private SwtAppUi getRelatedUi(Map<String, Object> eventProperties) {
564 WeakReference<SwtAppUi> uiRef = managedUis.get(get(eventProperties, CMS_VIEW_UID_PROPERTY));
565 if (uiRef == null)
566 return null;
567 return uiRef.get();
568 }
569
570 public static String get(Map<String, Object> eventProperties, String key) {
571 Object value = eventProperties.get(key);
572 if (value == null)
573 return null;
574 return value.toString();
575
576 }
577
578 /*
579 * Dependency injection.
580 */
581
582 public void addUiProvider(SwtUiProvider uiProvider, Map<String, Object> properties) {
583 if (properties.containsKey(Constants.SERVICE_PID)) {
584 String pid = (String) properties.get(Constants.SERVICE_PID);
585 RankedObject.putIfHigherRank(uiProvidersByPid, pid, uiProvider, properties);
586 }
587 if (properties.containsKey(EntityConstants.TYPE)) {
588 List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
589 for (String type : types) {
590 RankedObject.putIfHigherRank(uiProvidersByType, type, uiProvider, properties);
591 }
592 }
593 }
594
595 public void removeUiProvider(SwtUiProvider uiProvider, Map<String, Object> properties) {
596 if (properties.containsKey(Constants.SERVICE_PID)) {
597 String pid = (String) properties.get(Constants.SERVICE_PID);
598 if (uiProvidersByPid.containsKey(pid)) {
599 if (uiProvidersByPid.get(pid).equals(new RankedObject<SwtUiProvider>(uiProvider, properties))) {
600 uiProvidersByPid.remove(pid);
601 }
602 }
603 }
604 if (properties.containsKey(EntityConstants.TYPE)) {
605 List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
606 for (String type : types) {
607 if (uiProvidersByType.containsKey(type)) {
608 if (uiProvidersByType.get(type).equals(new RankedObject<SwtUiProvider>(uiProvider, properties))) {
609 uiProvidersByType.remove(type);
610 }
611 }
612 }
613 }
614 }
615
616 public void addLayer(SwtAppLayer layer, Map<String, Object> properties) {
617 if (properties.containsKey(Constants.SERVICE_PID)) {
618 String pid = (String) properties.get(Constants.SERVICE_PID);
619 RankedObject.putIfHigherRank(layersByPid, pid, layer, properties);
620 }
621 if (properties.containsKey(EntityConstants.TYPE)) {
622 // TODO check consistency of entity types with overridden ?
623 List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
624 for (String type : types)
625 RankedObject.putIfHigherRank(layersByType, type, layer, properties);
626 }
627 }
628
629 public void removeLayer(SwtAppLayer layer, Map<String, Object> properties) {
630 if (properties.containsKey(Constants.SERVICE_PID)) {
631 String pid = (String) properties.get(Constants.SERVICE_PID);
632 if (layersByPid.containsKey(pid)) {
633 if (layersByPid.get(pid).equals(new RankedObject<SwtAppLayer>(layer, properties))) {
634 layersByPid.remove(pid);
635 }
636 }
637 }
638 if (properties.containsKey(EntityConstants.TYPE)) {
639 List<String> types = LangUtils.toStringList(properties.get(EntityConstants.TYPE));
640 for (String type : types) {
641 if (layersByType.containsKey(type)) {
642 if (layersByType.get(type).equals(new RankedObject<SwtAppLayer>(layer, properties))) {
643 layersByType.remove(type);
644 }
645 }
646 }
647 }
648 }
649
650 public void setContentRepository(ContentRepository contentRepository) {
651 this.contentRepository = contentRepository;
652 }
653
654 public void setAppUserState(AppUserState appUserState) {
655 this.appUserState = appUserState;
656 }
657
658 /**
659 * Dedicated class to clean up the UI in order to avoid illegal access issues
660 * with lambdas.
661 */
662 private class CleanUpUi implements DisposeListener {
663 private static final long serialVersionUID = 1905900302262082463L;
664 final String uid;
665
666 public CleanUpUi(String uid) {
667 this.uid = uid;
668 }
669
670 @Override
671 public void widgetDisposed(DisposeEvent e) {
672 managedUis.remove(uid);
673 if (log.isDebugEnabled())
674 log.debug("App " + appPid + " - Suite UI " + uid + " has been disposed (" + managedUis.size()
675 + " UIs still being managed).");
676 }
677
678 }
679
680 }