/* $Header: /usr/local/cvsroot/SULIS/src/ext/tools/ContentToolbox.java,v 1.7 2007/01/22 10:41:43 mkr Exp $ */

/**
*** Purpose: 	Toolbox to modify content server side.
*** Autor:   	ghimmelsbach@ptc.com         
*** History: 	20050120 ghimmelsbach@ptc.com, initial release
***          	20051207 mkraegeloh@sulis.de   make writeStream write in chunks.
***          	20061006 mkraegeloh@sulis.de   implements RemoteAccess to use updatePrimary from outside vm, updatePrimary is now static
***          	20061006 mkraegeloh@sulis.de   add log4j
***/

package ext.tools;

import org.apache.log4j.*;
import java.io.*;
import java.util.*;
import java.net.*;
import java.util.zip.*;

import wt.doc.*;
import wt.epm.*;
import wt.folder.*;
import wt.content.*;
import wt.clients.util.http.HTTPUploadDownload;
import wt.util.*;
import wt.vc.wip.*;
import wt.fc.*;
import wt.pom.Transaction;
import wt.method.*;

/**
 */
public class ContentToolbox implements RemoteAccess{
   public static final String CVSVersion = "$Header: /usr/local/cvsroot/SULIS/src/ext/tools/ContentToolbox.java,v 1.7 2007/01/22 10:41:43 mkr Exp $";
  private static boolean debug = false;
  static Logger logger=Logger.getLogger("ext.tools.ContentToolbox");
  public ContentToolbox(){
    
  }

  /** The temp directory used during operations. */
  private File tempDir;
  
  /** Creates a temporary directoy under Windchill/temp for use with this ContentToolbox object. 
  @throws java.io.IOException if there is a problem reading WTProperties
  */
  public void createTempDir ( ) throws java.io.IOException {
      Properties wtprops = WTProperties.getLocalProperties();
      //String wthome = wtprops.getProperty("wt.temp");
      String tempDirPath = wtprops.getProperty("wt.temp"); //wthome + ((wthome.endsWith("\\")) ? "temp" : "\\temp");
      // if (debug) System.out.println ("Tempdir: " + tempDirPath);
      // create a tempfile first to ensure unique name. Then delete file and create directory
      tempDir = File.createTempFile ("contenttoolbox", "", new File(tempDirPath));
      tempDir.delete();
      tempDir.mkdir();
  }
  
  /** Returns the currently assigned temporary directory or null if it hasn't been assigned. 
  @return the current temporary directory.
  */
  public File getTempDir ( ) {
    return tempDir;
  }
  
  /** Assigns the temporary directory for this toolbox to the specified directory.
  @param theDirectory - existing directory 
   * @throws FileNotFoundException
  @throws java.io.FileNotFoundException if the directory does not exist */
  public void setTempDir ( File theDirectory ) throws FileNotFoundException {
      if (!theDirectory.isDirectory() ) throw new FileNotFoundException ("File passed was not a directory");
      tempDir = theDirectory;
  }
  

	/** Deletes directory and all subdirs and contained files.
	* @param d File
	@return boolean successful
		*/
	public boolean deleteDirectory (File d) {
		boolean f = true;
		if( ! d.isDirectory()) return false;
		try{
			File[] content = d.listFiles();
			for (int i=0; i<content.length;i++){
				if (content[i].isDirectory())
					if (!deleteDirectory(content[i]))f=false;
				else
					if (!content[i].delete()) f=false;
			}
			if(!d.delete())f=false;
		}catch(Exception e){
			e.printStackTrace();
			f=false;
		}
		return f;
	}			

	/** Deletes the temporary directory associated with this toolbox. If successful returns true.
	return boolean successful
		*/
	public boolean deleteTempDirectory ( ) {
		if (tempDir.isDirectory()){
			boolean successful = true;
			if(!deleteDirectory(tempDir)) successful=false;
			if (successful) 
			{
				tempDir = null;
				return successful;
			}
		}
		return false;
	}

  
  /**
   * Method writeStream.
   * @param ci ApplicationData
   * @param dir File
   * @param filename String
   * @return File
   * @throws WTException
   * @throws IOException
   */
  private File writeStream (ApplicationData ci , File dir, String filename) throws WTException, IOException{
      BufferedInputStream bis = new BufferedInputStream (ContentServerHelper.service.findContentStream (ci));
	byte[] buf=new byte[1024];
      File outFile = new File (dir , filename);
      if (outFile.exists() && outFile.isFile()) outFile.delete();  // open file for overwrite not append
      FileOutputStream fos = null;
      fos = new FileOutputStream (outFile);
      int nread;
	while ((nread=bis.read(buf,0,1024))!= -1)
        fos.write(buf,0,nread);
      fos.close();
      bis.close();
      return outFile;
   }
      
