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.Iterator;
040import java.util.List;
041import java.util.Map;
042import java.util.regex.Pattern;
043
044import org.apache.commons.collections.Transformer;
045import org.apache.commons.logging.Log;
046
047/** Bean for easy access to categories of a resource in JSPs. */
048public class CmsJspCategoryAccessBean {
049
050    /** The log object for this class. */
051    private static final Log LOG = CmsLog.getLog(CmsJspCategoryAccessBean.class);
052
053    /** The wrapped list of categories. */
054    List<CmsCategory> m_categories;
055
056    /** The path of the main category. All categories of {@link #m_categories} are sub-categories of the main category. */
057    String m_mainCategoryPath;
058
059    /** Map from the path of a main category to wrappers that hold only the sub-categories of that main category. */
060    Map<String, CmsJspCategoryAccessBean> m_subCategories;
061
062    /** The CMS context to use. */
063    private CmsObject m_cms;
064
065    /**
066     * Default constructor.
067     *
068     * @param cms the current {@link CmsObject}.
069     * @param resource the resource for which the categories should be read.
070     */
071    public CmsJspCategoryAccessBean(CmsObject cms, CmsResource resource) {
072        this(cms, getCategories(cms, resource), "");
073    }
074
075    /**
076     * Internal constructor for creating wrappers with a subset of the categories.
077     *
078     * @param cms the CMS context to use
079     * @param categories the original categories.
080     * @param mainCategoryPath path of the main category for which only sub-categories should be wrapped.
081     */
082    CmsJspCategoryAccessBean(CmsObject cms, List<CmsCategory> categories, String mainCategoryPath) {
083        m_cms = cms;
084        m_mainCategoryPath = mainCategoryPath.isEmpty() || mainCategoryPath.endsWith("/")
085        ? mainCategoryPath
086        : mainCategoryPath + "/";
087
088        if (m_mainCategoryPath.isEmpty()) {
089            m_categories = categories;
090        } else {
091            List<CmsCategory> filteredCategories = new ArrayList<CmsCategory>();
092            for (CmsCategory category : categories) {
093                if (category.getPath().startsWith(m_mainCategoryPath)
094                    && !category.getPath().equals(m_mainCategoryPath)) {
095                    filteredCategories.add(category);
096                }
097            }
098            m_categories = filteredCategories;
099        }
100        m_categories = CmsCategoryService.getInstance().localizeCategories(
101            cms,
102            m_categories,
103            cms.getRequestContext().getLocale());
104    }
105
106    /**
107     * Reads the categories for the given resource.
108     *
109     * @param cms the {@link CmsObject} used for reading the categories.
110     * @param resource the resource for which the categories should be read.
111     * @return the categories assigned to the given resource.
112     */
113    private static List<CmsCategory> getCategories(CmsObject cms, CmsResource resource) {
114
115        if ((null != resource) && (null != cms)) {
116            try {
117                return CmsCategoryService.getInstance().readResourceCategories(cms, resource);
118            } catch (CmsException e) {
119                LOG.error(e.getLocalizedMessage(), e);
120            }
121        }
122        return new ArrayList<CmsCategory>(0);
123    }
124
125    /**
126     * Returns all wrapped categories.
127     *
128     * @return all wrapped categories.
129     */
130    public List<CmsCategory> getAllItems() {
131
132        return m_categories;
133    }
134
135    /**
136     * Returns <code>true</code> if there is no category wrapped, otherwise <code>false</code>.
137     *
138     * @return <code>true</code> if there is no category wrapped, otherwise <code>false</code>.
139     */
140    public boolean getIsEmpty() {
141
142        return m_categories.isEmpty();
143    }
144
145    /**
146     * Returns only the leaf categories of the wrapped categories.
147     *
148     * The method assumes that categories are ordered in the list, i.e., parents are directly followed by their children.
149     *
150     * NOTE: In the complete category tree a leaf of the wrapped tree part may not be a leaf.
151     *
152     * @return only the leaf categories of the wrapped categories.
153     */
154    public List<CmsCategory> getLeafItems() {
155
156        List<CmsCategory> result = new ArrayList<CmsCategory>();
157        if (m_categories.isEmpty()) {
158            return result;
159        }
160        Iterator<CmsCategory> it = m_categories.iterator();
161        CmsCategory current = it.next();
162        while (it.hasNext()) {
163            CmsCategory next = it.next();
164            if (!next.getPath().startsWith(current.getPath())) {
165                result.add(current);
166            }
167            current = next;
168        }
169        result.add(current);
170        return result;
171    }
172
173    /**
174     * Returns a map from a category path to the wrapper of all the sub-categories of the category with the path given as key.
175     *
176     * @return a map from a category path to all sub-categories of the path's category.
177     */
178    public Map<String, CmsJspCategoryAccessBean> getSubCategories() {
179
180        if (m_subCategories == null) {
181            m_subCategories = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
182
183                @SuppressWarnings("synthetic-access")
184                public Object transform(Object pathPrefix) {
185
186                    return new CmsJspCategoryAccessBean(m_cms, m_categories, (String)pathPrefix);
187                }
188
189            });
190        }
191        return m_subCategories;
192    }
193
194    /**
195     * Returns all categories that are direct children of the current main category.
196     *
197     * @return all categories that are direct children of the current main category.
198     */
199    public List<CmsCategory> getTopItems() {
200
201        List<CmsCategory> categories = new ArrayList<CmsCategory>();
202        String matcher = Pattern.quote(m_mainCategoryPath) + "[^/]*/";
203        for (CmsCategory category : m_categories) {
204            if (category.getPath().matches(matcher)) {
205                categories.add(category);
206            }
207        }
208        return categories;
209    }
210}