dimarts, 19 d’octubre del 2010

Diagrames dels models dels documents de l'OpenOffice.org

En el post d'avui, uns diagrames que cal tenir presents a l'hora de desenvolupar macros i aplicacions amb Java, BeanShell o amb JavaScript per a l'OpenOffice.org. Extrets de la imprescindible wiki de l'OpenOffice.org.

Primer de tot: un diagrama extret de http://wiki.services.openoffice.org/wiki/JavaEclipseTuto en el que es descriu el procés per a compilar un component UNO des de la interfase fins el jar.


Més, els diagrames que ens expliquen el model, l'estructura, interfases i serveis,  que caracteritzen els diferents tipus de documents de l'OpenOffice.org. 




Model de document de full de càlcul:



Model dels documents de dibuix




Model dels documents de presentació


No existeixen els documents de base de dades, més aviat cal dir que una base de dades d'OpenOffice.org és una col·lecció d'objectes: taules, consultes, formularis i informes que són documents en ells mateixos. Per exemple, un formulari de base de dades és un objecte odt, és dir, és un formulari del mateix tipus que els formularis de document de text, amb el mateix model.  El model de les bases de dades d'OpenOffice.org serà tema de post un altre dia.

divendres, 15 d’octubre del 2010

Com fer una aplicació java standalone que utilitzi l'OpenOffice.org per a modificar o crear un document

Una tasca comú a l'ofimàtica és automatizar la generació de documents. En el cas de l'MS Office això s'aconsegueix amb l'automatització OLE i llenguatges com Visual Basic.

Per exemple, suposem que cal documentar l'esquema d'una base de dades. Una forma de fer-ho seria escriure un programa que analitzes les meta-dades de la base de dades i generés un document de text. 

O, per exemple, crear un document amb el resultat d'analitzar escenaris amb un full de càlcul. Es podria escriure un programa que provés els diferents escenaris i generés un informe  amb els resultats 

En el món Linux es pot aconseguir tot això fent servir Java i OpenOffice.

Aquest post és un exemple de programa java que agafa un document de text de OpenOffice.org Writer que està buit (prova.odt) i l'omple amb 100 línies de text.

M'he basat en el codi disponible a http://weblogs.java.net/blog/2005/12/30/open-office-java-api i en l'OpenOffice.org Developers Guide (en PDF), capítol First Contact, per al build.xml de l'ant.




Comencem: el codi de la classe java 



package com.serveisticiprogramarilliure.standalone;


import com.sun.star.uno.XComponentContext;
import com.sun.star.frame.XComponentLoader;
import com.sun.star.frame.XStorable;
import com.sun.star.lang.XComponent;
import com.sun.star.comp.helper.Bootstrap;
import com.sun.star.beans.PropertyValue;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.text.XTextDocument;
import com.sun.star.ucb.XFileIdentifierConverter;
import com.sun.star.util.XCloseable;
import com.sun.star.frame.XDesktop;


public class ProvaStandalone {
 
