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.

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

Com fer una llibreria de macros java de l’OpenOffice.org que accedeixen a la textbox d’un form de Writer.

Sense cap mena de dubte, la millor forma de fer macros per a l’OpenOffice.org és amb el OOoBasic.

Dit això, en ocasions pot ser interessant plantejar aplicacions java que modifiquin documents d’OpenOffice.org, les aproximacions java a OpenOffice poden ser de tres formes principals:

1. Aplicació StandAlone que és capaç d’obrir un OpenOffice.org i manipular-ne els documents
2. Una llibreria de macros java que és invocada des d’un OpenOffice.org
3. Component UNO java que és invocat des del menú o des d’una macro en OOoBasic (o en java, o python…) i que és una extensió del core de l’OpenOffice.org

En aquest post d’avui, faré una llibreria de macros java que invocaré des de botons al document de text i que escriuran  frases al final del document i modificaran el text d’una textbox que anomenaré textbox1.


parcel-descriptor.xml
La llibreria de macros java la posaré a /home/elmeuusuari/.openoffice.org/3/user/Scripts/java/ de forma que formarà part del conjunt de “les meves macros”. La llibreria també podria formar part de “les macros de l’OpenOffice.org”, de forma que estaria compartida a tots els usuaris d’aquesta instal·lació d’OpenOffice.org i aleshores caldria posar-la a /opt/openoffice.org/basis3.2/share/Scripts/java. No és possible associar  macros java a un document.

Per compilar una macro java de l’OpenOffice.org és necesari enllaçar, almenys, les llibreries:
/opt/openoffice.org/ure/share/java/jurt.jar
/opt/openoffice.org/ure/share/java/ridl.jar
/opt/openoffice.org/basis3.2/program/classes/unoil.jar

A més, per a que l’OpenOffice.org sàpiga que té una nova llibreria de macros, cal preparar un fitxer descriptor, el parcel-descriptor.xml, on s’indica quina és la nova llibreria, en quin jar es troba i quin mètode corresponen a cada macro.

En el cas que m’ocupa, el parcel-descriptor.xml és el següent:


<?xml version=”1.0″ encoding=”UTF-8″?>
<parcel language=”Java” xmlns:parcel=”scripting.dtd”>
  <!– mètode 1 –>
  <script language=”Java”>
    <locale lang=”Es-ca”>
      <displayname value=”MacroProves.DispName”/>
      <description>Mostra un missatge</description>
    </locale>

    <functionname value=”com.serveisticiprogramarilliure.MacroProves.print”/>
    <logicalname value=”MacroProves.print”/>

    <languagedepprops>
      <prop name=”classpath” value=”OOo-java-macro-01.jar”/>
    </languagedepprops>
  </script>

  <!– mètode 2 –>
  <script language=”Java”>
    <locale lang=”Es-ca”>
      <displayname value=”MacroProves.DispName”/>
      <description>Mostra un missatge</description>
    </locale>
    <functionname value=”com.serveisticiprogramarilliure.MacroProves.print2″/>
    <logicalname value=”MacroProves.print2″/>

    <languagedepprops>
      <prop name=”classpath” value=”OOo-java-macro-01.jar”/>
    </languagedepprops>
  </script>
</parcel>

parcel. Document que agrupa les macros, conceptualment: la llibreria
script. Bloc que defineix cada macro
functioname. És el nom del mètode java que correspon a la macro
logicalname. El nom lògic de la macro
languagedepprops. Indica la llibreria jar que cal carregar
displayname. Nom de la llibreria
description. Descripció de la llibreria

Vaig per la llibreria pròpiament dita. Faré una classe amb el parell de mètodes print i print2 que seran els que invocaré des dels botons.

MacroProves.java

Vet aquí la classe: MacroProves.java

