package org.fenixedu.bennu.portal.domain;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.fenixedu.bennu.portal.model.Functionality;
import org.fenixedu.commons.i18n.LocalizedString;
import pt.ist.fenixframework.FenixFramework;
/**
* {@link MenuFunctionality}s are the leafs of the functionality tree. They represent a concrete functionality installed in the
* menu.
*
* @author João Carvalho (joao.pedro.carvalho@tecnico.ulisboa.pt)
*
*/
public final class MenuFunctionality extends MenuFunctionality_Base {
/**
* Creates a new {@link MenuFunctionality} under the given container, based on the given {@link Functionality}.
*
* @param parent
* The parent container for the new item
* @param functionality
* The {@link Functionality} that will provide the require information for this functionality
*
* @throws IllegalArgumentException
* If {@code parent} is null.
*/
public MenuFunctionality(MenuContainer parent, Functionality functionality) {
super();
if (parent == null) {
throw new IllegalArgumentException("MenuFunctionality cannot be created without a parent!");
}
init(parent, functionality.isVisible(), functionality.getAccessGroup(), functionality.getTitle(),
functionality.getDescription(), functionality.getPath());
setDocumentationUrl("{base}");
setItemKey(functionality.getKey());
setProvider(functionality.getProvider());
}
/**
* Creates a new {@link MenuFunctionality} under the given container, based on the provider parameters.
*
* @param parent
* The parent container for the new item
* @param visible
* Whether this functionality is to be visible when rendering the menu
* @param key
* The unique key that represents this functionality
* @param provider
* The provider backend that will be responsible for rendering this functionality
* @param accessGroup
* The expression for this functionality's access group
* @param description
* The textual description for this functionality
* @param title
* The title for this functionality
* @param path
* The semantic-url path for this functionality
*
* @throws IllegalArgumentException
* If {@code parent} is null.
*/
public MenuFunctionality(MenuContainer parent, boolean visible, String key, String provider, String accessGroup,
LocalizedString description, LocalizedString title, String path) {
this(parent, visible, key, provider, accessGroup, description, title, path, "");
}
/**
* Creates a new {@link MenuFunctionality} under the given container, based on the provider parameters.
*
* @param parent
* The parent container for the new item
* @param visible
* Whether this functionality is to be visible when rendering the menu
* @param key
* The unique key that represents this functionality
* @param provider
* The provider backend that will be responsible for rendering this functionality
* @param accessGroup
* The expression for this functionality's access group
* @param description
* The textual description for this functionality
* @param title
* The title for this functionality
* @param path
* The semantic-url path for this functionality
* @param documentationUrl
* The URL for the documentation of this functionality
* @throws IllegalArgumentException
* If {@code parent} is null.
*/
public MenuFunctionality(MenuContainer parent, boolean visible, String key, String provider, String accessGroup,
LocalizedString description, LocalizedString title, String path, String documentationUrl) {
super();
if (parent == null) {
throw new IllegalArgumentException("MenuFunctionality cannot be created without a parent!");
}
init(parent, visible, accessGroup, title, description, path);
setDocumentationUrl(documentationUrl);
setItemKey(key);
setProvider(provider);
}
/*
* Creates a new copy of this functionality under the specified parent.
*/
private MenuFunctionality(MenuContainer newParent, MenuFunctionality original) {
super();
if (newParent == null) {
throw new IllegalArgumentException("MenuFunctionality cannot be created without a parent!");
}
init(newParent, original);
setDocumentationUrl(original.getDocumentationUrl());
setItemKey(original.getItemKey());
setProvider(original.getProvider());
}
/**
* Looks up in the functionality tree, the functionality with the given provider and key.
*
* @param provider
* The provider of the functionality
* @param key
* The key of the functionality
* @return
* The {@link MenuFunctionality} with the given provider and key. {@code null} if no such functionality is installed.
*/
public static MenuFunctionality findFunctionality(String provider, String key) {
String functionality = "$$bennuPortal$$provider:" + provider + "$$bennu;Portal$$key:" + key;
MenuFunctionality target =
cache.computeIfAbsent(functionality,
(funct) -> findFunctionality(PortalConfiguration.getInstance().getMenu(), provider, key));
if (target == null) {
// null is only returned if it wasn't present in the map, and functionality isn't really installed.
return null;
}
// Check if the functionality is still valid. As the key and provider are immutable, we must only
// ensure that the Domain Object still exists.
if (!FenixFramework.isDomainObjectValid(target)) {
cache.remove(functionality, target);
return findFunctionality(provider, key);
}
return target;
}
private static final ConcurrentMap<String, MenuFunctionality> cache = new ConcurrentHashMap<>();
public String resolveLayout() {
if (getLayout() != null) {
return getLayout();
}
return getParent().resolveLayout();
}
@Override
public String getProvider() {
//FIXME: remove when the framework enables read-only slots
return super.getProvider();
}
@Override
public String getItemKey() {
//FIXME: remove when the framework enables read-only slots
return super.getItemKey();
}
public String getParsedDocumentationUrl() {
if (this.getDocumentationUrl() != null && PortalConfiguration.getInstance().getDocumentationBaseUrl() != null) {
return this.getDocumentationUrl().replaceAll("\\{base\\}",
PortalConfiguration.getInstance().getDocumentationBaseUrl());
}
return "";
}
/**
* "Moves" this functionality to the selected point in the menu.
*
* As {@link MenuItem}s are immutable by default, this instance is deleted, and
* a new copy of it is created under the specified container.
*
* @param newParent
* The new parent in which to insert the copy of this functionality
* @return
* The newly created functionality
*/
@Override
public MenuFunctionality moveTo(MenuContainer newParent) {
MenuFunctionality copy = new MenuFunctionality(newParent, this);
delete();
return copy;
}
// Private methods
private static MenuFunctionality findFunctionality(MenuContainer container, String provider, String key) {
for (MenuItem item : container.getChildSet()) {
if (item.isMenuFunctionality()) {
MenuFunctionality functionality = item.getAsMenuFunctionality();
if (functionality.getProvider().equals(provider) && functionality.getItemKey().equals(key)) {
return functionality;
}
} else {
MenuFunctionality functionality = findFunctionality(item.getAsMenuContainer(), provider, key);
if (functionality != null) {
return functionality;
}
}
}
return null;
}
}