    public static void main(String[] args) {
try {
   System.out.println("Inici");
   // engega l'OpenOffice.org
   // Bootstrap
   System.out.println("Bootstrap");
   XComponentContext xContext = Bootstrap.bootstrap();
   // MultiComponentFactory
   System.out.println("MultiComponentFactory");
     XMultiComponentFactory xMCF = xContext.getServiceManager();
     // Crea el Desktop
     System.out.println("Raw Desktop");
   Object oRawDesktop = xMCF.createInstanceWithContext("com.sun.star.frame.Desktop", xContext);
   System.out.println("Desktop");
   XDesktop xDesktop = (XDesktop) UnoRuntime.queryInterface(XDesktop.class, oRawDesktop);
   // Obté el carregador de components
   System.out.println("Component loader");
   XComponentLoader xCompLoader = (XComponentLoader) 
                                   UnoRuntime.queryInterface(XComponentLoader.class, xDesktop);


   // URL del document a generar.
   System.out.println("convert to URL");
   String sUrl =  convertToURL(xContext, xMCF, null, "/home/albert/Documents/prova.odt");
   System.out.println("Fitxer: " + sUrl);
   
   // carrega un component document nou / 
   System.out.println("Document existent");
   PropertyValue[] voidProps = new PropertyValue[0];
   
   XComponent xComp = xCompLoader.loadComponentFromURL(sUrl, "_blank", 0, voidProps);
   // "private:factory/swriter";  document nou
   // "_blank":  frame on es crea
   // 0: FrameSearchFlag, 
            // http://api.openoffice.org/docs/common/ref/com/sun/star/frame/FrameSearchFlag.html
   // new PropertyValue[0], 
            // http://api.openoffice.org/docs/common/ref/com/sun/star/document/MediaDescriptor.html
   
   // si en comptes d'utilitzar un document ja existent, volgués crear un document nou
            // posaria "private:factory/swriter" en comptes de sUrl
   
   // obté la interfase XTextDocument el component document nou
   System.out.println("Escriu línies");
   XTextDocument xTextDocument = (XTextDocument)
                                          UnoRuntime.queryInterface(XTextDocument.class, xComp);
   for(int i=0; i<100; i++) {
xTextDocument.getText().getEnd().setString( "Escriu 100 línies. Línia " + i + ".\n" );
   }
   
   // el guarda en format PDF
   // http://www.oooforum.org/forum/viewtopic.phtml?t=71294
   // "Name", "Type", "UIName" 
   // "Rich Text Format", "writer_Rich_Text_Format", "Rich Text Format" 
   // "writer_pdf_Export", "pdf_Portable_Document_Format", "PDF - Portable Document Format" 
   // ...
   System.out.println("XStorable");
   XStorable xStorable = (XStorable)UnoRuntime.queryInterface(XStorable.class, xTextDocument);
   // indica el filtre de PDF


   // i guarda el fitxer
   System.out.println("StoreAsURL");
   xStorable.storeAsURL(sUrl, voidProps);


   // tancar l'openoffice que ha obert
   System.out.println("Tanca");
   XCloseable xcloseable = (XCloseable) UnoRuntime.queryInterface(XCloseable.class, xTextDocument);
   xcloseable.close(false);
System.out.println("Fi");
} catch (java.lang.Exception e) {
   e.printStackTrace();
} finally {
   System.exit(0);
}
    }
    
    /**
     * Converts a system path into an URL using OOo API
     * @param sBase
     * @param sSystemPath
     * @return
     */
    private static String convertToURL(XComponentContext xContext, 
     XMultiComponentFactory xMCF, 
     String sBase, 
     String sSystemPath) {
        String sURL = null;
        try {
         System.out.println("FileConverter");
            XFileIdentifierConverter xFileConverter =
           (XFileIdentifierConverter) UnoRuntime.queryInterface(
               XFileIdentifierConverter.class,
               xMCF.createInstanceWithContext("com.sun.star.ucb.FileContentProvider", xContext));
            System.out.println("getFileURLFromSystemPath");
            sURL = xFileConverter.getFileURLFromSystemPath(sBase, sSystemPath );
        } catch (com.sun.star.uno.Exception e) {
            e.printStackTrace();
        } finally {
            return sURL;
        }
    }
    
    /**
     * Converts an URL into a system path using OOo API
     * @param sURLPath
     * @return
     */
    private static String convertFromURL( XComponentContext xContext,
     XMultiComponentFactory xMCF,
     String sURLPath) {
        String sSystemPath = null;
        try {
            // m_xMCF = getMultiComponentFactory();
            XFileIdentifierConverter xFileConverter =
           (XFileIdentifierConverter) UnoRuntime.queryInterface(
               XFileIdentifierConverter.class,
               xMCF.createInstanceWithContext(
                            "com.sun.star.ucb.FileContentProvider", xContext));
            sSystemPath = xFileConverter.getSystemPathFromFileURL(sURLPath);

        } catch (com.sun.star.uno.Exception e) {
            e.printStackTrace(System.err);
        } finally {
            return sSystemPath;
        }
    }
}



A continuació el build.xml d'ant que he fet servir. Difereix del que es troba en la Developers Guide en els paths que aquí estan adaptats a un OpenOffice.org 3.2  sobre Linux.



<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="all" name="standalone01">
  <property name="OFFICE_ROOT" value="/opt/openoffice.org" />
  <property name="OFFICE_HOME" value="${OFFICE_ROOT}/basis3.2" />
  <property name="OO_SDK_HOME" value="${OFFICE_HOME}/sdk" />
  <property name="OO_URE_HOME" value="${OFFICE_ROOT}/ure" />

  <target name="init">
  <property name="OUT_DIR" value="${basedir}/build/" />
<property name="BIN_DIR" value="${basedir}/bin/" />
  </target>

  <path id="office.class.path">
<filelist dir="${OFFICE_HOME}/program/classes" files="unoil.jar" />
<filelist dir="${OO_URE_HOME}/share/java" files="jurt.jar,ridl.jar,juh.jar" />
  </path>

  <fileset id="bootstrap.glue.code" dir="${OO_SDK_HOME}/classes">
