Uno/Cpp/Tutorials/component tutorial

From Apache OpenOffice Wiki
< Uno‎ | Cpp‎ | Tutorials
Revision as of 15:13, 26 February 2008 by SergeMoutou (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Introduction

This tutorial is a general example of how to write a UNO component and how to use it in applications, for example StarOffice or OpenOffice.org. This example is written in C++, however differences for implementing the component in Java will be mentioned.

The component is a basic counter, whose value can be set, read, incremented and decremented.

Interfaces

The first step, in writing any component (in almost any language environment) is to specify one or more interfaces that the component must implement.

Interfaces are the key to hiding implementation details in any modern development environment. They are contracts between a client that wants to use a components' functionality and the component (serving this functionality). In UNO terminology, UNO components are called services.

Interfaces separate the specific implementation (there can more than one for a service) from the usage. The client using the component does not have any insight how the component is implemented.

Interfaces are specified using an Interface definition language (IDL). UNO uses UNO-IDL as the interface definition language. An interface for a Counter might look like this:

file counter.idl:

#include <com/sun/star/uno/XInterface.idl>
module foo
{
   /**
     * Interface to count things.
     */
    [ uik(3806AFF0-75A0-11d3-87B300A0-24494732), ident("XCountable", 1.0) ]
    interface XCountable : com::sun::star::uno::XInterface
    {
        long getCount();
        void setCount( [in] long nCount );
        long increment();
        long decrement();
    };
};

Any interface that is specified is derived from XInterface, the basic interface in UNO. The XInterface has methods for lifetime control of the interface (acquire() and release()) and the ability to query for further interfaces of the UNO object (queryInterface()). Once you have an interface of an object, you can query for any others the object provides. If there are no acquired interfaces (references) left on an UNO object, the object might disappear. Interfaces (and services) can be grouped in modules to avoid pollution of the global namespace.

Services

Any UNO component (service) exports one or more interfaces that the clients are using. All services are specified using the service directive in an IDL file:

module foo 
 
{
    service Counter
    {
        // exported interfaces:
        interface XCountable;
    };
};

The service declaration introduces a service called foo.Counter which supports the XCountable interface.

There are some more IDL features, e.g. attributes, structs, enums, that are omitted at this time. All IDL declarations are put into a typelibrary file (rdb file). This speeds up the back end generation of the language specific files.

To implement the interfaces, the appropriate interface code for the implementation language has to be generated from the rdb file.

In C++ a tool called cppumaker generates pure abstract classes that the component has to implement. This is a common way to describe interfaces in C++. The Java language directly supports interfaces as a language feature (javamaker will generate Java interfaces).

Implementation

A component that is implemented in C++ is normally packaged in a shared library. This shared lib exports two symbols, which are explained further below. Java implementations are normally packaged in a JAR file. In this case the manifest file identifies a class implementing two methods with similar semantics.

A simple implementation of the counting UNO service foo.Counter might be:

//file foo.cxx:
 
#include <rtl/ustring.hxx>
#include <cppuhelper/implbase1.hxx>
#include <cppuhelper/factory.hxx>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/registry/XRegistryKey.hpp>
#include <foo/XCountable.hpp>
 
using namespace rtl;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::registry;
using namespace foo;
 
//==================================================================================================
class MyCounterImpl : public cppu::WeakImplHelper1< XCountable >
{
	// to obtain other services if needed
	Reference< XMultiServiceFactory > _xServiceManager;
 
	sal_Int32 _nCount;
 
	Reference< XText > _xText;
	void dump( sal_Int32 nValue ) const;
 
public:
	MyCounterImpl( const Reference< XMultiServiceFactory > & xServiceManager );
	virtual ~MyCounterImpl();
 
	// XCountable implementation
	virtual sal_Int32 SAL_CALL getCount() throw (RuntimeException);
	virtual void SAL_CALL setCount( sal_Int32 nCount ) throw (RuntimeException);
	virtual sal_Int32 SAL_CALL increment() throw (RuntimeException);
	virtual sal_Int32 SAL_CALL decrement() throw (RuntimeException);
};
//__________________________________________________________________________________________________
MyCounterImpl::MyCounterImpl( const Reference< XMultiServiceFactory > & xServiceManager )
	: _xServiceManager( xServiceManager )
{
	cerr << "< MyCounterImpl ctor called >" << endl;
 
	if (_xServiceManager.is())
	{
		Reference< XComponentLoader > xLoader( _xServiceManager->createInstance(
			L"com.sun.star.frame.Desktop" ), UNO_QUERY );
		if (xLoader.is())
		{
			Reference< XTextDocument > xDoc( xLoader->loadComponentFromURL(
				L"private:scalc/factory", L"_blank", 0, Sequence< PropertyValue >() ), UNO_QUERY );
			if (xDoc.is())
				_xText = xTextDoc->getText();
		}
	}
}
//__________________________________________________________________________________________________
MyCounterImpl::~MyCounterImpl()
{
	cerr << "< MyCounterImpl dtor called >" << endl;
}
//__________________________________________________________________________________________________
void MyCounterImpl::dump( sal_Int32 nValue ) const
{
	if (_xText.is())
		_xText->setValue( nValue );
	else
		cerr << endl << nValue;
}
 
// XCountable implementation
//__________________________________________________________________________________________________
sal_Int32 MyCounterImpl::getCount() throw (RuntimeException)
{
	return _nCount;
}
//__________________________________________________________________________________________________
void MyCounterImpl::setCount( sal_Int32 nCount ) throw (RuntimeException)
{
	_nCount = nCount;
	dump( _nCount );
}
//__________________________________________________________________________________________________
sal_Int32 MyCounterImpl::increment() throw (RuntimeException)
{
	++_nCount;
	dump( _nCount );
	return _nCount;
}
//__________________________________________________________________________________________________
sal_Int32 MyCounterImpl::decrement() throw (RuntimeException)
{
	--_nCount;
	dump( _nCount );
	return _nCount;
}
 
/**
 * 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 > MyCounterImpl_create(
	const Reference< XMultiServiceFactory > & xMgr )
{
	return Reference< XInterface >( new MyCounterImpl( xMgr ) );
}
Personal tools