/*
 * $Id: VKCCategory.java,v 1.111 2007/11/02 19:52:21 marbobo Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: VKCCategory.java,v $
 * Revision 1.111  2007/11/02 19:52:21  marbobo
 * + Bugfix (Statt List werden HashSet benutzt)
 *
 * Revision 1.110  2006/10/04 03:15:17  marbobo
 * + Vollständige Unterstützung für Templates implementiert
 * + Alles von Virtoweb auf ProCoBa umgestellt
 *
 * Revision 1.109  2005/09/22 01:15:53  marbobo
 * + setDocuments() hinzugefügt
 *
 * Revision 1.108  2005/09/16 10:17:59  jonyp
 * added getParentAbsPathDescr method
 *
 * Revision 1.107  2005/09/12 15:20:11  jonyp
 * added isInTrash method
 *
 * Revision 1.106  2005/09/02 09:35:52  jonyp
 * folderImage: added return of trashImage if category is trash
 *
 * Revision 1.105  2005/08/19 15:10:12  jonyp
 * support for TrashCategory
 *
 * Revision 1.104  2005/08/09 19:03:21  jonyp
 * added methods to display lastChnage/lastAccessor as User.Info.Lastname format
 *
 * Revision 1.103  2005/07/20 09:16:00  jonyp
 * + getLastChangerName/LastAccessorName
 *
 * Revision 1.102  2005/07/19 21:04:23  jonyp
 * added method getCreator: get String of user info object of creator
 *
 * Revision 1.101  2005/02/22 00:42:00  jonyp
 * *** empty log message ***
 *
 * Revision 1.100  2005/02/17 00:52:01  jonyp
 * set lastChanger/Changed & accessor/Accessed initially to null
 *
 * Revision 1.99  2005/02/02 22:58:08  marbobo
 * Neue filterMethode (Unicode-Sensitive) wird bei der Beschreibung verwendet
 *
 * Revision 1.98  2005/01/28 09:37:57  jonyp
 * changed order of images in getFolderImage
 *
 * Revision 1.97  2005/01/20 15:25:50  marbobo
 * + Anpassungen an den Methoden getRootURL() und getContextName()
 *
 * Revision 1.96  2004/10/31 02:56:51  marbobo
 * + Refactoring
 *
 * Revision 1.95  2004/10/26 22:23:59  marbobo
 * + getAccessibleCountCatDocs()
 *
 * Revision 1.94  2004/10/03 05:18:33  jonyp
 * + added comment
 * + added code to increase performance: refer to internal cache in WebShare instead of asking SysParameterSerializer to ListAccessibleCategoriesOnly
 *
 * Revision 1.93  2004/09/09 08:15:13  jonyp
 * changed method name "isInTreeOf" to "isInTree"
 *
 * Revision 1.92  2004/09/08 19:44:58  marbobo
 * + isInTreeOf() - Zur Bestimmung ob die angegebene Kategorie im Baumstruktur
 * der aktuellen Kategorie sich befindet
 *
 * Revision 1.91  2004/08/09 19:15:07  marbobo
 * + Neues Flag eingeführt ( FLAG_NULL - dient als Ersatz für NULL-Objekte )
 * + Flag überprüfung im getFolderImage() wieder aktiviert
 *
 * Revision 1.90  2004/08/09 16:06:42  jonyp
 * added method getAbsPathDescString
 *
 * Revision 1.89  2004/08/04 23:18:26  marbobo
 * + Status auf Flag umgestellt
 *
 * Revision 1.88  2004/07/30 13:18:29  jonyp
 * added new methods for memebr var "flag"
 *
 * Revision 1.87  2004/07/26 15:14:19  jonyp
 * added status_deleted
 *
 * Revision 1.86  2004/07/20 15:14:02  jonyp
 * changed due to move of VKCCountCatDocs from util to view.bean
 *
 * Revision 1.85  2004/07/19 14:41:20  jonyp
 * deleted unused comment
 *
 * Revision 1.84  2004/07/16 12:42:20  jonyp
 * performance thing: delete unnecessary query for enablecountcatdocs in method getCountCatDocs
 *
 * Revision 1.83  2004/07/15 12:36:37  jonyp
 * used local cache to compute emptiness of category
 *
 * Revision 1.82  2004/07/14 11:25:05  jonyp
 * added functinality for displayEmptyCategoryWithEmptyIcon = true;
 *
 * Revision 1.81  2004/07/14 11:04:35  jonyp
 * added functinality for displayEmptyCategoryWithEmptyIcon = true;
 *
 * Revision 1.80  2004/07/14 10:24:35  jonyp
 * added support for DisplayEmptyCategorieWithEmptyIcon
 *
 * Revision 1.79  2004/07/07 09:47:30  jonyp
 * deleted unused import
 *
 * Revision 1.78  2004/07/01 14:47:52  marbobo
 * + Status für Kategorien eingeführt
 *
 * Revision 1.77  2004/06/24 23:24:13  marbobo
 * Quellcode maintenance
 *
 * Revision 1.76  2004/06/04 09:15:17  jonyp
 * deleted some obsolete comments
 *
 * Revision 1.75  2004/06/02 09:16:26  jonyp
 * - added "isEmpty" method 
 * - added "hasPermission" method
 * - added functionality to "folderImage" method
 *
 * Revision 1.74  2004/05/21 16:40:08  jonyp
 * delete obsolete comment
 *
 * Revision 1.73  2004/05/07 10:15:08  jonyp
 * minor bug fixes
 *
 * Revision 1.72  2004/05/04 12:28:37  jonyp
 * getFolderImage: added image for null-category
 *
 * Revision 1.71  2004/04/16 08:14:44  jonyp
 * added functions to find out weather this category is 
 * projectroot / templateroot / functionalviewroot
 *
 * Revision 1.70  2004/04/15 14:37:40  jonyp
 * added functions to find out weather this category is 
 * projectroot / templateroot / functionalviewroot
 *
 * Revision 1.69  2004/04/15 09:24:33  jonyp
 * modified getter for searchquery
 *
 * Revision 1.68  2004/04/08 09:36:57  marbobo
 * getVirtualDocuments() angepasst
 *
 * Revision 1.66  2004/04/07 15:19:22  jonyp
 * isVirtualDocument/isRealDocument
 *
 * Revision 1.65  2004/04/05 17:03:47  marbobo
 * getVirtualDocuments() und getSearchQuery() angepasst
 *
 * Revision 1.64  2004/04/05 12:48:11  jonyp
 * *** empty log message ***
 *
 * Revision 1.63  2004/04/01 13:33:21  bigmac
 * Entitäten für Projekte verschoben nach vkc.virtoweb.project.entities
 *
 * Revision 1.62  2004/03/31 13:49:56  bigmac
 * +getSubCategoriesIds()
 *
 * Revision 1.61  2004/03/24 13:08:10  bigmac
 * Property aclId enfernt
 *
 * Revision 1.60  2004/03/15 13:40:03  bigmac
 * Anpassungen an PermissionManager und weitere Refactorings
 *
 * Revision 1.59  2004/03/13 20:46:16  bigmac
 * Weitere Refactorings ...
 *
 * Revision 1.58  2004/03/12 15:44:17  bigmac
 * Methoden für (lokale) zusätzliche Attribute aus VKCDynAttributeSerializer 
 * nach VKCAddAttributeSerializer verschoben
 * 
 * Unbenutze private Methoden projektweit entsorgt
 *
 * Revision 1.57  2004/03/09 18:40:37  bigmac
 * Anpassungen an VKCSysParameterSerializer
 *
 * Revision 1.56  2004/03/07 16:53:22  bigmac
 * Anpassungen an VKCAclSerializer
 *
 * Revision 1.55  2004/03/03 16:26:34  jonyp
 * searchquery
 *
 * Revision 1.54  2004/03/02 23:05:45  bigmac
 * Anpassungen an VKCDocumentSerializer und VKCCategorySerializer
 *
 * Revision 1.53  2004/02/25 11:28:58  bigmac
 * Kategoriekonvertierungen nach vkc.utils.CategoryUtils verlagert
 *
 * Revision 1.52  2004/02/18 14:59:50  marbobo
 * + getCategorySysLinkNoSlash() (ohne führende Slash)
 *
 * Revision 1.51  2004/02/18 01:16:35  marbobo
 * + getCategoryLink() und getCategorySysLink()
 *
 * Revision 1.50  2004/02/17 17:06:36  vchris
 * *** empty log message ***
 *
 * Revision 1.49  2004/02/17 09:34:41  jonyp
 * timestamp -> date
 *
 * Revision 1.48  2004/02/11 14:57:51  bigmac
 * Subklasse VKCProjectSubfolder eingefügt
 *
 * Revision 1.47  2004/02/09 20:53:48  bigmac
 * projectSteps nach VKCProject verlagert
 *
 * Revision 1.46  2004/02/09 17:29:55  vchris
 * Erweiterungen Projektmgmt
 *
 * Revision 1.45  2004/01/31 19:21:52  bigmac
 * *** empty log message ***
 *
 * Revision 1.44  2004/01/27 15:52:36  bigmac
 * + Feld categoryType
 *
 * Revision 1.43  2004/01/21 15:07:57  jonyp
 * isProjectCategory
 *
 * Revision 1.42  2004/01/20 15:35:51  jonyp
 * getter/setter for projectSteps
 *
 * Revision 1.41  2004/01/13 15:03:17  jonyp
 * set/get status
 *
 * Revision 1.40  2004/01/13 09:57:06  jonyp
 * projectsteps vector
 *
 * Revision 1.39  2003/12/24 10:12:12  bigmac
 * Projektweite Import-Optimierung
 *
 * Revision 1.38  2003/12/19 10:34:16  lawern
 * Künstliche WebDav-Ordnernamen eingeführt um tiefere Ordner öffnen zu können.
 *
 * Revision 1.37  2003/12/18 12:29:33  lawern
 * getWebDAVPath korrigiert
 *
 * Revision 1.36  2003/12/18 10:08:41  lawern
 * WebDav-Pfad angepasst
 *
 * Revision 1.35  2003/12/16 12:56:23  bigmac
 * getWebDAV() angepasst
 *
 * Revision 1.34  2003/12/16 11:38:07  bigmac
 * getWebDAVPath() für Root-Kategorie geändert
 *
 * Revision 1.33  2003/12/15 12:43:57  bigmac
 * Method getWebDAVPath() added
 *
 * Revision 1.32  2003/12/02 17:16:29  vchris
 * *** empty log message ***
 *
 * Revision 1.31  2003/12/01 13:16:14  vchris
 * Anzeigen von accessible Subcategories umgestellt auf Methode in VKCCategory, nicht mehr in Categoryview.jsp
 *
 * Revision 1.30  2003/11/28 15:04:10  jonyp
 * changes due to new schema
 *
 * Revision 1.29  2003/11/13 08:48:01  jonyp
 * changed parameters for countcatdocsquery
 *
 * Revision 1.28  2003/09/17 08:26:59  jonyp
 * WebShare.getSysParemterValue mit neuer Signatur
 *
 * Revision 1.27  2003/09/09 13:29:40  jonyp
 * *** empty log message ***
 *
 * Revision 1.26  2003/09/04 17:04:48  marbobo
 * ungenutzte Imports entfernt
 *
 * Revision 1.25  2003/08/30 11:40:38  jonyp
 * additional attributes
 *
 * Revision 1.24  2003/08/29 09:02:29  jonyp
 * *** empty log message ***
 *
 * Revision 1.23  2003/08/28 08:53:53  jonyp
 * *** empty log message ***
 *
 * Revision 1.22  2003/08/14 15:30:29  marbobo
 * Kategoriepfad wird nun nach Änderung des Kategorienamens richtig angezeigt
 *
 * Revision 1.21  2003/08/05 20:25:11  bigmac
 * *** empty log message ***
 *
 * Revision 1.20  2003/07/31 13:59:31  bigmac
 * Compilerwarnungen beseitigt
 *
 * Revision 1.19  2003/07/30 12:01:46  jonyp
 * *** empty log message ***
 *
 * Revision 1.18  2003/06/30 10:22:28  marbobo
 * +getKeywordsWithWeight(): abfrage, ob keywords überhaupt existieren
 *
 * Revision 1.17  2003/06/30 09:55:57  jonyp
 * getKeywordsWithWeight(): abfrage, ob categorie existiert und keyliste mit keyowrdid ungleich null
 *
 * Revision 1.16  2003/06/06 23:24:22  koppy
 * + getNl2BrDescpription()
 *
 * Revision 1.15  2003/06/03 14:04:50  jonyp
 * bug fix: displaced tablename
 *
 * Revision 1.14  2003/06/03 13:21:39  jonyp
 * getKeyword(boolean withWeight)
 *
 * Revision 1.13  2003/04/15 15:34:26  koppy
 * + Analyse / Klassifikation
 *
 * Revision 1.12  2003/03/13 03:10:27  marbobo
 *         if (absPathDescr != null)  =>       if (absPathDescr == null)
 *
 * Revision 1.11  2003/03/12 11:45:17  jonyp
 * getAbsoultePath modification
 *
 * Revision 1.10  2003/03/03 22:53:45  koppy
 * Kleinigkeiten, bugfixes
 *
 * Revision 1.9  2003/02/23 13:26:21  koppy
 * *** empty log message ***
 *
 * Revision 1.8  2003/02/19 13:49:08  jonyp
 * getAbsolutePath()
 *
 * Revision 1.7  2003/01/31 03:58:18  koppy
 * accessCounter bei Dokumenten + checkPermissionTag abstrahiert
 *
 * Revision 1.6  2003/01/29 03:23:40  koppy
 * ACLs + Versionierung
 *
 * Revision 1.5  2002/12/16 20:23:15  koppy
 * Documents delete feature
 *
 * Revision 1.4  2002/11/25 11:04:52  jonyp
 * changes due to midification of table keywords: term/termdescription
 *
 * Revision 1.3  2002/11/05 01:40:20  koppy
 * delete() / save() angepasst
 *
 * Revision 1.2  2002/11/04 02:04:31  koppy
 * long --> Integer
 *
 * Revision 1.1  2002/10/21 14:35:38  bigmac
 * *** empty log message ***
 *
 * Revision 1.2  2002/10/04 14:59:30  koppy
 * + isRootCategory()
 *
 * Revision 1.1  2002/09/16 12:13:54  koppy
 * Initial version
 *
 */
