/************************************************************************
 *
 *  W2LExportFilter.java
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *  Copyright: 2002-2007 by Henrik Just
 *
 *  All Rights Reserved.
 * 
 *  Version 0.4.2 (2007-04-02)
 *
 */ 
 
// This file is based on OOo's XMergeBridge, which is (c) by Sun Microsystems

package writer2latex.filter;

/*Java Uno Helper Classes*/
import com.sun.star.lib.uno.adapter.XInputStreamToInputStreamAdapter;
import com.sun.star.lib.uno.adapter.XOutputStreamToOutputStreamAdapter;

/*StarOffice/Uno Classes*/
import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.lang.XServiceInfo;
import com.sun.star.lang.XTypeProvider;
import com.sun.star.util.XStringSubstitution;
import com.sun.star.uno.Type;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;
import com.sun.star.comp.loader.FactoryHelper;
import com.sun.star.lang.XServiceName;
import com.sun.star.lang.XSingleServiceFactory;
import com.sun.star.registry.XRegistryKey;
import com.sun.star.xml.sax.InputSource;
import com.sun.star.xml.sax.XParser;
import com.sun.star.io.XInputStream;
import com.sun.star.io.XOutputStream;
import com.sun.star.xml.sax.XDocumentHandler;
import com.sun.star.uno.AnyConverter;

/* Generated from Idls */
import com.sun.star.xml.XExportFilter;

/* Writer2LaTeX Classes */
import writer2latex.office.MIMETypes;
import writer2latex.util.Config;
import writer2latex.api.Converter;
import writer2latex.api.ConverterFactory;
import writer2latex.api.ConverterResult;
import writer2latex.api.OutputFile;

/*Java Classes*/
import java.util.Enumeration;
import java.util.Vector;
import java.io.*;
import javax.xml.parsers.*;
import org.xml.sax.SAXException;
import java.net.URI;


/** This outer class provides an inner class to implement the service
 * description, a method to instantiate the
 * component on demand (__getServiceFactory()), and a method to give
 * information about the component (__writeRegistryServiceInfo()).
 */
public class W2LExportFilter {
    
    private static XComponentContext xComponentContext = null;
    private static XMultiServiceFactory xMSF;
    private static XDocumentHandler exportDocHandler=null;
    private static XInputStream xInStream =null;
    private static XOutputStream xOutStream=null;
    private static String  udJarPath=null;
    private static XOutputStream xos = null;
    private static XOutputStreamToOutputStreamAdapter adaptedStream=null;
    private static String offMime=null;
    private static String sdMime=null;
    private static String sFileName=null; 
    private static String sURL="";
	

    //private static FileOutputStream adaptedStream =null;