  /** Downloads and returns the primary content of the WTDocument. Will return null if content does not exist. Will overwrite existing file. Uses server side methods.
  @param theDoc - WTDocument object to download primary content from 
  @return the File downloaded
  @throws java.io.IOException
   * @throws WTException
  @throws java.beans.PropertyVetoException
 @throws wt.util.WTException
   */
  public File downloadPrimary (FormatContentHolder theDoc ) throws java.io.IOException , WTException , java.beans.PropertyVetoException {
    File outFile = null;
    if (tempDir == null) createTempDir ();
    theDoc = (FormatContentHolder) ContentHelper.service.getContents(theDoc);
    ContentItem theContent = ContentHelper.getPrimary (theDoc);
    if (theContent != null && theContent instanceof ApplicationData)
      {
      String fn=((ApplicationData)theContent).getFileName();
	if(theDoc instanceof EPMDocument){
	   fn=fn.replaceFirst("\\{\\$CAD_NAME\\}",((EPMDocument)theDoc).getCADName());
	}
      outFile = writeStream ((ApplicationData)theContent , tempDir , fn );
      }
    return outFile;
  }
  
  /** Downloads and returns the primary content of the WTDocument to the specified location. Uses server side methods.
  Will return null if content does not exist.
  @param theDoc - WTDocument object to download primary content from 
  @param location - an existing directory to download to 
  @return the File downloaded
  @throws java.io.IOException
   * @throws WTException
  @throws java.beans.PropertyVetoException
 @throws wt.util.WTException
   */
  public File downloadPrimary (WTDocument theDoc , File location ) throws java.io.IOException , WTException , java.beans.PropertyVetoException {
    File theFile = null;
    if (tempDir == null) createTempDir ();
    theDoc = (WTDocument) ContentHelper.service.getContents(theDoc);
    ContentItem theContent = ContentHelper.getPrimary (theDoc);
    if (theContent != null && theContent instanceof ApplicationData)
      {
      theFile = writeStream ((ApplicationData)theContent , location , ((ApplicationData)theContent).getFileName() );
      }
    return theFile;
  }
  
  /** Deletes the primary content of a WTDocument. Uses server side methods. Invoke on working copy and checkin to create a new iteration (recommended).
  @param theDoc - WTDocument object to download primary content from 
   * @throws WTException
  @throws java.beans.PropertyVetoException
 @throws wt.util.WTException
   */
  public void deletePrimary (WTDocument theDoc ) throws WTException , java.beans.PropertyVetoException {
    
    theDoc = (WTDocument) ContentHelper.service.getContents(theDoc);
    ContentItem theContent = ContentHelper.getPrimary (theDoc);
    if (theContent != null) 
      {
      ContentServerHelper.service.deleteContent ( theDoc , theContent);
      ContentServerHelper.service.updateHolderFormat (theDoc);
      theDoc = (WTDocument) PersistenceHelper.manager.refresh ((Persistable) theDoc , true, true);
      }
    
  }
  