package vkc.db.entities;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Date;

import vkc.db.serializer.VKCAclSerializer;
import vkc.db.serializer.VKCCategorySerializer;
import vkc.db.serializer.VKCSysParameterSerializer;
import vkc.db.serializer.VKCUserSerializer;
import vkc.exception.DBException;
import vkc.procoba.project.entities.*;
import vkc.security.PermissionManager;
import vkc.security.VKCAcl;
import vkc.util.CategoryUtils;
import vkc.util.DocumentUtils;
import vkc.util.VKCCategoryAbsPath;
import vkc.util.VKCCategoryPathNode;
import vkc.view.bean.VKCCountCatDocs;
import vkc.util.VKCStringUtils;
import vkc.view.WebShare;
import vkc.view.form.SysLinkForm;
import vkc.webdav.URLUtil;
import vkc.webdav.WebDavServlet;
import vkc.view.bean.UserInfo;


/**
 * Stellt die Kategorie im VKC-System dar
 *
 * @author koppy
 * @version 1.0
 */
public class VKCCategory extends VKCEntity {
    public final static int ROOT_CATEGORY_ID = 1;

    public final static int TYPE_CATEGORY          = 0;
    public final static int TYPE_PROJECT           = 1;
    public final static int TYPE_PROJECT_SUBFOLDER = 2;
    
