/*
 * $Id: VKCDocumentDAO.java,v 1.18 2008/04/17 10:01:00 marbobo Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
 * THIS SOFTWARE OR ITS DERIVATIVES.
 *
 * $Log: VKCDocumentDAO.java,v $
 * Revision 1.18  2008/04/17 10:01:00  marbobo
 * + Velocity-Templates angepasst
 *
 * Revision 1.17  2007/07/27 11:14:32  marbobo
 * + Ungenutzte Anweisungen entfernt
 *
 * Revision 1.16  2007/06/29 13:40:24  marbobo
 * + Bugfix (Vermeiden von doppel Benachrichtigungen)
 *
 * Revision 1.15  2007/02/06 08:54:34  lawern
 * Dynamische Attribute zu Delegator-Objekten gemacht + getDynaAttributes()-Methoden implementiert.
 *
 * Revision 1.14  2007/01/09 11:56:27  korowa
 * VKCExternalException wird nicht mehr abgefangen sondern weitergereicht
 *
 * Revision 1.13  2007/01/05 09:08:52  lawern
 * addNewDocumentVersion setzt jetzt auch die DocSize richtig.
 *
 * Revision 1.12  2006/10/09 12:19:26  marbobo
 * + Beim erhöhen der Zugriffsanzahl wird nicht mehr saveDocument aufgerufen
 *
 * Revision 1.11  2006/09/29 12:18:39  korowa
 * Javadoc-Anpassungen
 *
 * Revision 1.10  2006/09/20 14:21:47  lawern
 * Überarbeitung
 *
 * Revision 1.9  2006/09/15 09:03:31  lawern
 * Javadoc-Anpassungen
 *
 * Revision 1.8  2006/09/14 09:04:33  lawern
 * DB-Persistenz nur noch nach .save-Aufruf
 *
 * Revision 1.7  2006/09/14 07:54:34  korowa
 * Suchfunktion auf dynamische Attribute implementiert
 *
 * Revision 1.6  2006/09/13 09:32:46  lawern
 * allgemeine Überarbeitung
 *
 * Revision 1.5  2006/09/01 16:14:27  marlin
 * Signatur für Suchfunktion / Schlüsselwörter eingefügt
 *
 * Revision 1.4  2006/09/01 11:05:30  korowa
 * Suchfunktion implementiert
 *
 * Revision 1.3  2006/08/24 14:53:40  marbobo
 * + kleine Anpassungen
 *
 * Revision 1.2  2006/08/24 01:01:32  marbobo
 * + Behandlung von dyn. Attributen
 * + Bugfixes
 *
 * Revision 1.1  2006/08/17 13:01:14  lawern
 * Erster unvollständiger Schnellschuss für die Externe API.
 *
 */
package vkc.external;

import vkc.converter.DocConverter;

import vkc.db.VKCDatabase;

import vkc.db.common.ConversionStatus;

import vkc.db.entities.VKCCategory;
import vkc.db.entities.VKCDocument;
import vkc.db.entities.VKCDocumentType;
import vkc.db.entities.VKCDynaAttribute;
import vkc.db.entities.VKCSearchQuery;
import vkc.db.entities.VKCUser;

import vkc.db.serializer.VKCAclSerializer;
import vkc.db.serializer.VKCDocumentSerializer;
import vkc.db.serializer.VKCDocumentTypeSerializer;

import vkc.exception.DBException;

import vkc.search.Search;
import vkc.search.SearchResultEntry;

import vkc.security.PermissionManager;

import vkc.server.archive.DataStoreException;

import vkc.util.CategoryUtils;
import vkc.util.VKCStringUtils;

import vkc.view.WebShare;

import java.io.InputStream;

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

import javax.servlet.http.HttpSession;

import javax.swing.text.BadLocationException;


/**
 * Diese Klasse kapselt ein im VKC-System liegendes Dokument. Mit Hilfe der
 * Methoden dieser Klasse können vorhandene Dokumente geholt und bestimmte
 * Metadaten verändert werden. Ebenso können auch neue Dokumente angelegt
 * werden.
 *
 * @author marbobo
 */
public class VKCDocumentDAO {
    private VKCDocument internalDocument; // internes VKCDocument-Objekt

    VKCDocumentDAO() {
        internalDocument = new VKCDocument();
        internalDocument.setName("");
        internalDocument.setTitle("");
        internalDocument.setDescription("");
        internalDocument.setDocumentType(VKCDocumentTypeSerializer.getDocumentTypeByExtension(
                "?"));
        internalDocument.setDocTypeId(internalDocument.getDocumentType()
                                                      .getDocTypeId());
        internalDocument.setCategoryId(new Integer(VKCCategory.ROOT_CATEGORY_ID));
    }