  /** Creates or Updates the primary content of a WTDocument using the specified file. Uses server side methods. Invoke on working copy and checkin to create a new iteration (recommended).
  @param theDoc - WTDocument object to download primary content from
  @param theFile - the new primary content
  @param filename - the filename to use when creating the content
   * @throws WTException
  @throws java.beans.PropertyVetoException
   * @throws ObjectNoLongerExistsException
   * @throws FileNotFoundException
   * @throws IOException
  @throws wt.fc.ObjectNoLongerExistsException
  @throws java.io.FileNotFoundException
  @throws wt.util.WTException
  @throws java.io.IOException
  */
  public static void updatePrimary (FormatContentHolder theDoc , File theFile , String filename ) throws WTException , java.beans.PropertyVetoException, ObjectNoLongerExistsException, FileNotFoundException, IOException{
     updatePrimary(theDoc, theFile, filename, false); // no c/o c/i
  }
  public static void updatePrimary (FormatContentHolder theDoc , File theFile , String filename , boolean iterate) throws WTException , java.beans.PropertyVetoException, ObjectNoLongerExistsException, FileNotFoundException, IOException{
    logger.debug("updatePrimary: holder="+theDoc+" filename="+filename+" iterate="+iterate);
    Transaction transaction = new Transaction();
    transaction.start();
    try{
	 boolean needCheckin=false;
	 if(iterate && ! WorkInProgressHelper.isCheckedOut((Workable)theDoc)){
	    Folder folder = WorkInProgressHelper.service.getCheckoutFolder();
	    WorkInProgressHelper.service.checkout((Workable)theDoc,folder,"auto checkout for ContentToolbox ...");
	    logger.debug(" checked it out ...");
	    needCheckin=true;
	    theDoc=(FormatContentHolder)WorkInProgressHelper.service.workingCopyOf((Workable)theDoc);
	 }
	 ApplicationData theContent = ApplicationData.newApplicationData(theDoc);
	 theContent.setFileName (filename);
	 theContent.setFileSize (theFile.length());
	 FileInputStream fis = new FileInputStream (theFile);
	 ContentServerHelper.service.updatePrimary (theDoc , theContent, fis);
	 fis.close();
	 ContentServerHelper.service.updateHolderFormat (theDoc);
	 theDoc = (FormatContentHolder) PersistenceHelper.manager.refresh ((Persistable) theDoc , true, true);
	 if(needCheckin)
	    WorkInProgressHelper.service.checkin((Workable)theDoc,"auto checkin after updatePrimary ...");
       transaction.commit();
    }
    catch(Exception err){
       transaction.rollback();
	 logger.debug("rollback because "+err);
	 if(err instanceof WTException)
	    throw (WTException)err;
	 else
	    throw new WTException(err);
    }
  }
  
  /** Returns all secondary contents for an object as an Enumeration. Remember ApplicationData objects are a
  type of ContentItem that represent a physical file as opposed to a URLData object (also a ContentItem) which
  represents a URL. Use the statement if (object instanceof wt.content.ApplicationData) to test objects for their type.
  @param theHolder - any ContentHolder
  @return an Enumeration of the secondary conetents
   * @throws WTException
  @throws java.beans.PropertyVetoException
 @throws wt.util.WTException
   */
  public Enumeration getContents ( ContentHolder theHolder ) throws WTException , java.beans.PropertyVetoException {
    theHolder = (ContentHolder) ContentHelper.service.getContents(theHolder);
    Vector contentList = ContentHelper.getContentList (theHolder);
    Enumeration e = contentList.elements();
    return e;
  }
  
  /** Download the specified ContentItem from the ContentHolder to the temp directory and return it as a File.
  @param theItem - an ApplicationData held by theHolder (remember ApplicationData is a type of ContentItem that represents a file)
  @return the File downloaded
  @throws WTException if the item can't by found
  @throws java.io.IOException
  */
  public File downloadContent ( ApplicationData theItem ) throws WTException, java.io.IOException {
    File theFile = null;
    if (tempDir == null) createTempDir ();
    theFile = writeStream (theItem , tempDir , theItem.getFileName() );
    return theFile;
  }
  //mkr
  public File downloadContent ( ApplicationData theItem, String fn ) throws WTException, java.io.IOException {
    File theFile = null;
    if (tempDir == null) createTempDir ();
    theFile = writeStream (theItem , tempDir , fn );
    return theFile;
  }
  
  /** Download the specified ContentItem from the ContentHolder to the temp directory and return it as a File.
  @param theItem - an ApplicationData held by theHolder (remember ApplicationData is a type of ContentItem that represents a file)
  @param location - an existing directory to download to
  @return the File downloaded
  @throws WTException if the item can't by found
  @throws java.io.FileNotFoundException if the file doesn't exist
  @throws java.io.IOException
  */
  public File downloadContent ( ApplicationData theItem , File location ) throws WTException, java.io.FileNotFoundException, java.io.IOException {
    File theFile = null;
    theFile = writeStream (theItem , location , theItem.getFileName() );
    return theFile;
  }
  
