org.opencms.ui.apps.CmsAppHierarchyBuilder Maven / Gradle / Ivy
Show all versions of opencms-test Show documentation
/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* For further information about Alkacon Software, please see the
* company website: http://www.alkacon.com
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.opencms.ui.apps;
import org.opencms.main.CmsLog;
import org.opencms.util.CmsStringUtil;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* Helper class for building a tree of categories/apps which should be displayed from the list of available apps and categories.
*/
public class CmsAppHierarchyBuilder {
/** The logger instance for this tree. */
private static final Log LOG = CmsLog.getLog(CmsAppHierarchyBuilder.class);
/** The list of available categories. */
private List m_appCategoryList = Lists.newArrayList();
/** The list of available app configurations. */
private List m_appConfigs = Lists.newArrayList();
/** The working set of category tree nodes. */
private Map m_nodes = Maps.newHashMap();
/** The root tree node. */
private CmsAppCategoryNode m_rootNode = new CmsAppCategoryNode();
/**
* Adds an app configuration.
*
* @param appConfig the app configuration to add
*/
public void addAppConfiguration(I_CmsWorkplaceAppConfiguration appConfig) {
m_appConfigs.add(appConfig);
}
/**
* Adds an app category.
*
* @param category the app category to add
*/
public void addCategory(I_CmsAppCategory category) {
m_appCategoryList.add(category);
}
/**
* Builds the tree of categories and apps.
*
* This tree will only include those categories which are reachable by following the parent chain of
* an available app configuration up to the root category (null).
*
* @return the root node of the tree
*/
public CmsAppCategoryNode buildHierarchy() {
// STEP 0: Initialize everything and sort categories by priority
Collections.sort(m_appCategoryList, new Comparator() {
public int compare(I_CmsAppCategory cat1, I_CmsAppCategory cat2) {
return ComparisonChain.start().compare(cat1.getPriority(), cat2.getPriority()).result();
}
});
m_rootNode = new CmsAppCategoryNode();
m_nodes.clear();
m_nodes.put(null, m_rootNode);
// STEP 1: Create a node for each category
for (I_CmsAppCategory category : m_appCategoryList) {
m_nodes.put(category.getId(), new CmsAppCategoryNode(category));
}
// STEP 2: Assign category nodes to nodes for their parent category
for (CmsAppCategoryNode node : m_nodes.values()) {
if (node != m_rootNode) {
addNodeToItsParent(node);
}
}
// STEP 3: Assign app configs to category nodes
for (I_CmsWorkplaceAppConfiguration appConfig : m_appConfigs) {
addAppConfigToCategory(appConfig);
}
// STEP 4: Validate whether there are unused categories / apps
Set usedNodes = findReachableNodes(m_rootNode, new HashSet());
if (usedNodes.size() < m_nodes.size()) {
LOG.warn("Unused app categories: " + Sets.difference(m_nodes.keySet(), usedNodes));
}
Set unusedApps = Sets.newHashSet();
for (I_CmsWorkplaceAppConfiguration appConfig : m_appConfigs) {
if (!usedNodes.contains(appConfig.getAppCategory())) {
unusedApps.add(appConfig.getId());
}
}
if (unusedApps.size() > 0) {
LOG.warn("Unused apps: " + unusedApps);
}
// STEP 5: Remove parts of the hierarchy which don't contain any apps
m_rootNode.removeApplessSubtrees();
// STEP 6: Sort all categories and app configurations for each node
m_rootNode.sortRecursively();
return m_rootNode;
}
/**
* Gets the root node.
*
* @return the root node
*/
public CmsAppCategoryNode getRootNode() {
return m_rootNode;
}
/**
* Adds an app configuration to the node belonging to its parent category id.
*
* @param appConfig the app configuration to add to its parent node
*/
protected void addAppConfigToCategory(I_CmsWorkplaceAppConfiguration appConfig) {
CmsAppCategoryNode node = m_nodes.get(appConfig.getAppCategory());
if (node == null) {
LOG.info(
"Missing parent ["
+ appConfig.getAppCategory()
+ "] for "
+ appConfig.getId()
+ " / "
+ appConfig.getClass().getName());
} else {
node.addAppConfiguration(appConfig);
}
}
/**
* Adds a category node to the category node belonging to its parent id.
*
* @param node the node which should be attached to its parent
*/
protected void addNodeToItsParent(CmsAppCategoryNode node) {
String parentId = node.getCategory().getParentId();
if (CmsStringUtil.isEmptyOrWhitespaceOnly(parentId)) {
parentId = null;
}
CmsAppCategoryNode parentNode = m_nodes.get(parentId);
if (parentNode == null) {
LOG.error(
"Missing parent [" + node.getCategory().getParentId() + "] for [" + node.getCategory().getId() + "]");
} else {
parentNode.addChild(node);
}
}
/**
* Finds the category nodes reachable from a node.
*
* @param rootNode the root node
* @param reachableNodes set used for collecting the reachable nodes
*
* @return the set of reachable node ids
*/
private Set findReachableNodes(CmsAppCategoryNode rootNode, HashSet reachableNodes) {
reachableNodes.add(rootNode.getCategory().getId());
for (CmsAppCategoryNode child : rootNode.getChildren()) {
findReachableNodes(child, reachableNodes);
}
return reachableNodes;
}
}