/*
 * $Id: SharkService.java,v 1.34 2006/07/17 13:04:03 lawern Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 * 
 * $Log: SharkService.java,v $
 * Revision 1.34  2006/07/17 13:04:03  lawern
 * Neuer typografiegewichteter Index und k-NN-Klassifikation aus meiner Diss.
 *
 * Revision 1.33  2006/03/23 11:47:20  marbobo
 * + Mögliche NPE abgefangen
 *
 * Revision 1.32  2005/03/21 20:30:06  marbobo
 * + DocSize auf Type Long umgestellt
 *
 * Revision 1.31  2005/01/03 20:10:30  jonyp
 * deleted uncecessary imports
 *
 * Revision 1.30  2005/01/03 17:53:48  marlin
 * Rückgabe-ID für Methode getNextSimilarStdTDoc() geändert
 *
 * Revision 1.29  2004/12/23 16:18:18  koppy
 * PARelevantLiteratur=false für alle Ergebnisdokumente
 *
 * Revision 1.28  2004/12/21 10:26:05  koppy
 * Viele kleine Änderungen, Mini-Bugfixes
 * + getStdtDocument(String docId)
 *
 * Revision 1.27  2004/12/20 15:29:43  jonyp
 * added cvs log
 *
 * 
 */

package vkc.ws.axis.service;

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

import org.apache.axis.AxisFault;
import org.apache.axis.MessageContext;

import vkc.db.entities.VKCAclType;
import vkc.db.entities.VKCCategory;
import vkc.db.entities.VKCDocument;
import vkc.db.entities.VKCDynaAttribute;
import vkc.db.entities.VKCUser;
import vkc.db.serializer.VKCAclSerializer;
import vkc.db.serializer.VKCCategorySerializer;
import vkc.db.serializer.VKCDocumentSerializer;
import vkc.db.serializer.VKCDocumentTypeSerializer;
import vkc.db.serializer.VKCDynaAttributeSerializer;
import vkc.misc.VKCLogManager;
import vkc.search.Search;
import vkc.search.SearchResultEntry;
import vkc.security.PermissionManager;
import vkc.security.VKCAcl;
import vkc.util.CategoryUtils;
import vkc.util.StringUtils;
import vkc.view.WebShare;
import vkc.ws.axis.service.patent.ByDocumentsClassifier;
import vkc.ws.axis.service.patent.ByKeywordClassifier;
import vkc.ws.axis.service.patent.ByStdTDocumentsClassifier;
import vkc.ws.axis.service.patent.PatentClassifyResultDocument;
import vkc.ws.axis.service.patent.PatentUtils;
import vkc.ws.axis.service.patent.SummarizedClassifier;

public class SharkService {

    public static final String ATTR_PATENT_APP_CAPABLE = "PatentApplicationCapable";
    public static final String ATTR_INCOMING_CAT = "PAIncomingCat";
    public static final String ATTR_ISSUED_CAT = "PAIssuedCat";
	public static final String ATTR_REJECTED_CAT = "PARejectedCat";
	public static final String ATTR_VETO_CAT = "PAVetoCat";
    public static final String ATTR_STATUS = "PAStatus";
	public static final String ATTR_TYPE = "PAType";
    public static final String ATTR_STATE_OF_THE_ART_DOC = "PAStateOfTheArtDoc";
	public static final String ATTR_PATENT_RELEVANT_LITERATURE = "PAPatentRelevantLiterature";
	public static final String ATTR_FORMAL_CORRECT = "PAFormalCorrect";
	public static final String ATTR_CHOSEN_CATEGORY = "PAChosenCategory";
	public static final String ATTR_STDT_START = "PAStdTStart";
    public static final String ATTR_STDT_END = "PAStdTEnd";
    
	public static final String WF_STARTED = "PAWFStarted";
    public static final String WF_DONE = "PAWFDone";
	public static final String CONFIRMED = "PAConfirmed";
	public static final String REJECTED = "PARejected";
	public static final String SIMILAR_STATE_OF_THE_ART_DOC = "PASimilarSotADoc";
    
    public static final int CLASSIFY_KEYWORDS_COUNT = -1;
    public static final int MAX_SIMILAR_SOT_DOCS_COUNT = 5;
    
    public static final String DEFAULT_STDT_START = "Description";
    public static final String DEFAULT_STDT_END = "BRIEF DESCRIPTION OF THE DRAWINGS;BRIEF DESCRIPTION OF DRAWING";

    /**
     * Die Konstante <code>MAX_LITRESEARCH_RESULT_ENTRIES</code>
     * legt die maximale Anzahl von Ergebnissen bei 
     * der Literaturrechecrche fest, die in das Ergebnisdokument geschrieben werden.
     */
    public static final int MAX_LITRESEARCH_RESULT_ENTRIES = 100;
    /**
     * Die Konstante <code>MIN_LITRESEARCH_PERCENTAGE</code>
     * gibt den Schwellwert an, ab welchem Ergebnisse bei der Literaturrescherche
     * in das Ergebnissdokument übernommen werden.
     */
    public static final int MIN_LITRESEARCH_PERCENTAGE = 5;
    
	/**
	 * Diese Methode überprüft, ob der anfragende Workflow (Name in processname) für die angegebene Kategorie
	 * (categorieToProcess) angewendet werden kann. Dadurch soll verhindert werden, dass beim Instanzieren des
	 * Workflows eine Kategorie angegeben wird, für die der Workflow nicht geeignet bzw. freigegeben ist.
	 * 
	 * @param processname Der Name des Workflows, der auf der Kategorie angewendet werden soll. Für den Workflow
	 * Patentanmeldung ist das der String "patent_application" (Konstante für den WF)
	 * @param categoryId Die Kategorie, auf dem der Workflow angewendet werden soll. Dieser Wert wird beim
	 * Instanzieren des Workflows als String eingegeben.
	 * @return Boolscher Wert, der angibt, ob der Workflow und die Kategorie zusammenpassen und ob die Authentifizierung
	 * stimmt. Optional ist auch eine Authorisierung möglich (hat der Benutzer das Recht, den Workflow zu starten).
	 * @throws AxisFault
	 */
	public boolean validityCheck(String processname, String categoryId)
	throws AxisFault {
		
		try {
			VKCLogManager.infoLog( this, "Methode validityCheck aufgerufen");
			VKCLogManager.infoLog( this, "processname: " + processname);
			VKCLogManager.infoLog( this, "categorieToProcess: " + categoryId);

			if( !authenticate())
			    return false;
			
			VKCCategory cat = VKCCategorySerializer.retrieveCategory(new Integer(categoryId));
			
			if(cat == null){
	            VKCLogManager.infoLog( this, "Kategorie mit ID = " +categoryId + " nicht vorhanden!" );
	            return false;
	        }
			
			return PatentUtils.isCapablePatentCat(cat);

		} catch (Exception ex) {
			VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
			
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
		}
	}