    /**
     * Konstruktor zum Anlegen eines Dokumentes
     *
     * @param name
     *            Der Dateiname/ URL
     * @param title
     *            Der Titel des Dokumentes
     * @param description
     *            Die Beschreibung des Dokumentes
     * @param extension
     *            Dokumenttype (z.B. doc, txt, pdf, link := HTTP-Verweis, note :=
     *            VKC-Notiz usw...)
     * @param categoryId
     *            Kategorie ID an der das Dokument angehängt werden soll
     */
    public VKCDocumentDAO(String name, String title, String description,
        String extension, Integer categoryId) {
        internalDocument = new VKCDocument();

        if (name != null) {
            internalDocument.setName(VKCStringUtils.filterUnicodeChars(name));
        } else {
            internalDocument.setName("");
        }

        if (title != null) {
            internalDocument.setTitle(VKCStringUtils.filterUnicodeChars(title));
        } else {
            internalDocument.setTitle("");
        }

        if (description != null) {
            description = VKCStringUtils.filterUnicodeChars(description);
            description = VKCStringUtils.decodeXmlEntities(description);
            description = VKCStringUtils.encodeNamedXmlEntities(description);
            internalDocument.setDescription(description);
        } else {
            internalDocument.setDescription("");
        }

        if (extension != null) {
            VKCDocumentType documentType = VKCDocumentTypeSerializer.getDocumentTypeByExtension(extension);

            if (documentType != null) {
                internalDocument.setDocumentType(documentType);
                internalDocument.setDocTypeId(documentType.getDocTypeId());
            } else {
                internalDocument.setDocumentType(VKCDocumentTypeSerializer.getDocumentTypeByExtension(
                        "?"));
                internalDocument.setDocTypeId(internalDocument.getDocumentType()
                                                              .getDocTypeId());
            }
        } else {
            internalDocument.setDocumentType(VKCDocumentTypeSerializer.getDocumentTypeByExtension(
                    "?"));
            internalDocument.setDocTypeId(internalDocument.getDocumentType()
                                                          .getDocTypeId());
        }

        if ((categoryId != null) && (categoryId.intValue() > 0)) {
            internalDocument.setCategoryId(categoryId);
        } else {
            internalDocument.setCategoryId(new Integer(
                    VKCCategory.ROOT_CATEGORY_ID));
        }
    }

    private VKCDocumentDAO(VKCDocument doc) {
        internalDocument = doc;
    }

    /**
     * Liefert für die angegebene ID und den Benutzer das entsprechende Dokument
     * zurück
     *
     * @param documentId
     *            ID des Dokumentes
     * @param session
     *            Session-Objekt in dem der aktuelle Benutzer hinterlegt ist
     * @return Das angeforderte Dokument
     * @throws VKCExternalException 
     */
    public static VKCDocumentDAO retrieveVKCDocumentDAO(Integer documentId,
        HttpSession session) throws VKCExternalException {
        return retrieveVKCDocumentDAO(documentId,
            (VKCUser) session.getAttribute(WebShare.USER_KEY));
    }

    /**
     * Liefert für die angegebene ID und den Benutzer das entsprechende Dokument
     * zurück
     *
     * @param documentId
     *            ID des Dokumentes
     * @param user
     *            Der aktuelle Benutzer
     * @return Das angeforderte Dokument, falls der Benutzer die entsprechenden
     *         Rechte hat, null sonst
     * @throws VKCExternalException 
     */
    public static VKCDocumentDAO retrieveVKCDocumentDAO(Integer documentId,
        VKCUserDAO user) throws VKCExternalException {
        if (user.isAuthorized()) {
            return retrieveVKCDocumentDAO(documentId, user.getIntUser());
        }

        return null;
    }

    /**
     * Liefert für die angegebene ID und den Benutzer das entsprechende Dokument
     * zurück
     *
     * @param documentId
     *            ID des Dokumentes
     * @param user
     *            Der aktuelle Benutzer
     * @return Das angeforderte Dokument, falls der Benutzer die entsprechenden
     *         Rechte hat, null sonst
     * @throws VKCExternalException 
     */
    private static VKCDocumentDAO retrieveVKCDocumentDAO(Integer documentId,
        VKCUser user) throws VKCExternalException {
        if (user == null) {
            return null;
        }

        VKCDocument doc = null;

        try {
            Integer categoryId = CategoryUtils.retrieveCategoryIdForDocument(documentId);

            if (PermissionManager.hasPermission(
                        VKCAclSerializer.getAclByCategoryId(categoryId), user,
                        'r')) {
                doc = VKCDocumentSerializer.retrieveDocument(documentId);

                if (doc != null) {
                    return new VKCDocumentDAO(doc);
                }

                throw new VKCExternalException(VKCExternalException.UNKNOWN);
            }

            throw new VKCExternalException(VKCExternalException.READ_PERMISSION_FAILED);
        } catch (DBException e) {
            e.printStackTrace();

            return null;
        } 
    }

    /**
     * Speichert das aktuelle Dokument
     *
     * @param session
     *            Session-Objekt in dem der aktuelle Benutzer hinterlegt ist
     * @throws VKCExternalException 
     */
    public void save(HttpSession session) throws VKCExternalException {
        save((VKCUser) session.getAttribute(WebShare.USER_KEY));
    }

    /**
     * Speichert das aktuelle Dokument
     *
     * @param user
     *            Der aktuelle Benutzer
     * @throws VKCExternalException 
     */
    public void save(VKCUserDAO user) throws VKCExternalException {
        if (user.isAuthorized()) {
            save(user.getIntUser());
        }
    }