	public final static Integer STATUS_CLOSED      = new Integer(0);
	public final static Integer STATUS_CREATED     = new Integer(1);
	public final static Integer STATUS_ACTIVE      = new Integer(2);
	public final static Integer STATUS_ARCHIVED    = new Integer(3);
	public final static Integer STATUS_DELETED     = new Integer(4);
    
	public final static Integer FLAG_NULL      	 = new Integer(-1);
	public final static Integer FLAG_CLOSED      = new Integer(0);
	public final static Integer FLAG_CREATED     = new Integer(1);
	public final static Integer FLAG_ACTIVE      = new Integer(2);
	public final static Integer FLAG_ARCHIVED    = new Integer(3);
	public final static Integer FLAG_DELETED     = new Integer(4);
	 
    private boolean initialCategory = true;
    private boolean showDescription = true;

    private Date created = new Date(System.currentTimeMillis());
    private Integer creatorId = null;
        
    private Date lastAccessed = null;
    private Integer lastAccessorId = null;
    
    private Date lastChanged = null;
    private Integer lastChangerId = null;

    private Integer accessCount = new Integer(0);
    private Integer categoryId = VKCEntity.ID_UNDEFINED;
    
    private Integer flag = FLAG_ACTIVE;
        
    private Integer parentCategoryId = null;
    private Integer status = STATUS_ACTIVE;