  /** Deletes the specified ContentItem from the ContentHolder. Uses server side methods. Invoke on working copy and checkin to create a new iteration (recommended).
  @param theHolder - any ContentHolder
  @param theItem - a ContentItem held by theHolder
  @throws WTException if the item can't by found
  @throws java.beans.PropertyVetoException
  */
  public void deleteContent ( ContentHolder theHolder , ContentItem theItem ) throws WTException, java.beans.PropertyVetoException {
    theHolder = (ContentHolder) ContentHelper.service.getContents(theHolder);
    ContentServerHelper.service.deleteContent ( theHolder , theItem );
    theHolder = (ContentHolder) PersistenceHelper.manager.refresh ((Persistable) theHolder , true, true);
  }
  
  /** Deletes all ContentItems (including Primary) from the ContentHolder. Uses server side methods. Invoke on working copy and checkin to create a new iteration (recommended).
  @param theHolder - any ContentHolder
  @return the refreshed ContentHolder
  @throws WTException if the item can't by found
  @throws java.beans.PropertyVetoException
  */
  public ContentHolder deleteAllContent ( ContentHolder theHolder ) throws WTException, java.beans.PropertyVetoException {
    theHolder = (ContentHolder) ContentHelper.service.getContents(theHolder);
    Vector contentList = ContentHelper.getContentListAll (theHolder);
    Enumeration e = contentList.elements();
    while (e.hasMoreElements())
      {
      deleteContent ( theHolder , (ContentItem)(e.nextElement()));
      }
    theHolder = (ContentHolder) PersistenceHelper.manager.refresh ((Persistable) theHolder , true, true);
    return theHolder;
  }
  
  /** Replaces an existing secondary ContentItem with the specified File if it has the same name or creates a new ContentItem
  if it does not. Uses server side methods. Invoke on working copy and checkin to create a new iteration (recommended).
  @param theHolder - any ContentHolder
  @param theFile - theFile on the server to upload
  @param filename - the filename to use when adding the content
  @return the refreshed ContentHolder
  @throws WTException
  @throws java.beans.PropertyVetoException
  @throws java.io.FileNotFoundException
  @throws java.io.IOException
  */
  public ContentHolder updateContent ( ContentHolder theHolder , File theFile, String filename ) throws WTException, java.beans.PropertyVetoException, java.io.FileNotFoundException, java.io.IOException  {
    Enumeration e = getContents (theHolder);
    while (e.hasMoreElements())
      {
      ContentItem thisItem = (ContentItem)e.nextElement();
            if (debug) System.out.println("thisItem :"+thisItem);
      // if content item exists, delete it.
      if (thisItem instanceof ApplicationData && ((ApplicationData)thisItem).getFileName().equals(filename))
        {
        deleteContent (theHolder , thisItem);
        break;
        }
      }
    ApplicationData theContent = ApplicationData.newApplicationData(theHolder);
    theContent.setFileName (filename);
            if (debug) System.out.println("filename :"+filename);
    theContent.setFileSize (theFile.length());
            if (debug) System.out.println("theFile.length() :"+theFile.length());
        /*-------------------------------------/
        Transaction transaction = new Transaction();
        transaction.start();
        /--------------------------------------*/

    FileInputStream fis = new FileInputStream (theFile);
    ContentServerHelper.service.updateContent (theHolder , theContent, fis);
    fis.close();
    theHolder = (ContentHolder) PersistenceHelper.manager.refresh ((Persistable) theHolder);

        /*---------------------------------------/
        transaction.commit();
        transaction = null;
        if(transaction != null)
            transaction.rollback();
        /-----------------------------------*/
    
    return theHolder;
  }

  
        /**
         * Method getIsFromContentHolder.
         * @param ch ContentHolder
         * @param crt ContentRoleType
         * @return InputStream
         * @throws WTException
         */
        public static InputStream getIsFromContentHolder(ContentHolder ch, ContentRoleType crt) throws WTException {//ContentRoleType.RENDITION
            InputStream is = null;
            String entryName = null;
            try{
                ch = ContentHelper.service.getContents( ch );
            if (debug) System.out.println("ch :"+ch);
                Vector clist = ContentHelper.getContentListAll( ch);
                Enumeration cenum = clist.elements();
                while( cenum.hasMoreElements() ){
                    ContentItem ci = (ContentItem)cenum.nextElement();
            if (debug) System.out.println("ci :"+ci);
                    if( ci.getRole().equals( crt) ){
                        if( ci instanceof ApplicationData){
                            is = ContentServerHelper.service.findContentStream( (ApplicationData)ci);
            if (debug) System.out.println("is :"+is);
                        }else{ // URLData, should never be reached!!
                        // URL must be authenticated!
                            is = new URL( ( (URLData)ci).getUrlLocation() ).openStream();
                        }
                    }
                }
            }catch(Exception e){
                throw new WTException( e);
            }
            return is;
        }