    /**
     * Speichert das aktuelle Dokument
     *
     * @param user
     *            Der aktuelle Benutzer
     * @throws VKCExternalException 
     */
    private void save(VKCUser user) throws VKCExternalException {
        if (internalDocument.getDocumentId() != null) {
            try {
                if (PermissionManager.hasPermission(
                            VKCAclSerializer.getAclByCategoryId(
                                internalDocument.getCategoryId()), user, 'w')) {
                    VKCDocument document = VKCDocumentSerializer.retrieveDocument(internalDocument.getDocumentId());

                    if (document != null) {
                        document.setLastChanged(new Date(
                                System.currentTimeMillis()));
                        document.setLastChangerId(user.getUserId());
                        VKCDocumentSerializer.saveDocument(internalDocument,
                            true, false);
                    } else {
                        if (internalDocument == null) {
                            document = new VKCDocument();
                        } else {
                            document = internalDocument;
                        }
                        
                        document.setPublished(new Date(
                                System.currentTimeMillis()));
                        document.setPublisherId(user.getUserId());

                        // set tracking data to null
                        document.setLastAccessed(null);
                        document.setLastAccessorId(null);
                        document.setLastChanged(null);
                        document.setLastChangerId(null);
                        internalDocument = document;
                        VKCDocumentSerializer.saveDocument(internalDocument,
                            true, true);
                    }
                } else {
                    throw new VKCExternalException(VKCExternalException.WRITE_PERMISSION_FAILED);
                }
            } catch (DBException e) {
                e.printStackTrace();
            } 
        } else {            
            throw new VKCExternalException(VKCExternalException.ID_MISSED);
        }
    }

    /**
     * Fügt diesem Dokumenten eine neue Version hinzu
     *
     * @param document
     *            Hinzufügendes Dokument
     * @param session
     *            Session-Objekt in dem der aktuelle Benutzer hinterlegt ist
     * @throws VKCExternalException 
     */
    public void addNewDocumentVersion(VKCDocumentDAO document,
        HttpSession session) throws VKCExternalException {
        if ((document.getDocTypeId().intValue() == DocConverter.link) ||
                (document.getDocTypeId().intValue() == DocConverter.note)) {
            addNewDocumentVersion(document, null,
                (VKCUser) session.getAttribute(WebShare.USER_KEY));
        } else {            
            throw new VKCExternalException(VKCExternalException.DOCUMENT_STREAM_MISSED);
        }
    }

    /**
     * Fügt diesem Dokumenten eine neue Version hinzu
     *
     * @param document
     *            Hinzufügendes Dokument
     * @param user
     *            Der aktuelle Benutzer
     * @throws VKCExternalException 
     */
    public void addNewDocumentVersion(VKCDocumentDAO document, VKCUserDAO user) throws VKCExternalException {
        if (user.isAuthorized()) {
            if ((document.getDocTypeId().intValue() == DocConverter.link) ||
                    (document.getDocTypeId().intValue() == DocConverter.note)) {
                addNewDocumentVersion(document, null, user.getIntUser());
            } else {               
                throw new VKCExternalException(VKCExternalException.DOCUMENT_STREAM_MISSED);
            }
        }
    }

    /**
     * Fügt dieser Kategorie ein Dokument mit dem angegebenem InputStream hinzu
     *
     * @param document
     *            Hinzufügendes Dokument
     * @param docStream
     *            InputStream des Dokuments
     * @param session
     *            Session-Objekt in dem der aktuelle Benutzer hinterlegt ist
     * @throws VKCExternalException 
     */
    public void addNewDocumentVersion(VKCDocumentDAO document,
        InputStream docStream, HttpSession session) throws VKCExternalException {
        if (docStream == null) {
            throw new VKCExternalException(VKCExternalException.DOCUMENT_STREAM_MISSED);            
        }

        addNewDocumentVersion(document, docStream,
            (VKCUser) session.getAttribute(WebShare.USER_KEY));
    }

    /**
     * Fügt dieser Kategorie ein Dokument mit dem angegebenem InputStream hinzu
     *
     * @param document
     *            Hinzufügendes Dokument
     * @param docStream
     *            InputStream des Dokuments
     * @param user
     *            Der aktuelle Benutzer
     * @throws VKCExternalException 
     */
    public void addNewDocumentVersion(VKCDocumentDAO document,
        InputStream docStream, VKCUserDAO user) throws VKCExternalException {
        if (user.isAuthorized()) {
            if (docStream == null) {                
                throw new VKCExternalException(VKCExternalException.DOCUMENT_STREAM_MISSED);
            }

            addNewDocumentVersion(document, docStream, user.getIntUser());
        }
    }