	/**
	 * Diese Methode gibt die ID des Dokuments einer neuen Patentanmeldung (mit Status "neu") zurück. Falls keine neue
	 * Patentanmeldung existiert, wird ein Leerstring zurückgegeben. Mehrere neue Patentanmeldungen können durch sukzessive Aufrufe
	 * der Methode erfragt werden. Es ist sinnvoll, a) das Dokument mit einem Status zu versehen, damit bei zwei aufeinanderfolgenden
	 * Aufrufen das neue Dokument nur einmal zurückgegeben wird (d.h. nach ersten Aufruf sollte der Status geändert werden) und b)
	 * das Dokument mit einem Typ zu versehen, der es als Patentanmeldung kennzeichnet (im Gegensatz zu weiteren Dokumenten, die während
	 * des Prozesses generiert werden wie z.B. das Ergebnisdokument einer Klassifizierung).
	 * 
	 * @param categoryId Die Kategorie, auf dem der Workflow angewendet werden soll bzw. die auf neue
	 * Patentanmeldungs-Dokumente überprüft werden soll.
	 * @return String-Darstellung eines neuen Patentanmeldungs-Dokuments. Dies kann auch ein Schlüssel für das Dokument sein.
	 * Der zurückgegebene Wert wird für nachfolgende Methoden zur Kennzeichnung der konkreten Patentanmeldung genutzt.
	 * @throws AxisFault
	 */
	public String getNextNewDoc(String categoryId)
	throws AxisFault {
		try {
			
		    VKCLogManager.infoLog(this, "Methode getNextNewDoc aufgerufen");
		    VKCLogManager.infoLog(this, "categorieToProcess: " + categoryId);

			if( !authenticate() ){
			    return "";
			}
			
			VKCCategory patentsCat = VKCCategorySerializer.retrieveCategory(new Integer(categoryId));

			if (patentsCat == null) {
			    throw new Exception("Patentkategorie nicht vorhanden!");
			}
			
			VKCLogManager.infoLog(this, "Patentkategorie: " + patentsCat.getAbsPathDescrString());
			
			VKCCategory incomingCat = PatentUtils.getIncomingCategory( patentsCat );

			if(incomingCat == null){
			    throw new Exception( "Patentkategorie/Neuzugänge nicht gefunden/vorhanden!" );
			}
			VKCLogManager.infoLog(this, "Patentkategorie/Neuzugänge: " +incomingCat.getAbsPathDescrString());
			
			
			VKCDocument oldestPatentDoc = PatentUtils.getOldestDocumentWithoutDynaAttributes(incomingCat);
			if(oldestPatentDoc == null ){
			    VKCLogManager.infoLog(this, "Es liegen keine neuen Patentanmeldungen vor.");
			    return "";
			}
			
			VKCLogManager.infoLog(this, "Älteste Patentanmeldung: " + oldestPatentDoc.getName());
			
			VKCDynaAttribute statusAttr = new VKCDynaAttribute();
			statusAttr.setDynaAttrId( new Integer( -1 ) );
			statusAttr.setName( SharkService.ATTR_STATUS );
			statusAttr.setValue( SharkService.WF_STARTED );
			oldestPatentDoc.getDynaAttributes().add(statusAttr);
			
			statusAttr = new VKCDynaAttribute();
			statusAttr.setDynaAttrId( new Integer( -1 ) );
			statusAttr.setName( SharkService.ATTR_PATENT_RELEVANT_LITERATURE );
			statusAttr.setValue( "false" );
			oldestPatentDoc.getDynaAttributes().add(statusAttr);
			
			VKCDocumentSerializer.saveDocument(oldestPatentDoc, true, false);
			
			
			//Kategorie für das neue Patent anlegen 
			VKCUser user = (VKCUser)MessageContext.getCurrentContext().getProperty( MessageContext.AUTHUSER );
			
			VKCAcl acl = VKCAclSerializer.getAclByCategoryId( incomingCat.getCategoryId() );
            PermissionManager.setCategoryOwnerPermissions( new Integer( VKCAclType.USER_TYPE ),
                    user.getUserId(), user.getUserId(), acl );
        
    		acl.setAclId(null);
    		
    		VKCCategory oldestDocCat = new VKCCategory();
    		oldestDocCat.setName( oldestPatentDoc.getName().substring(0, 
    		        	oldestPatentDoc.getName().lastIndexOf(".")) );
            oldestDocCat.setParentCategoryId(incomingCat.getCategoryId());
    		oldestDocCat.setAcl(acl);
    		
    		String catName = oldestDocCat.getName();
    		int count = 1;
    		while(CategoryUtils.isContained(catName, incomingCat.getSubCategories())){
    		    catName = oldestDocCat.getName() + " (" +(count++)+")";
    		}
    		oldestDocCat.setName(catName);
			
    		VKCCategorySerializer.saveCategory( oldestDocCat );
    		VKCLogManager.infoLog(this, "Kategorie angelegt: " + oldestDocCat.getName());
    		
    		//Neues Patent in die dafür vorgesehene Kategorie verschieben
    		Set docIdsToMove = new HashSet();
    		docIdsToMove.add(oldestPatentDoc.getDocumentId());
    		WebShare.database.move(incomingCat, oldestDocCat, null, docIdsToMove, user);
    		VKCLogManager.infoLog(this, "Patentanlmeldung von " + incomingCat.getName() + " nach "+ oldestDocCat.getName()+ " verschoben.");
    		
    		// Kategorie: Einsprüche mit anlegen
    		acl.setAclId(null);
    		VKCDynaAttribute vetoAttr = new VKCDynaAttribute();
    		vetoAttr.setDynaAttrId( new Integer( -1 ) );
    		vetoAttr.setName( SharkService.ATTR_VETO_CAT );
    		vetoAttr.setValue( "true" );
			
    		VKCCategory vetoCat = new VKCCategory();
    		vetoCat.setName( "Einsprüche" );
    		vetoCat.setParentCategoryId(oldestDocCat.getCategoryId());
    		vetoCat.setAcl(acl);
			vetoCat.addDynaAttribute(vetoAttr);
			
    		VKCCategorySerializer.saveCategory( vetoCat );
    		VKCLogManager.infoLog(this, "Kategorie " + vetoCat.getName() + " mit dyn Attr " +vetoAttr.getName() + " angelegt!");
    		
			return oldestPatentDoc.getDocumentId().toString();
			
		} catch (Exception ex) {
		    VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
		    
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
		}
	}

	/**
	 * Durch diese Methode wird das dyn. Attribute mit gegebenem Wert für das angegebene Dokument
	 * neu angelegt (falls nicht vorhanden) oder der Wert neu gesetzt (falls dyn. Attribut schon existiert).
	 * 
	 * @param patentDocumentId Schlüssel für das Dokument mit der Patentanmeldung (alle dazugehörige Dokument müssen
	 * über Namenskonventionen miteinander verknüpft sein.
	 * @param dynAttrKey Das dynamische Attribut, das anzulegen bzw. zu ändern ist
	 * @param dynAttrValue Der Wert des zu ändernden dynamischen Attributs
	 * @return Boolscher Wert, der den Erfolg der Aktion angibt
	 * @throws AxisFault
	 */
	public boolean setDynAttribute(String patentDocumentId, String dynAttrKey, String dynAttrValue)
	throws AxisFault {
		try {
			System.out.println("Methode setDynAttribut aufgerufen");
			System.out.println("patentApplDocKey: " + patentDocumentId);
			System.out.println("dynAttrKey: " + dynAttrKey);
			System.out.println("dynAttrValue: " + dynAttrValue);

			if(!authenticate())
			    return false;
			
			VKCDocument doc = VKCDocumentSerializer.retrieveDocument(new Integer(patentDocumentId));
			
			VKCDynaAttribute attr = doc.getDynaAttribute(dynAttrKey); 
			if( attr != null){
			    // Das zu setzende Attribut existiert bereits -> UPDATE
			    attr.setValue( dynAttrValue );
			    
			}else{
			    // Exisitert noch nicht -> INSERT
			    attr = new VKCDynaAttribute();
			    attr.setName( dynAttrKey );
				attr.setValue( dynAttrValue );
				doc.getDynaAttributes().add(attr);
			}
			
			VKCDocumentSerializer.saveDocument(doc, true, false);
			
			return true;

		} catch (Exception ex) {
			
		    VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
		}
	}