        /**
         * Method getIsFromContentHolder.
         * @param ch ContentHolder
         * @param contentName String
         * @return InputStream
         * @throws WTException
         */
        public static InputStream getIsFromContentHolder(ContentHolder ch, String contentName) throws WTException {//ContentRoleType.RENDITION
            InputStream is = null;
            String entryName = null;
            try{
                ch = ContentHelper.service.getContents( ch );
            if (debug) System.out.println("ch :"+ch);
                Vector clist = ContentHelper.getContentListAll( ch);
                Enumeration cenum = clist.elements();
                while( cenum.hasMoreElements() ){
                    ContentItem ci = (ContentItem)cenum.nextElement();
            if (debug) System.out.println("ci :"+ci);
                    if( ci instanceof ApplicationData){
            if (debug) System.out.println("(ApplicationData)ci).getFileName() :"+( (ApplicationData)ci).getFileName());
            if (debug) System.out.println("contentName :"+contentName);
                        if( ( (ApplicationData)ci).getFileName().equalsIgnoreCase( contentName) ){
                            return ContentServerHelper.service.findContentStream( (ApplicationData)ci);
                        }
                    }else{ // URLData, should never be reached!!
                    // URL must be authenticated!
                        try{
                            is = new URL( ( (URLData)ci).getUrlLocation() ).openStream();
                        }catch(Exception e){
                            throw new WTException( e);
                        }
                    }
                }
            }catch(java.beans.PropertyVetoException e){
                throw new WTException( e);
            }
            return is;
        }


    /**
     * Uncompress the incoming file.
     * @param inFileName Name of the file to be uncompressed
     * @param outFileName New file name, if null old - gz is used
     * @return File
     */
    public static File doUncompressFile(String inFileName, String outFileName) {
		File outFile = null;
        try {

            if (!getExtension(inFileName).equalsIgnoreCase("gz")) {
                if (debug) System.out.println("File name must have extension of \".gz\"");
            }

            if (debug) System.out.println("Opening the compressed file.");
            GZIPInputStream in = null;
            try {
                in = new GZIPInputStream(new FileInputStream(inFileName));
            } catch(FileNotFoundException e) {
                System.out.println("File not found. " + inFileName);
            }

            if (debug) System.out.println("Open the output file.");
            if (outFileName==null) outFileName = getFileName(inFileName);
            FileOutputStream out = null;
            try {
                outFile = new File (outFileName);
                out = new FileOutputStream(outFile);
            } catch (FileNotFoundException e) {
                System.out.println("Could not write to file. " + outFileName);
            }

            if (debug) System.out.println("Transfering bytes from compressed file to the output file.");
            byte[] buf = new byte[1024];
            int len;
            while((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }

            if (debug) System.out.println("Closing the file and stream");
            in.close();
            out.close();
        
        } catch (IOException e) {
            e.printStackTrace();
        }
		return(outFile);
    }

    /**
     * Used to extract and return the extension of a given file.
     * @param f Incoming file to get the extension of
     * @return <code>String</code> representing the extension of the incoming
     *         file.
     */  
    public static String getExtension(String f) {
        String ext = "";
        int i = f.lastIndexOf('.');

        if (i > 0 &&  i < f.length() - 1) {
            ext = f.substring(i+1);
        }        
        return ext;
    }

    /**
     * Used to extract the filename without its extension.
     * @param f Incoming file to get the filename
     * @return <code>String</code> representing the filename without its
     *         extension.
     */  
    public static String getFileName(String f) {
        String fname = "";
        int i = f.lastIndexOf('.');

        if (i > 0 &&  i < f.length() - 1) {
            fname = f.substring(0,i);
        }        
        return fname;
    }

}
