/*
* $Id: CategoryBean.java,v 1.10 2009/05/15 07:23:54 valdas Exp $
*
* Copyright (C) 2004 Idega. All Rights Reserved.
*
* This software is the proprietary information of Idega.
* Use is subject to license terms.
*
*/
package com.idega.content.business.categories;
import java.io.IOException;
import java.io.InputStream;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.httpclient.HttpException;
import org.apache.webdav.lib.PropertyName;
import org.apache.webdav.lib.WebdavResource;
import org.apache.webdav.lib.util.WebdavStatus;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.filter.Filter;
import org.jdom.input.SAXBuilder;
import com.idega.business.IBOLookup;
import com.idega.business.IBOLookupException;
import com.idega.content.data.CategoryComparator;
import com.idega.content.data.ContentCategory;
import com.idega.content.themes.helpers.business.ThemesConstants;
import com.idega.idegaweb.IWMainApplication;
import com.idega.presentation.IWContext;
import com.idega.slide.business.IWSlideService;
import com.idega.slide.business.IWSlideSession;
import com.idega.slide.util.WebdavRootResource;
import com.idega.util.CoreConstants;
import com.idega.util.CoreUtil;
import com.idega.util.IOUtil;
import com.idega.util.StringHandler;
/**
* <p>
* Class for manipulating Categories that are stored in slide.<br/>
* Includes functions for getting and setting all the available categories
* </p>
* Last modified: $Date: 2009/05/15 07:23:54 $ by $Author: valdas $
*
* @author <a href="mailto:Joakim@idega.com">Joakim</a>,<a href="mailto:tryggvi@idega.com">Tryggvi Larusson</a>
* @version $Revision: 1.10 $
*/
public class CategoryBean {
private static Logger LOGGER = Logger.getLogger(CategoryBean.class.getName());
private static String BEAN_KEY="ContentCategoryBean";
private static final String CATEGORY_CONFIG_PATH = CoreConstants.CONTENT_PATH + CoreConstants.SLASH;
/**
* @deprecated this file will no longer be used
*/
@Deprecated
private static final String CATEGORY_CONFIG_FILE = CATEGORY_CONFIG_PATH + "categories.prop";
public static final String CATEGORIES_FILE = "categories.xml";
private static final String CATEGORY_PROPERTIES_FILE = CATEGORY_CONFIG_PATH + CATEGORIES_FILE;
private IWMainApplication iwma;
protected Map<String, ContentCategory> categories;
public static final String CATEGORY_DELIMETER = CoreConstants.COMMA;
private CategoryBean() {
this(IWMainApplication.getDefaultIWMainApplication());
}
private CategoryBean(IWMainApplication iwma){
this.iwma=iwma;
this.categories = loadCategories();
if (this.categories == null) {
CategoriesMigrator migrator = new CategoriesMigrator();
Collection<String> oldCategories = getCategoriesFromString(getCategoriesAsString());
migrator.migrate(oldCategories);
}
}
protected class CategoriesMigrator {
private final String PROPERTY_NAME_CATEGORIES = new PropertyName("DAV","categories").toString();
private HashMap<String, String> valuesToKeys;
private IWSlideService service;
protected void migrate(Collection<String> cats) {
LOGGER.info("Migrating " + CATEGORY_CONFIG_FILE + " to new format at " + CATEGORY_PROPERTIES_FILE);
categories = new TreeMap<String, ContentCategory>();
valuesToKeys = new HashMap<String, String>();
String lang = getCurrentLocale();
for (Iterator<String> iter = cats.iterator(); iter.hasNext();) {
String cat = iter.next();
String key = CategoryBean.getCategoryKey(cat);
ContentCategory category = new ContentCategory(key);
category.addName(lang, cat);
categories.put(key, category);
valuesToKeys.put(cat, key);
}
storeCategories();
try {
service = IBOLookup.getServiceInstance(IWMainApplication.getDefaultIWApplicationContext(), IWSlideService.class);
updateCategoriesOnFiles(CATEGORY_CONFIG_PATH);
} catch (IBOLookupException e) {
LOGGER.log(Level.WARNING, "Error getting EJB bean: " + IWSlideService.class, e);
}
}
private void updateCategoriesOnFiles(String resourcePath) {
if (resourcePath.indexOf(ThemesConstants.THEMES_PATH) >= 0) {
return;
}
try {
String filePath = resourcePath;
String serverURI = service.getWebdavServerURI();
if(!resourcePath.startsWith(serverURI)) {
filePath = service.getURI(resourcePath);
}
WebdavResource resource = service.getWebdavResourceAuthenticatedAsRoot(filePath);
String oldCats = CATEGORY_DELIMETER;
Enumeration<?> enumerator = resource.propfindMethod(PROPERTY_NAME_CATEGORIES);
if (enumerator.hasMoreElements()) {
StringBuffer cats = new StringBuffer();
while(enumerator.hasMoreElements()) {
cats.append(enumerator.nextElement());
}
oldCats = cats.toString();
}
if (!oldCats.equals(CATEGORY_DELIMETER) && !oldCats.equals("")) {
LOGGER.info("Updating categories on resource " + resourcePath);
LOGGER.info("- " + oldCats);
StringTokenizer tokenizer = new StringTokenizer(oldCats, CATEGORY_DELIMETER);
StringBuffer newCats = new StringBuffer(CATEGORY_DELIMETER);
while (tokenizer.hasMoreTokens()) {
String cat = tokenizer.nextToken();
String key = valuesToKeys.get(cat);
// if we renamed the category key, replace category with it, otherwise leave as is
if (key != null) {
newCats.append(key);
} else {
newCats.append(cat);
}
newCats.append(CATEGORY_DELIMETER);
}
LOGGER.info("+ " + newCats.toString());
resource.proppatchMethod(PROPERTY_NAME_CATEGORIES, newCats.toString(), true);
}
// update categories on all child resources
Enumeration<?> children = resource.getChildResources().getResourceNames();
if (children.hasMoreElements()) {
while(children.hasMoreElements()) {
String child = (String) children.nextElement();
updateCategoriesOnFiles(child);
}
}
resource.close();
} catch (Exception e) {
LOGGER.warning("Exception updating categories on resource " + resourcePath + ": " + e.getMessage());
}
}
}
/**
* <p>
* Get the instance of the bean for this application.
* </p>
* @return
*/
public static CategoryBean getInstance(){
IWMainApplication iwma = IWMainApplication.getDefaultIWMainApplication();
CategoryBean bean = (CategoryBean) iwma.getAttribute(BEAN_KEY);
if (bean==null) {
bean = new CategoryBean(iwma);
iwma.setAttribute(BEAN_KEY, bean);
}
return bean;
}
public IWSlideService getSlideService() {
try {
return IBOLookup.getServiceInstance(this.iwma.getIWApplicationContext(),IWSlideService.class);
}
catch (IBOLookupException e) {
throw new RuntimeException("Error getting IWSlideService");
}
}
/**
* <p> Get a collection of categories, sorted by keys </p>
* @return collection of strings
*/
public Collection<ContentCategory> getCategories() {
return this.categories.values();
}
public ContentCategory getCategory(String id) {
return this.categories.get(id);
}
/**
* <p> Get a collection of categories, sorted by given locale </p>
* @return collection of strings
*/
public Collection<ContentCategory> getCategories(Locale locale) {
CategoryComparator comparator = new CategoryComparator(locale);
List<ContentCategory> list = new ArrayList<ContentCategory>(this.categories.values());
Collections.sort(list, comparator);
return Collections.unmodifiableCollection(list);
}
public String getCategoryName(String categoryKey) {
ContentCategory cat = this.categories.get(categoryKey);
if (cat == null) {
return categoryKey;
}
String lang = getCurrentLocale();
String name = cat.getName(lang);
if (name == null) {
lang = iwma.getDefaultLocale().toString();
name = cat.getName(lang);
}
if (name == null) {
name = categoryKey;
}
return name;
}
protected String getCurrentLocale() {
Locale locale = null;
IWContext iwContext = CoreUtil.getIWContext();
locale = iwContext == null ? IWMainApplication.getDefaultIWMainApplication().getDefaultLocale() : iwContext.getCurrentLocale();
locale = locale == null ? Locale.ENGLISH : locale;
return locale.toString();
}
/**
* <p>Getts all the categories as one String (comma separated)</p>
* @return categories
* @deprecated this file is no longer used
*/
@Deprecated
public String getCategoriesAsString() {
IWContext iwc = CoreUtil.getIWContext();
if (iwc == null)
return null;
String categories = null;
try {
IWSlideSession session = IBOLookup.getSessionInstance(iwc, IWSlideSession.class);
WebdavRootResource rootResource = session.getWebdavRootResource();
String path = getSlideService().getURI(CATEGORY_CONFIG_FILE);
categories = rootResource.getMethodDataAsString(path);
if(rootResource.getStatusCode() != WebdavStatus.SC_OK){
return "";
}
}
catch (IBOLookupException e) {
LOGGER.log(Level.WARNING, e.toString());
}
catch (RemoteException e) {
LOGGER.log(Level.WARNING, e.toString());
}
catch (HttpException e) {
LOGGER.log(Level.WARNING, e.toString());
}
catch (IOException e) {
LOGGER.log(Level.WARNING, e.toString());
}
return categories;
}
/**
* <p>
* Constructs a collection from a comma separated list of categories
* </p>
* @param categoryCommaSeparatedList
*/
public static Collection<String> getCategoriesFromString(String categoryCommaSeparatedList){
Collection<String> ret = new ArrayList<String>();
if( categoryCommaSeparatedList != null){
StringTokenizer st = new StringTokenizer(categoryCommaSeparatedList,CATEGORY_DELIMETER);
while(st.hasMoreTokens()) {
ret.add(st.nextToken().trim());
}
}
return ret;
}
/**
* <p>Adds a category to the available categories</p>
* @param category
*/
public void addCategory(String category) {
addCategory(category, getCurrentLocale());
}
/**
* Adds a category to the available categories
* @param category
* @param language
* @return
*/
public String addCategory(String category, String language) {
if (category == null || CoreConstants.EMPTY.equals(category)) {
return null;
}
String key = getCategoryKey(category);
ContentCategory cat = this.categories.get(key);
if (cat == null) {
cat = new ContentCategory(key);
}
cat.addName(language, category);
this.categories.put(key, cat);
storeCategories();
return key;
}
protected static final char[] LEAVE_AS_IS = {'-','0','1','2','3','4','5','6','7','8','9'};
protected static String getCategoryKey(String category) {
return StringHandler.stripNonRomanCharacters(category, LEAVE_AS_IS).toLowerCase();
}
/**
* Loads category definitions from <code>categories.xml</code> file.
* @return categories as Map<String, ContentCategory>, or <code>null</code> if loading failed.
*/
private Map<String, ContentCategory> loadCategories() {
Map<String, ContentCategory> map = new TreeMap<String, ContentCategory>();
InputStream stream = null;
try {
String resourcePath = getSlideService().getURI(CATEGORY_PROPERTIES_FILE);
stream = getSlideService().getInputStream(resourcePath);
if (stream == null)
return null;
SAXBuilder builder = new SAXBuilder();
Document document = builder.build(stream);
Element root = document.getRootElement();
Iterator<Element> cats = root.getDescendants(new Filter() {
public boolean matches(Object obj) {
if (obj instanceof Element) {
Element elem = (Element) obj;
if ("category".equals(elem.getName())) {
return true;
}
}
return false;
}
});
while (cats.hasNext()) {
Element cat = cats.next();
ContentCategory category = new ContentCategory(cat);
String key = category.getId();
if (key == null || CoreConstants.EMPTY.equals(key)) {
continue;
}
map.put(key, category);
}
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Error loading categories: " + e.getMessage(), e);
return null;
}
catch (JDOMException e) {
LOGGER.log(Level.WARNING, e.toString());
} finally {
IOUtil.closeInputStream(stream);
}
return map;
}
public void storeCategories() {
storeCategories(false);
}
public synchronized boolean storeCategories(boolean useThread) {
CategoriesWriter writer = null;
try {
writer = new CategoriesWriter(this.categories, CATEGORY_PROPERTIES_FILE, getSlideService());
} catch (Exception e) {
LOGGER.log(Level.INFO, "Failed to create CategoriesWriter: ", e);
return false;
}
if (useThread) {
IWContext iwc = CoreUtil.getIWContext();
if (iwc == null) {
useThread = false;
}
else {
useThread = !iwc.isIE();
}
}
if (useThread) {
writer.run();
}
else {
return writer.writeCategories();
}
return true;
}
public boolean deleteCategory(String id) {
try {
this.categories.remove(id);
} catch(Exception e) {
LOGGER.log(Level.WARNING, "Failed to remove category with id: "+id+ " from categories.xml", e);
return Boolean.FALSE;
}
return Boolean.TRUE;
}
public String getCategoryName(String id, String language, IWContext iwc) {
if (id == null) {
return null;
}
return getCategoryName(this.categories.get(id), language, iwc);
}
/**
* @param category Category content.
* @param language Category locale or default application locale: "en", "is_IS"
* @return Category name represented in:
*/
public String getCategoryName(ContentCategory category, String language, IWContext iwc) {
if (category == null || language == null || iwc == null) {
return null;
}
String name = category.getName(language);
if (name != null) {
return name;
}
// Didn't find category's name by current locale, looking for by default locale
Locale defaultLocale = IWMainApplication.getIWMainApplication(iwc).getDefaultLocale();
if (defaultLocale == null) {
return null;
}
return category.getName(defaultLocale.toString()); // Returning name by default locale or null if such doesn't exist
}
}