	/**
	 * Durch diese Methode wird das dyn. Attribute mit gegebenem Wert (boolsch) für das angegebene Dokument
	 * neu angelegt (falls nicht vorhanden) oder der Wert neu gesetzt (falls dyn. Attribut schon existiert).
	 * 
	 * @param patentDocumentId Schlüssel für das Dokument mit der Patentanmeldung (alle dazugehörige Dokument müssen
	 * über Namenskonventionen miteinander verknüpft sein.
	 * @param dynAttrKey Das dynamische Attribut, das anzulegen bzw. zu ändern ist
	 * @param dynAttrValue Der Wert des zu ändernden dynamischen Attributs
	 * @return Boolscher Wert, der den Erfolg der Aktion angibt
	 * @throws AxisFault
	 */
/* 
 	// Darf es als WebService nicht geben, da schon mit anderen Patametern existent (s.o.)
	public boolean setDynAttribute(String patentDocumentId, String dynAttrKey, boolean dynAttrValue)
	throws AxisFault {
		String stringValue = dynAttrValue ? "true" : "false";
		return setDynAttribute(patentDocumentId, dynAttrKey, stringValue);
	}
*/
	
	private boolean authenticate(){
	    MessageContext messageContext = MessageContext.getCurrentContext();
		VKCUser user = (VKCUser)messageContext.getProperty( MessageContext.AUTHUSER );
		
		if ( user!=null ) 
		    return true;
		
		// Benutzer hat sich nicht authentifiziert
		VKCLogManager.infoLog( this, "Anmeldung fehlgeschlagen!");
		return false;

	}
	
	/**
	 * Diese Methode implementiert die Dokumentaufbereitung für Patentanmeldungen. Bei der Dokumentaufbereitung
	 * werden nötige Vorarbeiten für den Workflow zur Patentanmeldung geleistet. Das kann z.B. das Extrahieren
	 * des "Stand der Technik"-Dokuments aus der Patentanmeldung sein.
	 * 
	 * @param patentApplDocId Schlüssel für das Dokument mit der Patentanmeldung (alle dazugehörige Dokument müssen
	 * über Namenskonventionen miteinander verknüpft sein.
	 * @return Flag, das mitteilt, ob der Aufruf erfolgreich war oder nicht. 
	 * @throws AxisFault
	 */
	public boolean doPatentDocumentPreparation(String patentApplDocId) 
		throws AxisFault{
		try 
		{	
		
			VKCLogManager.infoLog(this, "Methode doPatentDocumentPreparation called...");
			VKCLogManager.infoLog(this, "Parameter patentApplDocKey: " +  patentApplDocId);
		
			if( !authenticate()){
			    return false;
			}

			// Hier Überprüfung auf gültige Parameter und Existenz der Dokumente und
			// Kategorien
			if (StringUtils.isEmpty(patentApplDocId)){
				return false;
			}

			VKCUser user = (VKCUser)MessageContext.getCurrentContext().getProperty( MessageContext.AUTHUSER );
			
			// Hier Implementierung der eigentlichen Funktionalität. 
			VKCDocument patentDoc = VKCDocumentSerializer.retrieveDocument(new Integer(patentApplDocId));
			VKCCategory patentsRootCategory = PatentUtils.getPatentsCapableCategory(patentDoc);
			
			String sdtSection = PatentUtils.extractStdT( patentDoc, 
			        patentsRootCategory.getDynaAttribute(SharkService.ATTR_STDT_START).getValue(),
			        patentsRootCategory.getDynaAttribute(SharkService.ATTR_STDT_END).getValue());
			
			if(StringUtils.isEmpty(sdtSection)){
			    sdtSection = "Nichts gefunden";
			}
			
            VKCDocument patentStdTDoc = new VKCDocument();

    		patentStdTDoc.setCategoryId( patentDoc.getCategoryId() );
    		patentStdTDoc.setPublished( new Date() );
    		patentStdTDoc.setLastAccessed( null );
    		patentStdTDoc.setLastAccessorId( null );
    		patentStdTDoc.setLastChanged( new Date() );
    		patentStdTDoc.setLastChangerId( user.getUserId() );
    		
            patentStdTDoc.setPublisherId( user.getUserId());
    		patentStdTDoc.setTitle( "Stand der Technik" );
    		
			patentStdTDoc.setName( patentDoc.getName().substring(0, 
			        patentDoc.getName().lastIndexOf("."))+ "_StdT.txt" );
			patentStdTDoc.setArchiveId( WebShare.archive.store( sdtSection.getBytes() ) );
			patentStdTDoc.setDocSize( new Long(sdtSection.getBytes().length ));
            patentStdTDoc.setZipped( new Integer( 0 ) );
			patentStdTDoc.setDocumentType( VKCDocumentTypeSerializer.getDocumentTypeByExtension( "TXT" ) );
			
			VKCDynaAttribute a = new VKCDynaAttribute();
			a.setDynaAttrId( new Integer( -1 ) );
			a.setName( SharkService.ATTR_STATE_OF_THE_ART_DOC );
			a.setValue( "true" );
			
			patentStdTDoc.addDynaAttribute(a);
			
			PatentUtils.convertAndSave(patentStdTDoc);
			
			return true;
			
		}catch (Exception ex){
		    VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
		    
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
		}
	}
	
	/**
	 * Diese Methode implementiert die einfache Klassifizierung. Bei der einfachen Klassifizierung
	 * werden die in der Patentanmeldung vorkommenden Begriffe genutzt, um den Grad der Übereinstimmung
	 * mit den Patent-Kategorien zu bestimmen (z.B. über Vergleich mit gespeicherten Begriffen zu
	 * den Kategorien). Ergebnis ist eine Liste der gefundenen Kategorien inkl. Ranking (%-Wert).
	 * 
	 * @param categoryId Patentkategorie
	 * @param patentApplDocId Schlüssel für das Dokument mit der Patentanmeldung
	 * @return ID eines neu ertellten Dokuments, dass die IDs der Kategoriern und das Ranking dazu (in %)
	 * enthält. Das Format ist noch zu klären, XML scheint gut geeignet zu sein. Speicherort ist die
	 * gleiche Kategorie wie die Patentanmeldung (patentApplDocID). 
	 * @throws AxisFault
	 */
	public String getPatentCategories(String patentApplDocId, String categoryId) 
	throws AxisFault{
		try {
		
			VKCLogManager.infoLog( this, "Methode getPatentCategories called...");
			VKCLogManager.infoLog( this, "Parameter categorieToProcess: " +  categoryId);
			VKCLogManager.infoLog( this, "Parameter patentApplDocKey: " +  patentApplDocId);
			
			if(!authenticate()){
			    return "";
			}
			
			if (StringUtils.isEmpty(patentApplDocId)){
				return "";
			}
			
			VKCDocument patentDoc = VKCDocumentSerializer.retrieveDocument(new Integer(patentApplDocId));
			
			return PatentUtils.classify( new ByKeywordClassifier(), patentDoc, categoryId);
			
		}
		catch (Exception ex){
		    VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
		    
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
		}
	}
		
	/**
	 * Diese Methode implementiert die Ähnlichkeitsklassifizierung. Bei der
	 * Ähnlichkeitsklassifizierung werden die in der Patentanmeldung vorkommenden Begriffe genutzt,
	 * um den Grad der Übereinstimmung mit allen anderen, schon erteilten Patentschriften zu bestimmen.
	 * Ergebnis ist eine Liste der gefundenen Kategorien inkl. Ranking (%-Wert).
	 * 
	 * @param categoryId Patentkategorie
	 * @param patentApplDocKey Schlüssel für das Dokument mit der Patentanmeldung
	 * @return ID eines neu ertellten Dokuments, dass die IDs der Kategoriern und das Ranking dazu (in %)
	 * enthält. Das Format ist noch zu klären, XML scheint gut geeignet zu sein. Speicherort ist die
	 * gleiche Kategorie wie die Patentanmeldung (patentApplDocID).  
	 * @throws AxisFault
	 */
	public String getSimilarPatentCategories( String patentApplDocId, String categoryId) throws AxisFault{
		try {
			
		    VKCLogManager.infoLog( this, "Methode getSimilarPatentCategories called...");
		    VKCLogManager.infoLog( this, "Parameter categorieToProcess: " +  categoryId);
		    VKCLogManager.infoLog( this, "Parameter patentApplDocKey: " +  patentApplDocId);
			
		    if(!authenticate())
		        return "";
		    
		    if (StringUtils.isEmpty(patentApplDocId)){
				return "";
			}
			
			VKCDocument patentDoc = VKCDocumentSerializer.retrieveDocument(new Integer(patentApplDocId));
		    
		    return PatentUtils.classify( new ByDocumentsClassifier(), patentDoc, categoryId);

		}
		catch (Exception ex){
		    VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
		    
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
		}
	}
	