    private List documents = new ArrayList();
    private List subCategories = new ArrayList();
    private List virtualDocuments = null;

    private Set allowedDocumentTypes = new HashSet();
    
    private String description = "";
    private String name = "";

    private VKCAcl acl = new VKCAcl();
    private VKCCategoryAbsPath absPathDescr = null;
    private VKCCountCatDocs countCatDocs = null;
    private VKCCountCatDocs accessibleCountCatDocs = null;

    /**
     * Konstruktor
     */
    public VKCCategory() {
    }

    public void setCategoryId(Integer id) {
        categoryId = id;
    }

    public Integer getCategoryId() {
        return categoryId;
    }

    public boolean isRootCategory() {
        return getCategoryId().intValue() == VKCCategory.ROOT_CATEGORY_ID;
    }

    /**
     * Setzt die ID der übergeordneten Kategorie
     *
     * @param parentCategoryId ID der übergeordneten Kategorie
     */
    public void setParentCategoryId(Integer pid) {
        parentCategoryId = pid;
    }

    /**
     * Gibt die ID der übergeordneten Kategorie wieder
     */
    public Integer getParentCategoryId() {
        return parentCategoryId;
    }

    /**
     * Fügt eine Subkategorie hinzu
     *
     * @param subCategory das Subkategorie Objekt
     */
    public void addSubCategory(VKCSubCategory subCategory) {
        getSubCategories().add(subCategory);
    }

