X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=swt%2Forg.argeo.app.swt%2Fsrc%2Forg%2Fargeo%2Fapp%2Fswt%2Fux%2FSwtArgeoApp.java;h=d123aa09c1821b447b76d948c8eebe53b8eaf1c0;hb=616a062e9d061ce91589556624ed622298a21ac7;hp=733c0db6d6d4d1b922d7709377eb8946eca13cbe;hpb=e007028c81cf85df3c23b761856779427f86a633;p=gpl%2Fargeo-suite.git diff --git a/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/SwtArgeoApp.java b/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/SwtArgeoApp.java index 733c0db..d123aa0 100644 --- a/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/SwtArgeoApp.java +++ b/swt/org.argeo.app.swt/src/org/argeo/app/swt/ux/SwtArgeoApp.java @@ -1,34 +1,680 @@ package org.argeo.app.swt.ux; +import static org.argeo.api.cms.ux.CmsView.CMS_VIEW_UID_PROPERTY; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.TreeMap; + +import javax.xml.namespace.QName; +import org.argeo.api.acr.Content; +import org.argeo.api.acr.ContentRepository; +import org.argeo.api.acr.spi.ProvidedSession; +import org.argeo.api.app.AppUserState; +import org.argeo.api.app.EntityConstants; +import org.argeo.api.app.EntityName; +import org.argeo.api.app.EntityType; +import org.argeo.api.app.RankedObject; +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsEvent; +import org.argeo.api.cms.CmsEventSubscriber; +import org.argeo.api.cms.CmsLog; +import org.argeo.api.cms.CmsSession; +import org.argeo.api.cms.ux.CmsTheme; import org.argeo.api.cms.ux.CmsUi; +import org.argeo.api.cms.ux.CmsView; import org.argeo.app.ux.AbstractArgeoApp; +import org.argeo.app.ux.AppUi; +import org.argeo.app.ux.SuiteUxEvent; +import org.argeo.cms.LocaleUtils; +import org.argeo.cms.Localized; +import org.argeo.cms.swt.CmsSwtUtils; +import org.argeo.cms.swt.acr.SwtUiProvider; +import org.argeo.cms.swt.dialogs.CmsFeedback; +import org.argeo.cms.util.LangUtils; +import org.argeo.cms.ux.CmsUxUtils; +import org.argeo.eclipse.ui.specific.UiContext; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.widgets.Composite; +import org.osgi.framework.Constants; + +/** The Argeo Suite App. */ +public class SwtArgeoApp extends AbstractArgeoApp implements CmsEventSubscriber { + private final static CmsLog log = CmsLog.getLog(SwtArgeoApp.class); + + public final static String PUBLIC_BASE_PATH_PROPERTY = "publicBasePath"; + public final static String DEFAULT_UI_NAME_PROPERTY = "defaultUiName"; + public final static String DEFAULT_THEME_ID_PROPERTY = "defaultThemeId"; + public final static String DEFAULT_LAYER_PROPERTY = "defaultLayer"; + public final static String SHARED_PID_PREFIX_PROPERTY = "sharedPidPrefix"; + + private final static String LOGIN = "login"; + private final static String HOME_STATE = "~"; + + private String publicBasePath = null; + + private String appPid; + private String pidPrefix; + private String sharedPidPrefix; + +// private String headerPid; +// private String footerPid; +// private String leadPanePid; +// private String adminLeadPanePid; +// private String loginScreenPid; + + private String defaultUiName = "app"; + private String adminUiName = "admin"; + + // FIXME such default names make refactoring more dangerous + @Deprecated + private String defaultLayerPid = "argeo.suite.ui.dashboardLayer"; + @Deprecated + private String defaultThemeId = "org.argeo.app.theme.default"; + + // TODO use QName as key for byType + private Map> uiProvidersByPid = Collections.synchronizedMap(new HashMap<>()); + private Map> uiProvidersByType = Collections.synchronizedMap(new HashMap<>()); + private Map> layersByPid = Collections.synchronizedSortedMap(new TreeMap<>()); + private Map> layersByType = Collections.synchronizedSortedMap(new TreeMap<>()); + +// private CmsUserManager cmsUserManager; + + // TODO make more optimal or via CmsSession/CmsView + private static Timer janitorTimer = new Timer(true); + private Map> managedUis = new HashMap<>(); + + // ACR + private ContentRepository contentRepository; + private AppUserState appUserState; + // JCR +// private Repository repository; + + public void start(Map properties) { + for (SuiteUxEvent event : SuiteUxEvent.values()) { + getCmsContext().getCmsEventBus().addEventSubscriber(event.topic(), this); + } + + if (properties.containsKey(DEFAULT_UI_NAME_PROPERTY)) + defaultUiName = LangUtils.get(properties, DEFAULT_UI_NAME_PROPERTY); + if (properties.containsKey(DEFAULT_THEME_ID_PROPERTY)) + defaultThemeId = LangUtils.get(properties, DEFAULT_THEME_ID_PROPERTY); + if (properties.containsKey(DEFAULT_LAYER_PROPERTY)) + defaultLayerPid = LangUtils.get(properties, DEFAULT_LAYER_PROPERTY); + sharedPidPrefix = LangUtils.get(properties, SHARED_PID_PREFIX_PROPERTY); + publicBasePath = LangUtils.get(properties, PUBLIC_BASE_PATH_PROPERTY); + + if (properties.containsKey(Constants.SERVICE_PID)) { + appPid = properties.get(Constants.SERVICE_PID).toString(); + int lastDotIndex = appPid.lastIndexOf('.'); + if (lastDotIndex >= 0) { + pidPrefix = appPid.substring(0, lastDotIndex); + } + } else { + // TODO does it make sense to accept that? + appPid = ""; + } + Objects.requireNonNull(contentRepository, "Content repository must be provided"); + Objects.requireNonNull(appUserState, "App user state must be provided"); + + long janitorPeriod = 60 * 60 * 1000;// 1h + // long janitorPeriod = 60 * 1000;// min + janitorTimer.schedule(new TimerTask() { + + @Override + public void run() { + try { + // copy Map in order to avoid concurrent modification exception + Iterator>> uiRefs = new HashMap<>(managedUis).entrySet() + .iterator(); + refs: while (uiRefs.hasNext()) { + Map.Entry> entry = uiRefs.next(); + String uiUuid = entry.getKey(); + WeakReference uiRef = entry.getValue(); + SwtAppUi ui = uiRef.get(); + if (ui == null) { + if (log.isTraceEnabled()) + log.warn("Unreferenced UI " + uiUuid + " in " + appPid + ", removing it"); + managedUis.remove(uiUuid); + continue refs; + } + if (!ui.isDisposed() && !ui.getDisplay().isDisposed()) { + if (ui.isTimedOut()) { + if (log.isTraceEnabled()) + log.trace("Killing timed-out UI " + uiUuid + " in " + appPid); + UiContext.killDisplay(ui.getDisplay()); + } + } else { + if (log.isTraceEnabled()) + log.warn("Disposed UI " + uiUuid + " still referenced in " + appPid + ", removing it"); + managedUis.remove(uiUuid); + } + } + if (log.isTraceEnabled()) + log.trace(managedUis.size() + " UIs being managed by app " + appPid); + } catch (Exception e) { + log.error("Could not clean up timed-out UIs", e); + } + } + }, janitorPeriod, janitorPeriod); + + if (log.isDebugEnabled()) + log.info("Argeo Suite App " + appPid + " started"); + } + + public void stop(Map properties) { + refs: for (WeakReference uiRef : managedUis.values()) { + SwtAppUi ui = uiRef.get(); + if (ui == null) + continue refs; + if (!ui.isDisposed() && !ui.getDisplay().isDisposed()) { + ui.getDisplay().syncExec(() -> ui.dispose()); + } + } + managedUis.clear(); + if (log.isDebugEnabled()) + log.info("Argeo Suite App stopped"); + + } -public class SwtArgeoApp extends AbstractArgeoApp { @Override public Set getUiNames() { - // TODO Auto-generated method stub - return null; + HashSet uiNames = new HashSet<>(); + uiNames.add(defaultUiName); + uiNames.add(adminUiName); + return uiNames; } @Override - public CmsUi initUi(Object uiParent) { - // TODO Auto-generated method stub - return null; + public CmsUi initUi(Object parent) { + Composite uiParent = (Composite) parent; + String uiName = uiParent.getData(UI_NAME_PROPERTY) != null ? uiParent.getData(UI_NAME_PROPERTY).toString() + : null; + CmsView cmsView = CmsSwtUtils.getCmsView(uiParent); + if (cmsView == null) + throw new IllegalStateException("No CMS view is registered."); + CmsTheme theme = getTheme(uiName); + if (theme != null) + CmsSwtUtils.registerCmsTheme(uiParent.getShell(), theme); + SwtAppUi argeoSuiteUi = new SwtAppUi(uiParent, SWT.INHERIT_DEFAULT); + // TODO make timeout configurable + argeoSuiteUi.setUiTimeout(6 * 60 * 60 * 1000);// 6 hours + // argeoSuiteUi.setUiTimeout(60 * 1000);// 1 min + String uid = cmsView.getUid(); + argeoSuiteUi.addDisposeListener(new CleanUpUi(uid)); + managedUis.put(uid, new WeakReference<>(argeoSuiteUi)); + return argeoSuiteUi; + } + + @Override + public String getThemeId(String uiName) { + String themeId = System.getProperty("org.argeo.app.theme.default"); + if (themeId != null) + return themeId; + return defaultThemeId; } @Override public void refreshUi(CmsUi cmsUi, String state) { - // TODO Auto-generated method stub + try { + Content context = null; + SwtAppUi ui = (SwtAppUi) cmsUi; + ui.updateLastAccess(); + + String uiName = Objects.toString(ui.getParent().getData(UI_NAME_PROPERTY), null); + if (uiName == null) + throw new IllegalStateException("UI name should not be null"); + CmsView cmsView = CmsSwtUtils.getCmsView(ui); + + ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository, cmsView); + + SwtUiProvider headerUiProvider = findStructuralUiProvider(SwtAppUi.Structural.header.name()); + SwtUiProvider footerUiProvider = findStructuralUiProvider(SwtAppUi.Structural.footer.name()); + SwtUiProvider leadPaneUiProvider; + if (adminUiName.equals(uiName)) { + leadPaneUiProvider = findStructuralUiProvider(SwtAppUi.Structural.adminLeadPane.name()); + } else { + leadPaneUiProvider = findStructuralUiProvider(SwtAppUi.Structural.leadPane.name()); + } + + Localized appTitle = null; + if (headerUiProvider instanceof DefaultHeader) { + appTitle = ((DefaultHeader) headerUiProvider).getTitle(); + } + ui.setTitle(appTitle); + + if (cmsView.isAnonymous() && publicBasePath == null) {// internal app, must login + ui.logout(); + ui.setLoginScreen(true); + if (headerUiProvider != null) + refreshPart(headerUiProvider, ui.getHeader(), context); + ui.refreshBelowHeader(false); + SwtUiProvider loginScreenUiProvider = findStructuralUiProvider(SwtAppUi.Structural.loginScreen.name()); + refreshPart(loginScreenUiProvider, ui.getBelowHeader(), context); + if (footerUiProvider != null) + refreshPart(footerUiProvider, ui.getFooter(), context); + ui.layout(true, true); + setState(ui, LOGIN); + } else { + if (LOGIN.equals(state)) + state = null; + if (ui.isLoginScreen()) { + ui.setLoginScreen(false); + } + CmsSession cmsSession = cmsView.getCmsSession(); + if (ui.getUserDir() == null) { + // FIXME NPE on CMSSession when logging in from anonymous + if (cmsSession == null || cmsView.isAnonymous()) { + assert publicBasePath != null; + Content userDir = contentSession + .get(Content.ROOT_PATH + CmsConstants.SYS_WORKSPACE + publicBasePath); + ui.setUserDir(userDir); + } else { + Content userDir = appUserState.getOrCreateSessionDir(cmsSession); + ui.setUserDir(userDir); +// Node userDirNode = jcrContentProvider.doInAdminSession((adminSession) -> { +// Node node = SuiteUtils.getOrCreateCmsSessionNode(adminSession, cmsSession); +// return node; +// }); +// Content userDir = contentSession +// .get(ContentUtils.SLASH + CmsConstants.SYS_WORKSPACE + userDirNode.getPath()); +// ui.setUserDir(userDir); + } + } + initLocale(cmsSession); + context = stateToNode(ui, state); + if (context == null) + context = ui.getUserDir(); + + if (headerUiProvider != null) + refreshPart(headerUiProvider, ui.getHeader(), context); + ui.refreshBelowHeader(true); + for (String key : layersByPid.keySet()) { + SwtAppLayer layer = layersByPid.get(key).get(); + ui.addLayer(key, layer); + } + + if (leadPaneUiProvider != null) + refreshPart(leadPaneUiProvider, ui.getLeadPane(), context); + if (footerUiProvider != null) + refreshPart(footerUiProvider, ui.getFooter(), context); + ui.layout(true, true); + setState(ui, state != null ? state : defaultLayerPid); + } + } catch (Exception e) { + CmsFeedback.error("Unexpected exception", e); + } + } + + private void initLocale(CmsSession cmsSession) { + if (cmsSession == null) + return; + Locale locale = cmsSession.getLocale(); + UiContext.setLocale(locale); + LocaleUtils.setThreadLocale(locale); + + } + + private void refreshPart(SwtUiProvider uiProvider, Composite part, Content context) { + CmsSwtUtils.clear(part); + uiProvider.createUiPart(part, context); + } + + private SwtUiProvider findStructuralUiProvider(String suffix) { + SwtUiProvider res = null; + if (pidPrefix != null) + res = findUiProvider(pidPrefix + "." + suffix); + if (res != null) + return res; + if (sharedPidPrefix != null) + res = findUiProvider(sharedPidPrefix + "." + suffix); + return res; + } + + private SwtUiProvider findUiProvider(String pid) { + if (!uiProvidersByPid.containsKey(pid)) + return null; + return uiProvidersByPid.get(pid).get(); + } + + private SwtAppLayer findLayer(String pid) { + if (!layersByPid.containsKey(pid)) + return null; + return layersByPid.get(pid).get(); + } + + private List listTypes(Map byType, Content content) { + if (content == null) + throw new IllegalArgumentException("A content should be provided"); + List types = new ArrayList<>(); + if (content.hasContentClass(EntityType.entity.qName())) { + String type = content.attr(EntityName.type.qName()); + if (type != null && byType.containsKey(type)) + types.add(type); + } + List objectClasses = content.getContentClasses(); + for (QName cc : objectClasses) { + String type = cc.getPrefix() + ":" + cc.getLocalPart(); + if (byType.containsKey(type)) + types.add(type); + } + if (types.isEmpty()) + throw new IllegalArgumentException("No type found for " + content + " (" + objectClasses + ")"); + return types; + } + + private RankedObject findLayerByType(Content content) { + List types = listTypes(layersByType, content); + // we assume the types will be ordered by priority + // (no possible for LDAP at this stage) + for (String type : types) { + if (layersByType.containsKey(type)) + return layersByType.get(type); + } + throw new IllegalArgumentException("No layer found for " + content + " with type " + types); + } + + private RankedObject findUiProviderByType(Content content) { + RankedObject layerRO = findLayerByType(content); + List layerTypes = LangUtils.toStringList(layerRO.getProperties().get(EntityConstants.TYPE)); + List types = listTypes(uiProvidersByType, content); + // layer types are ordered by priority + for (String type : layerTypes) { + if (types.contains(type) && uiProvidersByType.containsKey(type)) + return uiProvidersByType.get(type); + } + throw new IllegalArgumentException("No UI provider found for " + content + " with types " + types); } @Override public void setState(CmsUi cmsUi, String state) { - // TODO Auto-generated method stub + AppUi ui = (AppUi) cmsUi; + if (state == null) + return; + if (!state.startsWith("/")) { + if (LOGIN.equals(state)) { + String appTitle = ""; + if (ui.getTitle() != null) + appTitle = ui.getTitle().lead(); + ui.getCmsView().stateChanged(state, appTitle); + return; + } + Map properties = new HashMap<>(); + String layerId = HOME_STATE.equals(state) ? defaultLayerPid : state; + properties.put(SuiteUxEvent.LAYER, layerId); + properties.put(SuiteUxEvent.CONTENT_PATH, HOME_STATE); + ui.getCmsView().sendEvent(SuiteUxEvent.switchLayer.topic(), properties); + return; + } + if (ui.isLoginScreen()) { + return; + } + + Content node = stateToNode(ui, state); + if (node == null) { + ui.getCmsView().navigateTo(HOME_STATE); + } else { + ui.getCmsView().sendEvent(SuiteUxEvent.switchLayer.topic(), SuiteUxEvent.eventProperties(node)); + ui.getCmsView().sendEvent(SuiteUxEvent.refreshPart.topic(), SuiteUxEvent.eventProperties(node)); + } + } + + // TODO move it to an internal package? + private static String nodeToState(Content node) { + return node.getPath(); + } + + private Content stateToNode(CmsUi suiteUi, String state) { + if (suiteUi == null) + return null; + if (state == null || !state.startsWith("/")) + return null; + + String path = state; + + ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository, + suiteUi.getCmsView()); + return contentSession.get(path); + } + + /* + * Events management + */ + + @Override + public void onEvent(String topic, Map event) { + + // Specific UI related events + SwtAppUi ui = getRelatedUi(event); + if (ui == null) + return; + ui.updateLastAccess(); + ui.getCmsView().runAs(() -> { + try { + String appTitle = ""; + if (ui.getTitle() != null) + appTitle = ui.getTitle().lead(); + + if (isTopic(topic, SuiteUxEvent.refreshPart)) { + Content content = getContentFromEvent(ui, event); + if (content == null) + return; + SwtUiProvider uiProvider = findUiProviderByType(content).get(); + SwtAppLayer layer = findLayerByType(content).get(); + ui.switchToLayer(layer, content); + layer.view(uiProvider, ui.getCurrentWorkArea(), content); + ui.getCmsView().stateChanged(nodeToState(content), + stateTitle(appTitle, CmsUxUtils.getTitle(content))); + } else if (isTopic(topic, SuiteUxEvent.openNewPart)) { + Content content = getContentFromEvent(ui, event); + if (content == null) + return; + SwtUiProvider uiProvider = findUiProviderByType(content).get(); + SwtAppLayer layer = findLayerByType(content).get(); + ui.switchToLayer(layer, content); + layer.open(uiProvider, ui.getCurrentWorkArea(), content); + ui.getCmsView().stateChanged(nodeToState(content), + stateTitle(appTitle, CmsUxUtils.getTitle(content))); + } else if (isTopic(topic, SuiteUxEvent.switchLayer)) { + String layerId = get(event, SuiteUxEvent.LAYER); + if (layerId != null && !"".equals(layerId.trim())) { + SwtAppLayer suiteLayer = findLayer(layerId); + if (suiteLayer == null) + throw new IllegalArgumentException("No layer '" + layerId + "' available."); + Localized layerTitle = suiteLayer.getTitle(); + // FIXME make sure we don't rebuild the work area twice + Composite workArea = ui.switchToLayer(layerId, ui.getUserDir()); + String title = null; + if (layerTitle != null) + title = layerTitle.lead(); + Content nodeFromState = getContentFromEvent(ui, event); + if (nodeFromState != null && nodeFromState.getPath().equals(ui.getUserDir().getPath())) { + // default layer view is forced + String state = defaultLayerPid.equals(layerId) ? "~" : layerId; + ui.getCmsView().stateChanged(state, stateTitle(appTitle, title)); + suiteLayer.view(null, workArea, nodeFromState); + } else { + Content layerCurrentContext = suiteLayer.getCurrentContext(workArea); + if (layerCurrentContext != null && !layerCurrentContext.equals(ui.getUserDir())) { + // layer was already showing a context so we set the state to it + ui.getCmsView().stateChanged(nodeToState(layerCurrentContext), + stateTitle(appTitle, CmsUxUtils.getTitle(layerCurrentContext))); + } else { + // no context was shown + ui.getCmsView().stateChanged(layerId, stateTitle(appTitle, title)); + } + } + } else { + Content content = getContentFromEvent(ui, event); + if (content != null) { + SwtAppLayer layer = findLayerByType(content).get(); + ui.switchToLayer(layer, content); + } + } + } + } catch (Exception e) { + CmsFeedback.error("Cannot handle event " + topic + " " + event, e); +// log.error("Cannot handle event " + event, e); + } + }); + } + + private String stateTitle(String appTitle, String additionalTitle) { + return additionalTitle == null ? appTitle : appTitle + " - " + additionalTitle; + } + + private boolean isTopic(String topic, CmsEvent cmsEvent) { + Objects.requireNonNull(topic); + return topic.equals(cmsEvent.topic()); + } + + protected Content getContentFromEvent(SwtAppUi ui, Map event) { + ProvidedSession contentSession = (ProvidedSession) CmsUxUtils.getContentSession(contentRepository, + ui.getCmsView()); + + String path = get(event, SuiteUxEvent.CONTENT_PATH); + + if (path != null && (path.equals(HOME_STATE) || path.equals(""))) + return ui.getUserDir(); + Content node; + if (path == null) { + return null; + } else { + node = contentSession.get(path); + } + return node; + } + + private SwtAppUi getRelatedUi(Map eventProperties) { + WeakReference uiRef = managedUis.get(get(eventProperties, CMS_VIEW_UID_PROPERTY)); + if (uiRef == null) + return null; + return uiRef.get(); + } + + public static String get(Map eventProperties, String key) { + Object value = eventProperties.get(key); + if (value == null) + return null; + return value.toString(); } + /* + * Dependency injection. + */ + + public void addUiProvider(SwtUiProvider uiProvider, Map properties) { + if (properties.containsKey(Constants.SERVICE_PID)) { + String pid = (String) properties.get(Constants.SERVICE_PID); + RankedObject.putIfHigherRank(uiProvidersByPid, pid, uiProvider, properties); + } + if (properties.containsKey(EntityConstants.TYPE)) { + List types = LangUtils.toStringList(properties.get(EntityConstants.TYPE)); + for (String type : types) { + RankedObject.putIfHigherRank(uiProvidersByType, type, uiProvider, properties); + } + } + } + + public void removeUiProvider(SwtUiProvider uiProvider, Map properties) { + if (properties.containsKey(Constants.SERVICE_PID)) { + String pid = (String) properties.get(Constants.SERVICE_PID); + if (uiProvidersByPid.containsKey(pid)) { + if (uiProvidersByPid.get(pid).equals(new RankedObject(uiProvider, properties))) { + uiProvidersByPid.remove(pid); + } + } + } + if (properties.containsKey(EntityConstants.TYPE)) { + List types = LangUtils.toStringList(properties.get(EntityConstants.TYPE)); + for (String type : types) { + if (uiProvidersByType.containsKey(type)) { + if (uiProvidersByType.get(type).equals(new RankedObject(uiProvider, properties))) { + uiProvidersByType.remove(type); + } + } + } + } + } + + public void addLayer(SwtAppLayer layer, Map properties) { + if (properties.containsKey(Constants.SERVICE_PID)) { + String pid = (String) properties.get(Constants.SERVICE_PID); + RankedObject.putIfHigherRank(layersByPid, pid, layer, properties); + } + if (properties.containsKey(EntityConstants.TYPE)) { + // TODO check consistency of entity types with overridden ? + List types = LangUtils.toStringList(properties.get(EntityConstants.TYPE)); + for (String type : types) + RankedObject.putIfHigherRank(layersByType, type, layer, properties); + } + } + + public void removeLayer(SwtAppLayer layer, Map properties) { + if (properties.containsKey(Constants.SERVICE_PID)) { + String pid = (String) properties.get(Constants.SERVICE_PID); + if (layersByPid.containsKey(pid)) { + if (layersByPid.get(pid).equals(new RankedObject(layer, properties))) { + layersByPid.remove(pid); + } + } + } + if (properties.containsKey(EntityConstants.TYPE)) { + List types = LangUtils.toStringList(properties.get(EntityConstants.TYPE)); + for (String type : types) { + if (layersByType.containsKey(type)) { + if (layersByType.get(type).equals(new RankedObject(layer, properties))) { + layersByType.remove(type); + } + } + } + } + } + + public void setContentRepository(ContentRepository contentRepository) { + this.contentRepository = contentRepository; + } + + public void setAppUserState(AppUserState appUserState) { + this.appUserState = appUserState; + } + + /** + * Dedicated class to clean up the UI in order to avoid illegal access issues + * with lambdas. + */ + private class CleanUpUi implements DisposeListener { + private static final long serialVersionUID = 1905900302262082463L; + final String uid; + + public CleanUpUi(String uid) { + this.uid = uid; + } + + @Override + public void widgetDisposed(DisposeEvent e) { + managedUis.remove(uid); + if (log.isDebugEnabled()) + log.debug("App " + appPid + " - Suite UI " + uid + " has been disposed (" + managedUis.size() + + " UIs still being managed)."); + } + + } }