	/**
	 * Diese Methode implementiert die StdT-Ähnlichkeitsklassifizierung.
	 * Bei der StdT Klassifizierung werden die im Stand der Technik (separates Dokument zur der Patentanmeldung)
	 * vorkommenden Begriffe genutzt, um den Grad der Übereinstimmung mit allen anderen StdT-Dokumenten der
	 * schon erteilten Patentschriften zu bestimmen. Ergebnis ist eine Liste der gefundenen Kategorien der 
	 * zugehörigen Patentschriften inkl. Ranking (%-Wert).
	 * 
	 * @param categoryId Patentkategorie
	 * @param patentApplDocId Key für das Dokument mit der Patentanmeldung (das dazugehörige StdT-Dokument muss
	 * über Namenskonventionen gefunden werden können)
	 * @return ID eines neu ertellten Dokuments, dass die IDs der Kategoriern und das Ranking dazu (in %)
	 * enthält. Das Format ist noch zu klären, XML scheint gut geeignet zu sein. Speicherort ist die
	 * gleiche Kategorie wie die Patentanmeldung (patentApplDocID).  
	 * @throws AxisFault
	 */
	public String getStdTSimilarPatentCategories(String patentApplDocId, String categoryId) 
		throws AxisFault{
		try {
		
			VKCLogManager.infoLog( this,"Methode getStdTSimilarPatentCategories called...");
			VKCLogManager.infoLog( this,"Parameter categorieToPRocess: " +  categoryId);
			VKCLogManager.infoLog( this,"Parameter patentApplDocKey: " +  patentApplDocId);
			
			if(!authenticate())
			    return "";

			if (StringUtils.isEmpty(patentApplDocId)){
				return "";
			}
			
			VKCDocument patentDoc = VKCDocumentSerializer.retrieveDocument(new Integer(patentApplDocId));
			
			return PatentUtils.classify( new ByStdTDocumentsClassifier(), patentDoc, categoryId);
			
		}catch (Exception ex){
		    VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
		    
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
		}
	}
	
	/**
	 * Diese Methode implementiert die Gesamtklassifizierung.
	 * Bei der Gesamtklassifizierung werden die Ergebnisse der Einzelklassifizierungen durch ein mathematisches
	 * Verfahren zusammengefasst. Denkbar sin z.B. einfache Addition der %-Werte für alle Kategorien und
	 * Normierung auf max. 100%. Kompliziertere Verfahren können die Teilergebisse oder einzelne Kategorien
	 * unterschiedlich gewichten. Ergebnis ist eine Liste der Kategorien als Gesamtklassifizierung 
	 * inkl. Ranking (%-Wert).
	 * 
	 * @param categoryId Patentkategorie
	 * @param patentApplDocId Schlüssel für das Dokument mit der Patentanmeldung. Alle weiteren benötigten Dokumente müssen
	 * über Namenskonventionen oder Referenzen gefunden werden
	 * @return ID der Kategorie, die insgesamt von der auto-klassifikation 
	 * für das gegebene Dokument vorgeschlagen wird. "", falls Klassifikation ohne Erfolg.  
	 * @throws AxisFault
	 */
	public String getSummarizedPatentCategories(String patentApplDocId, String categoryId) 
		throws AxisFault{
		try {

			VKCLogManager.infoLog( this, "Methode getSummarizedPatentCategories called...");
			VKCLogManager.infoLog( this, "Parameter categorieToProcess: " +  categoryId);
			VKCLogManager.infoLog( this, "Parameter patentApplDocKey: " +  patentApplDocId);
			
			if(!authenticate())
			    return "";
			 
			if (StringUtils.isEmpty(patentApplDocId)){
				return "";
			}
			
			VKCDocument patentDoc = VKCDocumentSerializer.retrieveDocument(new Integer(patentApplDocId));
			
			SummarizedClassifier classifier = new SummarizedClassifier();
			
			VKCCategory thisPatentCat = patentDoc.getCategory();
			for( Iterator it = thisPatentCat.getDocuments().iterator(); it.hasNext();){
	            VKCDocument doc = (VKCDocument)it.next();
	            if(doc.getName().toLowerCase().indexOf( "classification" ) > -1)
	                classifier.addClassifyResultDoc(new PatentClassifyResultDocument(doc));
	        }
			
			PatentUtils.classify( classifier, patentDoc, categoryId);
			
			return classifier.getFinalResultCategoryId(); 
			
		}catch (Exception ex){
		    VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
		    
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
		}
	}
	
	/**
	 * Diese Methode liefert einen Schlüssel für einen Einspruch zur Patentanmeldung (der ebenfalls als 
	 * der konkreten Patentanmeldung zugeordnetem Dokument vorliegt). Falls keine Einsprüche existieren,
	 * wird der Leerstring zurückgegeben. Auch hier darf ein Einspruch nur einmal zurückgegeben werden (Status).
	 * @param patentApplDocId Schlüssel für das Dokument mit der Patentanmeldung. Alle weiteren benötigten Dokumente müssen
	 * über Namenskonventionen oder Referenzen gefunden werden
	 * @return ID eines Dokuments, dass einen Einspruch enthält. Leerstring falls keine Einsprüche vorhanden.  
	 * @throws AxisFault
	 */
	public String getNextVeto(String patentApplDocId) throws AxisFault{		
	    
		VKCLogManager.infoLog( this, "Methode getNextVeto called...");		
		VKCLogManager.infoLog( this, "patentApplDocId: " + patentApplDocId);
		
		try {
		    
		    if( !authenticate())
		        return "";
		    
            VKCDocument patentDoc = VKCDocumentSerializer.retrieveDocument(new Integer(patentApplDocId));
            
            if(patentDoc == null){
                VKCLogManager.infoLog( this, "Patentdokument nicht gefunden!");
                return "";
            }
            
            VKCCategory patentCat = patentDoc.getCategory();
            
            VKCCategory vetoCat = PatentUtils.getCategoryWithDynaAttribute(patentCat, SharkService.ATTR_VETO_CAT, "true");
            if(vetoCat == null){
                VKCLogManager.infoLog( this, "Einspruchskategorie nicht gefunden/vorhanden!");
                return "";
            }
                
            
            VKCDocument oldestVetoDoc = PatentUtils.getOldestDocumentWithoutDynaAttributes(vetoCat);
			if(oldestVetoDoc == null ){
			    VKCLogManager.infoLog(this, "Es liegen keine Einsprüche vor.");
			    return "";
			}
			
			
			VKCDynaAttribute statusAttr = new VKCDynaAttribute();
			statusAttr.setDynaAttrId( new Integer( -1 ) );
			statusAttr.setName( SharkService.ATTR_STATUS );
			statusAttr.setValue( SharkService.WF_STARTED );
			
			oldestVetoDoc.getDynaAttributes().add(statusAttr);
			
			statusAttr = new VKCDynaAttribute();
			statusAttr.setDynaAttrId( new Integer( -1 ) );
			statusAttr.setName( SharkService.ATTR_PATENT_RELEVANT_LITERATURE );
			statusAttr.setValue( "false" );
			
			oldestVetoDoc.getDynaAttributes().add(statusAttr);
			
			VKCDocumentSerializer.saveDocument(oldestVetoDoc, true, false);
			
			VKCLogManager.infoLog(this, "Neuen Einspruch "+oldestVetoDoc.getName() + " gefunden und");
			VKCLogManager.infoLog(this, "mit dyn Attr. " + SharkService.ATTR_STATUS + "=" +SharkService.WF_STARTED + " versehen!");
			
			return oldestVetoDoc.getDocumentId().toString();
            
        } catch (Exception ex) {
            VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
		    
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
        }
		
	}