    /** This inner class provides the component as a concrete implementation
     * of the service description. It implements the needed interfaces.
     * @implements XTypeProvider
     */
    static public class _W2LExportFilter implements
        XExportFilter,						     
        XServiceName,
        XServiceInfo,
        XDocumentHandler,  
        XTypeProvider {
		
        /** <p>We need to get the Service Manager from the Component context to
         *  instatiate certain services, hence this constructor.</p>
         */
        public _W2LExportFilter(XComponentContext xComponentContext1) {
            xComponentContext = xComponentContext1;
        }
        
        /** The component will be registered under this name.
         */
        static private final String __serviceName = "writer2latex.filter.W2LExportFilter";
        
        // Some utility methods:
		
        String getFileName(String origName) {
            String name=null;
	        if (origName !=null) {
                if(origName.equalsIgnoreCase(""))
                    name = "OutFile"; 
                else {
                    if (origName.lastIndexOf("/")>=0) {
                        origName=origName.substring(origName.lastIndexOf("/")+1,origName.length());
                    }
                    if (origName.lastIndexOf(".")>=0) {
                        name = origName.substring(0,(origName.lastIndexOf(".")));
                    }
                    else {
                        name=origName;
                   }
               }
            }
            else{   
                name = "OutFile"; 
            }

            return name;
        }
		
        public String replace(String origString, String origChar, String replaceChar){
	        String tmp="";	
	        int index=origString.indexOf(origChar);
	        if(index !=-1){
                while (index !=-1){
		            String first =origString.substring(0,index);
		            first=first.concat(replaceChar);
                    tmp=tmp.concat(first);
                    origString=origString.substring(index+1,origString.length());
 		            index=origString.indexOf(origChar);
         	        if(index==-1) {
                        tmp=tmp.concat(origString);
                    }
        	    }
	        }
	        return tmp;
        }
	   
        public String needsMask(String origString) {
            if (origString.indexOf("&")!=-1){
                origString=replace(origString,"&","&amp;");
     	    }    
	        if (origString.indexOf("\"")!=-1){
        		origString=replace(origString,"\"","&quot;");
    	    }    
	        if (origString.indexOf("<")!=-1){
        		origString=replace(origString,"<","&lt;");
    	    }  
    	    if (origString.indexOf(">")!=-1){
	        	origString=replace(origString,">","&gt;");
    	    }  
	        return origString;
	  
        }


        // Implementation of XExportFilter:

        public boolean exporter(com.sun.star.beans.PropertyValue[] aSourceData, 
	 		       java.lang.String[] msUserData) throws com.sun.star.uno.RuntimeException{
		 		  
            sFileName=null;		   
            sURL=null;
	        //String sDirectory = null;
            String title=null;
			
            // Get user data from configuration (type detection)
            String udConvertClass=msUserData[0];
            udJarPath=msUserData[1];
            String udImport =msUserData[2];
            String udExport =msUserData[3];
            offMime =msUserData[4];
            sdMime = msUserData[5];
		
            // Get source data (only the OutputStream and the URL are actually used)
            com.sun.star.beans.PropertyValue[] pValue = aSourceData;
            for  (int  i = 0 ; i < pValue.length; i++) {
                try{
                    if (pValue[i].Name.compareTo("OutputStream")==0){
                        xos=(com.sun.star.io.XOutputStream)AnyConverter.toObject(new Type(com.sun.star.io.XOutputStream.class), pValue[i].Value);
                    }
                    if (pValue[i].Name.compareTo("FileName")==0){
                        sFileName=(String)AnyConverter.toObject(new Type(java.lang.String.class), pValue[i].Value);
                    }  
                    if (pValue[i].Name.compareTo("URL")==0){
                        sURL=(String)AnyConverter.toObject(new Type(java.lang.String.class), pValue[i].Value);
                    }
                    if (pValue[i].Name.compareTo("Title")==0){
                        title=(String)AnyConverter.toObject(new Type(java.lang.String.class), pValue[i].Value);
                    }
                } 
                catch(com.sun.star.lang.IllegalArgumentException AnyExec){
                    System.err.println("\nIllegalArgumentException "+AnyExec);
                }
            }
	     
					 
            if (sURL==null){
                sURL="";
            }
	     
            // Create a pipe to be used by the XDocumentHandler implementation:
            try{
                Object xPipeObj=xMSF.createInstance("com.sun.star.io.Pipe");
                xInStream = (XInputStream) UnoRuntime.queryInterface(
                            XInputStream.class , xPipeObj );
                xOutStream = (XOutputStream) UnoRuntime.queryInterface(
                            XOutputStream.class , xPipeObj );
            }
            catch (Exception e){
                System.err.println("Exception "+e);
                return false;
            }
	      
            return true;
        }

		
	
        // Implementation of XDocumentHandler:
        // Flat xml is created by the sax events and passed through the pipe
        // created by exporter()

        public void  startDocument () {
 	        //Do nothing
        }
	
    public void endDocument()throws com.sun.star.uno.RuntimeException {	   
        try{
            xOutStream.closeOutput();	
		    convert(xInStream,xos);
	    }
	    catch (IOException e){
            throw new com.sun.star.uno.RuntimeException(e.getMessage());

	    }
	     catch (Exception e){
		throw new com.sun.star.uno.RuntimeException("Writer2LaTeX Exception");

	    }
	}
	


	public void startElement (String str, com.sun.star.xml.sax.XAttributeList xattribs)
	{
	   
	    str="<".concat(str);
	    if (xattribs !=null)
	    {
		str= str.concat(" ");
		int len=xattribs.getLength();
		for (short i=0;i<len;i++)
		    {
			str=str.concat(xattribs.getNameByIndex(i));
			str=str.concat("=\"");
			str=str.concat(needsMask(xattribs.getValueByIndex(i)));
			str=str.concat("\" ");
		    }
	    }
	    str=str.concat(">");
	    try{
		 xOutStream.writeBytes(str.getBytes("UTF-8"));
	    }
	    catch (Exception e){
		System.err.println("\n"+e);
	    }
	    
	}

	public void endElement(String str){
	   
	    str="</".concat(str);
	    str=str.concat(">");
	    try{
		 xOutStream.writeBytes(str.getBytes("UTF-8"));
		
	    }
	    catch (Exception e){
		System.err.println("\n"+e);
	    }
    
	   
	}
	public void characters(String str){
	    str=needsMask(str);
	    try{
		 xOutStream.writeBytes(str.getBytes("UTF-8"));
	    }
	   catch (Exception e){
	       System.err.println("\n"+e);
	   }
	     
	    
	}
	
	public void ignorableWhitespace(String str){
	   
	   
	}
       public void processingInstruction(String aTarget, String aData){
	  
       }
	
	public void setDocumentLocator(com.sun.star.xml.sax.XLocator xLocator){
	  
	}

     


        // This is the actual conversion method, using Writer2LaTeX to convert
        // the flat xml recieved from the XInputStream, and writing the result
        // to the XOutputStream. The XMLExporter does not support export to
        // compound documents with multiple output files; so the main file
        // is written to the XOutStream and other files are written using ucb.

        public void convert (com.sun.star.io.XInputStream xml,com.sun.star.io.XOutputStream exportStream)
            throws com.sun.star.uno.RuntimeException, IOException {
		
            // Initialise the ucb
            UCBWrapper ucbWrapper = new UCBWrapper(xMSF);
			
            // Get base name from the url provided by OOo
            String sName= getFileName(sURL);
	  
            // Adapter for input stream (OpenDocument flat xml)
            XInputStreamToInputStreamAdapter xis =new XInputStreamToInputStreamAdapter(xml);
			
            // Adapter for output stream (Main output file)
            XOutputStreamToOutputStreamAdapter newxos =new XOutputStreamToOutputStreamAdapter(exportStream);

            // Create Writer2LaTeX configuration and read user config
            Config config = new Config();
            try {
                Object pathSubstitutionService = xMSF.createInstance(
                   "com.sun.star.util.PathSubstitution");
                XStringSubstitution xStringSub = (XStringSubstitution)UnoRuntime.queryInterface( 
                XStringSubstitution.class, pathSubstitutionService);
                String sUser = xStringSub.getSubstituteVariableValue("$(user)");
                String sConfigFileName = sUser+"/writer2latex.xml";
                XInputStream xIs = ucbWrapper.read(sConfigFileName);
                if (xIs!=null) {
                    // Read the file with success, read the contents into the config
                    InputStream is = new XInputStreamToInputStreamAdapter(xIs);
                    config.read(is);
                }
                else {
                    // No configuration, in this case we will write one
                    // UCBWrapper demands an InputStream, so we need a pipe
                    Object xPipeObj=xMSF.createInstance("com.sun.star.io.Pipe");
                    XInputStream xInStream
                      = (XInputStream) UnoRuntime.queryInterface(XInputStream.class, xPipeObj );
                    XOutputStream xOutStream
                      = (XOutputStream) UnoRuntime.queryInterface(XOutputStream.class, xPipeObj );
                    OutputStream outStream
                      = new XOutputStreamToOutputStreamAdapter(xOutStream);
                    // Feed the pipe with content...
                    config.write(outStream);
                    outStream.close();
                    xOutStream.closeOutput();
                    // ...and then write the content to the url
                    ucbWrapper.write(xInStream,sConfigFileName);
                }
                
            }
            catch (com.sun.star.container.NoSuchElementException e) {
                // The variable $(user) didn't exist, which of course doesn't happen...
            }
            catch (com.sun.star.uno.Exception e) {
                // Failed to create service, we will do without reading the config
            }
			
            // Do conversion 
            ConverterResult dataOut = null;
            ConverterFactory factory = new ConverterFactory();
            Converter converter = factory.createConverter(sdMime);
            if (converter!=null) {
                converter.setConfig(config);
                converter.setGraphicConverter(new GraphicConverterImpl(xComponentContext));
                try {
                    dataOut = converter.convert(xis,sName);
                }
                catch (IOException e) {
                    // Fail silently
                }
            }

            // Write out files
            Enumeration docEnum = dataOut.getDocumentEnumeration();
			
            // Remove the file name part of the url
            String sNewURL = null;
            if (sURL.lastIndexOf("/")>-1) {
                // Take the url up to and including the last slash
                sNewURL = sURL.substring(0,sURL.lastIndexOf("/")+1);
            }
            else {
                // The url does not include a path; this should not really happen,
                // but in this case we will write to the current default directory
                sNewURL = "";
            }
			
            while (docEnum.hasMoreElements() && sURL.startsWith("file:")) {
                OutputFile docOut      = (OutputFile)docEnum.nextElement();
				String fileName      = docOut.getFileName();
                if (dataOut.getMasterDocument()==docOut) {
                    // The master document is written to the XOutStream supplied
                    // by the XMLFilterAdaptor
                    docOut.write(newxos);
				     
                    newxos.flush();
                    newxos.close();
                }
                else {				
                    // Additional documents are written directly using ucb

                    try{
                        // UCBWrapper demands an InputStream, so we need a pipe
                        Object xPipeObj=xMSF.createInstance("com.sun.star.io.Pipe");
                        XInputStream xInStream
                          = (XInputStream) UnoRuntime.queryInterface(XInputStream.class, xPipeObj );
                        XOutputStream xOutStream
                          = (XOutputStream) UnoRuntime.queryInterface(XOutputStream.class, xPipeObj );
                        OutputStream outStream
                          = new XOutputStreamToOutputStreamAdapter(xOutStream);
                        // Feed the pipe with content...
                        docOut.write(outStream);
                        outStream.flush();
                        outStream.close();
                        xOutStream.closeOutput();
                        // ...and then write the content to the url
                        ucbWrapper.write(xInStream,sNewURL+fileName);
                    }
                    catch (Throwable e){
                        System.err.println("Exception "+e);
                    }
                }
					 
			}
			
        }


        // Implement methods from interface XTypeProvider
        // Implementation of XTypeProvider
		
        public com.sun.star.uno.Type[] getTypes() {
            Type[] typeReturn = {};

            try {
                typeReturn = new Type[] {
                new Type( XTypeProvider.class ),
                new Type( XExportFilter.class ),
                new Type( XServiceName.class ),
                new Type( XServiceInfo.class ) };
            }
            catch( Exception exception ) {
		
            }

            return( typeReturn );
        }


        public byte[] getImplementationId() {
            byte[] byteReturn = {};

            byteReturn = new String( "" + this.hashCode() ).getBytes();

            return( byteReturn );
        }

        // Implement method from interface XServiceName
        public String getServiceName() {
            return( __serviceName );
        }
    
        // Implement methods from interface XServiceInfo
        public boolean supportsService(String stringServiceName) {
            return( stringServiceName.equals( __serviceName ) );
        }
    
        public String getImplementationName() {
            return( _W2LExportFilter.class.getName() );
        }
    
        public String[] getSupportedServiceNames() {
            String[] stringSupportedServiceNames = { __serviceName };
            return( stringSupportedServiceNames );
        }
    } // end of inner class
   