package com.serveisticiprogramarilliure;


  import com.sun.star.script.provider.XScriptContext;
  import com.sun.star.uno.UnoRuntime;
  import com.sun.star.text.XTextDocument;
  import com.sun.star.text.XTextRange;
  import com.sun.star.text.XText;
  import com.sun.star.awt.MouseEvent;
  import com.sun.star.container.XIndexAccess;
  import com.sun.star.container.XNameAccess;
  import com.sun.star.container.XNameContainer;
  import com.sun.star.drawing.XDrawPage;
  import com.sun.star.drawing.XDrawPageSupplier;
  import com.sun.star.form.XForm;
  import com.sun.star.form.XFormComponent;
  import com.sun.star.form.XFormsSupplier;
  import com.sun.star.frame.XModel;
  import com.sun.star.frame.XController;


  /**
   *  MacroProves
   *
   */


  public class MacroProves {
    public static void main(String[] args) {
     // TODO Auto-generated method stub
    }


    public static void print(XScriptContext xSc) {
      // getting the text document object
      XTextDocument xtextdocument = (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class, xSc.getDocument());
      XText xText = xtextdocument.getText();
      XTextRange xTextRange = xText.getEnd();
      xTextRange.setString( “MacroProves.print.\n” );
      snippet(xSc, “a”);
    }// printHW




    public static void print(XScriptContext xSc, MouseEvent meEvent) {
        // getting the text document object
        XTextDocument xtextdocument = (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class, xSc.getDocument());
        XText xText = xtextdocument.getText();
        XTextRange xTextRange = xText.getEnd();
        xTextRange.setString( “MacroProves.print. Invocació des de botó.\n” );
        snippet(xSc, “b”);
    }   


    public static void print2(XScriptContext xSc) {
        // getting the text document object
        XTextDocument xtextdocument = (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class, xSc.getDocument());
        XText xText = xtextdocument.getText();
        XTextRange xTextRange = xText.getEnd();
        xTextRange.setString( “MacroProves.print2.\n” );
        snippet(xSc, “c”);
      }// printHW




      public static void print2(XScriptContext xSc, MouseEvent meEvent) {
          // getting the text document object
          XTextDocument xtextdocument = (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class, xSc.getDocument());
          XText xText = xtextdocument.getText();
          XTextRange xTextRange = xText.getEnd();
          xTextRange.setString( “MacroProves.print2. Invocació des de botó.\n” );
          snippet(xSc, “d”);
      }    
    
    
    private static void snippet(XScriptContext xSc, String sXY) {
        try {
              XTextDocument xdocument = (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class, xSc.getDocument());
              XController xcontroller = (XController) UnoRuntime.queryInterface(XController.class, xdocument.getCurrentController());
              XModel xmodel = (XModel) UnoRuntime.queryInterface(XModel.class, xcontroller.getModel());
              XDrawPageSupplier xDrawPageSupplier = (XDrawPageSupplier) UnoRuntime.queryInterface(XDrawPageSupplier.class, xmodel);
              XDrawPage xDrawPage = xDrawPageSupplier.getDrawPage();
              XFormsSupplier xFormsSupplier = (XFormsSupplier) UnoRuntime.queryInterface(XFormsSupplier.class, xDrawPage);
              XNameContainer xNameContainer = xFormsSupplier.getForms();
              XIndexAccess xIndexAccess = (XIndexAccess) UnoRuntime.queryInterface(XIndexAccess.class, xNameContainer);
              XForm xForm = (XForm) UnoRuntime.queryInterface(XForm.class, xIndexAccess.getByIndex(0));
              XNameAccess xNameAccess = (XNameAccess) UnoRuntime.queryInterface(XNameAccess.class, xForm);
              XFormComponent xFormComponent = (XFormComponent) UnoRuntime.queryInterface(XFormComponent.class, xNameAccess.getByName(“textbox1”));
              XTextRange xTextRange = (XTextRange) UnoRuntime.queryInterface(XTextRange.class, xFormComponent);
              String sActual = xTextRange.getString();
              xTextRange.setString(sActual + ” ” + sXY);
        } catch (Exception e4) {
         // getByName 
         e4.printStackTrace();
        }
    }    
  }  

Vàries a coses a comentar:

1. Els mètodes print i print2 estan sobrecarregats. amb signatures (XScriptContext xSc) i  (XScriptContext xSc, MouseEvent meEvent). El motiu és que depenent de l’esdeveniment que invoca la macro es passa un segon argument que pot ser de diferents tipus.

  • Els mètodes amb un únic argument són invocats des del Dialeg d’Executar Macro, o des d’un menú, o des d’una barra d’eines. Als fòrums d’OpenOffice.org es diu que quan s’invoca el mètode de macro des d’una barra d’eines es rep un Short com segon argument. Però en les meves proves amb un OOo3.2.1 sobre Ubuntu 10.04 quan invoco des d’una barra d’eines no es rep aquest segon argument.
  • Els mètodes de la macro que s’executen en resposta a un esdeveniment de mouse, com “en prémer el botó del ratolí”, de la llista d’esdeveniments del botó, reben un MouseEvent.
  • Els mètodes de la macro que s’executen en resposta a un esdeveniment d’acció, com “Executa l’acció” de la llista d’esdeveniments del botó, reben un ActionEvent.
  • Els mètodes de la macro que s’executen en resposta a un esdeveniment de focus, com “En rebre el focus” de la llista d’esdeveniments del botó, reben un FocusEvent.

2. Els mètodes de macro han de ser public static void

3. El primer argument que reben els mètodes de macro sempre és un XScriptContext.
XScripContext és una interfase UNO que permet accedir a la resta d’interfases i de serveis (la funcionalitat) del document.

En particular, la forma d’accedir al Document de Writer a partir del XScripContext és:

XTextDocument xtextdocument = (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class, xSc.getDocument());