    /**
     * Fügt dieser Kategorie ein Dokument mit dem angegebenem InputStream hinzu
     *
     * @param document
     *            Hinzufügendes Dokument
     * @param docStream
     *            InputStream des Dokuments
     * @param user
     *            Der aktuelle Benutzer
     * @throws VKCExternalException 
     */
    private void addNewDocumentVersion(VKCDocumentDAO document,
        InputStream docStream, VKCUser user) throws VKCExternalException {
        if (document == null) {
            return;
        }

        if (internalDocument.getDocumentId() != null) {
            try {
                if (PermissionManager.hasPermission(
                            VKCAclSerializer.getAclByCategoryId(
                                internalDocument.getCategoryId()), user, 'w')) {
                    String archiveId = null;

                    VKCDocument doc = document.getInternalDocument();
                    
                    if (docStream != null) {
                        doc.setDocSize(new Long(docStream.available()));
                        
                        // Stream im Archiv speichern
                        archiveId = WebShare.archive.store(docStream);
                    } else {
                        doc.setDocSize(new Long(0));
                    }
                    
                    doc.setPublished(new Date(System.currentTimeMillis()));
                    doc.setPublisherId(user.getUserId());

                    // set tracking data to null
                    doc.setLastAccessed(null);
                    doc.setLastAccessorId(null);
                    doc.setLastChanged(null);
                    doc.setLastChangerId(null);

                    doc.setArchiveId(archiveId);
                    doc.setPreDocId(internalDocument.getDocumentId());
                    doc.setVersionNo(new Integer(internalDocument.getVersionNo()
                                                                 .intValue() +
                            1));
                    doc.setCategoryId(internalDocument.getCategoryId());

                    // Dokument aus dem Suchindex entfernen
                    Search.removeDocument(internalDocument.getDocumentId(), true);
                    // Dokument aus der Kategorie entfernen
                    VKCDocumentSerializer.deleteCategoryDocument(internalDocument.getDocumentId());
                    internalDocument.setCategoryId(null);

                    try {
                        VKCDocumentType docType = doc.getDocumentType();

                        if (docType.isConvertible()) {
                            VKCDocumentSerializer.saveDocument(doc, false, true);

                            ConversionStatus cs = new ConversionStatus();
                            cs.setDocumentId(doc.getDocumentId());
                            cs.setSendmail(false);
                            cs.makePersistent();
                        } else {
                            VKCDocumentSerializer.saveDocument(doc, true, true);
                        }
                    } catch (DBException e3) {
                        if (!e3.getMessage().equals(VKCDatabase.QUOTA_EXCEEDED)) {
                            
                           throw new VKCExternalException(VKCExternalException.QUOTA_EXCEEDED);
                           
                        }

                        if (archiveId != null) {
                            WebShare.archive.delete(archiveId);
                        }

                        return;
                    }
                } else {
                    throw new VKCExternalException(VKCExternalException.WRITE_PERMISSION_FAILED);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            throw new VKCExternalException(VKCExternalException.ID_MISSED);
        }
    }

    /**
     * Methode zum Suchen über die VKC-Dokumente
     *
     * @param query
     *            Suchanfrage in <a href="http://lucene.apache.org/java/docs/queryparsersyntax.html">Lucene-Syntax</a>.
     *            In folgenden Feldern kann gesucht werden:
     *            
     * @param documentType
     *            gibt an, welcher Dokumenttyp in der Trefferliste auftreten
     *            darf. Falls documentType == *, werden alle Typen erlaubt.
     *            Folgende Dokument-Typen werden unterstützt:
     *            <table border=1>
     *            <tr><td><b>documentType</b></td><td><b>description</b></td></tr>
     *            <tr><td>?</td><td>undefined</td></tr>
     *            <tr><td>TXT</td><td>Generic ASCII Text</td></tr>
     *            <tr><td>PDF</td><td>Adobe Portable Document Format</td></tr>
     *            <tr><td>DOC</td><td>Microsoft Winword Document</td></tr>
     *            <tr><td>HTML</td><td>Hypertext Markup Language Document</td></tr>
     *            <tr><td>PS</td><td>Postscript Document</td></tr>
     *            <tr><td>PPT</td><td>Microsoft Powerpoint Document</td></tr>
     *            <tr><td>XLS</td><td>Microsoft Excel Document</td></tr>
     *            <tr><td>RTF</td><td>Rich Text Format Document</td></tr>
     *            <tr><td>WPD</td><td>Word Perfect Document</td></tr>
     *            <tr><td>TDF</td><td>Typography Description Format Document</td></tr>
     *            <tr><td>NOTE</td><td>Note</td></tr>
     *            <tr><td>LINK</td><td>Link</td></tr>
     *            <tr><td>GIF</td><td>Graphics Interchange Format</td></tr>
     *            <tr><td>JPG</td><td>Joint Photographic Experts Group Graphic</td></tr>
     *            <tr><td>BMP</td><td>Bit Map Picture</td></tr>
     *            <tr><td>PNG</td><td>Portable Network Graphics</td></tr>
     *            <tr><td>EXE</td><td>Executable</td></tr>
     *            <tr><td>ZIP</td><td>ZIP File</td></tr>
     *            <tr><td>XML</td><td>Extensible Markup Language File</td></tr>
     *            <tr><td>EML</td><td>Email File</td></tr>
     *            <tr><td>MSG</td><td>Outlook Email File</td></tr>
     *            <tr><td>HTM</td><td>Hypertext Markup Language Document</td></tr>
     *            <tr><td>TEX</td><td>TeX document</td></tr>
     *            <tr><td>SXI</td><td>StarOffice Presentation</td></tr>
     *            <tr><td>SXW</td><td>StarOffice Write Document</td></tr>
     *            <tr><td>SXC</td><td>StarOffice Tabel Calculation Document</td></tr>
     *            <tr><td>FH8</td><td>Bildformat für &quot;Free Hand&quot;</td></tr>
     *            <tr><td>FH11</td><td>Bildformat für &quot;Free Hand&quot;</td></tr>
     *            <tr><td>ODT</td><td>OpenOffice Textdatei</td></tr>
     *            </table>
     *            
     * @param session
     *            Benutzer-Session für welche die Ergebnisliste gefiltert werden
     *            soll.
     * @param categoryId
     *            Wird hier eine gültige categoryId übergeben, so werden nur
     *            Ergebnisse angezeigt, die unterhalb dieser Kategorie liegen.
     *
     * @return Liste mit Objekten vom Typ VKCDocumentDAO der gefundenen Dokumente
     *         oder <code>null</code> falls etwas schief gegangen ist
     */
    public static List search(String query, Date from, Date until,
        String documentType, HttpSession session, Integer categoryId) {
        if (session != null) {
            return search(query, from, until, documentType,
                (VKCUser) session.getAttribute(WebShare.USER_KEY), categoryId);
        }

        return null;
    }

    /**
     * Methode zum Suchen über die VKC-Dokumente
     *
     * @param query
     *            Suchanfrage
     * @param documentType
     *            gibt an, welcher Dokumenttyp in der Trefferliste auftreten
     *            darf. Falls documentType == *, werden alle Typen erlaubt.
     * @see #search(String query, Date from, Date until, String documentType, HttpSession session, Integer categoryId)
     * @param user
     *            VKCUserDAO für den die Ergebnisliste gefiltert werden soll.
     * @param categoryId
     *            Wird hier eine gültige categoryId übergeben, so werden nur
     *            Ergebnisse angezeigt, die unterhalb dieser Kategorie liegen.
     *
     * @return Liste mit Objekten vom Typ VKCDocumentDAO der gefundenen Dokumente
     *         oder <code>null</code> falls etwas schief gegangen ist
     */
    public static List search(String query, Date from, Date until,
        String documentType, VKCUserDAO user, Integer categoryId) {
        if (user.isAuthorized()) {
            return search(query, from, until, documentType, user.getIntUser(),
                categoryId);
        }

        return null;
    }
    
    
    /**
     * Methode zum Suchen nach dynamischen Attributen auf VKC-Objekten
     *
     * @param dynaNames
     *            String-Array, das alle Namen von dyn. Attributen enthält, auf denen die Suche durchgeführt
     *            werden soll
     * @param dynaValues
     *            String-Array, das die zu suchenden Werte enthält
     * @param session
     *            Benutzer-Session für welche die Ergebnisliste gefiltert werden
     *            soll.
     * @param categoryId
     *            Wird hier eine gültige categoryId übergeben, so werden nur
     *            Ergebnisse angezeigt, die unterhalb dieser Kategorie liegen.
     *
     * @return Liste mit Objekten vom Typ VKCDocumentDAO der gefundenen Dokumente
     *         oder <code>null</code> falls etwas schief gegangen ist
     */
    public static List search(String[] dynaNames, String[] dynaValues, Integer categoryId, HttpSession session) {
    	Set resultSet = new HashSet();
    	String value = "";
    	VKCUser user = (VKCUser) session.getAttribute(WebShare.USER_KEY);
	 	for(int a=0;a<dynaNames.length;a++){
	 		value+=dynaValues[a]+" ";
	 	}
	 	VKCSearchQuery query = new VKCSearchQuery();
	 	query.setSelectedCategoryId(categoryId.toString());
	 	query.setSearchForDynaAttributeValues(value);
    	for(int i=0;i<dynaNames.length;i++){    	   
    	   query.setSearchForDynaAttributeNames(dynaNames[i]);
    	   List partialResult = search(query.getSearchQuery(),null,null,"*",user,categoryId);
    	   resultSet.addAll(partialResult);     	   
       }    	
        return (List)resultSet;
    }

    /**
     * Methode zum Suchen nach dynamischen Attributen auf VKC-Objekten
     *
     * @param dynaNames
     *            String-Array, das alle Namen von dynamischen Attributen enthält, auf denen die Suche durchgeführt
     *            werden soll
     * @param dynaValues
     *            String-Array, das die zu suchenden Werte enthält
     * @param user
     *            VKCUserDAO für den die Ergebnisliste gefiltert werden soll.
     * @param categoryId
     *            Wird hier eine gültige categoryId übergeben, so werden nur
     *            Ergebnisse angezeigt, die unterhalb dieser Kategorie liegen.
     *
     * @return Liste mit Objekten vom Typ VKCDocumentDAO der gefundenen Dokumente
     *         oder <code>null</code> falls etwas schief gegangen ist
     */
    public static List search(String[] dynaNames, String[] dynaValues, Integer categoryId, VKCUserDAO user) {
    	Set resultSet = new HashSet();    	
    	String value = "";
	 	for(int a=0;a<dynaNames.length;a++){
	 		value+=dynaValues[a]+" ";
	 	}
	 	VKCSearchQuery query = new VKCSearchQuery();
	 	query.setSelectedCategoryId(categoryId.toString());
	 	query.setSearchForDynaAttributeValues(value);
    	for(int i=0;i<dynaNames.length;i++){    	   
    	   query.setSearchForDynaAttributeNames(dynaNames[i]);
    	   List partialResult = search(query.getSearchQuery(),null,null,"*",user.getIntUser(),categoryId);
    	   resultSet.addAll(partialResult);     	   
       }    	
        return (List)resultSet;
    }

    /**
     * Methode zum Suchen über die VKC-Dokumente
     *
     * @param query
     *            Suchanfrage
     * @param dateRangeMode
     *            gibt das Alter an das die Dokumente haben dürfen, die gesucht
     *            werden sollen
     * @param documentType
     *            gibt an, welcher Dokumenttyp in der Trefferliste auftreten
     *            darf. Falls documentType == *, werden alle Typen erlaubt.
     * @see #search(String query, Date from, Date until, String documentType, HttpSession session, Integer categoryId)
     * @param user
     *            VKCUser für den die Ergebnisliste gefiltert werden soll.
     * @param categoryId
     *            Wird hier eine gültige categoryId übergeben, so werden nur
     *            Ergebnisse angezeigt, die unterhalb dieser Kategorie liegen.
     *
     * @return Liste mit Objekten vom Typ VKCDocumentDAO der gefundenen Dokumente
     *         oder <code>null</code> falls etwas schief gegangen ist
     */
    private static List search(String query, Date from, Date until,
        String documentType, VKCUser user, Integer categoryId) {
        List results = Search.search(query, from, until, documentType, user,
                categoryId);
        List documents = new ArrayList();

        try {
            for (Iterator iter = results.iterator(); iter.hasNext();) {
                SearchResultEntry element = (SearchResultEntry) iter.next();
                VKCDocumentDAO document = new VKCDocumentDAO(element.toVKCDocument());
                documents.add(document);
            }
        } catch (DBException e) {
            e.printStackTrace();
        }

        return documents;
    }

    /**
     * Methode die alle vom Konverter ermittelten Schlüsselwörter zurückgibt
     *
     * @return Alle Schlüsselwörter zum Dokument
     */
    public List getKeyWords() {
        // ToDo
        return null;
    }

    /**
     * Löscht das aktuelle Dokument
     *
     * @param session
     *            Session-Objekt in dem der aktuelle Benutzer hinterlegt ist
     * @throws VKCExternalException 
     */
    public void delete(HttpSession session) throws VKCExternalException {
        delete((VKCUser) session.getAttribute(WebShare.USER_KEY));
    }

    /**
     * Löscht das aktuelle Dokument
     *
     * @param user
     *            Der aktuelle Benutzer
     * @throws VKCExternalException 
     */
    public void delete(VKCUserDAO user) throws VKCExternalException {
        if (user.isAuthorized()) {
            delete(user.getIntUser());
        }
    }

    /**
     * Löscht das aktuelle Dokument
     *
     * @param user
     *            Der aktuelle Benutzer
     * @throws VKCExternalException 
     */
    private void delete(VKCUser user) throws VKCExternalException {
        try {
            if (PermissionManager.hasPermission(
                        VKCAclSerializer.getAclByCategoryId(
                            internalDocument.getCategoryId()), user, 'd')) {
                VKCDocumentSerializer.deleteDocument(user,
                    internalDocument.getDocumentId(),
                    !WebShare.isTrashEnabled(), null);
            } else {
                throw new VKCExternalException(VKCExternalException.DELETE_PERMISSION_FAILED);
            }
        } catch (DBException e) {
            e.printStackTrace();
        } 
    }

    /**
     * Liefert die Kategorie ID dieses Dokumentes
     *
     * @return Die Kategorie ID
     */
    public Integer getCategoryId() {
        return internalDocument.getCategoryId();
    }

    /**
     * Liefert die Beschreibung dieses Dokumentes
     *
     * @return Die Beschreibung
     */
    public String getDescription() {
        if (internalDocument != null) {
            String s = VKCStringUtils.filterUnicodeChars(internalDocument.getDescription());
            s = VKCStringUtils.htmlFormatedText(s);
            s = VKCStringUtils.decodeXmlEntities(s);
            s = VKCStringUtils.decodeNamedXmlEntitiesNotInSwing(s);

            try {
                s = VKCStringUtils.removeHtmlTags(s);
            } catch (BadLocationException e) {
                e.printStackTrace();
            }

            return s;
        }

        return "";
    }

    /**
     * Liefert die Dokumenttype ID dieses Dokumentes
     *
     * @return Dokumenttype ID
     */
    public Integer getDocTypeId() {
        return internalDocument.getDocTypeId();
    }

    /**
     * Liefert die ID dieses Dokumentes
     *
     * @return Die Dokument ID
     */
    public Integer getDocumentId() {
        return internalDocument.getDocumentId();
    }

    /**
     * Liefert die ID der ersten Version dieses Dokumentes
     *
     * @return Die Dokument ID
     * @throws DBException
     */
    public Integer getFirstVersionDocumentId() throws DBException {
        return internalDocument.getFirstVersionDocumentId();
    }

    /**
     * Liefert den Namen dieses Dokumentes
     *
     * @return Der Name
     */
    public String getName() {
        if (internalDocument != null) {
            return VKCStringUtils.decodeXmlEntities(VKCStringUtils.filterUnicodeChars(
                    internalDocument.getName()));
        }

        return "";
    }

    /**
     * Liefert die ID des vorgänger Version dieses Dokumentes
     *
     * @return Die Dokument ID
     * @throws DBException
     */
    public Integer getPredecessorVersionDocumentId() {
        return internalDocument.getPredecessorVersionDocumentId();
    }

    /**
     * Liefert die ID des nachfolger Version dieses Dokumentes
     *
     * @return Die Dokument ID
     * @throws DBException
     */
    public Integer getSuccessorVersionDocumentId() throws DBException {
        return internalDocument.getSuccessorVersionDocumentId();
    }

    /**
     * Liefert den Titel dieses Dokumentes
     *
     * @return Der Titel
     */
    public String getTitle() {
        if (internalDocument != null) {
            return VKCStringUtils.decodeXmlEntities(VKCStringUtils.filterUnicodeChars(
                    internalDocument.getTitle()));
        }

        return "";
    }

    /**
     * Liefert die ID die neuste Version dieses Dokumentes
     *
     * @return Die Dokument ID
     * @throws DBException
     */
    public Integer getTopVersionDocumentId() throws DBException {
        return internalDocument.getTopVersionDocumentId();
    }

    VKCDocument getInternalDocument() {
        return internalDocument;
    }

    /**
     * Fügt diesem Dokumenten ein dynamisches Attribut hinzu
     *
     * @param dynaAttribute
     *            Hinzufügendes Attribute
     */
    public void addDynaAttribute(VKCDynaAttributeDAO dynaAttribute) {
        if (internalDocument.getDocumentId() != null) {
            VKCDynaAttribute attr = new VKCDynaAttribute();
            attr.setName(VKCStringUtils.filterUnicodeChars(
                    dynaAttribute.getName()));
            attr.setValue(VKCStringUtils.filterUnicodeChars(
                    dynaAttribute.getValue()));
            internalDocument.addDynaAttribute(attr);
        }
    }

    /**
     * Gibt das dynamische Attribute zurück
     *
     * @param name
     *            Der Name des Attributes
     * @return Falls vorhanden das dynamische Attribut, sonst null
     */
    public VKCDynaAttributeDAO getDynaAttribute(String name) {
        for (Iterator iter = internalDocument.getDynaAttributes().iterator();
                iter.hasNext();) {
            VKCDynaAttribute da = (VKCDynaAttribute) iter.next();

            if (da.getName().equals(name)) {
                VKCDynaAttributeDAO attr = new VKCDynaAttributeDAO(da);

                return attr;
            }
        }

        return null;
    }
    
    /**
     * Gibt eine Liste der dynamischen Attribute zurück
     * 
     * @return Falls vorhanden eine Collection mit dynamischen Attributen
     *         (VKCDynaAttributeDAO), sonst null.
     */
    public Collection getDynaAttributes() {
        Collection attributes = internalDocument.getDynaAttributes();

        if (attributes != null) {
            ArrayList extAttributes = new ArrayList(attributes.size());
            for (Iterator iter = attributes.iterator(); iter.hasNext();) {
                VKCDynaAttribute da = (VKCDynaAttribute) iter.next();

                extAttributes.add(new VKCDynaAttributeDAO(da));
            }

            return extAttributes;
        } else {
            return null;
        }
    }

    /**
     * Löscht das dynamische Attribute mit dem angegebenen Namen
     *
     * @param name
     *            Der Name des Attributes
     */
    public void deleteDynaAttribute(String name) {
        if (internalDocument.getDocumentId() != null) {
            VKCDynaAttribute attr = null;

            for (Iterator iter = internalDocument.getDynaAttributes().iterator();
                    iter.hasNext();) {
                attr = (VKCDynaAttribute) iter.next();

                if (attr.getName()
                            .equals(VKCStringUtils.filterUnicodeChars(name))) {
                    break;
                }
            }

            if (attr != null) {
                internalDocument.getDynaAttributes().remove(attr);
            }
        }
    }

    /**
     * Ändert den Wert des angegebenen dynamischen Attributes in diesem Dokumenten
     *
     * @param name
     *            Zu änderndes Attribute
     * @param newValue
     *            Der neue Wert
     * @param session
     *            Session-Objekt in dem der aktuelle Benutzer hinterlegt ist
     * @throws VKCExternalException 
     */
    public void setDynaAttribute(String name, String newValue,
        HttpSession session) throws VKCExternalException {
        setDynaAttribute(name, newValue,
            (VKCUser) session.getAttribute(WebShare.USER_KEY));
    }

    /**
     * Ändert den Wert des angegebenen dynamischen Attribute in diesem Dokumenten
     *
     * @param name
     *            Zu änderndes Attribute
     * @param newValue
     *            Der neue Wert
     * @param user
     *            Der aktuelle Benutzer
     * @throws VKCExternalException 
     */
    public void setDynaAttribute(String name, String newValue, VKCUserDAO user) throws VKCExternalException {
        if (user.isAuthorized()) {
            setDynaAttribute(name, newValue, user.getIntUser());
        }
    }
    
    /**
     * Vergleicht vorgegebenen DocumentDAO Objekt mit existierenden
     *
     * @param o
     *            Zu vergleichendes Objekt
     * 
     * * @return Falls Objecte gleich sind true , sonst false
     */
        
    public boolean equals(Object o){
		if (!(o instanceof VKCDocumentDAO)) {
			return false;
		}
		
		if (getDocumentId() == null) {
			return false;
		}
		
		return getDocumentId().equals(((VKCDocumentDAO)o).getDocumentId());
	}

    /**
     * Ändert den Wert des angegebenen dynamischen Attributes in diesem Dokumenten
     *
     * @param name
     *            Zu änderndes Attribute
     * @param newValue
     *            Der neue Wert
     * @param user
     *            Der aktuelle Benutzer
     * @throws VKCExternalException 
     */
    private void setDynaAttribute(String name, String newValue, VKCUser user) throws VKCExternalException {
        if (internalDocument.getDocumentId() != null) {
            try {
                if (PermissionManager.hasPermission(
                            VKCAclSerializer.getAclByCategoryId(
                                internalDocument.getCategoryId()), user, 'w')) {
                    VKCDynaAttribute attr = null;

                    for (Iterator iter = internalDocument.getDynaAttributes()
                                                         .iterator();
                            iter.hasNext();) {
                        attr = (VKCDynaAttribute) iter.next();

                        if (attr.getName()
                                    .equals(VKCStringUtils.filterUnicodeChars(
                                        name))) {
                            break;
                        }
                    }

                    if (attr != null) {
                        internalDocument.getDynaAttributes().remove(attr);
                        attr.setValue(VKCStringUtils.filterUnicodeChars(
                                newValue));
                        internalDocument.addDynaAttribute(attr);
                        internalDocument.setLastChanged(new Date(
                                System.currentTimeMillis()));
                        internalDocument.setLastChangerId(user.getUserId());
                        VKCDocumentSerializer.saveDocument(internalDocument,
                            true, false);
                    }
                } else {
                    throw new VKCExternalException(VKCExternalException.WRITE_PERMISSION_FAILED);
                }
            } catch (DBException e) {
                e.printStackTrace();
            } 
        }
    }

    /**
     * Liefert das Originaldokument als Stream.
     * @return InputStream des Originaldokuments
     */
    public InputStream getDocumentStream() {
        try {
            return WebShare.archive.getInputStream(internalDocument.getArchiveId());
        } catch (DataStoreException e) {
            // TODO Auto-generated catch block
            return null;
        }
    }

    /**
     * Liefert den MimeType des Originaldokuments.
     * @return MimeType des Originaldokuments
     */
    public String getDocumentMimeType() {
        return internalDocument.getDocumentType().getMimeType();
    }

    /**
     * Liefert den Namen des Autors falls angegeben, sonst name des Publizierer
     * @return String
     */
    public String getAuthorName() {
        return internalDocument.getAuthorName();
    }

    /**
     * Liefert das Publizierdatum von dem Dokument
     * @return Publizierdatum
     */
    public Date getCreationDate() {
        return internalDocument.getCreationDate();
    }

    /**
     * Liefert die Grösse des Dokuments
     * @return Dateigrösse
     */
    public Long getDocSize() {
        return internalDocument.getDocSize();
    }

    /**
     * Liefert das Enddatum für die Gültigkeit des Dokuments
     * @return Enddatum
     */
    public Date getExpires() {
        return internalDocument.getExpires();
    }

    /**
     * Liefert BenutzerId des Publizierers
     * @return BenutzerId
     */
    public Integer getPublisherId() {
        return internalDocument.getPublisherId();
    }

    /**
     * Liefert, falls angegeben, Beschreibung der Version vom Dokument
     * @return Beschreibung
     */
    public String getVersionDescr() {
        return internalDocument.getVersionDescr();
    }

    /**
     * Zusammen mit versionLow ergibt eine vom Benutzer vorgegebene Versionierung des Dokuments
     * z.B. Version 4.1 bedeutet versionHigh=4 und versionLow=1
     * @return Integer
     */
    public Integer getVersionHigh() {
        return internalDocument.getVersionHigh();
    }

    /**
     * Zusammen mit versionHigh ergibt eine vom Benutzer vorgegebene Versionierung des Dokuments
     * z.B. Version 4.1 bedeutet versionHigh=4 und versionLow=1
     * @return Integer
     */
    public Integer getVersionLow() {
        return internalDocument.getVersionLow();
    }

    /**
     * Lifert die Anzahl von Versionen des Dokuments
     * @return Anzahl der Versionen
     */
    public Integer getVersionNo() {
        return internalDocument.getVersionNo();
    }

    /**
     * Liefert das Publizierdatum vom Dokument
     * @param creationDate
     * 		Publizierdatum
     */
    public void setCreationDate(Date creationDate) {
        internalDocument.setCreationDate(creationDate);
    }

    /**
     * Setzt Beschreibung des Dokuments auf übergebenen String
     * @param s
     * 		Neue Beschreibung des Dokuments
     */
    public void setDescription(String s) {
        internalDocument.setDescription(s);
    }

    /**
     * Setzt das Ablaufdatum des Dokuments auf übergebenes Datum
     * @param d
     * 		Ablaufdatum des Dokuments
     */
    public void setExpires(Date d) {
        internalDocument.setExpires(d);
    }

    /**
     * Setzt die Schlüßelwörter des Dokuments auf übergebenen String
     * @param keys
     * 		Schlüßelwörter des Dokuments
     */
    public void setKeywords(String keys) {
        internalDocument.setKeywordsByString(keys);
    }

    /**
     * Setzt den Namen des Dokuments auf übergebenen String
     * @param s
     * 		Name
     */
    public void setName(String s) {
        internalDocument.setName(s);
    }

    /**
     * Setzt den VKC-Title des Dokuments auf übergebenen String
     * @param s
     * 		Title
     */
    public void setTitle(String s) {
        internalDocument.setTitle(s);
    }

    /**
     * Setzt die Beschreibung der aktuellen Version von dem Dokument auf übergebenen String
     * @param s
     * 		Beschreibung der aktuellen Version
     */
    public void setVersionDescr(String s) {
        internalDocument.setVersionDescr(s);
    }

    /**
     * Setzt versionHigh auf übergebenen Parameter, der zusammen mit versionLow
     * eine vom Benutzer vorgegebene Versionierung des Dokuments ergibt
     * z.B. Version 4.1 bedeutet versionHigh=4 und versionLow=1
     * @param i
     */
    public void setVersionHigh(Integer i) {
        internalDocument.setVersionHigh(i);
    }

    /**
     * Setzt versionLow auf übergebenen Parameter, der zusammen mit versionHigh
     * eine vom Benutzer vorgegebene Versionierung des Dokuments ergibt
     * z.B. Version 4.1 bedeutet versionHigh=4 und versionLow=1
     * @param i
     */
    public void setVersionLow(Integer i) {
        internalDocument.setVersionLow(i);
    }
}