	/**
	 * Es wird false zurückgegeben, falls noch nicht alle Einsprüche bearbeitet worden sind. Es müssen also immer alle
	 * Stati geprüft werden.
	 * @param patentApplDocId Schlüssel für das Dokument mit der Patentanmeldung. Alle weiteren benötigten Dokumente müssen
	 * über Namenskonventionen oder Referenzen gefunden werden
	 * @return true, falls alle Einsprüche bearbeitet worden sind
	 * false sonst  
	 * @throws AxisFault
	 */
	public boolean isAllVetoProcessed(String patentApplDocId) throws AxisFault{
		
	    VKCLogManager.infoLog( this, "Methode isAllVetoProcessed called...");		
		VKCLogManager.infoLog( this, "patentApplDocId: " + patentApplDocId);
		
		try {
		    
		    if( !authenticate())
		        return false;
		    
            VKCDocument patentDoc = VKCDocumentSerializer.retrieveDocument(new Integer(patentApplDocId));
            
            if(patentDoc == null){
                VKCLogManager.infoLog( this, "Patentdokument nicht gefunden!");
                return false;
            }
            
            VKCCategory patentCat = patentDoc.getCategory();
            
            VKCCategory vetoCat = PatentUtils.getCategoryWithDynaAttribute(patentCat, SharkService.ATTR_VETO_CAT, "true");
            if(vetoCat == null){
                VKCLogManager.infoLog( this, "Einspruchskategorie nicht gefunden/vorhanden!");
                return false;
            }
            
            
            // false, falls mind. eines der Dokumente in der Kategorie "Einsprüche" 
            // den Wert WF_STARTED für das dyn. Attribut ATTR_STATUS hat
            if(PatentUtils.hasDocumentWithDynaAttribute(vetoCat, SharkService.ATTR_STATUS, SharkService.WF_STARTED)){
                return false;
            }
            
            return true;            
        } catch (Exception ex) {
            VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
		    
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
        }
	}
	
	/**
	 * Diese Methode fragt ab, ob es einen gerechtfertigten Einspruch für die Patentanmeldung gibt. Es wird true
	 * zurückgegeben, wenn alle Einsprüche bearbeitet worden sind und mind. einer davon berechtigt ist. Andernfalls
	 * wird false zurückgegeben.
	 * @param patentApplDocId Schlüssel für das Dokument mit der Patentanmeldung. Alle weiteren benötigten Dokumente müssen
	 * über Namenskonventionen oder Referenzen gefunden werden
	 * @return true, falls es einen gerechtfertigten Einspruch gibt
	 * false sonst
	 * @throws AxisFault
	 */
	public boolean existsAcceptedVeto(String patentApplDocId) throws AxisFault{
		
		VKCLogManager.infoLog( this, "Methode existsAcceptedVeto called...");		
		VKCLogManager.infoLog( this, "patentApplDocId: " + patentApplDocId);
		
		try {
		    
			if( !authenticate())
				return true;
		    
			VKCDocument patentDoc = VKCDocumentSerializer.retrieveDocument(new Integer(patentApplDocId));
            
			if(patentDoc == null){
				VKCLogManager.infoLog( this, "Patentdokument nicht gefunden!");
				return true;
			}
            
			VKCCategory patentCat = patentDoc.getCategory();
            
			VKCCategory vetoCat = PatentUtils.getCategoryWithDynaAttribute(patentCat, SharkService.ATTR_VETO_CAT, "true");
			if(vetoCat == null){
				VKCLogManager.infoLog( this, "Einspruchskategorie nicht gefunden/vorhanden!");
				return true;
			}
                     
			// true, falls kein Dokument in der Kategorie "Einsprüche" 
			// den Wert WF_STARTED für das dyn. Attribut ATTR_STATUS hat 
			// und mind. ein Dokument den Wert "CONFIRMED" hat
			if(PatentUtils.hasDocumentWithDynaAttribute(vetoCat, SharkService.ATTR_STATUS, SharkService.CONFIRMED)){
				return true;
			}
            
			// "false", sonst (das  Attribut hat für alle Dokumente den Wert "REJECTED")
			for (Iterator iter = vetoCat.getDocuments().iterator(); iter.hasNext();) {

				VKCDocument doc = (VKCDocument)iter.next();
				VKCDynaAttribute attr = doc.getDynaAttribute(SharkService.ATTR_STATUS);

				if (attr == null || !attr.getValue().equalsIgnoreCase(SharkService.REJECTED)) {
					return true;
				}

			}
            
			return false;
            
		} catch (Exception ex) {
			VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
		    
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
		}
	}

	/**
	 * Zum gegebenen Dokument für eine Patentanmeldung (noch in Bearbeitung oder schon erteilt) wird das zugehörige "Stand der Technik" Dokument zurückgegeben.
	 *
	 * @param patentApplDocId patentApplDocId Schlüssel für das Dokument mit der Patentanmeldung. Alle weiteren benötigten Dokumente müssen
	 * über Namenskonventionen oder Referenzen gefunden werden
	 * @return ID des zugehörigen Stand der Technik Dokuments
	 * @throws AxisFault
	 */
	public String getStdTDocument(String patentApplDocId) throws AxisFault
	{
	    VKCLogManager.infoLog( this, "Methode getStdTDocument called...");		
		VKCLogManager.infoLog( this, "patentApplDocId: " + patentApplDocId);
		
		try {
		    
			if( !authenticate())
				return "";
		    
			VKCDocument patentDoc = VKCDocumentSerializer.retrieveDocument(new Integer(patentApplDocId));
            
			if(patentDoc == null){
				VKCLogManager.infoLog( this, "Patentdokument nicht gefunden!");
				return "";
			}
            
			return PatentUtils.getStdTDocument(patentDoc).getDocumentId().toString();
            
		} catch (Exception ex) {
			VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
		    
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
		}
		
	}
		
