Contents
Programación en OpenOffice
Terminología
- Objeto
Es un conjunto de funciones "llamadas métodos" y variables "llamadas propiedades", que representan un cosa o funcionalidad de un sistema. Ejemplo un objeto puede ser un número complejo, sus propiedades son los valores real e imaginario del complejo, y sus métodos son todas las operaciones del complejo: suma, resta, multiplicación, conjugado etc...
- Herencia
La herencia es la propiedad de un objeto a heredar características (métodos, propiedades, etc) de otro objeto.
- Instancia
Es genera o poner una función u objeto en memoria, un objeto puede tener varias funciones incorporadas dentro de él; que por lo general se llaman métodos, y también puede tener variables asociadas a él llamadas propiedades. Se puede tener al mismo tiempo varios objetos de la misma clase instanciados en memoria, y no necesariamente compartir funciones.
- Constructor
El constructor de un objeto, es el método o función que se encarga de instanciar el objeto en el momento de crearse.
- Destructor
El destructor de un objeto, es el método que se encarga de liberar la memoria de las cosas que cree el objeto, dependiendo el lenguaje es más o menos simple de hacer, en C++ como no hay colector de basura toca liberar la memoria de los objetos que se instancian dentro de un objeto.
- Método Virtual
Un método virtual, es un método que pertenece a la clase padre en la herencia, y que por tal se hereda en la clase hija, pero se puede sobreescribir, de tal forma que no se llame al de la clase padre sino el implementado en el momento de hacer una instancia de la hija y llamar luego dicho método.
- UNO
UNO es el conjunto de funciones para programación de OpenOffice, tanto las que vienen predefinidas, como las que se hagan posteriormente.
- Service
Es una abstracción para un Obejeto de UNO. Un servicio no necesariamente representa un objeto subyacente. Un servicio puede tener diferentes interfaces implementadas. Un servicio tienen propiedades. Qué métodos se pueden llamar en un servicio es determinado puramente por qué interfaces del servicio son implementadas.
- Component
Un componente UNO, escrito en cualquier lenguaje de programación soportado, se instala con el comando pkgchk. Un componente puede proveer uno o más Servicios los cuales se registran en el OOo registry. Estos servicios se pueden instanciar lo mismo que un servicio original de OpenOffice OOo. Por ejemplo, puede llamar createUnoService ( "name.DannyBrewer.magic.AlgunServicio" ) para tener la instancia del servici, y luego llamar sus métodos o propiedades. De forma similar Python, Java o Visual Basis, o cualquier otro lenguaje puede usar el servicio así instalado.
Un componente puede proveer uno o más servicios.
Un item del menú de herramientas, o un item de la barra de taréas es un AddOn. Se pueden instalar modificando la el sub node de Configuración para AddOn. Un Component puede adicionar cosas al menú de AddOn usando el programa pkgchk.
Un Componente que adiciona nuevas funciones a la hoja de cálculo. Estas funciones se despliegan en el diálogo Insert --> Function List and Insert --> Function...
Gracias a que un Add-In de Calc es sólo un componente, el cual implementa ciertas interfaces particulares y servicios, el AddIn de Calc es instalado y registrado de la misma forma que cualquier otro componente. Esto es, un AddIn es instalado o removido usando pkgchk. Hacer un AddIn de Calc es lo mismo que hacer cualquier otro servicio. Debe ser un com.sun.star.sheet.AddIn servicio particular, y debe implementar todas sus interfaces, las cuales son muchas. Pero una vez se hace eso, los servicios proveen mágicamente nuevas funciones a la hoja de cálculo.
- Scriptable
Un componente se dice que es scriptable cuando se puede llamar desde OOoBasic, y luego desde cualquier otro lenguaje.
Términos útiles para entender UNO
Los siguientes términos son útiles para entender UNO [7].
API (Application Programming Interface): es un conjúnto de métodos o funciones y atributos disponibles. La API OpenOffice.org es también conocida como IDL Reference.
Binding: Para los programadores de OpenOffice.org, es una traducción de las especificaciones de IDL dentro en un lenguaje de programación dado. Aunque una inclución binding tiene una dependencia del lenguaje, métodos y helpers para manejar los conceptos de UNO.
- Bridge: este concepto está íntimamente relacionado con la noción de binding. Un bridge para UNO es una pieza de código el cual permite un binding a un lenguaje. Desarrollar un bridge implica tener un buen conocimiento de UNO y todos sus mecanismos.
- Component: Un componente es un bloque que provee funcionalidades descritas por sus especificaciones. La mejor práctica es tratar de desarrollar trantando de mantener los componentes lo más independiente posible.
IDL (Interface Description Language): lenguaje común de descripción de especificaciones. Este lenguaje es usado para describir los tipos definidos por un componente. Este lenguaje es convertido en una forma de lenguaje-especificación, dependiendo del binding usado. Los archivos de especificación generalmente tienen la extensión .idl.
Registry: un archivo binario conteniendo todo la especificación compilada de un componente. Hay dos tipos de registros: tipos de registros los cuales contienen definiciones de tipos UNO y servicios registrados uniendo las especificaciones a la implementación. Este archivo generalmente tiene la extensión .rdb.
SDK (Software Development Kit): es un conjunto de herramientas de un programa. OpenOffice.org SDK se descarga de la página de OpenOffice.org, en Debian existe el paquete [8].
- Specification: la descripción de qué puede hacer el código. En UNO, esta contiene la documentación mostrada en la referencia.
UNO (Universal Network Objects): es el modelo de componentes de OpenOffice.org. Se puede ver más detallado en [7].
URE (UNO Runtime Environment): es un subconjunto de OpenOffice.org que habilita el desarrollo de otras aplicaciones basadas en UNO. Puede descargarse de la página de OpenOffice.org, o hay un paquete para Debian.
Compilación de un componente
Para crear un componente UNO, se requiere un archivo IDL y un archivo cpp o f95 "primero lo hacemos con un cpp que es c++". La finalidad de todo es crear un archivo uno.so y un archivo rdb.
Las flechas negras en la gráfica indican cómo pasar de un tipo de archivo a otro. Por ejemplo, use idlc para crear some.urd desde un some.idl. La flecha roja indica que regcomp es usado para colocar el nombre de la librería hecha dentro del archivo rdb en lugar de crear un archivo rdb. Finalmente la flecha azul indica una dependencia. El proceso entero es cómo sigue:
- Crear los archivos idl y el cpp "o f95". La figura los muestra como some.idl y some.cpp.
- Use idlc para compilar el archivo idl y generar así el archivo urd.
- Transforme el archivo urd al archivo rdb usando regmerg. El archivo rdb no está aún completo porque debe contener el nombre del archivo uno.so, el cual es adicionado al final usando regcomp.
- Use cppumaker para crear los archivos de encabezados hpp y hdl, [6]. "En fortran no sé cómo hacerlo aún".
- Use gcc para compilar el archivo cpp.
- El paso del registro final ha sido discutido previamente y no se muestra en la figura. "Supongo que es el de instalarlo usando el pkgchk"
Nota: el Service Manager UNO usa un concepto de factoría para instanciar componenetes en un ambiente determinado. Esta factoría es llamada el administrador de servicios "service manager". El administrador de servicios mantiene una base de datos del registro de componentes. Su aplicación puede instanciar y comunicarse con cualquier componente que reconozca el administrador de servicios, sin importar en qué lenguaje ha sido implementado. La comunicación tiene lugar a través de las llamadas a la interface especificada por la IDL UNO.
Para crear un componente, debe primero crear un archivo idl. El archivo idl describe la interface. El objetivo del ejemplo que vamos a implementar es crear un componente, visible a OpenOffice.org específicamente, desde OOoBasic. El SDK contiene un ejemplo en : “<OpenOffice.org1.1_SDK>/examples/DevelopersGuide/Components/CppComponent” y también una descripción en la guía de Desarrollador. La guía hecha anteriormente sirve para tener una visión global de cómo se hace un componente.
Primer Ejemplo : acceder a un contador desde OOoBasic
Lo primero que haremos es transformar un contador en un componente. Un componente que se puede llamar desde OOoBasic y luego desde cualquier otro lenguaje de programación se llama scriptable. Comenzaremos describiendo el contador.
Un contador simple
Haremos dos archivos que trabajarán juntos counter.cxx y countermain.cxx. Lo primero es hacer un programa que sea el contador, y luego lo separaremos en dos archivos, uno la librería y el otro el que la llama.
Implementación con un archivo
Haremos nuestro contador con un solo archivo (exceptuando el archivo de encabezado):
//Listado de el contador counter.cxx
//Lenguaje de programacion c++
#include <stdio.h>
#include "counter.hxx"
//Constructor de la clase MyCounterImpl
//Pone en 0 el valor de la variable m_nCount e
//imprime el mensaje "< MyCounterImpl ctor called >"
MyCounterImpl::MyCounterImpl()
: m_nCount( 0 )
{ printf( "< MyCounterImpl ctor called >\n" ); }
//Destructor de la clase MyCounterImpl
//Pone en 0 el valor de la variable m_nCount e
//imprime el mensaje "< MyCounterImpl dtor called >"
MyCounterImpl::~MyCounterImpl()
{ printf( "< MyCounterImpl dtor called >\n" ); }
//Obtiene el valor de contador m_nCount
int MyCounterImpl::getCount()
{ return m_nCount; }
//Establece el valor para el contador m_nCount en el valor pasado como parámetro nCount
void MyCounterImpl::setCount( int nCount )
{ m_nCount = nCount; }
//Incrementa en uno el valor del contador m_nCount
int MyCounterImpl::increment()
{ return (++m_nCount); }
//Decrementa en uno el valor del contador m_nCount
int MyCounterImpl::decrement()
{ return (--m_nCount); }
//Función principal
int main(int argc, char **argv)
{
//Se crea el contador Cnt de tipo MyCounterImpl
MyCounterImpl Cnt;
//Pone el contador en 50
Cnt.setCount(50);
//Imprime el valor del contador
printf("-- %d\n",Cnt.getCount());
//Incrementa el contador
Cnt.increment();
printf("-- %d\n",Cnt.getCount());
//Decrementa el contador
Cnt.decrement();
printf("-- %d\n",Cnt.getCount());
//Finaliza
return 0;
}Ahora falta es hacer el archivo de encabezados counter.hxx:
//Listing 2 The header File of the Counter
// C++
// counter.hxx
class MyCounterImpl {
int m_nCount;
public:
MyCounterImpl();
~MyCounterImpl();
int getCount();
void setCount( int nCount );
int increment();
int decrement() ;
};Lo que tenemos que hacer es separar el código en dos archivos.
Implementación con dos archivos
Primero haremos la librería dinámica, que básicamente contiene al objeto MyCounterImpl :
//Libreria Dinamica
//c++
//counter.cxx
#include <stdio.h>
#include "counter.hxx"
MyCounterImpl::MyCounterImpl()
: m_nCount( 0 )
{ printf( "< MyCounterImpl ctor called >\n" ); }
MyCounterImpl::~MyCounterImpl()
{ printf( "< MyCounterImpl dtor called >\n" ); }
int MyCounterImpl::getCount()
{ return m_nCount; }
void MyCounterImpl::setCount( int nCount )
{ m_nCount = nCount; }
int MyCounterImpl::increment()
{ return (++m_nCount); }
int MyCounterImpl::decrement()
{ return (--m_nCount); }Se compila :
gcc -shared -fPIC -o counter.uno.so counter.cxx
Para usar el anterior código se escribe el archivo countermain.cxx:
//c++
//countermain.cxx
#include "counter.hxx"
#include <stdio.h>
int main(int argc, char **argv)
{
MyCounterImpl Cnt;
Cnt.setCount(50);
printf("-- %d\n",Cnt.getCount());
Cnt.increment();
printf("-- %d\n",Cnt.getCount());
Cnt.decrement();
printf("-- %d\n",Cnt.getCount());
return 0;
}Se compila con:
gcc -o test1 countermain.cxx -L. ./counter.uno.so -lstdc++
Al ejecutarlo produce:
$ ./test1 < MyCounterImpl ctor called > -- 50 -- 51 -- 50 < MyCounterImpl dtor called >
Este ejemplo se ha realizado hasta aquí sin Openoffice.org.
Registrando el Contador
Todas las librerías dinámicas no son registrables. ¿Qué pasaría si registramos la anterior librería? Asegúrese de tener instalado el paquete ure, que es el UNO runtime enviropment de Openoffice.
$ apt-get install ure $ sudo /usr/lib/ure/bin/regcomp -register -r counter.uno.rdb -c counter.uno.so counter.uno.so register component 'counter.uno.so' in registry 'counter.uno.rdb' failed! error (CannotRegisterImplementationException): loading component library failed: counter.uno.so
Regcomp no puede hacer su trabajo de registrar la librería, porque hay que seguir unas reglas extrictamente.
- Regla 1
Tiene que contruir el archivo IDL. Este archivo le permite construir luego un archivo urd para adicionarlo en un arcihvo rdb. Esto ya se discutió con anterioridad. Vea la siguiente gráfica:
Al examinar esta gráfica puede ver que se requiere some.idl.
- Regla 2
Su librería dinámica debe contener subrutinas extern C. Estos se nombran así:
// C++
extern "C" void SAL_CALL component_getImplementationEnvironment(
sal_Char const ** ppEnvTypeName, uno_Environment ** ppEnv );
extern "C" sal_Bool SAL_CALL component_writeInfo(
lang::XMultiServiceFactory * xMgr, registry::XRegistryKey * xRegistry );
extern "C" void * SAL_CALL component_getFactory(
sal_Char const * implName, lang::XMultiServiceFactory * xMgr, void * );La primera y la segunda subrutinas son para registrase y la tercera es para permitir las llamadas UNO_QUERY. Una cuarta pero es opcional es getDescription.
- Regla 3 (regla provisional)
El componente debe contener otras interfaces que las descritas en su archivo IDL : com.sun.star.uno.XInterace, com.sun.star.lang.XServiceInfo. El ejemplo del contador provee estas interfaces en el archivo counter.hxx:
//Las interfaces del contador
// C++
class MyCounterImpl
: public XCountable
, public XServiceInfo
{
....Aquí se ha supuesto que XCountable hereda de XInterface. La figura muestra una vista del esquemático de un componente.
La regla tres se va a presentar un poquito más tarde, primero voy a describir unas cosas importantes para entenderla.
El Ejemplo del contador de SDK
Este ejemplo es bastante complicado, se explica en [1], aquí voy a replicarlo con ciertas salvedades que no vienen al caso.
Modificando el Contador para ver el registro en tiempo real
De nuevo empezamos con el ejemplo incluido en el sdk y que está en el paquete de documentación del desarrollo de openoffice.org-dev-doc, primero instale este paquete si quiere ver la documentación y el ejemplo counter, el archivo se encuentra en: /usr/lib/openoffice/basis3.2/sdk/examples/cpp/counter, instale los paquetes:
apt-get install openoffice.org-dev openoffice.org-dev-doc
Voy a modificar este ejemplo para ver en la consola cómo trabaja el registro y particularmente la necesidad de la regla 2 descrita anteriormente. La modificación solamente consiste en adicionar printf como muestro en el listado siguiente. La idea es ayudarnos para ver cómo funciona. No voy a copiar todo el programa, sino simplemente la parte modificada, que está marcada con //** al final de cada la línea o líneas adicionadas:
Nota: No olvide abrirlo como superusuario
//counter.cxx modificado
// C++
... // aquí va el código que hace falta de counter.cxx
//*************************************************************************
OUString SAL_CALL MyCounterImpl::getImplementationName( )
throw(RuntimeException)
{
printf("MyCounterImpl::getImplementationName( ) called \n"); //**
return OUString( RTL_CONSTASCII_USTRINGPARAM(IMPLNAME) );
}
//*************************************************************************
sal_Bool SAL_CALL MyCounterImpl::supportsService( const OUString& ServiceName )
throw(RuntimeException)
{
printf("MyCounterImpl::supportsService called\n"); //**
Sequence< OUString > aSNL = getSupportedServiceNames();
const OUString * pArray = aSNL.getArray();
for( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
if( pArray[i] == ServiceName )
return sal_True;
return sal_False;
}
//*************************************************************************
Sequence<OUString> SAL_CALL MyCounterImpl::getSupportedServiceNames( )
throw(RuntimeException)
{
printf("MyCounterImpl::getSupportedServiceNames( ) called \n"); //**
return getSupportedServiceNames_Static();
}
//*************************************************************************
Sequence<OUString> SAL_CALL MyCounterImpl::getSupportedServiceNames_Static( )
{
OUString aName( RTL_CONSTASCII_USTRINGPARAM(SERVICENAME) );
printf("MyCounterImpl::getSupportedServiceNames_Static( ) called with %s\n"
, RTL_CONSTASCII_USTRINGPARAM(SERVICENAME)); //**
return Sequence< OUString >( &aName, 1 );
}
/**
* Function to create a new component instance; is needed by factory helper implementation.
* @param xMgr service manager to if the components needs other component instances
*/
Reference< XInterface > SAL_CALL MyCounterImpl_create(
const Reference< XMultiServiceFactory > & xMgr )
{
printf("MyCounterImpl_create called\n"); //**
return Reference< XCountable >( new MyCounterImpl( xMgr ) );
}
//#####################################################################################
//#### EXPORTED ####################################################################################
//######################################################################################
/**
* Gives the environment this component belongs to.
*/
extern "C" void SAL_CALL component_getImplementationEnvironment(const sal_Char ** ppEnvTypeName,
uno_Environment ** ppEnv) {
printf("getImplementationEnvironnement return %s\n",CPPU_CURRENT_LANGUAGE_BINDING_NAME); //**
*ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
}
/**
* This function creates an implementation section in the registry and another subkey
*
* for each supported service.
* @param pServiceManager the service manager
* @param pRegistryKey the registry key
*/
extern "C" sal_Bool SAL_CALL component_writeInfo(void * pServiceManager, void * pRegistryKey)
{
sal_Bool result = sal_False;
printf("component_writeInfo called\n"); //**
if (pRegistryKey)
{
try
{
Reference< XRegistryKey > xNewKey(
reinterpret_cast< XRegistryKey * >( pRegistryKey )->createKey(
OUString( RTL_CONSTASCII_USTRINGPARAM("/" IMPLNAME "/UNO/SERVICES") ) ) );
printf("New key : %s\n",RTL_CONSTASCII_USTRINGPARAM("/" IMPLNAME "/UNO/SERVICES")); //**
printf("--component_writeInfo calls MyCounterImpl::getSupportedServiceNames_Static()\n"); //**
const Sequence< OUString > & rSNL =
MyCounterImpl::getSupportedServiceNames_Static();
const OUString * pArray = rSNL.getConstArray();
for ( sal_Int32 nPos = rSNL.getLength(); nPos--; )
{ //**
xNewKey->createKey( pArray[nPos] );
printf("----Sub-Key : %s build\n",OUStringToOString(pArray[nPos],
RTL_TEXTENCODING_ASCII_US)
.pData->buffer); //**
}//**
return sal_True;
}
catch (InvalidRegistryException &)
{
// we should not ignore exceptions
}
}
return result;
}
/**
* This function is called to get service factories for an implementation.
*
* @param pImplName name of implementation
* @param pServiceManager a service manager, need for component creation
* @param pRegistryKey the registry key for this component, need for persistent data
* @return a component factory
*/
/**/
extern "C" void * SAL_CALL component_getFactory(const sal_Char * pImplName,
void * pServiceManager, void * pRegistryKey){
void * pRet = 0;
printf("component_getFactory called\n"); //**
if (rtl_str_compare( pImplName, IMPLNAME ) == 0)
{
Reference< XSingleServiceFactory > xFactory( createSingleFactory(
reinterpret_cast< XMultiServiceFactory * >( pServiceManager ),
OUString( RTL_CONSTASCII_USTRINGPARAM(IMPLNAME) ),
MyCounterImpl_create,
MyCounterImpl::getSupportedServiceNames_Static() ) );
if (xFactory.is())
{
xFactory->acquire();
pRet = xFactory.get();
}
}
return pRet;
}Este archivo debe ser modificado en su directorio original, y bajo el superusuario, para poder compilarlo hay que establecer una serie de enlaces simbólicos ausentes en la instalación de openoffice.org-dev y de ure, para poder usar el make; así:
sudo su /usr/lib/openoffice/basis3.2/sdk/setsdkenv_unix
Al hacer el setsdkenv_unix, las variables se deben establecer como se muestra a continuación, que es básicamente ingresar el directorio de openoffice "esto es /usr/lib/openoffice", el de ure, y dar enter a cada pregunta a continuación, así:
************************************************************************ * * You have to configure your SDK environment first before you can * can use it! The configuration has to be done only once. * ************************************************************************ cd: 1: can't cd to /usr/lib/openoffice/basis3.2/sdk/../../ure Enter the Office installation directory [../lib/openoffice]: /usr/lib/openoffice Enter GNU make (3.79.1 or higher) tools directory [/usr/bin]: Enter zip (2.3 or higher) tool directory [/usr/bin]: C++ compilers where for example a language binding exist: - Solaris, Sun WorkShop 6 update 1 C++ 5.2 2000/09/11 or higher - Linux, GNU C++ compiler, gcc version 4.0.1 or higher - MacOS, GNU C++ compiler, gcc version 4.0.1 or higher Enter the directory of the C++ compiler, the directory where the compiler is located (optional) [/usr/bin]: Enter Java SDK (1.5, recommendation is 1.6 or higher) installation directory (optional) [/usr]: /usr/lib/jvm/java-6-sun-1.6.0.20 Default output directory is in your HOME directory. Enter an existent directory if you prefer a different output directory (optional) [/root]: Automatic deployment of UNO components (YES/NO) [YES]: ************************************************************************ * ... your SDK environment has been prepared. * For each time you want to use this configured SDK environment, you * have to run the "setsdkenv_unix" script file! * Alternatively can you source one of the scripts * "/root/openoffice.org3.2_sdk/haumea/setsdkenv_unix.sh" * "/root/openoffice.org3.2_sdk/haumea/setsdkenv_unix.csh" * to get an environment without starting a new shell. ************************************************************************
Si aparece el error, como en este caso:
cd: 1: can't cd to /usr/lib/openoffice/basis3.2/sdk/../../ure
Hay que corregir el script /usr/lib/openoffice/basis3.2/sdk/configure.pl en la línea 38 en dónde dice:
if ( $main::operatingSystem =~ m/darwin/ )
{
# $main::OO_SDK_URE_HOME = `cd $main::sdkpath/../ure-link && pwd`;
} else {
$main::OO_SDK_URE_HOME = `cd $main::sdkpath/../../ure && pwd`;
}Debe decir
if ( $main::operatingSystem =~ m/darwin/ )
{
# $main::OO_SDK_URE_HOME = `cd $main::sdkpath/../ure-link && pwd`;
} else {
$main::OO_SDK_URE_HOME = `cd $main::sdkpath/../../../ure && pwd`;
}Y ahora corra el script con la opción --force-configure:
# ./setsdkenv_unix --force-configure ************************************************************************ * * You have to configure your SDK environment first before you can * can use it! The configuration has to be done only once. * ************************************************************************ Enter the Office installation directory [../lib/openoffice]: /usr/lib/openoffice Enter GNU make (3.79.1 or higher) tools directory [/usr/bin]: Enter zip (2.3 or higher) tool directory [/usr/bin]: C++ compilers where for example a language binding exist: - Solaris, Sun WorkShop 6 update 1 C++ 5.2 2000/09/11 or higher - Linux, GNU C++ compiler, gcc version 4.0.1 or higher - MacOS, GNU C++ compiler, gcc version 4.0.1 or higher Enter the directory of the C++ compiler, the directory where the compiler is located (optional) [/usr/bin]: Enter Java SDK (1.5, recommendation is 1.6 or higher) installation directory (optional) [/usr]: /usr/lib/jvm/java-6-sun-1.6.0.20 Default output directory is in your HOME directory. Enter an existent directory if you prefer a different output directory (optional) [/root]: Automatic deployment of UNO components (YES/NO) [YES]: ************************************************************************ * ... your SDK environment has been prepared. * For each time you want to use this configured SDK environment, you * have to run the "setsdkenv_unix" script file! * Alternatively can you source one of the scripts * "/root/openoffice.org3.2_sdk/haumea/setsdkenv_unix.sh" * "/root/openoffice.org3.2_sdk/haumea/setsdkenv_unix.csh" * to get an environment without starting a new shell. ************************************************************************ ************************************************************************ * * SDK environment is prepared for Linux * * SDK = /usr/lib/openoffice/basis3.2/sdk * Office = /usr/lib/openoffice * Office Base = /usr/lib/openoffice/basis3.2 * URE = /usr/lib/ure * Make = /usr/bin * Zip = /usr/bin * C++ Compiler = /usr/bin * Java = /usr * SDK Output directory = /root/openoffice.org3.2_sdk * Auto deployment = YES * ************************************************************************
Este comando genera dos archivos en el directorio por defecto del root , este directorio en mi caso que mi host es aumea aumea es el nombre del computador es:
#ls /root/openoffice.org3.2_sdk/haumea setsdkenv_unix.csh setsdkenv_unix.sh
Dichos archivos contienen la configuración para compilar los ejemplos del sdk de openoffice, es importante modificar el .bashrc del root para que al llamar el shell se cargen en memoria, haga lo siguiente, liste las variables de entorno antes de modificar el .profile:
set ... set |grep SDK
No aparecerá ninguna variable de entorno que contenga la subcadena SDK, ahora si modifique el /root/.bashrc y agrege lo siguiente:
include /root/openoffice.org3.2_sdk/$HOSTNAME/setsdkenv_unix.sh
Ahora reinicie un shell de root y le aparecerá un lo siguiente:
haumea:/home/juanfe# sudo su ************************************************************************ * * SDK environment is prepared for Linux * * SDK = /usr/lib/openoffice/basis3.2/sdk * Office = /usr/lib/openoffice * Office Base = /usr/lib/openoffice/basis3.2 * URE = /usr/lib/ure * Make = /usr/bin * Zip = /usr/bin * C++ Compiler = /usr/bin * Java = /usr * SDK Output directory = /root/openoffice.org3.2_sdk * Auto deployment = YES * ************************************************************************ haumea:/home/juanfe# haumea:/home/juanfe# set |grep SDK OO_SDK_CC_55_OR_HIGHER= OO_SDK_CPP_HOME=/usr/bin OO_SDK_HOME=/usr/lib/openoffice/basis3.2/sdk OO_SDK_JAVA_HOME=/usr OO_SDK_MAKE_HOME=/usr/bin OO_SDK_NAME=openoffice.org3.2_sdk OO_SDK_OUT=/root/openoffice.org3.2_sdk OO_SDK_OUTPUT_DIR=/root OO_SDK_URE_BIN_DIR=/usr/lib/ure/bin OO_SDK_URE_HOME=/usr/lib/ure OO_SDK_URE_JAVA_DIR=/usr/lib/ure/share/java OO_SDK_URE_LIB_DIR=/usr/lib/ure/lib OO_SDK_ZIP_HOME=/usr/bin SDK_AUTO_DEPLOYMENT=YES SDK_GXX_INCLUDE_PATH=/usr/include/c++/4.4 haumea:/home/juanfe#
Hay que crear un enlace simbólico para el regmerge así:
ln -s /usr/lib/ure/bin/regmerge /bin/regmerge
Ahora si debe funcionar el make:
cd /usr/lib/openoffice/basis3.2/sdk/examples/cpp/counter make
En caso de tener error verifique que los printf estén bien, si no vaya poniendo uno por uno, y cada vez ejecute lo siguiente:
make clean make
Para ejecutar el ejemplo use make countermain.run:
# make countermain.run cd /root/openoffice.org3.2_sdk/LINUXexample.out/bin && countermain getImplementationEnvironnement return gcc3 component_writeInfo called New key : /com.sun.star.comp.example.cpp.Counter/UNO/SERVICES --component_writeInfo calls MyCounterImpl::getSupportedServiceNames_Static() MyCounterImpl::getSupportedServiceNames_Static( ) called withfoo.Counter ----Sub-Key : foo.Counter build getImplementationEnvironnement return gcc3 MyCounterImpl::getSupportedServiceNames_Static( ) called withfoo.Counter MyCounterImpl_create called < MyCounterImpl ctor called > 42,43,42 < MyCounterImpl dtor called >
Un registro directo produce Al correr """regcomp -registry -r counter.uno.rdb -c counter.uno.so""" lo siguiente:
# regcomp -register -r counter.uno.rdb -c counter.uno.so counter.uno.so getImplementationEnvironnement return gcc3 component_writeInfo called New key : /com.sun.star.comp.example.cpp.Counter/UNO/SERVICES --component_writeInfo calls MyCounterImpl::getSupportedServiceNames_Static() MyCounterImpl::getSupportedServiceNames_Static( ) called withfoo.Counter ----Sub-Key : foo.Counter build register component 'counter.uno.so' in registry 'counter.uno.rdb' succesful!
Los dos registros lo primero que hacen es obtener el Environnement que es gcc3, luego llama a la función component_writeInfo, que es la responsable del registro. En este momento podemos ver nuestro contador en OOoBasic pero no podemos usarlo todavía. Para poder usarlo tenemos que hacer un truco y es usar getchar en el counter main para darle tiempo y lanzar el OOoBasic. El programa de OOoBasic puede ser como sigue:
REM ***** BASIC *****
Sub Main
ocmpt = createUnoService("foo.Counter")
End SubEste programa de OOoBasic ejecuta bien, pero no hace nada, porque el Counter no está instanciado en memoria, pero si registrado, y por eso no saca ningún error. Esto es un poco frustrante, para ver un poco mejor el cuento, es bueno instalar una herramienta que se llama XRay, es una macro muy buena que permite ver varias cosas de la la variable recién creada. La herramienta se baja de [2]. Para instalarla hay que descromprimirla y ejecutar en el mismo directorio en donde se descomprime la hoja de cálculo "odstats.ods", no olvide poner la seguridad de OpenOffice en "Medium" siguiendo la ruta "Tools -> Options... -> OpenOffice.org -> Security -> Macro Security" o en español "Herramientas -> Opciones... -> OpenOffice.org > Seguridad -> Seguridad de Macros".
mkdir xray cd xray wget http://sourceforge.net/projects/ooomacros/files/X-Ray/Version%205.2/XrayTool52_en.zip/download unzip XrayTool52_en.zip rm XrayTool52_en.zip openoffice.org XrayTool52_en.sxw
A lo cual le aparece un mensaje para activar macros, y debe activar la macro, ahora aparece un documento de open doc, con las instrucciones, vale la pena leer el documento que abre porque da unos tips muy interesantes del funcionamiento de UNO. Si es impaciente puede instalar ya el XRay presionando el botón en la página dos: Instal XRay, le preguntará si quiere instalarlo, y hay que contestar "yes", a lo cual aparecerá el mensaje de "Installation completed!". Modifique el programa de OOoBasic así:
REM ***** BASIC *****
Sub Main
ocmpt = createUnoService("foo.Counter")
XRay ocmpt
End SubY luego ejecútela. Debe aparecer que el objeto es Nulo, y claro que lo es porque no hay una instancia de foo.Counter. En el siguiente ejemplo haremos una instancia de un objeto para que permanezca en memoria, este objeto es mucho más útil y lo usaremos lueog para hacer nuestros propios componentes.
Usando un Helper para hacer scriptable un Componente
A partir del ejemplo de Counter, vamos a modificar la clase counter y hacerla scriptable. Si quiere hacer su componente scriptable, tiene que modificar la regla 3 así:
- Regla 3
El componente debe proveer las siguientes interfaces: com.sun.star.uno.XInterface, com.sun.star.lang.XServiceInfo, com.sun.star.uno.XWeak, y com.sun.star.lang.XTypeProvider. La última, es la que caracteriza al componente como scriptable.
La gráfica muestra un esquema de un componente scriptable. Implementando la clase MyClass significa implementar todos los métodos virtuales gracias a la herencia (y también las funciones externas de C). Como mostraremos más tarde de hecho usaremos un helper y esto significa que no tenemos que implementar todas las Interfaces; solamente se necesita com.sun.star.lang.XServiceInfo, las otras son implementadas automáticamente por el helper.
De nuevo miraremos una figura: cómo construir un componente con un helper. Como mostramos en la figura de abajo tendremos que implementar siete métodos por nuestra cuenta.
- Regla 4
El compoenente debe proveer dos interfaces como se mostró en la figura previa, luego usará un ::cppu::WeakImplHelper2 cuando defina su clase.
En cuando al Makefile, ya no nos interesa llamar al componente con el programa countermain.cxx sino usarlo desde OOoBasic que significa que tenemos que registrar nuestra librería dinámica. Hay dos soluciones :
podríamos tratar de nuevo con countermain para registrar el contador y pararlo luego después del registro y luego lanzar un programa de OOoBasic,
o la mejor sería mirar otro ejemplo que haga el registro correctamente. Uno de esto es provisto por el SDK : <OOo_SDK>/examples/DevelopersGuide/CppComponent y usar el Makefile para este compoenente.
Usando de base el Ejemplo CppComponent
El ejemplo CppComponent se encuentra en el directorio /usr/lib/openoffice/basis3.2/sdk/examples/DevelopersGuide/Components /CppComponent, la idea es hacer una copia de este en un directorio paresido para minimizar los cambios que se tengan que hacer en el Makefile, conservando parte del path <OOo_SDK>/examples/DevelopersGuide/Component/MyComponent, así:
sudo su cd /usr/lib/openoffice/basis3.2/sdk/examples/DevelopersGuide/Components mkdir MyComponent cp CppComponent/* MyComponent cd MyComponent
Nota, yo instalé el programa libuno-cli-cppuhelper1.0-cil, pero no tengo la certeza que sirva para algo, aunque instala una serie de paquetes con él, que parecen importates.
sudo apt-get install libuno-cli-cppuhelper1.0-cil
El archivo CppComponent.uno.xml
Si usa Lenny, en el directorio encontrará un archivo CppComponent.uno.xml, ábralo y edítelo "si no lo encuentra es porque tiene otra distro diferente a Lenny, yo pienso que se puede crear sin ningún problema", en este encontrará algo como:
<type>my_module.XSomething</type> <type>my_module.MyService1</type> <type>my_module.MyService2</type>
Debe quedar como:
<type>foo.XCountable</type> <type>foo.Counter</type>
Esto corresponde al servicio counter, y a la interface que se pretente instalar.
El Makefile
En el archivo Makefile reemplace:
COMP_NAME=CppComponent
por
#COMP_NAME=CppComponent COMP_NAME=counter
IDLFILES = some.idl
por
#IDLFILES = some.idl IDLFILES = Counter.idl
En un segundo verá qué debe ir en el Counter.idl, por el momento reemplace:
CXXFILES = service1_impl.cxx \
service2_impl.cxxpor
#CXXFILES = service1_impl.cxx \ # service2_impl.cxx CXXFILES = counter.cxx
TYPELIST=-Tmy_module.XSomething \
-Tmy_modules.MyService1 \
-Tmy_module.MyService2por
TYPELIST=com.sun.star.lang.XSingleServiceFactory \
com.sun.star.lang.XMultiServiceFactory \
com.sun.star.lang.XServiceInfo \
com.sun.star.registry.XRegistryKey \
com.sun.star.uno.XInterface \
com.sun.star.registry.CannotRegisterImplementationException \
foo.XCountable \
foo.Counter
#-Tmy_module.XSomething \
# -Tmy_modules.MyService1 \
# -Tmy_module.MyService2$(CPPUMAKER) -Gc -BUCR -O$(SAMPLE_INC_OUT) $(TYPESLIST) $(COMP_RDB) -X$(URE_TYPES) #-X$(OFFICE_TYPES)
por
$(CPPUMAKER) -Gc -BUCR -O$(SAMPLE_INC_OUT) $(TYPESLIST) $(COMP_RDB) -X$(URE_TYPES) -X$(OFFICE_TYPES)
Notas: mirar la diferencia entre el original y el nuevo Makefile, pues hay varios cambios.
Archivo IDL
Cree el archivo Counter.idl con:
#include <com/sun/star/uno/XInterface.idl>
module foo
{
/**
* Interface to count things.
*/
interface XCountable : com::sun::star::uno::XInterface
{
long getCount();
void setCount( [in] long nCount );
long increment();
long decrement();
};
service Counter
{
// exported interface:
interface XCountable;
};
};La explicación de este archivo la puede encontrar en [1].
Archivo Counter en C++
Ahora si puede crear el archivo counter.cxx así:
//Listing 9 The complete Counter Program
// C++
// counter.cxx
/***************************************************************************************************
***************************************************************************************************
*
* implementación del servicio: foo.Counter
* interfaces exportadas: foo.XCounter
*
* Ejemplo simple de la implementación de un componente counter
*
***************************************************************************************************
**************************************************************************************************/
#ifndef _RTL_USTRING_HXX_
#include <rtl/ustring.hxx>
#endif
#ifndef _CPPUHELPER_QUERYINTERFACE_HXX_
#include <cppuhelper/queryinterface.hxx> // helper para la impl de queryInterface()
#endif
#ifndef _CPPUHELPER_FACTORY_HXX_
#include <cppuhelper/factory.hxx> // helper para componente factory
#endif
// Nuevo
#include <cppuhelper/implbase2.hxx> // "2" implementando tres interfaces
#include <cppuhelper/implementationentry.hxx>
// interfaces de c++ generadas
#ifndef _COM_SUN_STAR_LANG_XSINGLESERVICEFACTORY_HPP_
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XMULTISERVICEFACTORY_HPP_
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XSERVICEINFO_HPP_
#include <com/sun/star/lang/XServiceInfo.hpp>
#endif
#ifndef _COM_SUN_STAR_REGISTRY_XREGISTRYKEY_HPP_
#include <com/sun/star/registry/XRegistryKey.hpp>
#endif
#ifndef _FOO_XCOUNTABLE_HPP_
#include <foo/XCountable.hpp>
#endif
#define SERVICENAME "foo.Counter"
#define IMPLNAME "com.sun.star.comp.example.cpp.Counter"
using namespace ::rtl;
using namespace ::osl;
using namespace ::cppu;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::registry;
using namespace ::foo;
//==================================================================================================
namespace my_sc_impl
{
static Sequence< OUString > getSupportedServiceNames_MyCounterImpl()
{
static Sequence < OUString > *pNames = 0;
if( ! pNames )
{
// MutexGuard guard( Mutex::getGlobalMutex() );
if( !pNames )
{
static Sequence< OUString > seqNames(1);
seqNames.getArray()[0] = OUString(RTL_CONSTASCII_USTRINGPARAM(SERVICENAME));
pNames = &seqNames;
}
}
return *pNames;
}
static OUString getImplementationName_MyCounterImpl()
{
static OUString *pImplName = 0;
if( ! pImplName )
{
// MutexGuard guard( Mutex::getGlobalMutex() );
if( ! pImplName )
{
static OUString implName( RTL_CONSTASCII_USTRINGPARAM(IMPLNAME) );
pImplName = &implName;
}
}
return *pImplName;
}
// Nuevo
class MyCounterImpl : public ::cppu::WeakImplHelper2<
XCountable, XServiceInfo >
{
// para obtener otros servicio si es necesario
Reference< XMultiServiceFactory > m_xServiceManager;
sal_Int32 m_nCount;
public:
// implementación de XServiceInfo
virtual OUString SAL_CALL getImplementationName( ) throw(RuntimeException);
virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) throw(RuntimeException);
virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) throw(RuntimeException);
static Sequence< OUString > SAL_CALL getSupportedServiceNames_Static( );
// implementación de XCountable
virtual sal_Int32 SAL_CALL getCount() throw (RuntimeException)
{ return m_nCount; }
virtual void SAL_CALL setCount( sal_Int32 nCount ) throw (RuntimeException)
{ m_nCount = nCount; }
virtual sal_Int32 SAL_CALL increment() throw (RuntimeException)
{ return (++m_nCount); }
virtual sal_Int32 SAL_CALL decrement() throw (RuntimeException)
{ return (--m_nCount); }
};
//*************************************************************************
OUString SAL_CALL MyCounterImpl::getImplementationName( )
throw(RuntimeException)
{
return OUString( RTL_CONSTASCII_USTRINGPARAM(IMPLNAME) );
}
//*************************************************************************
sal_Bool SAL_CALL MyCounterImpl::supportsService( const OUString& ServiceName )
throw(RuntimeException)
{
Sequence< OUString > aSNL = getSupportedServiceNames();
const OUString * pArray = aSNL.getArray();
for( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
if( pArray[i] == ServiceName )
return sal_True;
return sal_False;
}
//*************************************************************************
Sequence<OUString> SAL_CALL MyCounterImpl::getSupportedServiceNames( )
throw(RuntimeException)
{
return getSupportedServiceNames_Static();
}
//*************************************************************************
Sequence<OUString> SAL_CALL MyCounterImpl::getSupportedServiceNames_Static( )
{
OUString aName( RTL_CONSTASCII_USTRINGPARAM(SERVICENAME) );
return Sequence< OUString >( &aName, 1 );
}
/**
* Funcion para crear una nueva instancia de un componente; es necesario para la implementacion
* de la factory helper.
* @param xMgr administrador del servicio por si los componentes necesitan otras instancias de
* componentes
*/
//***********Nuevo
Reference< XInterface > SAL_CALL MyCounterImpl_create(
Reference< XComponentContext > const & xContext )
SAL_THROW( () )
{
return static_cast< XTypeProvider * >( new MyCounterImpl() );
}
}
//##################################################################################################
//#### EXPORTADO ###################################################################################
//##################################################################################################
/* Nuevo ****/
namespace my_sc_impl
{
static struct ::cppu::ImplementationEntry s_component_entries [] =
{
{
MyCounterImpl_create, getImplementationName_MyCounterImpl,
getSupportedServiceNames_MyCounterImpl, ::cppu::createSingleComponentFactory,
0, 0
},
{ 0, 0, 0, 0, 0, 0 }
};
}
extern "C"
{
void SAL_CALL component_getImplementationEnvironment(
sal_Char const ** ppEnvTypeName, uno_Environment ** ppEnv )
{
*ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
}
sal_Bool SAL_CALL component_writeInfo(
XMultiServiceFactory * xMgr, XRegistryKey * xRegistry )
{
return ::cppu::component_writeInfoHelper(
xMgr, xRegistry, ::my_sc_impl::s_component_entries );
}
void * SAL_CALL component_getFactory(
sal_Char const * implName, XMultiServiceFactory * xMgr,
XRegistryKey * xRegistry )
{
return ::cppu::component_getFactoryHelper(
implName, xMgr, xRegistry, ::my_sc_impl::s_component_entries );
}
}
El archivo TestCppComponent.cxx
Debido a que se va a usar otro programa diferente, este archivo queda obsoleto, pero si se va a usar para evitar que aparezca algún error en el make, para eso hay que comentar el main() del TestCppComponent.cxx, desde la línea 55 hasta el final. De esta forma:
...
using namespace com::sun::star::frame;
int main(){}
/*SAL_IMPLEMENT_MAIN()
{
try
...
return 0;
}*/Ahora para compilarlo puede usar el make simplemente, no se puede hacer con: make TestCppComponent.run pues aparece un error que no viene al caso, mejor usar, bajo superusuario lo siguiente:
make SimpleComponent.odt.load
Si esto no funciona haga el comando manual, puede mirar el archivo Makefile, y para entenderlo es bueno mirar el manual de make, que es bastante útil y corto: [3], ya si quiere un master en make [4].
Esto ejecutará el archivo odt, con un botón, precionelo, y ya tiene el comando corriendo. Antes que nada báje los niveles de seguridad con: Herramientas->Opciones->OpenOffice.org->Seguridad->Seguridad de macros->Bajo, salvelo, salga y vuelve y entre al archivo SimpleComponent.odt. Modifique la macro: Herramientas->Macros->Organizar Macros->OpenOffice.org Basic->SimpleComponent.odt->Standard->SimpleComponent->demonstrateSimpleComponent->Editar, dice algo como:
REM ***** BASIC *****
Sub demonstrateSimpleComponent
Dim oSimpleComponent
oSimpleComponent = CreateUnoService( "my_module.MyService1" )
msgbox oSimpleComponent.methodOne( "Component succesfully instantiated and running!" )
End SubCambielo para ejecutar el componente recién hecho:
REM ***** BASIC *****
Sub demonstrateSimpleComponent
ocmpt = createUnoService("foo.Counter")
XRay ocmpt
End SubEjecute desde la consola el xray, e instaleló presionando el botón, recuerde que debe ingresar con el mismo usuario de root.
openoffice.org XrayTool52_en.sxw
Para tener corriendo Xray en forma permanente, adicionela en las librerías standard de open office la siguiente función:
Sub LoadingLibraries
BasicLibraries.LoadLibrary("XrayTool")
End SubY en el menú OpenOffice.org Basic, Herramientas->Personalizar->Eventos->Comenzar aplicación doble click al lado derecho y selecciona la función LoadLibraries para que se ejecute al comenzar cada aplicación, allí ya puede probar el XRay para los componentes creados.
[1] http://wiki.services.openoffice.org/wiki/Counter_Example
[2] http://sourceforge.net/projects/ooomacros/files/X-Ray/Version%205.2/XrayTool52_en.zip/download
[3] http://www.student.cs.uwaterloo.ca/~isg/res/unix/make/index
[4] http://www.gnu.org/software/make/manual/make.html
[5] http://wiki.services.openoffice.org/wiki/Documentation/DevGuide
[6] http://udk.openoffice.org/common/man/tools.html#cppumaker
[7] http://wiki.services.openoffice.org/wiki/Uno/Article/Understanding_Uno