<patternset>
<include name="com/sun/star/lib/loader/*.class" />
</patternset>
  </fileset>

  <target name="compile" depends="init" unless="eclipse.running">
<mkdir dir="${BIN_DIR}" />
<javac debug="true" deprecation="true" destdir="${BIN_DIR}" srcdir=".">
<classpath refid="office.class.path" />
</javac>
  </target>

  <target name="jar" depends="init,compile">
<mkdir dir="${OUT_DIR}" />
<jar basedir="${BIN_DIR}" compress="true" jarfile="${OUT_DIR}/standalone01.jar">
<exclude name="**/*.java" />
<exclude name="*.jar" />
<fileset refid="bootstrap.glue.code" />
<manifest>
<attribute name="Main-Class" value="com.sun.star.lib.loader.Loader" />
<section name="com/sun/star/lib/loader/Loader.class">
<attribute name="Application-Class" 
value="com.serveisticiprogramarilliure.standalone.ProvaStandalone" />
</section>
</manifest>
</jar>
  </target>

  <target name="all" description="compila i linka" depends="init,compile,jar">
<echo message="OK. standalone01.jar" />
  </target>

    <target name="cleanbin" description="neteja binaris" unless="eclipse.running">
<delete>
<fileset dir="${BIN_DIR}">
<include name="**/*.class" />
</fileset>
</delete>
  </target>

  <target name="cleanall" description="neteja tot" depends="init,cleanbin">
<delete file="${OUT_DIR}/standalone01.jar" />
  </target>
</project>



Finalment, el fitxer executa.sh amb el que llenço l'execució. Recordo que l'entorn amb el que treballo és un OpneOffice.org 3.2.1 (i SDK), sobre Linux Ubuntu Lucid Lynx 10.04. compilat amb JDK 1.6. Versions diferents de qualsevol d'aquests elements pot provocar que algun path no sigui el correcte i que l'aplicació no funcioni, compte amb això!


OFFICE_ROOT=/opt/openoffice.org
OFFICE_HOME=$OFFICE_ROOT/basis3.2
OO_SDK_HOME=$OFFICE_HOME/sdk
OO_URE_HOME=$OFFICE_ROOT/ure


echo java -cp "$OFFICE_HOME/program/classes/unoil.jar","$OFFICE_HOME/program/classes/sandbox.jar","$OO_URE_HOME/share/java/jurt.jar","$OO_URE_HOME/share/java/ridl.jar","$OO_URE_HOME/share/java/juh.jar" -Djava.library.path="/opt/openoffice.org/basis3.2/sdk/classes" -Dcom.sun.star.lib.loader.unopath="/opt/openoffice.org3/program"  -jar standalone01.jar


java -cp "$OFFICE_HOME/program/classes/unoil.jar","$OFFICE_HOME/program/classes/sandbox.jar","$OO_URE_HOME/share/java/jurt.jar","$OO_URE_HOME/share/java/ridl.jar","$OO_URE_HOME/share/java/juh.jar" -Djava.library.path="/opt/openoffice.org/basis3.2/sdk/classes" -Dcom.sun.star.lib.loader.unopath="/opt/openoffice.org3/program"  -jar standalone01.jar


El resultat de l'execució és un document de text prova.odt amb 100 línies de text i els següents missatges a la consola:



albert@atenea:/media/Nuevo vol/wk-java/OOo-standalone-01/build$ ./executa.sh 
java -cp /opt/openoffice.org/basis3.2/program/classes/unoil.jar,/opt/openoffice.org/basis3.2/program/classes/sandbox.jar,/opt/openoffice.org/ure/share/java/jurt.jar,/opt/openoffice.org/ure/share/java/ridl.jar,/opt/openoffice.org/ure/share/java/juh.jar -Djava.library.path=/opt/openoffice.org/basis3.2/sdk/classes -Dcom.sun.star.lib.loader.unopath=/opt/openoffice.org3/program -jar standalone01.jar
Inici
Bootstrap
MultiComponentFactory
Raw Desktop
Desktop
Component loader
convert to URL
FileConverter
getFileURLFromSystemPath
Fitxer: file:///home/albert/Documents/prova.odt
Document existent
Escriu línies
XStorable
StoreAsURL
Tanca
Fi
albert@atenea:/media/Nuevo vol/wk-java/OOo-standalone-01/build$ 
  


Albert Baranguer
Barcelona 2010