	/**
	 * Es wird eine Ähnlichkeitssuche zwischen dem "Stand der Technik Dokument" des angegebenen Dokuments
	 * und allen Dokumenten in den VKC-Kategorien (inkl. Unterkategorien) gemacht, bei denen die Kategorie
	 * das dyn. Attribut "PAPatentRelevantLiterature" mit Wert "true" hat. Dokumente innerhalb der (Sub-)
	 * Kategorie, die das dyn. Attribut "PAPatentRelevantLiterature" mit Wert "false" haben, sind davon
	 * ausgeschlossen. Dadurch können Zwischenergebnisse, die während einer Patentanmeldung angefallen sind,
	 * davon ausgenommen werden.
	 * Die Kategorie "Erteilte Patente" sollte dieses Attribut per Default haben (falls es über den WebService
	 * angelegt wird). Ausserdem muss jedes darin enthaltene Dokument mit Ausnahme "des Stand der Technik"-
	 * Dokuments das Attribut mit Wert "false" bekommen (in Methode get). Das Kennzeichnen der "excluded"-Dokumente statt dem
	 * Kennzeichen der "included" Dokumente ist nötig, weil beliebige andere VKC-Kategorien für die Literatur-
	 * recherche nutzbar sein sollen, ohne das der Benutzer explizit dyn. Attribute hinzufügen muss.
	 * gesetzt). Das Ergebnis (Dokument, %-Wert) wird wie bei der Klassifizierung gespeichert.
	 * 
	 * @param investigationDocId ID für ein StdT-Dokument 
	 * Alle weiteren benötigten Dokumente müssen
	 * über Namenskonventionen oder Referenzen gefunden werden
	 * @param patentDocId ID des dazugehörigen PatentDokumentes
	 * @return true, false Aufruf erfolgreich
	 * @throws AxisFault
	 */
	public boolean doLiteratureResearch(String investigationDocId, String patentDocId) throws AxisFault{
		
	    VKCLogManager.infoLog( this, "Methode doLiteratureResearch called...");		
		VKCLogManager.infoLog( this, "investigationDocId: " + investigationDocId);
		VKCLogManager.infoLog( this, "patentDocId: " + patentDocId);
			
		try {
		    if( !authenticate())
				return false;
		    
			VKCDocument investidationDoc = VKCDocumentSerializer.retrieveDocument(new Integer(investigationDocId));
            
			if(investidationDoc == null){
				VKCLogManager.infoLog( this, "Dokument nicht gefunden!");
				return false;
			}
			
			VKCUser user = (VKCUser)MessageContext.getCurrentContext().getProperty( MessageContext.AUTHUSER );
			
			Collection results = new ArrayList();
			Collection searchInCatIds = VKCCategorySerializer.getIdsOfCategoriesWithDynaAttribute( SharkService.ATTR_PATENT_RELEVANT_LITERATURE, "true");
			for(Iterator it = searchInCatIds.iterator();it.hasNext();){
			    Integer searchInCatId = (Integer)it.next();
			    
				Collection thisResults = Search.whatsRelated(
		                investidationDoc, null, null, "*", user, searchInCatId);
				
				if(thisResults == null)
				    continue;
				
				results.addAll(thisResults);
			}
			
			// Ergebnis speichern
			StringBuffer resultContent = new StringBuffer();
			
			if(results.isEmpty()){
			    VKCLogManager.infoLog(this, "Literaturrecherche ergab kein Ergebnis!!");
			    resultContent.append("Literaturrecherche ergab kein Ergebnis!!");
			}else{
			    
			    int writtenResultsCount = 0;
			    
			    for (Iterator iter = results.iterator(); iter.hasNext();) {
	                SearchResultEntry res = (SearchResultEntry) iter.next();
	                
	                if(res.getPercent() < SharkService.MIN_LITRESEARCH_PERCENTAGE ){
	                    continue;
	                }
	                
	                String dynAttrsId = res.getDynaAttrsId();
	                if(dynAttrsId != null){
	                    Collection attrs = VKCDynaAttributeSerializer.retrieveDynaAttributes( new Integer(dynAttrsId));
	                    if(PatentUtils.hasDynaAttribute(attrs, SharkService.ATTR_PATENT_RELEVANT_LITERATURE, "false")){
	                        continue;
	                    }
	                }
	                
	                resultContent.append(
	                        VKCCategorySerializer.getCategoryAbsPath(new Integer(res.getCategoryId())).getAbsolutePathString());
	                resultContent.append(res.getName()).append("|");
	                resultContent.append(res.getDocumentId()).append("|");
	                resultContent.append(res.getPercent()).append("%\n");
	                
	                if(++writtenResultsCount >= SharkService.MAX_LITRESEARCH_RESULT_ENTRIES){
	                    break;
	                }
	            }   
			}
			
			VKCDocument resultDoc = new VKCDocument();

    		resultDoc.setCategoryId( CategoryUtils.retrieveCategoryIdForDocument(new Integer(patentDocId)) );
    		resultDoc.setPublished( new Date() );
    		resultDoc.setLastAccessed( null );
    		resultDoc.setLastAccessorId( null );
    		resultDoc.setLastChanged( new Date() );
    		resultDoc.setLastChangerId( user.getUserId() );
    		
            resultDoc.setPublisherId( user.getUserId());
    		resultDoc.setTitle( "Ergebnis der Literaturrecherche für Dokument mit ID=" + investidationDoc.getDocumentId() );
    		
			resultDoc.setName( investidationDoc.getName().substring(0, 
			        investidationDoc.getName().lastIndexOf("."))+ "_Literaturrecherche.txt" );
			resultDoc.setArchiveId( WebShare.archive.store( resultContent.toString().getBytes() ) );
			resultDoc.setDocSize( new Long(resultContent.toString().getBytes().length ));
            resultDoc.setZipped( new Integer( 0 ) );
			resultDoc.setDocumentType( VKCDocumentTypeSerializer.getDocumentTypeByExtension( "TXT" ) );
			
			VKCDynaAttribute a = new VKCDynaAttribute();
			a.setDynaAttrId( new Integer( -1 ) );
			a.setName( SharkService.ATTR_PATENT_RELEVANT_LITERATURE);
			a.setValue( "false" );
			
			resultDoc.addDynaAttribute(a);
			
			VKCDocumentSerializer.saveDocument(resultDoc, false, false);
			
			return true;	
		} catch (Exception ex) {
			VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
		    
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
		}	
	}
	
	/**
	 * Die Methode realisiert einen mehrstufigen Prozess.
	 * 1. Aus dem angegeben Dokument werden Keywords extrahiert (max. 10 Stück)
	 * 2. Diese Keywords werden für eine Suche mit Google benutzt. Dazu kann z.B. die WebService-Schnittstelle
	 * von Google benutzt werden (für nicht-kommerzielle Zwecke kostenfrei, aber Ergbnisliste beschränkt).
	 * Das Ergebnis dieses Schrittes (bzw. Methode) ist eine Liste mit URLs (als String- oder URL-Objekt).
	 * Diese Funktionalität wird noch nicht implementiert. Stattdessen wird zunächst eine Liste mit statischen
	 * URLs benutzt (z.B. allgemeingültige, grosse Themenbereiche umfassende Dokumente im Netz und spezialiserte,
	 * zu den vorhandenen bzw. zu testenden Patentanmeldungen passende).
	 * 3. Alle Dokumente der URL-Ergebnisliste werden (temporär) heruntergeladen und dann eine Ähnlichkeitssuche
	 * mit dem mitgegebenen Dokument gemacht und das Ergebnis wie (Dokumenten-URL, %-Wert) wird wie bei der
	 * Klassifizierung gespeichert.
	 * 
	 * * @param investigationDocId ID für ein StdT-Dokument 
	 * Alle weiteren benötigten Dokumente müssen
	 * über Namenskonventionen oder Referenzen gefunden werden
	 * @param patentDocId ID des dazugehörigen PatentDokumentes
	 * 
	 * @return true, false Aufruf erfolgreich
	 * @throws AxisFault
	 */
	public boolean doInternetResearch(String investigationDocId, String patentDocId) throws AxisFault{
	    
	    VKCLogManager.infoLog( this, "Methode doInternetResearch called...");		
		VKCLogManager.infoLog( this, "investigationDocId: " + investigationDocId);
		VKCLogManager.infoLog( this, "patentDocId: " + patentDocId);
			
		try {
		    if( !authenticate())
				return false;
		    
			VKCDocument investigationDoc = VKCDocumentSerializer.retrieveDocument(new Integer(investigationDocId));
            
			if(investigationDoc == null){
				VKCLogManager.infoLog( this, "Patentdokument nicht gefunden!");
				return false;
			}
			
			// TODO Internetrecherche
			java.util.ArrayList urlList = new java.util.ArrayList();
			urlList.add("http://mitglied.lycos.de/Autoelektrik/Ebc.htm");
			urlList.add("http://www.kfzelektronik.de/buecher.php");
			urlList.add("www.ub.uni-duisburg.de/ETD-db/theses/ available/duett-05232001-094631/unrestricted/anhang.pdf");
			
			
			return true;	
		} catch (Exception ex) {
			VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
		    
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
		}	
	}