    /**
     * Returns a factory for creating the service.
     * This method is called by the <code>JavaLoader</code>
     *
     * @return  returns a <code>XSingleServiceFactory</code> for creating the
     *          component
     *
     * @param   implName     the name of the implementation for which a
     *                       service is desired
     * @param   multiFactory the service manager to be used if needed
     * @param   regKey       the registryKey
     *
     * @see                  com.sun.star.comp.loader.JavaLoader
     */
    public static XSingleServiceFactory __getServiceFactory(String implName,
    XMultiServiceFactory multiFactory,
    XRegistryKey regKey) {
	xMSF= multiFactory;
        XSingleServiceFactory xSingleServiceFactory = null;
        if (implName.equals(_W2LExportFilter.class.getName()) ) {
            xSingleServiceFactory = FactoryHelper.getServiceFactory(_W2LExportFilter.class,
            _W2LExportFilter.__serviceName,
            multiFactory,						    
            regKey);
        }
        
        return xSingleServiceFactory;
    }
    
    /**
     * Writes the service information into the given registry key.
     * This method is called by the <code>JavaLoader</code>
     * <p>
     * @return  returns true if the operation succeeded
     * @param   regKey       the registryKey
     * @see                  com.sun.star.comp.loader.JavaLoader
     */
    public static boolean __writeRegistryServiceInfo(XRegistryKey regKey) {

        return FactoryHelper.writeRegistryServiceInfo(_W2LExportFilter.class.getName(),
        _W2LExportFilter.__serviceName, regKey);
    }
}



