001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.jsp.util; 029 030import org.opencms.file.CmsObject; 031import org.opencms.file.CmsResource; 032import org.opencms.main.CmsException; 033import org.opencms.main.CmsLog; 034import org.opencms.relations.CmsCategory; 035import org.opencms.relations.CmsCategoryService; 036import org.opencms.util.CmsCollectionsGenericWrapper; 037 038import java.util.ArrayList; 039import java.util.Collection; 040import java.util.Iterator; 041import java.util.List; 042import java.util.Map; 043import java.util.regex.Pattern; 044 045import org.apache.commons.collections.Transformer; 046import org.apache.commons.logging.Log; 047 048/** Bean for easy access to categories of a resource in JSPs. */ 049public class CmsJspCategoryAccessBean { 050 051 /** The log object for this class. */ 052 private static final Log LOG = CmsLog.getLog(CmsJspCategoryAccessBean.class); 053 054 /** The wrapped list of categories. */ 055 List<CmsCategory> m_categories; 056 057 /** The path of the main category. All categories of {@link #m_categories} are sub-categories of the main category. */ 058 String m_mainCategoryPath; 059 060 /** Map from the path of a main category to wrappers that hold only the sub-categories of that main category. */ 061 Map<String, CmsJspCategoryAccessBean> m_subCategories; 062 063 /** The CMS context to use. */ 064 private CmsObject m_cms; 065 066 /** 067 * Default constructor. 068 * 069 * @param cms the current {@link CmsObject}. 070 * @param resource the resource for which the categories should be read. 071 */ 072 public CmsJspCategoryAccessBean(CmsObject cms, CmsResource resource) { 073 this(cms, getCategories(cms, resource), ""); 074 } 075 076 /** 077 * Internal constructor for creating wrappers with a subset of the categories. 078 * 079 * @param cms the CMS context to use 080 * @param categories the original categories. 081 * @param mainCategoryPath path of the main category for which only sub-categories should be wrapped. 082 */ 083 CmsJspCategoryAccessBean(CmsObject cms, List<CmsCategory> categories, String mainCategoryPath) { 084 m_cms = cms; 085 m_mainCategoryPath = mainCategoryPath.isEmpty() || mainCategoryPath.endsWith("/") 086 ? mainCategoryPath 087 : mainCategoryPath + "/"; 088 089 if (m_mainCategoryPath.isEmpty()) { 090 m_categories = categories; 091 } else { 092 List<CmsCategory> filteredCategories = new ArrayList<CmsCategory>(); 093 for (CmsCategory category : categories) { 094 if (category.getPath().startsWith(m_mainCategoryPath) 095 && !category.getPath().equals(m_mainCategoryPath)) { 096 filteredCategories.add(category); 097 } 098 } 099 m_categories = filteredCategories; 100 } 101 m_categories = CmsCategoryService.getInstance().localizeCategories( 102 cms, 103 m_categories, 104 cms.getRequestContext().getLocale()); 105 } 106 107 /** 108 * Reads the categories for the given resource. 109 * 110 * @param cms the {@link CmsObject} used for reading the categories. 111 * @param resource the resource for which the categories should be read. 112 * @return the categories assigned to the given resource. 113 */ 114 private static List<CmsCategory> getCategories(CmsObject cms, CmsResource resource) { 115 116 if ((null != resource) && (null != cms)) { 117 try { 118 return CmsCategoryService.getInstance().readResourceCategories(cms, resource); 119 } catch (CmsException e) { 120 LOG.error(e.getLocalizedMessage(), e); 121 } 122 } 123 return new ArrayList<CmsCategory>(0); 124 } 125 126 /** 127 * Checks if the wrapped categories contain a given category. 128 * 129 * @param categoryPath the complete path of the category to check, e.g. "location/africa/" 130 * @return <code>true</code> iff the wrapped categories contain a category with the provided categoryPath. 131 */ 132 public boolean contains(String categoryPath) { 133 134 if ((null != m_categories) && (categoryPath != null)) { 135 return m_categories.stream().anyMatch((c) -> categoryPath.equals(c.getPath())); 136 } 137 return false; 138 } 139 140 /** 141 * Checks if the wrapped categories contain all given categories. 142 * 143 * @param categoryPaths the complete paths of the categories to check, e.g. ["location/africa/", "news/"] 144 * @return <code>true</code> iff for each of the provided categoryPaths, the wrapped categories contain a category with this path. 145 */ 146 public boolean containsAll(Collection<String> categoryPaths) { 147 148 if ((categoryPaths == null) || (categoryPaths.size() == 0)) { 149 return true; 150 } 151 return categoryPaths.stream().allMatch((path) -> this.contains(path)); 152 } 153 154 /** 155 * Checks if the wrapped categories contain at lease one of the given categories. 156 * 157 * @param categoryPaths the complete paths of the categories to check, e.g. ["location/africa/", "news/"] 158 * @return <code>true</code> iff for at least one of the provided categoryPaths, the wrapped categories contain a category with this path. 159 */ 160 public boolean containsAny(Collection<String> categoryPaths) { 161 162 if ((categoryPaths == null) || (categoryPaths.size() == 0)) { 163 return false; 164 } 165 return categoryPaths.stream().anyMatch((path) -> this.contains(path)); 166 } 167 168 /** 169 * Returns all wrapped categories. 170 * 171 * @return all wrapped categories. 172 */ 173 public List<CmsCategory> getAllItems() { 174 175 return m_categories; 176 } 177 178 /** 179 * Returns <code>true</code> if there is no category wrapped, otherwise <code>false</code>. 180 * 181 * @return <code>true</code> if there is no category wrapped, otherwise <code>false</code>. 182 */ 183 public boolean getIsEmpty() { 184 185 return m_categories.isEmpty(); 186 } 187 188 /** 189 * Returns only the leaf categories of the wrapped categories. 190 * 191 * The method assumes that categories are ordered in the list, i.e., parents are directly followed by their children. 192 * 193 * NOTE: In the complete category tree a leaf of the wrapped tree part may not be a leaf. 194 * 195 * @return only the leaf categories of the wrapped categories. 196 */ 197 public List<CmsCategory> getLeafItems() { 198 199 List<CmsCategory> result = new ArrayList<CmsCategory>(); 200 if (m_categories.isEmpty()) { 201 return result; 202 } 203 Iterator<CmsCategory> it = m_categories.iterator(); 204 CmsCategory current = it.next(); 205 while (it.hasNext()) { 206 CmsCategory next = it.next(); 207 if (!next.getPath().startsWith(current.getPath())) { 208 result.add(current); 209 } 210 current = next; 211 } 212 result.add(current); 213 return result; 214 } 215 216 /** 217 * Returns a map from a category path to the wrapper of all the sub-categories of the category with the path given as key. 218 * 219 * @return a map from a category path to all sub-categories of the path's category. 220 */ 221 public Map<String, CmsJspCategoryAccessBean> getSubCategories() { 222 223 if (m_subCategories == null) { 224 m_subCategories = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() { 225 226 @SuppressWarnings("synthetic-access") 227 public Object transform(Object pathPrefix) { 228 229 return new CmsJspCategoryAccessBean(m_cms, m_categories, (String)pathPrefix); 230 } 231 232 }); 233 } 234 return m_subCategories; 235 } 236 237 /** 238 * Returns all categories that are direct children of the current main category. 239 * 240 * @return all categories that are direct children of the current main category. 241 */ 242 public List<CmsCategory> getTopItems() { 243 244 List<CmsCategory> categories = new ArrayList<CmsCategory>(); 245 String matcher = Pattern.quote(m_mainCategoryPath) + "[^/]*/"; 246 for (CmsCategory category : m_categories) { 247 if (category.getPath().matches(matcher)) { 248 categories.add(category); 249 } 250 } 251 return categories; 252 } 253}