	/**
	 * Es wird eine Ähnlichkeitssuche zwischen der angegebenen Patentanmeldung und allen anderen Patentanmeldungen
	 * (erteilte und eingereichte älteren! Datums mit gestartetem Workflow) durchgeführt. Das sind also alle
	 * Dokumente in den Verzeichnissen "Erteilte Patente" und "Abgelehnte Patente" (bzw. den entsprechenden dyn. Attributen)
	 * mit älterem Datum.
	 * Für alle gefundenen Dokumente (Begrenzung für Anzahl (max. 5) und Übereinstimmung (> 50%)) wird das
	 * "Stand der Technik" Dokument ermittelt und als Referenz oder eigenes Dokument, das die ID enthält
	 * abgespeichert. Den n Ergebnisdokumenten bzw. Referenzen (nicht dem Original StdT-Dokument!) wird das
	 * dyn. Attribut "PAType" mit Wert "PASimilarSotADoc" zugewiesen.
	 *
	 * @param patentApplDocId patentApplDocId Schlüssel für das Dokument mit der Patentanmeldung. Alle weiteren benötigten Dokumente müssen
	 * über Namenskonventionen oder Referenzen gefunden werden
	 * @return true, false Aufruf erfolgreich
	 * @throws AxisFault
	 */
	public boolean computeSimilarStdTDocs(String patentApplDocId) throws AxisFault{	
		
		VKCLogManager.infoLog( this, "Methode computeSimilarStdTDocs called...");		
		VKCLogManager.infoLog( this, "patentApplDocId: " + patentApplDocId);
		
		try {
		    
			if( !authenticate())
				return false;
		    
			VKCDocument patentDoc = VKCDocumentSerializer.retrieveDocument(new Integer(patentApplDocId));
            
			if(patentDoc == null){
				VKCLogManager.infoLog( this, "Patentdokument nicht gefunden!");
				return false;
			}
            
			VKCCategory patentCat = patentDoc.getCategory();
			VKCCategory incomingCat = VKCCategorySerializer.retrieveCategory(patentCat.getParentCategoryId());
			VKCCategory patentsRootCategory = VKCCategorySerializer.retrieveCategory(incomingCat.getParentCategoryId());
			
			VKCUser user = (VKCUser)MessageContext.getCurrentContext().getProperty( MessageContext.AUTHUSER );
			
			Collection results = Search.whatsRelated(
	                patentDoc, null, null, "*", user, PatentUtils.getIssuedCategory(patentsRootCategory).getCategoryId());
			
			if(results == null)
			    results = new ArrayList();
			
			Collection resultEntriesInRejectedCat = Search.whatsRelated(
	                patentDoc, null, null, "*", user, PatentUtils.getRejectedCategory(patentsRootCategory).getCategoryId());
			
			if(resultEntriesInRejectedCat != null)
			    results.addAll(resultEntriesInRejectedCat);
			
			// Ergebnisse absteigend nach Trefferquote sortieren
			Collections.sort((List)results, new Comparator() {
				public int compare( Object o1, Object o2 ) {
				    SearchResultEntry d1 = (SearchResultEntry)o1;
				    SearchResultEntry d2 = (SearchResultEntry)o2;
				    
				    if( d1.getScore() > d2.getScore())
				        return -1;
				    
				    if( d1.getScore() < d2.getScore())
				        return 1;
				    
					return 0;
				}
			});
			
			int resultsOver50Percent = 0;
			for (Iterator iter = results.iterator(); iter.hasNext();) {
                SearchResultEntry res = (SearchResultEntry) iter.next();
                if(res.getPercent() < 50){
                    //VKCLogManager.infoLog(this, "Nur " + res.getPercent() + " für Dokument mit Id = " + res.getDocumentId() );
                    continue;
                }
                
                VKCDocument similarDoc = res.getDocument();
                VKCDocument stdtDoc = null;
                
                if(similarDoc.hasDynaAttribute(SharkService.ATTR_STATE_OF_THE_ART_DOC))
                    stdtDoc = similarDoc;
                else
                    stdtDoc = PatentUtils.getStdTDocument( similarDoc );
                
                if(stdtDoc == null){
                    VKCLogManager.infoLog(this, "Kein StdT-Dokument für " +similarDoc.getName() + " gefunden!" );
                    continue;
                }
                
                // Ergebnisdokument erstellen ( enthält ID auf das gefundene StdT-Dokument )
                resultsOver50Percent++;
                String resultContent = String.valueOf(stdtDoc.getDocumentId());
                
                VKCDocument resultDoc = new VKCDocument();

        		resultDoc.setCategoryId( patentDoc.getCategoryId() );
        		resultDoc.setPublished( new Date() );
        		resultDoc.setLastAccessed( null );
        		resultDoc.setLastAccessorId( null );
        		resultDoc.setLastChanged( new Date() );
        		resultDoc.setLastChangerId( user.getUserId() );
        		
                resultDoc.setPublisherId( user.getUserId());
        		resultDoc.setTitle( "Ähnliches StdT-Dokument " + resultsOver50Percent+ " mit ID = " +resultContent );
        		
    			resultDoc.setName( patentDoc.getName().substring(0, 
    			        patentDoc.getName().lastIndexOf("."))+ "_SimilarStdTDoc"+ resultsOver50Percent+ ".txt" );
    			resultDoc.setArchiveId( WebShare.archive.store( resultContent.getBytes() ) );
    			resultDoc.setDocSize( new Long(resultContent.getBytes().length ));
                resultDoc.setZipped( new Integer( 0 ) );
    			resultDoc.setDocumentType( VKCDocumentTypeSerializer.getDocumentTypeByExtension( "TXT" ) );
    			
    			VKCDynaAttribute a = new VKCDynaAttribute();
    			a.setDynaAttrId( new Integer( -1 ) );
    			a.setName( SharkService.ATTR_TYPE );
    			a.setValue( SharkService.SIMILAR_STATE_OF_THE_ART_DOC );
    			
    			resultDoc.addDynaAttribute(a);
    			
    			a = new VKCDynaAttribute();
    			a.setDynaAttrId( new Integer( -1 ) );
    			a.setName( SharkService.ATTR_PATENT_RELEVANT_LITERATURE);
    			a.setValue( "false" );
    			
    			resultDoc.addDynaAttribute(a);
    			
    			
    			VKCDocumentSerializer.saveDocument(resultDoc, false, false);
    			
    			if(resultsOver50Percent == SharkService.MAX_SIMILAR_SOT_DOCS_COUNT)
    			    break;
            }
			
			return true;
            
		} catch (Exception ex) {
			VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
		    
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
		}	
		
	}