    /**
     * Gibt die Unterkategorien als Vektor zurück
     *
     * @return Vektor der Unterkategorien
     */
    public List getSubCategories() {
        return subCategories;
    }

	/**
	 * Methode liefert ein (Integer-) Set mit den Ids aller Unterkategorien
	 */
	public Set getSubCategoriesIds() {
		HashSet set = new HashSet();
		
		for (Iterator iter = getSubCategories().iterator(); iter.hasNext();) {
			VKCSubCategory subCategory = (VKCSubCategory)iter.next();
			set.add(subCategory.getSubCategoryId());
		}

		return set;
	}

    public List getAccessibleSubCategories(VKCUser user) {
        // ist in den Sysparameters 'ListAccessibleCategoriesOnly' gesetzt?
		if (Integer.parseInt(WebShare.getSysParameterValue("CATEGORYVIEW", "ListAccessibleCategoriesOnly", "0")) != 0) {
            Iterator it = getSubCategories().iterator();
            List accessibleSubcats = new ArrayList();

            while (it.hasNext()) {
                VKCSubCategory sub = (VKCSubCategory) it.next();

                try {
                    if (PermissionManager.hasPermission(VKCAclSerializer.getAclByCategoryId(sub.getSubCategoryId()), user, 'r')) {
                        accessibleSubcats.add(sub);
                    }
                } catch (DBException ex) {
                }
            }

            return accessibleSubcats;
        }

        // ansonsten einfach die subcategories zurückgeben
        return getSubCategories();
    }

    public void addDocument(VKCDocument document) {
        getDocuments().add(document);
    }

    public List getDocuments() {
        // hier noch evtl. sortieren....
        return documents;
    }
    
    public void setDocuments(List documents) {
    	this.documents = documents;
    }

    public void setShowDescription(boolean b) {
        showDescription = b;
    }

    public boolean getShowDescription() {
        return showDescription;
    }

    public void setName(String s) {
        name = s;
    }

    public String getName() {
        return name;
    }

    public void setDescription(String s) {
        if (s == null) {
            description = "";
        } else {
            description = s.trim();
        }
    }

    public String getDescription() {
        return description;
    }

    public String getNl2BrDescription() {
        return VKCStringUtils.filterUnicodeChars(getDescription());
    }

    public void setInitialCategory(boolean b) {
        initialCategory = b;
    }

    public boolean isInitialCategory() {
        return initialCategory;
    }

    public void addAllowedDocumentType(VKCDocumentType type) {
        getAllowedDocumentTypes().add(type);
    }

    public void setAllowedDocumentTypes(Set set) {
        allowedDocumentTypes = set;
    }

    public Set getAllowedDocumentTypes() {
        return allowedDocumentTypes;
    }

    public void setCreated(Date d) {
        created = d;
    }

    public Date getCreated() {
        return created;
    }

    public void setCreatorId(Integer i) {
        creatorId = i;
    }

    public Integer getCreatorId() {
        return creatorId;
    }

    public String getCreatorName() {
    	try {
    		UserInfo ui = VKCUserSerializer.retrieveUserInfo(getCreatorId());
    		
    		if (ui != null) { 
                return ui.getLastnameLabel();
    		}
    		
           	return "";
    	} catch (DBException ex){
    		return "";
    	}
    }
    
    public void setLastChanged(Date d) {
        lastChanged = d;
    }

    public Date getLastChanged() {
        return lastChanged;
    }

    public void setLastChangerId(Integer i) {
        lastChangerId = i;
    }

    public Integer getLastChangerId() {
        return lastChangerId;
    }

    public String getLastChangerName() {
    	try {
    		UserInfo ui = VKCUserSerializer.retrieveUserInfo(getLastChangerId());
    		
    		if (ui != null) {
                return ui.getLastnameLabel();
    		}
    		
    		return "";
    	} catch (DBException ex){
    		return "";
    	}
    }
    
