/*
* File : $Source: /alkacon/cvs/alkacon/com.alkacon.opencms.documentcenter/src/com/alkacon/opencms/documentcenter/CmsCategory.java,v $
* Date : $Date: 2010/03/19 15:31:13 $
* Version: $Revision: 1.3 $
*
* This file is part of the Alkacon OpenCms Add-On Module Package
*
* Copyright (c) 2010 Alkacon Software GmbH (http://www.alkacon.com)
*
* The Alkacon OpenCms Add-On Module Package is free software:
* you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alkacon OpenCms Add-On Module Package 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Alkacon OpenCms Add-On Module Package.
* If not, see http://www.gnu.org/licenses/.
*
* For further information about Alkacon Software GmbH, please see the
* company website: http://www.alkacon.com.
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org.
*/
package com.alkacon.opencms.v8.documentcenter;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsPropertyDefinition;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsResourceFilter;
import org.opencms.jsp.CmsJspNavBuilder;
import org.opencms.jsp.CmsJspNavElement;
import org.opencms.main.CmsException;
import org.opencms.util.CmsFileUtil;
import org.opencms.util.CmsStringUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* A single category for the document management, with methods to get
* a sorted list of main and sub category. Additionally, there are getter and setter
* methods for the needed information to build a category overview.<p>
*
* @author Andreas Zahner
*
* @version $Revision: 1.3 $
*
* @since 6.0.0
*/
public class CmsCategory implements Comparable<CmsCategory> {
/** Property value for sorting down first. */
public static final String C_CATEGORY_SORT_ORDER_DOWN = "down";
/** Property value for sorting right first. */
public static final String C_CATEGORY_SORT_ORDER_RIGHT = "right";
/** Separator in property value of sub categories. */
public static final String CATEGORY_SEPARATOR = ".";
/** Property name to look for the displayed title of the category. */
public static final String CATEGORY_TITLE = CmsPropertyDefinition.PROPERTY_TITLE;
/** Property name to look for the category languages. */
public static final String PROPERTY_CATEGORY_LANGUAGES = "docs.catlanguage";
/** Property value for all category languages. */
public static final String VALUE_CATEGORY_LANGUAGES_ALL = "all";
/** Absolute path to the category folder. */
private String m_cmsResource;
/** List with all main category elements. */
private List<CmsCategory> m_mainCategories;
/** Indicates if this is a main category. */
private boolean m_mainCategory;
/** Position in the category list (e.g "4" for a main, "4.2" for a sub category or the value of the NavPos property). */
private String m_position;
/** Map with all sub category elements with position of the main category as key. */
private Map<String, List<CmsCategory>> m_subCategories;
/** The displayed title of the category. */
private String m_title;
/**
* Default constructor for CmsCategory.
*/
public CmsCategory() {
setTitle("");
setPosition("");
setCmsResource("");
}
/**
* Constructor for initialization of the category lists.<p>
*
* Use this constructor to get an initialized instance of CmsCategory
* to have access to the category lists.<p>
*
* @param cms the CmsObject
* @param folderUri the folder URI, all subelements are searched for the property
* @param propertyName the name of the category property, usually "category"
*/
public CmsCategory(CmsObject cms, String folderUri, String propertyName) {
init(cms, folderUri, propertyName);
}
/**
* Constructor with pre set values for all member variables.<p>
*
* @param title the category title
* @param position the category position
* @param cmsResource the absolute path to the CmsResource
* @param mainCategory <code>true</code> if this is a main category, otherwise <code>false</code>
*/
public CmsCategory(String title, String position, String cmsResource, boolean mainCategory) {
setTitle(title);
setPosition(position);
setCmsResource(cmsResource);
setMainCategory(mainCategory);
}
/**
* Compares this instance to another given object instance of this class to sort a set of categories.<p>
*
* @param obj the other given object instance to compare with
* @return integer value for sorting the objects
*/
public int compareTo(CmsCategory obj) {
if (obj == this) {
return 0;
}
try {
// get the float values of the positions
return new Float(getPosition()).compareTo(new Float(obj.getPosition()));
} catch (Exception e) {
// ignore, return 0
}
return 0;
}
/**
* Tests if a given object is equal to this instance.<p>
*
* @param obj the other given object instance to compare with
* @return true if the given object is equal to this instance
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof CmsCategory) {
return getCmsResource().equals(((CmsCategory)obj).getCmsResource());
}
return false;
}
/**
* Returns the CmsResource String of this category.<p>
*
* @return the CmsResource String of this category
*/
public String getCmsResource() {
return m_cmsResource;
}
/**
* Returns the sorted list with all main categories.<p>
*
* @return the list with all main categories
*/
public List<CmsCategory> getMainCategories() {
return m_mainCategories;
}
/**
* Returns the position of the category.<p>
*
* @return the position of the category
*/
public String getPosition() {
return m_position;
}
/**
* Returns the list with all sub categories.<p>
*
* @return the list with all sub categories
*/
public Map<String, List<CmsCategory>> getSubCategories() {
return m_subCategories;
}
/**
* Returns a sorted list of all sub categories of the specified main category.<p>
*
* @param mainCategory the value of the property "category" of the main category
* @return sorted list of all sub categories of the specified main category
*/
public List<CmsCategory> getSubCategory(String mainCategory) {
List<CmsCategory> subCat = getSubCategories().get(mainCategory);
if (subCat == null) {
return Collections.emptyList();
}
// set the size of the sub category list to an even value
if ((subCat.size() % 2) == 1) {
subCat.add(new CmsCategory());
}
return subCat;
}
/**
* Returns a sorted list of all sub categories of the specified main category.<p>
*
* @param mainCategory the value of the property "category" of the main category
* @param sortOrder the sort order, this is either "down" or "right"
* @return sorted list of all sub categories of the specified main category
*/
public List<CmsCategory> getSubCategory(String mainCategory, String sortOrder) {
List<CmsCategory> unsortedCategories = getSubCategory(mainCategory);
// if the sort order is right first, we can use the precalculated result
if (sortOrder.equals(C_CATEGORY_SORT_ORDER_RIGHT)) {
return unsortedCategories;
} else {
// otherwise we must resort the list
int size = unsortedCategories.size();
int middle = size / 2;
List<CmsCategory> sortedCategories = new ArrayList<CmsCategory>();
for (int i = 0; i < middle; i++) {
sortedCategories.add(unsortedCategories.get(i));
if ((middle + i) <= size) {
sortedCategories.add(unsortedCategories.get(middle + i));
}
}
return sortedCategories;
}
}
/**
* Returns the title of the category.<p>
*
* @return the title of the category
*/
public String getTitle() {
return m_title;
}
/**
* Calculate the hash code of this object, based on its resource name String.
*
* @return the hash code of the object
*/
@Override
public int hashCode() {
return getCmsResource().hashCode();
}
/**
* Initializes the lists of main and sub categories for the LGT category overview.<p>
*
* @param cms the CmsObject
* @param folderUri the folder URI, all sub elements are searched for the property
* @param propertyName the name of the category property, usually "category"
*/
public void init(CmsObject cms, String folderUri, String propertyName) {
List<CmsResource> allCategories = new ArrayList<CmsResource>();
List<CmsCategory> mainCategories = new ArrayList<CmsCategory>();
Map<String, List<CmsCategory>> subCategories = new HashMap<String, List<CmsCategory>>();
Locale locale = cms.getRequestContext().getLocale();
String locTitle = CATEGORY_TITLE + "_" + locale.toString();
// get all resources in the current folder with the property propertyName set
try {
allCategories = cms.readResourcesWithProperty(folderUri, propertyName, null, CmsResourceFilter.DEFAULT);
} catch (CmsException e) {
// should never happen
}
if (allCategories.isEmpty()) {
// did not find resources with propertyName set, read categories using navigation properties
CmsJspNavBuilder navBuilder = new CmsJspNavBuilder(cms);
// first get all main categories (i.e. folders with navigation properties set)
List<CmsJspNavElement> firstLevelNavigation = navBuilder.getNavigationForFolder(CmsFileUtil.addTrailingSeparator(folderUri));
for (CmsJspNavElement firstLevelItem : firstLevelNavigation) {
// accept only folders as categories
if (firstLevelItem.isFolderLink()) {
// create category from navigation element
CmsCategory mCat = getCategoryFromNavElement(firstLevelItem, locale, locTitle, true);
if (mCat != null) {
mainCategories.add(mCat);
// determine sub categories of main category
List<CmsJspNavElement> secondLevelNavigation = navBuilder.getNavigationForFolder(mCat.getCmsResource());
List<CmsCategory> directSubCategories = new ArrayList<CmsCategory>(secondLevelNavigation.size());
for (CmsJspNavElement secondLevelItem : secondLevelNavigation) {
// accept only folders as sub categories
if (secondLevelItem.isFolderLink()) {
// create sub category from navigation element
CmsCategory sCat = getCategoryFromNavElement(secondLevelItem, locale, locTitle, false);
if (sCat != null) {
directSubCategories.add(sCat);
}
}
}
// put sub categories to map
subCategories.put(mCat.getPosition(), directSubCategories);
}
}
}
} else {
Iterator<CmsResource> i = allCategories.iterator();
while (i.hasNext()) {
// get the resource and the absolute path
CmsResource curRes = i.next();
try {
// first check if this category is shown in the current frontend language
String catLanguage = "";
try {
catLanguage = cms.readPropertyObject(curRes, PROPERTY_CATEGORY_LANGUAGES, false).getValue();
} catch (CmsException e) {
// ignore, property might not be defined
}
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(catLanguage)) {
if (!catLanguage.equals(VALUE_CATEGORY_LANGUAGES_ALL) && !catLanguage.equals(locale.toString())) {
// category is not shown in this frontend locale
continue;
}
}
// get the properties of the current resource
String positionString = cms.readPropertyObject(curRes, propertyName, false).getValue();
positionString = CategoryTree.cutPrefix(positionString);
String resourceName = cms.getSitePath(curRes);
String title = "";
try {
title = cms.readPropertyObject(curRes, locTitle, false).getValue();
} catch (CmsException e) {
// ignore, property might not be defined
}
if (CmsStringUtil.isEmptyOrWhitespaceOnly(title)) {
title = cms.readPropertyObject(curRes, CATEGORY_TITLE, false).getValue();
}
// check the presence of the title property
if (CmsStringUtil.isEmptyOrWhitespaceOnly(title)) {
title = CmsResource.getName(resourceName);
// cut the trailing "/" if present
if (CmsResource.isFolder(title)) {
title = title.substring(0, (title.length() - 1));
}
}
int separator = positionString.indexOf(CATEGORY_SEPARATOR);
if (separator == -1) {
// add resources which are a "main" category to the main list
mainCategories.add(new CmsCategory(title, positionString, resourceName, true));
} else {
// add resources which are a "sub" category to the correct sub list
// get the main category subString from the position value
String mainCatPosition = positionString.substring(0, separator);
List<CmsCategory> subCat = subCategories.get(mainCatPosition);
if (subCat == null) {
subCat = new ArrayList<CmsCategory>();
}
// determine sub category position
String subPosition = positionString.substring(separator + 1);
subCat.add(new CmsCategory(title, subPosition, resourceName, false));
Collections.sort(subCat);
// put list with added category back to map
subCategories.put(mainCatPosition, subCat);
}
} catch (CmsException e) {
// do nothing, current resource is not added to the category list
}
}
// sort the list of main categories by the value of the property
if (mainCategories.size() >= 2) {
Collections.sort(mainCategories);
}
}
// set the size of the main category list to an even value
if ((mainCategories.size() % 2) == 1) {
mainCategories.add(new CmsCategory());
}
// set the member variables
setMainCategories(mainCategories);
setSubCategories(subCategories);
}
/**
* Returns if this is a main category.<p>
*
* @return <code>true</code> if this is a main category, otherwise <code>false</code>
*/
public boolean isMainCategory() {
return m_mainCategory;
}
/**
* Sets the CmsResource String of this category.<p>
*
* @param cmsResource the CmsResource String of this category
*/
public void setCmsResource(String cmsResource) {
m_cmsResource = cmsResource;
}
/**
* Sets the list with all main categories.<p>
*
* @param list the list with all main categories
*/
public void setMainCategories(List<CmsCategory> list) {
m_mainCategories = list;
}
/**
* Sets if this is a main category.<p>
*
* @param mainCategory <code>true</code> if this is a main category, otherwise <code>false</code>
*/
public void setMainCategory(boolean mainCategory) {
m_mainCategory = mainCategory;
}
/**
* Sets the position of the category.<p>
*
* @param string with the position of the category
*/
public void setPosition(String string) {
m_position = string;
}
/**
* Sets the map with all sub categories.<p>
*
* @param map the map with all sub categories
*/
public void setSubCategories(Map<String, List<CmsCategory>> map) {
m_subCategories = map;
}
/**
* Sets the title of the category.<p>
*
* @param string the title of the category
*/
public void setTitle(String string) {
m_title = string;
}
/**
* Returns an initialized category created from the given navigation element.<p>
*
* @param nav the navigation element to get the category from
* @param locale the current locale
* @param locTitle the property name to get the localized title
* @param mainCategory <code>true</code> if this is a main category, otherwise <code>false</code>
*
* @return an initialized category or <code>null</code> if no category could be created for the current locale
*/
protected CmsCategory getCategoryFromNavElement(
CmsJspNavElement nav,
Locale locale,
String locTitle,
boolean mainCategory) {
// first check if this category is shown in the current frontend language
String catLanguage = nav.getProperty(PROPERTY_CATEGORY_LANGUAGES);
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(catLanguage)) {
if (!catLanguage.equals(VALUE_CATEGORY_LANGUAGES_ALL) && !catLanguage.equals(locale.toString())) {
// category is not shown in this frontend locale
return null;
}
}
// determine the category title
String title = nav.getProperty(locTitle);
if (CmsStringUtil.isEmptyOrWhitespaceOnly(title)) {
// fall back 1: the Title
title = nav.getProperty(CATEGORY_TITLE);
}
if (CmsStringUtil.isEmptyOrWhitespaceOnly(title)) {
// fall back 2: the navigation text
title = nav.getNavText();
}
// check if anything was found
if (CmsStringUtil.isEmptyOrWhitespaceOnly(title)) {
// fall back 3: the resource name
title = nav.getFileName();
// cut the trailing "/" if present
if (CmsResource.isFolder(title)) {
title = CmsFileUtil.removeTrailingSeparator(title);
}
}
return new CmsCategory(title, String.valueOf(nav.getNavPosition()), nav.getResourceName(), mainCategory);
}
}