3.1 La invocació al UnoRuntime .queryInterface és un mantra que es repeteix constantment a l’hora d’accedir als diferents elements del document. Quan invoco UnoRuntime.queryInterface el resultat és que l’objecte fa un “casting” a la interfase demanada. Adonem-nos que les interfases no són interfases java, sinó interfases UNO. Un objecte UNO “implementa” un conjunt de interfases UNO. La forma d’accedir a les diferents interfases és amb el mecanisme UnoRuntime.queryInterface. 


3.2 L’altra clau de l’èxit en el treball amb macros java és disposar de documentació sobre les interfases i els mètodes que les defineixen. No hi ha més remei que fer servir la documentació: Hi han centenars (milers?) d’interfases. Consulteu http://api.openoffice.org/.

Per exemple,  XScriptContext té els següents mètodes:







getDocument Obtain the document reference on which the script can operate  
getInvocationContext provides access to the context where the script was invoked  
getDesktop Obtain the desktop reference on which the script can operate  
getComponentContext Obtain the component context which the script can use to create other uno components




I XTextDocument, aquests altres:







getText
reformat reformats the contents of the document.  




També he fet servir XText i XTextRange. En realitat XText és una interfase que deriva de la interfase XSimpleText, que a la seva vegada deriva de XTextRange. O sigui: XText té tots els mètodes de XSimpleText i de XTextRange. 


XText






insertTextContent inserts a content, such as a text table, text frame or text field.  
removeTextContent removes the specified content from the text object. 




XSimpleText






createTextCursor
createTextCursorByRange
insertString inserts a string of characters into the text.  
insertControlCharacter inserts a control character (like a paragraph break or a hard space) into the text.

XTextRange






getText
getStart
getEnd
getString
setString the whole string of characters of this piece of text is replaced.




Per tant, les línies 






XText xText = xtextdocument.getText();
XTextRange xTextRange = xText.getEnd();
xTextRange.setString( “MacroProves.print.\n” )




Es poden simplificar així:






xtextdocument.getText().getEnd().setString( “MacroProves.print.\n” )






3.3 la tercera clau de l’èxit, juntament amb el mantra UnoRuntime.queryInterface i la documentació és la comprensió de l’estructura interna dels documents d’OpenOffice.org.


El mètode snippet és molt interessant i desvetlla part d’aquesta estructura interna. Vet aquí el detall del que fa:


Primer de tot, obtinc la interfase del XTextDocument a partir de l’ScriptContext 






XTextDocument xdocument = (XTextDocument) UnoRuntime.queryInterface(XTextDocument.class, xSc.getDocument());

Ara obtinc el Controlador, interfase XController,  del XTextDocument. Els document d’OpenOffice segueixen internament el patró FMC (Frame Model Controller) que és una variant del prou conegut patró MVC (Model View Controller).  
XController xcontroller = (XController) UnoRuntime.queryInterface(XController.class, xdocument.getCurrentController());


A través del controlador obtinc el model, interfase XModel, que representa les dades i els objectes del document.
XModel xmodel = (XModel) UnoRuntime.queryInterface(XModel.class, xcontroller.getModel());


Del model obtinc el proveïdor de la DrawPage, és dir la interfase XDrawPageSupplier. Al tractar-se d’un TextDocument només té una DrawPage. Si es tractes d’un document de Draw, Impress o Calc, aleshores podria tenir vàries DrawPages (en plural), la interfase que caldria obtenir seria la XDrawPagesSupplier (adoneu-vos del plural). 
La DrawPage és un contenidor dels objectes dibuixats al document. En particular conté formularis i els controls del formulari.  
XDrawPageSupplier xDrawPageSupplier = (XDrawPageSupplier) UnoRuntime.queryInterface(XDrawPageSupplier.class, xmodel);


Un cop tinc el proveïdor, obtinc la DrawPage. En aquest cas, el proveïdor em proporciona l’objecte i per això és una crida de mètode directa. No cal invocar la interfase amb UnoRuntime.queryInterface.
XDrawPage xDrawPage = xDrawPageSupplier.getDrawPage();


De la DrawPage obtinc la interfase del proveïdor de Forms (XFormsSupplier):
XFormsSupplier xFormsSupplier = (XFormsSupplier) UnoRuntime.queryInterface(XFormsSupplier.class, xDrawPage);


El proveïdor de forms em dona, directament, “l’enumeració” (XNameContainer) dels forms.    
XNameContainer xNameContainer = xFormsSupplier.getForms();


De l’enumeració dels forms obtinc una interfase per accedir als forms per índex (XIndexAccess)
XIndexAccess xIndexAccess = (XIndexAccess) UnoRuntime.queryInterface(XIndexAccess.class, xNameContainer);


En el cas que ens ocupa només hi ha un form, per tant és el d’índex 0. Obtinc la interfase XForm  per índex.
XForm xForm = (XForm) UnoRuntime.queryInterface(XForm.class, xIndexAccess.getByIndex(0));