    public void setLastAccessed(Date d) {
        lastAccessed = d;
    }

    public Date getLastAccessed() {
        return lastAccessed;
    }

    public void setLastAccessorId(Integer i) {
        lastAccessorId = i;
    }

    public Integer getLastAccessorId() {
        return lastAccessorId;
    }

    public String getLastAccessorName()  {
    	try {
    		UserInfo ui = VKCUserSerializer.retrieveUserInfo(getLastAccessorId());
    		
    		if (ui != null) {
                return ui.getLastnameLabel();
    		}
    		
    		return "";
    	} catch (DBException ex){
    		return "";
    	}
    }
    
    public void setAccessCount(Integer i) {
        accessCount = i;
    }

    public Integer getAccessCount() {
        return accessCount;
    }

    public String toString() {
        return getName() + " ( ID=" + getCategoryId() + " ) Keywords: " + getKeywordsAsString();
    }

    public void setAbsPathDescr(VKCCategoryAbsPath pathDescr) {
        absPathDescr = pathDescr;
    }

    
    public VKCCategoryAbsPath getAbsPathDescr() throws DBException {
        if (absPathDescr == null) {
            absPathDescr = VKCCategorySerializer.getCategoryAbsPath(getCategoryId());
        }

        return absPathDescr;
    }

	public String getAbsPathDescrString() throws DBException {
		return getAbsPathDescr().getPathString();
    }

    public VKCCategoryAbsPath getParentAbsPathDescr() throws DBException {       
        if (getParentCategoryId() != null) {
            return VKCCategorySerializer.getCategoryAbsPath(getParentCategoryId());
        }

        return null;
    }
    
    /**
     * Liefert eine Category ACL
     *
     */
    public VKCAcl getAcl() {
        return acl;
    }

    /**
     * Setzt eine Category ACL
     *
     */
    public void setAcl(VKCAcl a) {
        acl = a;
    }

    /**
     *  Liefert die Anzahl von Untercategorien und Dokumenten
     */
    public VKCCountCatDocs getCountCatDocs() throws DBException {
        if (countCatDocs == null) {
            countCatDocs = CategoryUtils.getCountCatDocs(getCategoryId());
        }

        return countCatDocs;
    }

    /**
     *  Liefert die Anzahl von Untercategorien und Dokumenten die von Benutzer sichtbar sind
     */
    public VKCCountCatDocs getAccessibleCountCatDocs(VKCUser user) throws DBException {
        if (accessibleCountCatDocs == null) {
            accessibleCountCatDocs = CategoryUtils.getAccessibleCountCatDocs(getCategoryId(), user);
        }

        return accessibleCountCatDocs;
    }

    /**
     * Method returns the encoded absolute WebDAV path associated
     * with this category
     * <p>
     * @return encoded path
     * @throws UnsupportedEncodingException
     */
    public final String getWebDAVPath() throws DBException {
        StringBuffer sb = new StringBuffer(WebShare.getRootURL() + WebShare.getContextPath());
        sb.append("/dav/");

        String folderName;

        ArrayList longNames = new ArrayList();
        ArrayList shortNames = new ArrayList();

        for (Iterator iter = getAbsPathDescr().getPathNodes().iterator(); iter.hasNext();) {
            VKCCategoryPathNode node = (VKCCategoryPathNode) iter.next();

            if (!node.isRootNode()) {
                String cleanNodeName = WebDavServlet.cleanCatName(node.getPathName(), node.getPathId());
                folderName = URLUtil.URLEncode(cleanNodeName, "ISO-8859-1");

                String shortFolderName = "(CID " + node.getPathId() + ")";

                if (shortFolderName.length() > folderName.length()) {
                    shortFolderName = folderName;
                }

                longNames.add(folderName);
                shortNames.add(shortFolderName);
            }
        }

        //längsten Pfadnamen berechnen
        int longestPath = sb.length();
        int shortestPath = longestPath;

        for (int i = longNames.size() - 1; i >= 0; i--) {
            longestPath += ((String) longNames.get(i)).length() + 1;
        }

        if (longestPath > 99) {
            //langer Pfad ist zu lang
            //
            //kürzesten Pfad berechnen
            for (int i = shortNames.size() - 1; i >= 0; i--) {
                shortestPath += ((String) shortNames.get(i)).length() + 1;
            }

            if (shortestPath > 99) {
                //kürzester Pfad ist auch zu lang -> kürzen
                for (int i = 0; i < shortNames.size(); i++) {
                    String shortName = (String) shortNames.get(i);

                    if ((sb.length() + shortName.length()) < 99) {
                        sb.append(shortName);
                        sb.append('/');
                    }
                }
            } else {
                //beste Kombination aus langen und kurzen Pfadnamen finden
                //(lange Pfadnamen zuerst)
                //
                //rausfinden wieviele lange Namen möglich sind
                int pathLength = shortestPath;
                String shortName;
                String longName;

                for (int i = 0; i < longNames.size(); i++) {
                    shortName = (String) shortNames.get(i);
                    longName = (String) longNames.get(i);

                    int pathDiff = longName.length() - shortName.length();

                    if ((pathLength + pathDiff) < 99) {
                    	sb.append(longName);
                    	pathLength += pathDiff;
                    } else {
						sb.append(shortName);
                    }
                    
                    sb.append('/');
                }
            }
        } else {
            //langer Pfad ist ok
            for (int i = 0; i < longNames.size(); i++) {
                sb.append((String) longNames.get(i));
                sb.append('/');
            }
        }

        return sb.toString();
    }