	/**
	 * Diese Methode liefert einen Schlüssel für ein StdT Dokument, das über die Ähnlichkeitssuche (s. Methode 
	 * computeSimilarStdTDocs(...)) gefunden wurde, also Dokumente mit dyn. Attribut "PAType" und Wert
	 * "PASimilarSotADoc", bei denen das dyn. Attribut "PAStatus" noch nicht gesetzt wurde. Bevor die
	 * ID zurückgegeben wird, wird dem dyn. Attribut "PAStatus" der Wert "PAWFStarted" zugewiesen.
	 * Falls kein solches Dokument mehr existiert, wird der Leerstring zurückgegeben (auch hier darf ein Dokument
	 * nur einmal zurückgegeben werden (Status)).
	 * @param patentApplDocId Schlüssel für das Dokument mit der Patentanmeldung. Alle weiteren benötigten Dokumente müssen
	 * über Namenskonventionen oder Referenzen gefunden werden
	 * @return ID eines StdT Dokuments, dass über die Ähnlichkeitssuche gefunden wurde. Leerstring falls keine
	 * Einsprüche vorhanden.  
	 * @throws AxisFault
	 */
	public String getNextSimilarStdTDoc(String patentApplDocId) throws AxisFault{		
	    
		VKCLogManager.infoLog( this, "Methode getNextSimilarStdTDoc called...");		
		VKCLogManager.infoLog( this, "patentApplDocId: " + patentApplDocId);
		
		try {
		    
			if( !authenticate())
				return "";
		    
			VKCDocument patentDoc = VKCDocumentSerializer.retrieveDocument(new Integer(patentApplDocId));
            
			if(patentDoc == null){
				VKCLogManager.infoLog( this, "Patentdokument nicht gefunden!");
				return "";
			}
            
			VKCCategory patentCat = patentDoc.getCategory();
			for( Iterator it = patentCat.getDocuments().iterator(); it.hasNext();){
	            VKCDocument doc = (VKCDocument)it.next();
	            if(!doc.hasDynaAttribute( SharkService.ATTR_TYPE, SharkService.SIMILAR_STATE_OF_THE_ART_DOC)){
	                continue;
	            }
	            
	            VKCDynaAttribute statusAttr = doc.getDynaAttribute(SharkService.ATTR_STATUS);
	            
	            if( statusAttr != null ){
	                continue;
	            }
	            
                statusAttr = new VKCDynaAttribute();
                statusAttr.setDynaAttrId( new Integer(-1));
                statusAttr.setName(SharkService.ATTR_STATUS);
                statusAttr.setValue(SharkService.WF_STARTED);
                doc.addDynaAttribute(statusAttr);
                
                VKCDocumentSerializer.saveDocument(doc, true, false);
                
                // AF: Es muss die ID des Ergebnisdokuments zurückgegeben werden und
                // nicht die des Original StdT-Dokuments.
                return  doc.getDocumentId().toString();
                
                /* AF: s.o.
                // ID des StdT auslesen und zurückgeben
                InputStream is = WebShare.archive.getInputStream(doc.getArchiveId());
                String stdtDocId = FileUtils.toStringBuffer(is).toString();
                is.close();
                
                return stdtDocId;
                */ 
	        }
			
			return "";
            
		} catch (Exception ex) {
			VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
		    
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
		}	
	}
	
	/**
	 * Methode, die zyklisch aufgerufen wird und true zurückgibt, sobald alle Dokumente vom
	 * mit dyn. Attribut "PAType" = "PASimilarSotADoc" für das dyn. Attribut "PAStatus" den
	 * Wert "PAWFDone" haben (also die Literatur- / Internetrecherche abgeschlossen ist).
	 * 
	 * @param patentApplDocId Schlüssel für das Dokument mit der Patentanmeldung. Alle weiteren benötigten Dokumente müssen
	 * über Namenskonventionen oder Referenzen gefunden werden
	 * @return   
	 * @throws AxisFault
	 */
	public boolean isStdTResearchDone(String patentApplDocId) throws AxisFault{
	    
	    VKCLogManager.infoLog( this, "Methode isStdTResearchDone called...");		
		VKCLogManager.infoLog( this, "patentApplDocId: " + patentApplDocId);
		
		try {
		    
		    if( !authenticate())
		        return false;
		    
		    VKCDocument patentDoc = VKCDocumentSerializer.retrieveDocument(new Integer(patentApplDocId));
            
			if(patentDoc == null){
				VKCLogManager.infoLog( this, "Patentdokument nicht gefunden!");
				return false;
			}
            
			VKCCategory patentCat = patentDoc.getCategory();
			for( Iterator it = patentCat.getDocuments().iterator(); it.hasNext();){
			    VKCDocument doc = (VKCDocument)it.next();
			    VKCDynaAttribute paType = doc.getDynaAttribute(SharkService.ATTR_TYPE);
			    if(paType == null || !paType.getValue().equals(SharkService.SIMILAR_STATE_OF_THE_ART_DOC))
			        continue;
			    
			    // Dokument hat dynAttr "PAType" = "PASimilarSotADoc"
			    VKCDynaAttribute paStatus = doc.getDynaAttribute(SharkService.ATTR_STATUS);
			    if(paStatus == null || !paStatus.getValue().equals(SharkService.WF_DONE)){
			        // Dokument hat kein "Status" dyn. Attr oder
			        // es ist nicht auf "PAWFDone" gesetzt
			        return false;
			    }
			        
			}
			
			return true;
        } catch (Exception ex) {
            VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
		    
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
        }
	}
	
	/**
	 * Dieser Web-Service wird aufgerufen, nachdem der Sachbearbeiter das Patent erteilt (patentAccepted == true)
	 * oder abgewiesen hat.
	 * Abhängig davon wird die gesamte Kategorie mit der Patentanmeldung in die Kategorie "Erteilte Patente" oder
	 * "Abgewiesene Patente" verschoben (Erkennung über entsprechendes dyn. Attribut). Ausserdem erhält das dyn. Attribut 
	 * "PAStatus" den Wert "PAConfirmed" bzw. "PARejected".
	 */
	public boolean setPatentAccepted(String patentApplDocId, boolean patentAccepted) throws AxisFault{
	    
	    VKCLogManager.infoLog( this, "Methode setPatentAccepted called...");		
		VKCLogManager.infoLog( this, "patentApplDocId: " + patentApplDocId);
		VKCLogManager.infoLog( this, "patentAccepted: " + patentAccepted);
		
		try {
		    
		    if( !authenticate())
		        return false;
		    
		    VKCDocument patentDoc = VKCDocumentSerializer.retrieveDocument(new Integer(patentApplDocId));
            
			if(patentDoc == null){
				VKCLogManager.infoLog( this, "Patentdokument nicht gefunden!");
				return false;
			}
            
			
			VKCCategory patentCat = patentDoc.getCategory();
			VKCCategory incomingCat = VKCCategorySerializer.retrieveCategory( patentCat.getParentCategoryId()); 
    		
			VKCCategory target = null;
			if(patentAccepted){
			    // in die klassifizierte Kategorie in 
			    // "Erteilte Patente" verschieben
			    target = VKCCategorySerializer.retrieveCategory(
			            new Integer(patentDoc.getDynaAttribute(
			                    SharkService.ATTR_CHOSEN_CATEGORY).getValue()));
			    patentDoc.getDynaAttribute( SharkService.ATTR_STATUS )
			    			.setValue( SharkService.CONFIRMED);
			}else{
			    // nach "Abgewiesene Patente" verschieben
			    VKCCategory patentsRootCategory = VKCCategorySerializer.retrieveCategory(incomingCat.getParentCategoryId());
			    target = PatentUtils.getRejectedCategory(patentsRootCategory);
			    patentDoc.getDynaAttribute( SharkService.ATTR_STATUS )
			    			.setValue( SharkService.REJECTED);
			}
			
			VKCDocumentSerializer.saveDocument(patentDoc, true, false);
			
			VKCUser user = (VKCUser)MessageContext.getCurrentContext().getProperty( MessageContext.AUTHUSER );

			// Gegebenfalls muss der Kategoriename umbenannt werden
			String catName = patentCat.getName();
    		int count = 1;
    		while(CategoryUtils.isContained(catName, target.getSubCategories())){
    		    catName = patentCat.getName() + " (" +(count++)+")";
    		}
    		
    		if(!catName.equals( patentCat.getName())){
    		    patentCat.setName(catName);
    		    VKCCategorySerializer.saveCategory(patentCat);
    		}
    		
			// Verschieben
			Set catIds = new HashSet();
			catIds.add(patentCat.getCategoryId());
			WebShare.database.move(incomingCat, target, catIds, null, user);
			
			return true;
        } catch (Exception ex) {
            VKCLogManager.errorLog(this, "", ex.getMessage(), ex);
		    
			AxisFault fault = AxisFault.makeFault( ex );
			fault.addFaultDetailString( "Internal server error" );
			throw fault;
        }
	}
}