Ara buscaré dins del form al meu objecte textbox1 per nom, per tant obtinc una interfase d’acceś per nom. (XNameAccess)
XNameAccess xNameAccess = (XNameAccess) UnoRuntime.queryInterface(XNameAccess.class, xForm);


Finalment obtinc el component textbox1 (XFormComponent) 
XFormComponent xFormComponent = (XFormComponent) UnoRuntime.queryInterface(XFormComponent.class, xNameAccess.getByName(“textbox1”));


I del component, n’obtinc les dades de text (XtextRange)
XTextRange xTextRange = (XTextRange) UnoRuntime.queryInterface(XTextRange.class, xFormComponent);


N’obtinc el text actual i li afegeixo una lletra
String sActual = xTextRange.getString();
xTextRange.setString(sActual + ” ” + sXY)



build.xml


I tot això com és desplega?
Com he dit al principi, posaré la llibreria de macros a /home/elmeuusuari/.openoffice.org/3/user/Scripts/java/

Si observem aquesta carpeta trobarem l’exemple de la macro java HelloWorldJava que és el que ha servit de base per al post d’avui. A la carpeta HelloWorldJava hi trobem el jar, el fitxer font java i el parcel-descriptor.xml.  Es tracta de fer el mateix.

Una forma de fer ho és amb un fitxer d’ant. Jo he fet servir aquest:
  

<project name=”MacroProves” default=”dist” basedir=”.”>
  <description>
  compila i genera el jar de la macro per a l’OpenOffice
  </description>
  
  <!– estableix propietats globals –>
  <property name=”src” location=”src”/>
  <property name=”build” location=”build”/>
  <property name=”config” location=”config”/>
  <property name=”dist”  location=”MacroProves”/>
  <property name=”cp-java-OOo-1″ 
            location=”/opt/openoffice.org/ure/share/java/” />
  <property name=”cp-java-OOo-2″ 
            location=”/opt/openoffice.org/basis3.2/program/classes/” />
  <property name=”OOo-user-macros”
            location=”/home/albert/.openoffice.org/3/user/Scripts/java/” />
  <property name=”OOo-shared-macros” 
            location=”/opt/openoffice.org/basis3.2/share/Scripts/java/” />



  <target name=”init” depends=”clean”>
    <!– time stap –>
    <tstamp/>
    <!– Crea el directory build utilitzat en la compilació –>
    <mkdir dir=”${build}”/>
  </target>


  <target name=”compile” depends=”init”
    description=”compila els fitxers font” >


    <!– Compila les fonts java de ${src} en ${build} –>
    <javac srcdir=”${src}” destdir=”${build}”>
      <classpath>
        <pathelement location=”${cp-java-OOo-1}/jurt.jar”/>
       <pathelement location=”${cp-java-OOo-1}/ridl.jar”/>
       <pathelement location=”${cp-java-OOo-2}/unoil.jar”/>
      </classpath>
   </javac>
  </target>


  <target name=”dist” depends=”compile” description=”genera la  distribució” >
    <!– Crea el directori de distribució –>
    <mkdir dir=”${dist}”/>


    <!– Posa tot el que hi ha a ${build} dins de OOo-java-macro-01.jar –>
    <jar jarfile=”${dist}/OOo-java-macro-01.jar” basedir=”${build}” />
  
   <!– també posa el fitxer font –>
   <copy todir=”${dist}”> 
   <fileset dir=”${src}” />
   </copy>
  
   <!– i el parcel-descriptor.xml –>
   <copy file=”${config}/parcel-descriptor.xml” todir=”${dist}” />
  
   <!– i ho copia tot a la carpeta de macros java de l’usuari–>
   <copy todir=”${OOo-user-macros}/MacroProves”>
   <fileset dir=”${dist}” />
   </copy>
  
   <!– també ho podria copiar a la carpeta de macros java compartides –>
   <!–
   <copy todir=”${OOo-shared-macros}/HelloWorldJava”>
   <fileset src=”${dist}” />
   </copy>
   –>
  </target>


  <target name=”clean” description=”fa neteja” >
    <!– esborra els directoris ${build} and ${dist}–>
    <delete dir=”${build}”/>
    <delete dir=”${dist}”/>
  </target>
</project>




El document de Writer amb el formulari
Finalment, creo un document de text amb Writer. En aquest document hi posaré una textbox que anomenaré textbox1; un botó que anomeno boto1, i enllaço l’esdeveniment “en prémer el botó del mouse” amb MacroProves.print; i un botó que anomeno boto2 i enllaço l’esdeveniment “en prémer el botó del mouse” amb MacroProves.print2;
Cada botó  activa respectivament una macro java.



Albert Baranguer
Barcelona 12/10/2010