	public Integer getStatus() {
		return status;
	}

	public void setStatus(Integer i) {
		status = i;
	}

    public boolean isProjectCategory() {
    	return this instanceof VKCProject;
    }

	public boolean isProjectSubfolder() {
		return this instanceof VKCProjectSubfolder;
	}

	public boolean isDocumentCategory() {
		return !isProjectCategory() && !isProjectSubfolder();
	}

	public String getCategoryLink() {
		if (getCategoryId().intValue() != -1) {
			return "/showCategory.do?categoryId=" + getCategoryId().intValue();
		}

		return "";
	}
	
	public String getCategorySysLink() {
		if (getCategoryId().intValue() != -1) {
			return "/sysLink.do?linkType=" + SysLinkForm.CATEGORY_LINK + "&objectId=" + getCategoryId().intValue();
		}

		return "";
	}

	public String getCategorySysLinkNoSlash() {
		if (getCategoryId().intValue() != -1) {
			return "sysLink.do?linkType=" + SysLinkForm.CATEGORY_LINK + "&objectId=" + getCategoryId().intValue();
		}

		return "";
	}
	
	public List getVirtualDocuments(VKCUser user) throws DBException {
		if (virtualDocuments == null) {
			virtualDocuments = DocumentUtils.getVirtualDocuments(getCategoryId(), user);
		}
		
		return virtualDocuments;
	}
	
	public VKCSearchQuery getSearchQuery() throws DBException {
		VKCSearchQuery sq = VKCCategorySerializer.retrieveSearchQuery(getCategoryId()); 
		
		if (sq == null || sq.isEmpty()) {
			return null;
		}
		
		return sq;
	}
	
	/**
	 * checks wether a given document is virtual document 
	 * @param VKCDocument
	 * @return true/false
	 */
	public boolean isVirtualDocument(VKCDocument doc) {
	    if (virtualDocuments != null) {
	        return virtualDocuments.contains(doc);
	    }
	    
	    return false;    
	}

	/**
	 * checks wether a given document is virtual document 
	 * @param Integer docId
	 * @return true/false
	 */
	public boolean isVirtualDocument(Integer docId) {
		if (virtualDocuments == null) {
			return false;
		}
		
		Iterator it = virtualDocuments.iterator();

        while (it.hasNext()) {
            VKCDocument doc = (VKCDocument) it.next();
            
            if (doc.getDocumentId().equals(docId)) {
				// yes, it's in the list
				return true;            	  
            }
	    }
		
		// whole list iterated with no result
		return false;    
	}
	
    /**
	 * checks wether a given document is real document 
	 * @param VKCDocument
	 * @return true/false
	 */
	public boolean isRealDocument(VKCDocument doc) {
	    if (getDocuments() != null) {
	        return getDocuments().contains(doc);
	    }
	    
	    return false;    
	}

	/**
	 * checks wether a given document is real document 
	 * @param Integer docId
	 * @return true/false
	 */
	public boolean isRealDocument(Integer docId) {
		if (getDocuments() == null) { 
			return false;
		}
		
		Iterator it = getDocuments().iterator();

        while (it.hasNext()) {
            VKCDocument doc = (VKCDocument) it.next();
            
            if (doc.getDocumentId().equals(docId)) {
				// yes, it's in the list
				return true;            	  
            }
	    }
		
		// whole list iterated with no result
		return false;    
	}

	public boolean isTrashCategory() {
	    if (VKCSysParameterSerializer.isTrashEnabled() && VKCSysParameterSerializer.getTrashCategoryId() == getCategoryId().intValue()) {
			return true;	
		}
		
	    return false;	
	}
	
	public boolean isProjectsRootCategory() {
		if (VKCSysParameterSerializer.getProjectsRootCategoryId() == getCategoryId().intValue()) {
			return true;	
		}
		
	    return false;	
	}
	
	public boolean isFunctionalViewsRootCategory() {
        if (VKCSysParameterSerializer.getFunctionalViewsRootCategoryId() == getCategoryId().intValue()) {
        	return true;	
        }
        
        return false; 		
	}
	
	public boolean isTemplatesRootCategory() {
		if (VKCSysParameterSerializer.getTemplatesRootCategoryId() == getCategoryId().intValue()) {
			return true;	
		}
        
		return false; 		
	}	
	
	public String getFolderImage() {
		if (getCategoryId() == null || getCategoryId().intValue() <= 0) {
			return "folder-notexistent.gif";
		}

		if (FLAG_DELETED.equals(getFlag())) {
			return "folder-deleted.gif"; 
		}

		if (isProjectsRootCategory()) {
			return "folder-projects-root.gif";
		}
		
		if (isFunctionalViewsRootCategory()) {
			return "folder-views-root.gif";
		}
		
		if (isTemplatesRootCategory()) {
			return "folder-templates-root.gif";
		}

		if (isTrashCategory()) {
		    return "trash.gif";
		}
		
        try {
			if (getSearchQuery() != null) {
				return "folder-with-virtual-docs.gif";
			}
        } catch (DBException e) {
        }
		
		try {
			if (Integer.parseInt(WebShare.getSysParameterValue("CATEGORYVIEW", "DisplayEmptyFoldersWithEmptyIcon", "0")) != 0) {
				if (isEmpty()) {
					return "folder-empty-shaded.gif";
				}
			}
		} catch (Exception ex) {
		}


        //	TODO: add image, if subcat is not accessible
        return "folder.gif";
	}

	public String getFolderImageShaded() {
		if (getCategoryId() == null || getCategoryId().intValue() == 0) {
			return "folder-notexistent.gif";
		}
		
		if (FLAG_DELETED.equals(getFlag())) {
			return "folder-deleted.gif"; 
		}
		
		if (isProjectsRootCategory()) {
			return "folder-projects-root-shaded.gif";
		}
		
		if (isFunctionalViewsRootCategory()) {
			return "folder-views-root-shaded.gif";
		}
		
		if (isTemplatesRootCategory()) {
			return "folder-templates-root-shaded.gif";
		}

        try {
			if (Integer.parseInt(WebShare.getSysParameterValue("CATEGORYVIEW", "DisplayEmptyFoldersWithEmptyIcon", "0")) != 0) {
				if (isEmpty()) {
					return "folder-empty-shaded.gif";
				}
			}
        } catch (Exception ex) {
        }

		try {
			if (getSearchQuery() != null) {
				return "folder-with-virtual-docs-shaded.gif";
			}
		} catch (DBException e) {
		}

		return "folder-shaded.gif";
	}
	
   public boolean isEmpty() {
       if (getDocuments().isEmpty() && getSubCategories().isEmpty()) {
           return true; 	
       }
       
       return false;
   }
   
   public boolean hasPermission(VKCUser user, char permToken) {
	try {
		if (getCategoryId() != null) {
			if (!user.hasAdminPrivileges()) {								
				return PermissionManager.hasPermission(getAcl(), user, permToken);
			} 
			
			return true;					
		}		
	} catch (Exception e) {
	}	
   	
	return false;
   }
   
	public Integer getFlag() {
	    if (flag == null) {
	        return FLAG_NULL;
	    }

	    return flag;
	}

	public void setFlag(Integer i) {
	    if (i == null) {
	        flag = FLAG_NULL;
	    } else {
			flag = i;
		}
	}
	
	public boolean isInTrash() {
	    try {
            return CategoryUtils.isInTrash(getCategoryId());
        } catch (DBException e) {
            return false;
        }
	}
	
	/**
	 * Diese Methode bestimmt ob die Kategorie mit der ID "catId" sich im 
	 * hierarchischen Baumstruktur dieser Kategorie befindet
	 */
	public boolean isInTree(Integer catId) {
	    try {
            return CategoryUtils.isInTree(getCategoryId(), catId);
        } catch (DBException e) {
            return false;
        }
	}

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        
        if (obj instanceof VKCCategory) {
            VKCCategory category = (VKCCategory) obj;
            
            return getCategoryId().equals(category.getCategoryId());
        }
        
        return false;
    }
}