[ Previous document | Content Table | Next document ]

2    First Steps

This chapter describes the setting up of a Java UNO development environment to achieve the solutions you need. The OpenOffice.org documents contain detailed descriptions on how to use them in your own programs.

2.1    Programming with UNO

UNO (pronounced ['ju:nou]) stands for Universal Network Objects and is the base component technology for OpenOffice.org. You can utilize and write components that interact across languages, component technologies, computer platforms, and networks. Currently, UNO is available on Linux, Solaris, and Windows for Java, C++ and OpenOffice.org Basic. As well, UNO is available through the component technology Microsoft COM for many other languages.

UNO is used to access OpenOffice.org, using its Application Programming Interface (API). The OpenOffice.org API is the comprehensive specification that describes the programmable features of OpenOffice.org.

2.2    Fields of Application for UNO

You can connect to a local or remote instance of OpenOffice.org from C++, Java and COM/DCOM. C++ and Java Desktop applications, Java servlets, Java Server Pages, JScript and VBScript, and languages, such as Delphi, Visual Basic and many others can use OpenOffice.org to work with Office documents.

It is possible to develop UNO Components in C++ or Java that can be instantiated by the office process and add new capabilities to OpenOffice.org. For example, you can write Chart Add-ins or Calc Add-ins, linguistic extensions, new file filters, database drivers. You can even write complete applications, such as a groupware client.

UNO components, as Java Beans, integrate with Java IDEs (Integrated Development Environment) to give easy access to OpenOffice.org. Currently, a set of such components is under development that will allow editing OpenOffice.org documents in Java Frames.

OpenOffice.org Basic cooperates with UNO, so that UNO programs can be directly written in OpenOffice.org. With this method, you supply your own office solutions and wizards based on an event-driven dialog environment.

The OpenOffice.org database engine and the data aware forms open another wide area of opportunities for database driven solutions.

The Sun One Webtop, Sun Microsystem's server based Office suite, uses UNO and the OpenOffice.org API.

2.3    Getting Started

A number of files and installation sets are required before beginning with the OpenOffice.org API.

2.3.1    Required Files

These files are required for any of the languages you use.

OpenOffice.org Installation

Install a copy of OpenOffice.org. The current versions are:

Note: Earlier releases are not covered in this book.

API Reference

The OpenOffice.org API reference is part of the Software Development Kit and provides detailed information about OpenOffice.org objects. The latest version can be downloaded from the documents section at api.openoffice.org. Alternatively, you can also use CVS to check out the module oo/api/common/www/ref from OpenOffice.org's CVS server. Use :pserver:anoncvs@anoncvs.openoffice.org:/cvs as CVSROOT, the password is anoncvs. The starting folder is the folder named ref.

2.3.2    Installation Sets

The following installation sets are necessary to develop OpenOffice.org API applications with Java. This chapter describes how to set up a Java IDE for the OpenOffice.org API.

JDK 1.3.1

Java applications for the current versions of OpenOffice.org require the Java Development Kit 1.3.1. Download and install JDK 1.3.1 from java.sun.com.

Java IDE

Download an Integrated Development Environment (IDE), such as NetBeans from www.netbeans.org or Forte for Java from Sun Microsystems. Other IDEs can be used, but NetBeans/Forte offers the best integration. The integration of OpenOffice.org with IDEs, such as NetBeans is an ongoing effort. Check the files section of api.openoffice.org for the latest information about NetBeans and other IDEs.

OpenOffice.org Software Development Kit (SDK)

To write programs for the OpenOffice.org API, you need this installation set. Using C++, note that the OpenOffice.org API only works for selected compilers.

To use the OpenOffice.org API , you need the StarOffice SDK or you can download and install the OpenOffice.org Software Development Kit from www.openoffice.org. For detailed instructions for selected compilers and how to set up your development environment, refer to the SDK installation guide.

2.3.3    Configuration

Enable Java in OpenOffice.org

OpenOffice.org uses a Java Virtual Machine to instantiate components written in Java. You can easily tell the office which JVM to use: launch the jvmsetup executable from the programs folder under the OpenOffice.org, select an installed JRE or JDK and click OK. Close the OpenOffice.org including the Quickstarter in the taskbar and restart OpenOffice.org. Furthermore, open the Tools - Options dialog in OpenOffice.org, select the section OpenOffice.org Security and make sure that the Java enable option is checked.

Use Java UNO class files

Include the jar files from the programs/classes folder under the OpenOffice.org in the CLASSPATH environment variable or use the java -classpath option accordingly. In a Java IDE, make these jars known to the IDE by mounting the jars, and configuring a library.

Use the following steps to create a new project and mount the Java UNO jars in NetBeans:

Make the office listen

Java uses a TCP/IP socket to talk to the office. To use with Java, the OpenOffice.org must be told to listen for TCP/IP connections, by using a special connection url parameter. There are two ways to achieve this, you can make the office listen always or just once.

To always connect to the office, open the file <OfficePath>/share/config/registry/instance/org/openoffice/Setup.xml in an editor, and look for the element

<ooSetupConnectionURL cfg:type="string"/>

 and extend it with the following code:

<ooSetupConnectionURL cfg:type="string">

socket,port=8100;urp;

</ooSetupConnectionURL>

This setting configures OpenOffice.org to provide a socket on port 8100, where it will serve connections through the UNO remote protocol (urp). If port 8100 is already in use on your machine, it may be necessary to adjust the port number. Block port 8100 for connections from outside your network in your firewall. If you have a OpenOffice.org network installation, this setting will affect all users. To make a single installation listen for connections create a file <OfficePath>/user/config/registry/instance/org/openoffice/Setup.xml with the same structure as the file above and adding the element ooSetupConnectionURL.

An alternative is to launch the office in listening mode using command-line options. To do this, close OpenOffice.org, including the Quickstarter and the Help window, and restart it from the command-line:

<OfficePath>/program/soffice “-accept=socket,port=8100;urp;”

Using the command-line option, the office will only listen during the current session. If you use this method, always be aware how you started the office. The above command-line only works if the soffice executable was launched through it. It does not make a running office listen and it does not affect instances of the office that are started after a listening instance of the office has been closed. Note, that in Windows, the Quickstarter in the system tray at the bottom right of your desktop keeps the OpenOffice.org running.

Choose the procedure that suits your requirements and launch OpenOffice.org in listening mode now. Check if it is listening by calling netstat -a on the command-line. An output similar to the following shows that the office is listening:

TCP    <Hostname>:8100   <Fully qualified hostname>: 0 Listening

If the office is not listening, it was not restarted with the connection url parameter. Close all OpenOffice.org windows, the Help window, and the Quickstarter icon on the taskbar. Check the Setup.xml file or your command-line for typing errors and try again.

Add the API Reference to your IDE

We recommend to add the API reference to your Java IDE to get online help for the OpenOffice.org API. In NetBeans, follow these steps:

2.3.4    First Connection

The following demonstrates how to write a small program that connects to the office. Start the Java IDE or source editor, and enter the following source code for the FirstConnection class. FirstConnection tries to connect to the office and tells you if it was able to establish the connection or not.

To create and run the class in the NetBeans IDE, use the following steps:

import com.sun.star.bridge.XUnoUrlResolver;

import com.sun.star.uno.UnoRuntime;

import com.sun.star.uno.XComponentContext;

import com.sun.star.lang.XMultiComponentFactory;

import com.sun.star.beans.XPropertySet;

public class FirstConnection extends java.lang.Object {

    private XComponentContext xRemoteContext = null;

    private XMultiComponentFactory xRemoteServiceManager = null;

   

    public static void main(String[] args) {

        FirstConnection firstConnection1 = new FirstConnection();

        try {

            firstConnection1.useConnection();

        }

        catch (java.lang.Exception e){

            e.printStackTrace();

        }

        finally {

            System.exit(0);

        }

    }

   

    protected void useConnection() throws java.lang.Exception {

        try {

            xRemoteServiceManager = this.getRemoteServiceManager(

                    "uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager");

            String available = (null != xRemoteServiceManager ? "available" : "not available");

            System.out.println( "remote ServiceManager is " + available );

            // do something with the service manager...

        }

        catch( com.sun.star.connection.NoConnectException e )

        {

                System.err.println( “No process listening on the resource” );

                e.printStackTrace();

                throw e;

        }

        catch( com.sun.star.lang.DisposedException e ) { //works from Patch 1

            xRemoteContext = null;

            throw e;

        }          

    }

   

    protected XMultiComponentFactory getRemoteServiceManager(String unoUrl) throws java.lang.Exception {

        if (xRemoteContext == null) {

            // First step: create local component context, get local servicemanager and

            // ask it to create a UnoUrlResolver object with an XUnoUrlResolver interface

            XComponentContext xLocalContext =

                com.sun.star.comp.helper.Bootstrap.createInitialComponentContext(null);

            XMultiComponentFactory xLocalServiceManager = xLocalContext.getServiceManager();

            Object urlResolver  = xLocalServiceManager.createInstanceWithContext(

                "com.sun.star.bridge.UnoUrlResolver", xLocalContext );

            // query XUnoUrlResolver interface from urlResolver object

            XUnoUrlResolver xUnoUrlResolver = (XUnoUrlResolver) UnoRuntime.queryInterface(

                XUnoUrlResolver.class, urlResolver );

            // Second step: use xUrlResolver interface to import the remote StarOffice.ServiceManager,

            // retrieve its property DefaultContext and get the remote servicemanager

            Object initialObject = xUnoUrlResolver.resolve( unoUrl );

            XPropertySet xPropertySet = (XPropertySet)UnoRuntime.queryInterface(

                XPropertySet.class, initialObject);

            Object context = xPropertySet.getPropertyValue("DefaultContext");            

            xRemoteContext = (XComponentContext)UnoRuntime.queryInterface(

                XComponentContext.class, context);

        }

        return xRemoteContext.getServiceManager();

    }

}

For an example that connects to the office with C++, see chapter 3.4.2 Professional UNO - UNO Language Bindings - UNO C++ Binding.

In this Java example, OpenOffice.org acts as server, while FirstConnection is a simple client for the OpenOffice.org server process.

Consider the getRemoteServiceManager() method, which retrieves a service manager (com.sun.star.lang.ServiceManager) from the OpenOffice.org process. With UNO, the creation of objects is done by service managers which exist in component contexts. Our client needs its own component context with a service manager that can create UNO objects in the client process, and it needs the component context of the server side with another service manager that can create objects in the server process.

Therefore, two steps are necessary to connect to the office: First, use the class com.sun.star.comp.helper.Bootstrap to get a local UNO component context containing a small service manager that knows how to create the necessary services to talk to other component contexts. One such service is a com.sun.star.bridge.UnoUrlResolver, so we ask our service manager to create the service. Next, use the UnoUrlResolver object to get the component context together with the service manager from the server-side. Do not worry about the queryInterface() calls taking place. There is now a reference to the remote service manager in our client.


Illustration 1.1: UnoUrlResolver gets Remote ServiceManager

For a thorough description of the objects used here, refer to the chapter 3.3.1 Professional UNO - UNO Concepts - UNO Interprocess Connections. A remote connection can fail under certain conditions:

When the bridge has become unavailable and access is tried, it throws a com.sun.star.lang.DisposedException. Whenever you access remote references in your program, catch com.sun.star.lang.DisposedExceptions in such a way that you set your remote references to null and inform the user accordingly. If your client is designed to run for a longer period of time, be prepared to get new remote references when you find that they are currently null.

The other possibility is to register a listener at the remote bridge that underlies the UnoUrlResolver. OpenOffice.org allows you to listen for a "bridge disposed" event at the remote bridge so that you can release invalid references, inform the user what has happened or throw a suitable exception if need be. To do this, you must manually create a bridge and register a listener at the bridge. A connection created by UnoUrlResolver simply throws a java.lang.RuntimeException whenever you try to use a reference that no longer works because of a connection failure. The chapter 3.3.1 Professional UNO - UNO Concepts - UNO Interprocess Connections shows how to write such a connection aware client.

2.4    How to get Objects in OpenOffice.org

An object is an instance of an implemented class that has methods you can call. Objects are required to do something with OpenOffice.org.

New objects

In general, new objects or objects which are necessary for a first access are created by object factories, which are called service managers in OpenOffice.org. In the FirstConnection example, the local service manager created a UnoUrlResolver object:

Object urlResolver  = xLocalServiceManager.createInstanceWithContext(

                "com.sun.star.bridge.UnoUrlResolver", xLocalContext );

The remote service manager works exactly like the local service manager. The remote service manager creates the remote Desktop object, which handles application windows and loaded documents in OpenOffice.org:

Object desktop = xRemoteServiceManager.createInstanceWithContext(
               "com.sun.star.frame.Desktop", xRemoteContext);

Document objects

Document objects represent the files that are opened with OpenOffice.org. They are created by the Desktop object, which has a loadComponentFromURL() method for this purpose.

Objects that are provided by other objects

Objects can hand out other objects. There are two cases:

Sets of objects

Objects can be elements in a set of similar objects. In sets, to access an object you need to know how to get a particular element from the set. The OpenOffice.org API allows four ways to provide an element in a set. The first three ways are objects with element access methods that allow access by name, index, or enumeration. The fourth way is a sequence of elements which has no access methods but can be used as an array directly. How these sets of elements are used will be discussed later.

The designer of an object decides which of those opportunities to offer, based on special conditions of the object, such as how it performs remotely or which access methods best work with implementation.

2.5    Working with Objects

Working with OpenOffice.org API objects involves the following:

2.5.1    Services

In the OpenOffice.org API, objects are called services. However, objects and services are not the same thing. Services are abstract specifications for objects. All UNO objects have to follow a service specification and have to support at least one service. An UNO object is called a service, because it fulfills a service specification.

A service describes an object by combining interfaces and properties into an abstract object specification. Do not get confused by the meanings the word service has in other contexts. In UNO, a service is precisely this: a composition of interfaces and properties.

An interface is a set of methods that together define one single aspect of a service. For instance, the com.sun.star.view.XPrintable interface prescribes the methods print(), getPrinter() and setPrinter().

A property is a feature of a service which is not considered an integral or structural part of the service and therefore is handled through generic getPropertyValue()/setPropertyValue() methods instead of specialized methods, such as getPrinter(). An object containing properties only has to support the com.sun.star.beans.XPropertySet interface to be prepared to handle all kinds of properties. Typical examples are properties for character or paragraph formatting. With properties, you can set multiple features of an object through a single call to setPropertyValues(), which greatly improves the remote performance. For instance, paragraphs support the setPropertyValues() method through their com.sun.star.beans.XMultiPropertySet interface.

The concept of services was created for the following reasons:

From a user's perspective, a service can be ordered from a factory by its name and the user receives an object that fulfills the service specification, no matter how it is implemented internally.


Illustration 1.2: Text Document

Let us consider the service com.sun.star.text.TextDocument in UML notation. The UML chart shown in Illustration depicts the mandatory interfaces of a TextDocument. These interfaces express the basic aspects of a text document in OpenOffice.org. It contains text, that is searchable and refreshable, can use number formats, and is printable and storable. The UML chart shows how this is specified in the API.

On the left of Illustration , the services com.sun.star.text.TextDocument and com.sun.star.document.OfficeDocument are shown. Every TextDocument must include these services by definition. The properties compartment of each service shows which properties a TextDocument service may have.

On the right of Illustration , the interfaces the services must export are shown. Their method compartments list the methods contained in the various interfaces. In the OpenOffice.org API, all interface names have to start with an X to be distinguishable from other object names.

Every TextDocument must support four interfaces: XTextDocument, XSearchable, XRefreshable, and XNumberFormatsSupplier. In addition, because a TextDocument is always an OfficeDocument, it must also export the interfaces XPrintable and XStorable. The methods contained in these interfaces cover these aspects.

A TextDocument has optional interfaces, among them the XPropertySet interface which must be supported if properties are present at all. The interfaces shown in Illustration 2.2 are only the mandatory interfaces of a TextDocument. The current implementation of the TextDocument service in OpenOffice.org does not only support these interfaces, but all optional interfaces as well. Additional information can be found in 7 Text Documents.

Using Interfaces

The fact that every UNO object must be accessed through its interfaces and properties has an effect in languages like Java and C++, where the compiler needs the correct type of an object reference before you can call a method from it. In Java or C++, you normally just cast an object before you access an interface it implements. When working with UNO objects this is different: You must ask the UNO environment to get the appropriate reference for you whenever you want to access methods of an interface which your object supports, but your compiler does not yet know about. Only then you can cast it safely.

The Java UNO environment has a method queryInterface() for this purpose. It looks complicated at first sight, but once you understand that queryInterface() is about safe casting of UNO types across process boundaries, you will soon get used to it. Remember how we created a UnoUrlResolver and afterwards had to call queryInterface() in our FirstConnection class:

Object urlResolver  = xLocalServiceManager.createInstanceWithContext(

                "com.sun.star.bridge.UnoUrlResolver", xLocalContext );

// query XUnoUrlResolver interface from urlResolver object

XUnoUrlResolver xUnoUrlResolver = (XUnoUrlResolver) UnoRuntime.queryInterface(

                XUnoUrlResolver.class, urlResolver );

We asked the local service manager to create a com.sun.star.bridge.UnoUrlResolver using its factory method createInstanceWithContext(). This method is defined to return a Java Object type, which should not surprise you—after all the factory must be able to return any type:

java.lang.Object createInstanceWithContext(String serviceName, XComponentContext context)

The object we receive is a com.sun.star.bridge.UnoUrlResolver service. Below you find its specification in UML notation. The service UnoUrlResolver has no properties and it supports one interface com.sun.star.bridge.XUnoUrlResolver with one method, namely resolve():


Illustration 1.3: UnoUrlResolver

The point is, while we know that the object we ordered at the factory is a UnoUrlResolver and exports the interface XUnoUrlResolver, the compiler does not. Therefore, we have to use the UNO runtime environment to ask or query for the interface XUnoUrlResolver, since we want to use the resolve() method on this interface. The method queryInterface() makes sure we get a reference that can be cast to the needed interface type, no matter if the target object is a local or a remote object. There are two queryInterface definitions in the Java UNO language binding:

java.lang.Object UnoRuntime.queryInterface(java.lang.Class targetInterface, Object sourceObject)

java.lang.Object UnoRuntime.queryInterface(com.sun.star.uno.Type targetInterface, Object sourceObject)

Since UnoRuntime.queryInterface() is specified to return a java.lang.Object just like the factory method createInstanceWithContext(), we still must explicitly cast our interface reference to the needed type. The difference is that after queryInterface() we can safely cast the object to our interface type and, most important, that the reference will now work even with an object in another process. Here is the queryInterface() call, explained step by step:

XUnoUrlResolver xUnoUrlResolver = (XUnoUrlResolver) UnoRuntime.queryInterface(

           XUnoUrlResolver.class, urlResolver );

XUnoUrlResolver is the interface we want to use, so we define a XUnoUrlResolver variable named xUnoUrlResolver (lower x) to store the interface we expect from queryInterface.

Then we query our urlResolver object for the XUnoUrlResolver interface, passing in XUnoUrlResolver.class as target interface and urlResolver as source object. Finally we cast the outcome to XUnoUrlResolver and assign the resulting reference to our variable xUnoUrlResolver.

If the source object doesn't support the interface we are querying for, queryInterface() will return null.

In Java, this call to queryInterface() is necessary whenever you have a reference to an object which is known to support an interface that you need, but you do not have the proper reference type yet. Fortunately, you are not only allowed to queryInterface() from java.lang.Object source types, but you may also query an interface from another interface reference, like this:

// loading a blank spreadsheet document gives us its XComponent interface:

XComponent xComponent = xComponentLoader.loadComponentFromURL(

                "private:factory/scalc", "_blank", 0, loadProps);

// now we query the interface XSpreadsheetDocument from xComponent

XSpreadsheetDocument xSpreadsheetDocument = (XSpreadsheetDocument)UnoRuntime.queryInterface(

                XSpreadsheetDocument.class, xComponent);  

Furthermore, if a method is defined in such a way that it already returns an interface type, you need not query the interface, but you can use its methods right away. In the snippet above, the method loadComponentFromURL is specified to return an com.sun.star.lang.XComponent interface, so you may call the XComponent methods addEventListener() and removeEventListener() directly at the xComponent variable, if you want to be notified that the document is being closed.


It is possible that future versions of the Java UNO language binding will no longer need explicit queries for interfaces.

The corresponding step in C++ is done by a Reference<> template that takes the source instance as parameter:

// instantiate a sample service with the servicemanager.

Reference< XInterface > rInstance =

        rServiceManager->createInstanceWithContext(

                OUString::createFromAscii("com.sun.star.bridge.UnoUrlResolver" ),

                rComponentContext );

// Query for the XUnoUrlResolver interface

Reference< XUnoUrlResolver > rResolver( rInstance, UNO_QUERY );

Using Properties

A service must offer its properties through interfaces that allow you to work with properties. The most basic form of these interfaces is the interface com.sun.star.beans.XPropertySet. There are other interfaces for properties, such as com.sun.star.beans.XMultiPropertySet, that gets and sets a multitude of properties with a single method call. The XPropertySet is always supported when properties are present in a service.

In XPropertySet, two methods carry out the property access, which are defined in Java as follows:

void setPropertyValue(String propertyName, Object propertyValue)

Object getPropertyValue(String propertyName)

In the FirstConnection example, the XPropertySet interface was used to get the remote component context from the initial object. The initial object was a StarOffice.ServiceManager and therefore had a property DefaultContext which contained the remote component context. The following code explains how this property was retrieved and queried its com.sun.star.uno.XComponentContext interface:

// query the XPropertySet interface from the initial object, which is a StarOffice.ServiceManager

XPropertySet xPropertySet = (XPropertySet)UnoRuntime.queryInterface(

                XPropertySet.class, initialObject);

// get the property DefaultContext

Object context = xPropertySet.getPropertyValue("DefaultContext");            

// query XComponentContext from the context object, we want to call XComponentContext.getServiceManager

xRemoteContext = (XComponentContext)UnoRuntime.queryInterface(

                XComponentContext.class, context);

You are now ready to start working with a OpenOffice.org document.

2.5.2    Example: Working with a Spreadsheet Document

In this example, we will ask the remote service manager to give us the remote Desktop object and use its loadComponentFromUrl() method to create a new spreadsheet document. From the document we get its sheets container where we insert and access a new sheet by name. In the new sheet, we enter values into A1 and A2 and summarize them in A3. The cell style of the summarizing cell gets the cell style Result, so that it appears in italics, bold and underlined. Finally, we make our new sheet the active sheet, so that the user can see it.

Add these import lines to the FirstConnection example above: (FirstSteps/FirstLoadComponent.java)

import com.sun.star.beans.PropertyValue;

import com.sun.star.lang.XComponent;

import com.sun.star.sheet.XSpreadsheetDocument;

import com.sun.star.sheet.XSpreadsheets;

import com.sun.star.sheet.XSpreadsheet;

import com.sun.star.sheet.XSpreadsheetView;

import com.sun.star.table.XCell;

import com.sun.star.frame.XModel;

import com.sun.star.frame.XController;

import com.sun.star.frame.XComponentLoader;

Edit the useConnection method as follows:

protected void useConnection() throws java.lang.Exception {

    try {

        xRemoteServiceManager = this.getRemoteServiceManager(

                "uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager");

        // get the Desktop, we need its XComponentLoader interface to load a new document

        Object desktop = xRemoteServiceManager.createInstanceWithContext(

            "com.sun.star.frame.Desktop", xRemoteContext);

       

        // query the XComponentLoader interface from the desktop

        XComponentLoader xComponentLoader = (XComponentLoader)UnoRuntime.queryInterface(

            XComponentLoader.class, desktop);

        // create empty array of PropertyValue structs, needed for loadComponentFromURL

        PropertyValue[] loadProps = new PropertyValue[0];

       

        // load new calc file

        XComponent xSpreadsheetComponent = xComponentLoader.loadComponentFromURL(

            "private:factory/scalc", "_blank", 0, loadProps);

        // query its XSpreadsheetDocument interface, we want to use getSheets()

        XSpreadsheetDocument xSpreadsheetDocument = (XSpreadsheetDocument)UnoRuntime.queryInterface(

            XSpreadsheetDocument.class, xSpreadsheetComponent);

        // use getSheets to get spreadsheets container

        XSpreadsheets xSpreadsheets = xSpreadsheetDocument.getSheets();

        //insert new sheet at position 0 and get it by name, then query its XSpreadsheet interface

        xSpreadsheets.insertNewByName("MySheet", (short)0);

        Object sheet = xSpreadsheets.getByName("MySheet");

        XSpreadsheet xSpreadsheet = (XSpreadsheet)UnoRuntime.queryInterface(

            XSpreadsheet.class, sheet);

        // use XSpreadsheet interface to get the cell A1 at position 0,0 and enter 21 as value

        XCell xCell = xSpreadsheet.getCellByPosition(0, 0);

        xCell.setValue(21);

        // enter another value into the cell A2 at position 0,1

        xCell = xSpreadsheet.getCellByPosition(0, 1);

        xCell.setValue(21);

        // sum up the two cells

        xCell = xSpreadsheet.getCellByPosition(0, 2);

        xCell.setFormula("=sum(A1:A2)");

        // we want to access the cell property CellStyle, so query the cell's XPropertySet interface

        XPropertySet xCellProps = (XPropertySet)UnoRuntime.queryInterface(

            XPropertySet.class, xCell);

        // assign the cell style "Result" to our formula, which is available out of the box

        xCellProps.setPropertyValue("CellStyle", "Result");

        // we want to make our new sheet the current sheet, so we need to ask the model

        // for the controller: first query the XModel interface from our spreadsheet component

        XModel xSpreadsheetModel = (XModel)UnoRuntime.queryInterface(

           XModel.class, xSpreadsheetComponent);

       

        // then get the current controller from the model

        XController xSpreadsheetController = xSpreadsheetModel.getCurrentController();

        // get the XSpreadsheetView interface from the controller, we want to call its method

        // setActiveSheet

        XSpreadsheetView xSpreadsheetView = (XSpreadsheetView)UnoRuntime.queryInterface(

           XSpreadsheetView.class, xSpreadsheetController);

        // make our newly inserted sheet the active sheet using setActiveSheet

        xSpreadsheetView.setActiveSheet(xSpreadsheet);    

    }

    catch( com.sun.star.lang.DisposedException e ) { //works from Patch 1

        xRemoteContext = null;

        throw e;

    }          

}

Listing 1.1: FirstLoadComponent.java

Alternatively, you can add FirstLoadComponent.java from the samples directory to your current project, because it contains the changes shown above.

2.5.3    Common Types

Until now, literals and common Java types for method parameters and return values have been used as if the OpenOffice.org API was made for Java. However, it is important to understand that the OpenOffice.org API is designed to be language independent and therefore has its own internal types which have to be mapped to the proper types for your language environment. Refer to 3 Professional UNO for detailed information about type mappings. The type mappings are briefly described in the following sections.

Simple Types

Simple types occur in structs, method return values or parameters. The following table shows the simple types in UNO and, if available, their exact mappings to Java, C++, OpenOffice.org and Basic types.

UNO

Type description

Java

C++

Basic

char

16-bit unicode character type

char

sal_Unicode

-

boolean

boolean type; true and false

boolean

sal_Bool

Boolean

byte

8-bit ordinal type

byte

sal_Int8

Integer

short

signed 16-bit ordinal type

short

sal_Int16

Integer

unsigned short

unsigned 16-bit ordinal type

-

sal_uInt16

-

long

signed 32-bit ordinal type

int

sal_Int32

Long

unsigned long

unsigned 32-bit type

-

sal_uInt32

-

hyper

signed 64-bit ordinal type

long

sal_Int64

-

unsigned hyper

unsigned 64-bit ordinal type

-

sal_uInt64

-

float

processor dependent float

float

float (IEEE float)

Single

double

processor dependent double

double

double (IEEE double)

Double

There are special conditions for types that do not have an exact mapping in this table. Check for details about all these types in the corresponding sections about type mappings in 3.4 Professional UNO - UNO Language Bindings.


The OpenOffice.org API does not use unsigned numeric types because Java does not support such types.

Strings

UNO considers strings to be simple types, but since they need special treatment in some environments, we discuss them separately here.

UNO

Description

Java

C++

Basic

string

string of 16-bit unicode characters

java.lang.String

::rtl::OUString

String

In Java, use UNO strings as if they were native java.lang.String objects.

In C++, strings must be converted to UNO unicode strings by means of SAL conversion functions, usually the function createFromAscii() in the ::rtl::OUString class:

//C++

static OUString createFromAscii( const sal_Char * value ) throw();

In Basic, Basic strings are mapped to UNO strings transparently.

Enum Types and Groups of Constants

The OpenOffice.org API offers many enumeration types (called enums) and groups of constants (called constant groups. Enums are used to list every plausible value in a certain context. The constant groups define possible values for properties, parameters, return values and struct members.

For example, there is an enum com.sun.star.table.CellVertJustify that describes the possible values for the vertical adjustment of table cell content. The vertical adjustment of table cells is determined by their property com.sun.star.table.CellProperties:VertJustify. The possible values for this property are, according to CellVertJustify, the values STANDARD, TOP, CENTER and BOTTOM.

// adjust a cell content to the upper cell border

// The service com.sun.star.table.Cell includes the service com.sun.star.table.CellProperties

// and therefore has a property VertJustify that controls the vertical cell adjustment

// we have to use the XPropertySet interface of our Cell to set it

xCellProps.setPropertyValue("VertJustify", com.sun.star.table.CellVertJustify.TOP);

'StarBasic

oCellProps.VertJustify = com.sun.star.table.CellVertJustify.TOP

//C++

rCellProps->setPropertyValue(OUString::createFromAscii( "VertJustify" ),

        ::com::sun::star::table::CellVertJustify.TOP);

2.5.4    Struct

Structs in the OpenOffice.org API are used to create compounds of every other UNO type. They correspond to C structs or Java classes consisting of public member variables only.

While structs do not encapsulate data, they are easier to transport instead of marshalling get() and set() calls back and forth. In particular, this has advantages for remote communication.

You gain access to struct members through the . (dot) operator as in

aProperty.Name = "ReadOnly";

In Java, C++ und StarBasic, the keyword new instantiates Structs. In OLE automation, use com.sun.star.reflection.CoreReflection to get a UNO struct. Do not use the service manager to create structs.

//In Java:

com.sun.star.beans.PropertyValue aProperty = new com.sun.star.beans.PropertyValue();

'In StarBasic

Dim aProperty as new com.sun.star.beans.PropertyValue

2.5.5    Any

The OpenOffice.org API frequently uses an any type, which is the counterpart of the Variant type known from other environments. The any type holds one arbitrary UNO type. The any type is frequently used in generic UNO interfaces.

For instance, common occurrences of any are the method parameters and return values for the following methods:

Interface

returning an any type

taking an any type

XPropertySet

any getPropertyValue(string propertyName)

void setPropertyValue(any value)

XNameContainer

any getByName(string name)

void replaceByName(string name, any element)

void insertByName(string name, any element)

XIndexContainer

any getByIndex(long index)

void replaceByIndex(long index, any element)

void insertByIndex(long index, any element)

XEnumeration

any nextElement()

-

Furthermore, the any type occurs in the com.sun.star.beans.PropertyValue struct.


Illustration 1.4: PropertyValue

This struct has two member variables, Name and Value, and is frequently used in sets of PropertyValue structs, where every PropertyValue is a name-value pair that describes a property by name and value. If you need to set the value of such a PropertyValue struct, you must assign an any type.

These are only some of the areas where the any type occurs. The following explains how you have to use the any type in your programs.

In Java, the any type is wrapped in a java.lang.Object. There are two simple rules to follow:

When you are supposed to pass in an any type, always pass in a java.lang.Object or a Java UNO object.

For instance, if you use setPropertyValue() to set a property that has a fundamental type in the target object, you must pass in a java.lang.Object for the new value. If the new value is a fundamental type in Java, create the corresponding Object type for the fundamental type:

xCellProps.setPropertyValue("CharWeight", new Double(200.0));

Another example would be a PropertyValue struct you want to use for loadComponentFromURL:

com.sun.star.beans.PropertyValue aProperty = new com.sun.star.beans.PropertyValue();

aProperty.Name = "ReadOnly";

aProperty.Value = new Boolean(true);

When you receive an any type, there are three different methods to evaluate it, depending on the UNO type you expect. If the incoming object has interfaces, use queryInterface() against it. If the incoming object is a struct, cast the incoming object to a Java UNO struct. If the incoming object is a simple type, use the com.sun.star.uno.AnyConverter.

The following is an example of a cast:

// the com.sun.star.table.TableBorder property that can be found in tables is a struct

// simply cast the property to the correct UNO struct type

com.sun.star.table.TableBorder bord = (TableBorder)xTableProps.getPropertyValue("TableBorder");

// now you can access the struct member fields

com.sun.star.table.BorderLine theLine = bord.TopLine;

int col = theLine.Color;

System.out.println(col);

However, the AnyConverter deserves a closer look. For instance, if you want to get a property which contains a fundamental type, you must be aware that getPropertyValue() returns a java.lang.Object containing your fundamental type wrapped in an any type. The com.sun.star.uno.AnyConverter is a converter for such objects. Actually it can do more than just conversion, you can find its specification in the Java UDK reference. The following list sums up the conversion functions in the AnyConverter:

static java.lang.Object toArray(java.lang.Object object)

static boolean toBoolean(java.lang.Object object)

static byte toByte(java.lang.Object object)

static char toChar(java.lang.Object object)

static double toDouble(java.lang.Object object)

static float toFloat(java.lang.Object object)

static int toInt(java.lang.Object object)

static long toLong(java.lang.Object object)

static java.lang.Object toObject(Type type, java.lang.Object object)

static short toShort(java.lang.Object object)

static java.lang.String toString(java.lang.Object object)

static Type toType(java.lang.Object object)

Its usage is straightforward:

import com.sun.star.uno.AnyConverter;

long cellColor = AnyConverter.toLong(xCellProps.getPropertyValue("CharColor"));

In OpenOffice.org Basic, an any type becomes a Variant:

'StarBasic

cellColor = oCellProps.CharColor

In C++, there are special operators for the any type:

//C++ has >>= and <<= for Any (the pointed brackets are always left)

sal_Int32 cellColor;

Any any;

any = rCellProps->getPropertyValue(OUString::createFromAscii( "CharColor" ));

// extract the value from any

any >>= cellColor;

2.5.6    Sequence

A sequence is a set of UNO types with a variable number of elements that can be accessed directly without element access methods. Sequences map to arrays in most current language bindings. Although these sets in UNO are often implemented as objects with element access methods, there is also the sequence type, to be used where remote performance matters. Sequences are always written with pointed brackets in the API reference:

// the following notation refer to a sequence of strings

sequence < string > aStringSequence; // UNO Interface Definition Language

In Java, you treat sequences as arrays. Empty arrays are created using new and assigning a length of null. Furthermore, if you create an array of Java objects, you only create an array of references, the actual objects are not allocated. Therefore, you must use new to create the array itself, then you must again use new for every single object and finally you have to assign the new objects to the array.

An empty sequence of PropertyValue structs is frequently needed for loadcomponentFromURL:

// create an empty array of PropertyValue structs for loadComponentFromURL

PropertyValue[] emptyProps = new PropertyValue[0];

For instance, a sequence of PropertyValue structs is needed to use loading parameters with loadComponentFromURL(). The possible parameter values for loadComponentFromURL() and the com.sun.star.frame.XStorable interface can be found in the service com.sun.star.document.MediaDescriptor.

// create an array with one PropertyValue struct for loadComponentFromURL, it contains references only

PropertyValue[] loadProps = new PropertyValue[1];

// instantiate PropertyValue struct and set its member fields

PropertyValue asTemplate = new PropertyValue();

asTemplate.Name = "AsTemplate";

asTemplate.Value = new Boolean(true);

// assign PropertyValue struct to first element in array of references for PropertyValue structs

loadProps[0] = asTemplate;

// load calc file as template

XComponent xSpreadsheetComponent = xComponentLoader.loadComponentFromURL(

    "file:///X:/Office60/share/samples/english/spreadsheets/OfficeSharingAssoc.sxc",

    "_blank", 0, loadProps);

In OpenOffice.org Basic, a simple Dim creates an empty array.

'StarBasic

Dim loadProps()  'empty array

A sequence of structs is created using new together with Dim.

'StarBasic

Dim loadProps(0) as new com.sun.star.beans.PropertyValue 'one PropertyValue

In C++, there is a template for sequences. An empty sequence can be created by omitting the number of elements required.

//C++

Sequence < ::com::sun::star::beans::PropertyValue > loadProperties; // empty sequence

If you pass a number of elements, you get an array of the required type.

//C++

Sequence< ::com::sun::star::beans::PropertyValue > loadProps( 1 );

// the structs are default constructed

loadProps[0].Name = OUString::createFromAscii( "AsTemplate" );

loadProps[0].Handle <<= true;

Reference < XComponent > rComponent = rComponentLoader->loadComponentFromURL(

        OUString::createFromAscii("private:factory/swriter"),

        OUString::createFromAscii("_blank"),

        0,

        loadProps);

2.5.7    Element Access

The OpenOffice.org API sets of objects can be provided through element access methods. The three most important kinds of element access interfaces are com.sun.star.container.XNameContainer, [com.sun.star.container.XIndexContainer] and com.sun.star.container.XEnumeration.

The three element access interfaces are examples of how the fine-grained interfaces of the OpenOffice.org allow consistent object design.

All three interfaces inherit from XElementAccess and include the methods:

type getElementType()

boolean hasElements()

The methods are used to find out basic information about the set of elements. The method hasElements() answers the question if a set contains elements at all, and which type a set contains. In Java and C++, you can get information about a type through com.sun.star.uno.Type, cf. the Java UNO and the C++ UNO reference.

The com.sun.star.container.XIndexContainer and com.sun.star.container.XNameContainer interface have a parallel design. Consider both interfaces in UML notation.


Illustration 1.5: Indexed and Named Container

In the comparison between indexed and named containers, the X...Access interfaces are about getting an element. The X...Replace interfaces allow you to replace existing elements without changing the number of elements in the set, whereas the X...Container interfaces allow you to increase and decrease the number of elements by inserting and removing elements.

Many sets of named or indexed objects do not support the whole inheritance hierarchy of XIndexContainer or XNameContainer, because the capabilities added by every subclass are not always logical for any set of elements.

The XEumerationAccess interface works differently from named and indexed containers below the XElementAccess interface. XEnumerationAccess does not provide single elements like XNameAccess and XIndexAccess, but it creates an enumeration of objects which has methods to go to the next element as long as there are more elements.

Many sets of objects support name, index, and enumeration access. Always look up the various types in the API reference to see which access methods are available.

For instance, the method getSheets() at the interface com.sun.star.sheet.XSpreadsheetDocument is specified to return a com.sun.star.sheet.XSpreadsheets interface inherited from XNameContainer. In addition, the API reference tells you that the provided object supports a com.sun.star.sheet.Spreadsheets service, which defines additional element access interfaces besides XSpreadsheets.

Examples that show how to work with XNameAccess, XIndexAccess, and XEnumerationAccess are provided below.


Illustration 1.6: Enumerated Container

Name Access

The basic interface which hands out elements by name is the com.sun.star.container.XNameAccess interface. It has three methods:

any getByName( [in] string name)

sequence < string > getElementNames()

boolean hasByName( [in] string name)

In the FirstLoadComponent example above, the method getSheets returned a com.sun.star.sheet.XSpreadsheets interface inherited from XNameAccess. Therefore, you can use getByName() to obtain the sheet "MySheet" by name from the XSpreadsheets container:

XSpreadsheets xSpreadsheets = xSpreadsheetDocument.getSheets();

Object sheet = xSpreadsheets.getByName("MySheet");

XSpreadsheet xSpreadsheet = (XSpreadsheet)UnoRuntime.queryInterface(

            XSpreadsheet.class, sheet);

// use XSpreadsheet interface to get the cell A1 at position 0,0 and enter 42 as value

XCell xCell = xSpreadsheet.getCellByPosition(0, 0);

Since getByName() returns an any, you have to use queryInterface() before you can call methods at the spreadsheet object.

Index Access

The interface which hands out elements by index is the com.sun.star.container.XIndexAccess interface. It has two methods:

any getByIndex( [in] long index)

long getCount()

The FirstLoadComponent allows to demonstrate XIndexAccess. The API reference tells us that the service returned by getSheets() is a com.sun.star.sheet.Spreadsheet service and supports not only the interface com.sun.star.sheet.XSpreadsheets, but XIndexAccess as well. Therefore, the sheets could have been accessed by index and not just by name by performing a query for the XIndexAccess interface from our xSpreadsheets variable:

XIndexAccess xSheetIndexAccess = (XIndexAccess)UnoRuntime.queryInterface(

           XIndexAccess.class, xSpreadsheets);

Object sheet = XSheetIndexAccess.getByIndex(0);

Enumeration Access

The interface com.sun.star.container.XEnumerationAccess creates enumerations that allow traveling across a set of objects. It has one method:

com.sun.star.container.XEnumeration createEnumeration()

The enumeration object gained from createEnumeration() supports the interface com.sun.star.container.XEnumeration. With this interface we can keep pulling elements out of the enumeration as long as it has more elements. XEnumeration supplies the methods:

boolean hasMoreElements()

any nextElement()

which are meant to build loops such as this:

while (xCells.hasMoreElements()) {

    Object cell = xCells.nextElement();

    // do something with cell

}

For example, in spreadsheets you have the opportunity to find out which cells contain formulas. The resulting set of cells is provided as XEnumerationAccess.

The interface that queries for cells with formulas is com.sun.star.sheet.XCellRangesQuery, it defines (among others) a method

XSheetCellRanges queryContentCells(short cellFlags)

which queries for cells having content as defined in the constants group com.sun.star.sheet.CellFlags. One of these cell flags is FORMULA. From queryContentCells() we receive an object with an com.sun.star.sheet.XSheetCellRanges interface, which has these methods:

XEnumerationAccess getCells()

String getRangeAddressesAsString()

sequence< com.sun.star.table.CellRangeAddress > getRangeAddresses()

The method getCells() can be used to list all formula cells and the containing formulas in the spreadsheet document from our FirstLoadComponent example, utilizing XEnumerationAccess.(FirstSteps/FirstLoadComponent.java)

XCellRangesQuery xCellQuery = (XCellRangesQuery)UnoRuntime.queryInterface(

    XCellRangesQuery.class, sheet);

XSheetCellRanges xFormulaCells = xCellQuery.queryContentCells(

    (short)com.sun.star.sheet.CellFlags.FORMULA);

XEnumerationAccess xFormulas = xFormulaCells.getCells();

XEnumeration xFormulaEnum = xFormulas.createEnumeration();

while (xFormulaEnum.hasMoreElements()) {

    Object formulaCell = xFormulaEnum.nextElement();

    // do something with formulaCell

    xCell = (XCell)UnoRuntime.queryInterface(XCell.class, formulaCell);

    XCellAddressable xCellAddress = (XCellAddressable)UnoRuntime.queryInterface(

        XCellAddressable.class, xCell);

    System.out.print("Formula cell in column " + xCellAddress.getCellAddress().Column

        + ", row " + xCellAddress.getCellAddress().Row

        + " contains " + xCell.getFormula());

}

2.6    How do I know Which Type I Have?

A common problem is deciding what capabilities an object that you receive from a method really has.

By observing the code completion in Java IDE, you can discover the base interface of an object returned from a method. You will notice that loadComponentFromURL() returns a com.sun.star.lang.XComponent. By pressing Shift + F1 in the NetBeans IDE, you can also read specifications about the interfaces and services you are using.

However, methods can only be specified to return one interface type. The interface you get from a method very often supports more interfaces than the one that is returned by the method. Furthermore, the interface does not tell about the properties the object contains.

Therefore you should usually get an idea how things work using this manual. Then start writing code, using the code completion and the API reference.

In addition, you can try the InstanceInspector, a Java example which is part of the OpenOffice.org SDK. It is a Java component that can be registered with the office and shows interfaces and properties of the object you are currently working with.

In OpenOffice.org Basic, you can inspect objects using the following Basic properties.

sub main

  oDocument = thiscomponent

  msgBox(oDocument.dbg_methods)

  msgBox(oDocument.dbg_properties)

  msgBox(oDocument.dbg_supportedInterfaces)

end sub

2.7    Finding Your Way through the API Reference

((The organization of module-ix pages, Service and Interface pages – Later, when the new API reference will be available))

2.8    Example: Hello Text, Hello Table, Hello Shape

The goal of this section is to give a brief overview of those mechanisms in the OpenOffice.org API, which are common to all document types. The three main application areas of OpenOffice.org are text, tables and drawing shapes. The point is: texts, tables and drawing shapes can occur in all three document types, no matter if we are dealing with a Writer, Calc or Draw/Impress file. Therefore we will now concentrate on the mechanisms that allow you to deal with text, tables and drawings everywhere. When you master these mechanisms, you will be able to insert and use texts, tables and drawings in all document types.

We want to stress the common ground, therefore we start with the interfaces and properties that allow to manipulate existing texts, tables and drawings. Afterwards we will demonstrate how you can create text, table and drawings in each document type.

The complete listing is contained in HelloTextTableShape.java.

The key interfaces and properties to work with existing texts, tables and drawings are the following:

For text the interface com.sun.star.text.XText contains the methods that change the actual text and other text contents (examples for text contents besides conventional text paragraphs are text tables, text fields, graphic objects and similar things, but such contents are not available in all contexts). When we talk about text here, we mean any text - text in text documents, text frames, page headers and footers, table cells or in drawing shapes. XText is the key for text everywhere in OpenOffice.org.


Illustration 1.7: XTextRange

The interface com.sun.star.text.XText has the ability to set or get the text as a single string, and to locate the beginning and the end of a text. Furthermore, XText can insert strings at an arbitrary position in the text and create text cursors to select and format text. Finally, XText handles text contents through the methods insertTextContent and removeTextContent, although not all texts accept text contents other than conventional text. In fact, XText covers all this by inheriting from com.sun.star.text.XSimpleText that is inherited from com.sun.star.text.XTextRange.

Text formatting happens through the properties which are described in the services com.sun.star.style.ParagraphProperties and com.sun.star.style.CharacterProperties.

The following example method manipulateText() adds text, then it uses a text cursor to select and format a few words using CharacterProperties, afterwards it inserts more text. The method manipulateText() contains the most basic methods of XText so that it works with every text object. In particular, it avoids insertTextContent(), since there are no text contents except for conventional text that can be inserted in all text objects.(FirstSteps/HelloTextTableShape.java)

protected void manipulateText(XText xText) throws com.sun.star.uno.Exception {

        // simply set whole text as one string

        xText.setString("He lay flat on the brown, pine-needled floor of the forest, "

            + "his chin on his folded arms, and high overhead the wind blew in the tops "

            + "of the pine trees.");

           

        // create text cursor for selecting and formatting

        XTextCursor xTextCursor = xText.createTextCursor();

        XPropertySet xCursorProps = (XPropertySet)UnoRuntime.queryInterface(

            XPropertySet.class, xTextCursor);

        // use cursor to select "He lay" and apply bold italic

        xTextCursor.gotoStart(false);

        xTextCursor.goRight((short)6, true);        

        // from CharacterProperties

        xCursorProps.setPropertyValue("CharPosture",    

            com.sun.star.awt.FontSlant.ITALIC);

        xCursorProps.setPropertyValue("CharWeight",

            new Float(com.sun.star.awt.FontWeight.BOLD));

       

        // add more text at the end of the text using insertString

        xTextCursor.gotoEnd(false);

        xText.insertString(xTextCursor, " The mountainside sloped gently where he lay; "

            + "but below it was steep and he could see the dark of the oiled road "

            + "winding through the pass. There was a stream alongside the road "

            + "and far down the pass he saw a mill beside the stream and the falling water "

            + "of the dam, white in the summer sunlight.", false);

        // after insertString the cursor is behind the inserted text, insert more text

        xText.insertString(xTextCursor, "\n  \"Is that the mill?\" he asked.", false);  

}

In tables and table cells, the interface com.sun.star.table.XCellRange allows you to retrieve single cells and subranges of cells. Once you have a cell, you can work with its formula or numeric value through the interface com.sun.star.table.XCell.


Illustration 1.8: Cell and Cell Range

Table formatting is partially different in text tables and spreadsheet tables. Text tables use the properties specified in com.sun.star.text.TextTable, whereas spreadsheet tables use com.sun.star.table.CellProperties. Furthermore there are special table cursors that allow to select and format cell ranges and the contained text, but since a com.sun.star.text.TextTableCursor works quite differently from a com.sun.star.sheet.SheetCellCursor, we will discuss them in the chapters about text and spreadsheet documents.(FirstSteps/HelloTextTableShape.java)

protected void manipulateTable(XCellRange xCellRange) throws com.sun.star.uno.Exception {

       

        String backColorPropertyName = "";

        XPropertySet xTableProps = null;

       

        // enter column titles and a cell value

        // Enter "Quotation" in A1, "Year" in B1. We use setString because we want to change the whole

        // cell text at once

        XCell xCell = xCellRange.getCellByPosition(0,0);

        XText xCellText = (XText)UnoRuntime.queryInterface(XText.class, xCell);

        xCellText.setString("Quotation");

        xCell = xCellRange.getCellByPosition(1,0);

        xCellText = (XText)UnoRuntime.queryInterface(XText.class, xCell);

        xCellText.setString("Year");

       

        // cell value

        xCell = xCellRange.getCellByPosition(1,1);

        xCell.setValue(1940);

       

        // select the table headers and get the cell properties

        XCellRange xSelectedCells = xCellRange.getCellRangeByName("A1:B1");

        XPropertySet xCellProps = (XPropertySet)UnoRuntime.queryInterface(

            XPropertySet.class, xSelectedCells);

       

        // format the color of the table headers and table borders

        // we need to distinguish text and spreadsheet tables:

        // - the property name for cell colors is different in text and sheet cells

        // - the common property for table borders is com.sun.star.table.TableBorder, but

        //   we must apply the property TableBorder to the whole text table,

        //   whereas we only want borders for spreadsheet cells with content.

        // XServiceInfo allows to distinguish text tables from spreadsheets

        XServiceInfo xServiceInfo = (XServiceInfo)UnoRuntime.queryInterface(

            XServiceInfo.class, xCellRange);

       

        // determine the correct property name for background color and the XPropertySet interface

        // for the cells that should get colored border lines

        if (xServiceInfo.supportsService("com.sun.star.sheet.Spreadsheet")) {

            backColorPropertyName = "CellBackColor";

            // select cells

           xSelectedCells = xCellRange.getCellRangeByName("A1:B2");

           // table properties only for selected cells

            xTableProps = (XPropertySet)UnoRuntime.queryInterface(

                XPropertySet.class, xSelectedCells);

        }

        else if (xServiceInfo.supportsService("com.sun.star.text.TextTable")) {

            backColorPropertyName = "BackColor";

          // table properties for whole table

            xTableProps = (XPropertySet)UnoRuntime.queryInterface(

                XPropertySet.class, xCellRange);

        }      

        // set cell background color

        xCellProps.setPropertyValue(backColorPropertyName, new Integer(0x99CCFF));

       

        // set table borders

        // create description for blue line, width 10

        // colors are given in ARGB, comprised of four bytes for alpha-red-green-blue as in 0xAARRGGBB  

        BorderLine theLine = new BorderLine();

        theLine.Color = 0x000099;

        theLine.OuterLineWidth = 10;

        // apply line description to all border lines and make them valid

        TableBorder bord = new TableBorder();

        bord.VerticalLine = bord.HorizontalLine =

            bord.LeftLine = bord.RightLine =

            bord.TopLine = bord.BottomLine =

                theLine;

        bord.IsVerticalLineValid = bord.IsHorizontalLineValid =

            bord.IsLeftLineValid = bord.IsRightLineValid =

            bord.IsTopLineValid = bord.IsBottomLineValid =

                true;

       

        xTableProps.setPropertyValue("TableBorder", bord);

       

}

On drawing shapes, the interface com.sun.star.drawing.XShape is used to determine the position and size of a shape.


Illustration 1.9: XShape

Everything else is a matter of property-based formatting and there is a multitude of properties to use. OpenOffice.org comes with eleven different shapes that are the basis for the drawing tools in the GUI (graphical user interface). Six of the shapes have properties that reflect their characteristics. The six shapes are:

Five shapes share the properties defined in the service com.sun.star.drawing.PolyPolygonBezierDescriptor:

 
The eleven shapes use the properties from the following services:

Consider the following example showing how these properties work: (FirstSteps/HelloTextTableShape.java)

protected void manipulateShape(XShape xShape) throws com.sun.star.uno.Exception {

        // for usage of setSize and setPosition in interface XShape see method useDraw() below

        XPropertySet xShapeProps = (XPropertySet)UnoRuntime.queryInterface(XPropertySet.class, xShape);

        // colors are given in ARGB, comprised of four bytes for alpha-red-green-blue as in 0xAARRGGBB

        xShapeProps.setPropertyValue("FillColor", new Integer(0x99CCFF));

        xShapeProps.setPropertyValue("LineColor", new Integer(0x000099));

        // angles are given in hundredth degrees, rotate by 30 degrees

        xShapeProps.setPropertyValue("RotateAngle", new Integer(3000));

}

The three manipulateXXX methods above took text, table and shape objects as parameters and altered them. The following methods show how to create such objects in the various document types.

First, a small convenience method is used to create new documents.(FirstSteps/HelloTextTableShape.java)

protected XComponent newDocComponent(String docType) throws java.lang.Exception {

        String loadUrl = "private:factory/" + docType;

        xRemoteServiceManager = this.getRemoteServiceManager(unoUrl);

        Object desktop = xRemoteServiceManager.createInstanceWithContext(

            "com.sun.star.frame.Desktop", xRemoteContext);

        XComponentLoader xComponentLoader = (XComponentLoader)UnoRuntime.queryInterface(

            XComponentLoader.class, desktop);

        PropertyValue[] loadProps = new PropertyValue[0];

        return xComponentLoader.loadComponentFromURL(loadUrl, "_blank", 0, loadProps);    

}

The method useWriter creates a writer document and manipulates its text, then uses the document's internal service manager to instantiate a text table and a shape, inserts them and manipulates the table and shape (FirstSteps/HelloTextTableShape.java). Refer to 7 Text Documents for more detailed information.

protected void useWriter() throws java.lang.Exception {

        try {

            // create new writer document and get text, then manipulate text

            XComponent xWriterComponent = newDocComponent("swriter");

            XTextDocument xTextDocument = (XTextDocument)UnoRuntime.queryInterface(

                XTextDocument.class, xWriterComponent);

            XText xText = xTextDocument.getText();

           

            manipulateText(xText);

           

            // get internal service factory of the document

            XMultiServiceFactory xWriterFactory = (XMultiServiceFactory)UnoRuntime.queryInterface(

                XMultiServiceFactory.class, xWriterComponent);

           

            // insert TextTable and get cell text, then manipulate text in cell

            Object table = xWriterFactory.createInstance("com.sun.star.text.TextTable");

            XTextContent xTextContentTable = (XTextContent)UnoRuntime.queryInterface(

                XTextContent.class, table);

           

            xText.insertTextContent(xText.getEnd(), xTextContentTable, false);

            XCellRange xCellRange = (XCellRange)UnoRuntime.queryInterface(

                XCellRange.class, table);

            XCell xCell = xCellRange.getCellByPosition(0, 1);

            XText xCellText = (XText)UnoRuntime.queryInterface(XText.class, xCell);

           

            manipulateText(xCellText);

            manipulateTable(xCellRange);

           

            // insert RectangleShape and get shape text, then manipulate text

            Object writerShape = xWriterFactory.createInstance(

                "com.sun.star.drawing.RectangleShape");

            XShape xWriterShape = (XShape)UnoRuntime.queryInterface(

                XShape.class, writerShape);

            xWriterShape.setSize(new Size(10000, 10000));

            XTextContent xTextContentShape = (XTextContent)UnoRuntime.queryInterface(

                XTextContent.class, writerShape);

           

            xText.insertTextContent(xText.getEnd(), xTextContentShape, false);

           

            XPropertySet xShapeProps = (XPropertySet)UnoRuntime.queryInterface(

                XPropertySet.class, writerShape);

            // wrap text inside shape

            xShapeProps.setPropertyValue("TextContourFrame", new Boolean(true));

           

           

            XText xShapeText = (XText)UnoRuntime.queryInterface(XText.class, writerShape);

           

            manipulateText(xShapeText);

            manipulateShape(xWriterShape);

        }

        catch( com.sun.star.lang.DisposedException e ) { //works from Patch 1

            xRemoteContext = null;

            throw e;

        }

           

}    

The method useCalc creates a calc document, uses its document factory to create a shape and manipulates the cell text, table and shape. The chapter 8 Spreadsheet Documents treats all aspects of spreadsheets. (FirstSteps/HelloTextTableShape.java)

protected void useCalc() throws java.lang.Exception {

        try {

            // create new calc document and manipulate cell text

            XComponent xCalcComponent = newDocComponent("scalc");

            XSpreadsheetDocument  xSpreadsheetDocument  =

                (XSpreadsheetDocument)UnoRuntime.queryInterface(

                    XSpreadsheetDocument .class, xCalcComponent);

            Object sheets = xSpreadsheetDocument.getSheets();

            XIndexAccess xIndexedSheets = (XIndexAccess)UnoRuntime.queryInterface(

                XIndexAccess.class, sheets);

            Object sheet =  xIndexedSheets.getByIndex(0);

           

            //get cell A2 in first sheet

            XCellRange xSpreadsheetCells = (XCellRange)UnoRuntime.queryInterface(

                XCellRange.class, sheet);

            XCell xCell = xSpreadsheetCells.getCellByPosition(0,1);

            XPropertySet xCellProps = (XPropertySet)UnoRuntime.queryInterface(

                XPropertySet.class, xCell);

            xCellProps.setPropertyValue("IsTextWrapped", new Boolean(true));

           

            XText xCellText = (XText)UnoRuntime.queryInterface(XText.class, xCell);

           

            manipulateText(xCellText);

            manipulateTable(xSpreadsheetCells);

            // get internal service factory of the document

            XMultiServiceFactory xCalcFactory = (XMultiServiceFactory)UnoRuntime.queryInterface(

                XMultiServiceFactory.class, xCalcComponent);

            // get Drawpage

            XDrawPageSupplier xDrawPageSupplier =

               (XDrawPageSupplier)UnoRuntime.queryInterface(XDrawPageSupplier.class, sheet);

            XDrawPage xDrawPage = xDrawPageSupplier.getDrawPage();

           

            // create and insert RectangleShape and get shape text, then manipulate text

            Object calcShape = xCalcFactory.createInstance(

                "com.sun.star.drawing.RectangleShape");

            XShape xCalcShape = (XShape)UnoRuntime.queryInterface(

                XShape.class, calcShape);

            xCalcShape.setSize(new Size(10000, 10000));

            xCalcShape.setPosition(new Point(7000, 3000));

           

            xDrawPage.add(xCalcShape);

           

            XPropertySet xShapeProps = (XPropertySet)UnoRuntime.queryInterface(

                XPropertySet.class, calcShape);

            // wrap text inside shape

            xShapeProps.setPropertyValue("TextContourFrame", new Boolean(true));

           

           

            XText xShapeText = (XText)UnoRuntime.queryInterface(XText.class, calcShape);

           

            manipulateText(xShapeText);

            manipulateShape(xCalcShape);

           

        }

        catch( com.sun.star.lang.DisposedException e ) { //works from Patch 1

            xRemoteContext = null;

            throw e;

        }

       

}    

The method useDraw creates a draw document and uses its document factory to instantiate and add a shape, then manipulates the shape. The chapter 9 Drawing casts more light on drawings and presentations. (FirstSteps/HelloTextTableShape.java)

protected void useDraw() throws java.lang.Exception {

        try {

            //create new draw document and insert ractangle shape

            XComponent xDrawComponent = newDocComponent("sdraw");

            XDrawPagesSupplier xDrawPagesSupplier =

                (XDrawPagesSupplier)UnoRuntime.queryInterface(

                    XDrawPagesSupplier.class, xDrawComponent);

                       

            Object drawPages = xDrawPagesSupplier.getDrawPages();

            XIndexAccess xIndexedDrawPages = (XIndexAccess)UnoRuntime.queryInterface(

                XIndexAccess.class, drawPages);

            Object drawPage = xIndexedDrawPages.getByIndex(0);

            XDrawPage xDrawPage = (XDrawPage)UnoRuntime.queryInterface(XDrawPage.class, drawPage);

           

            // get internal service factory of the document

            XMultiServiceFactory xDrawFactory =

                (XMultiServiceFactory)UnoRuntime.queryInterface(

                    XMultiServiceFactory.class, xDrawComponent);

           

            Object drawShape = xDrawFactory.createInstance(

                "com.sun.star.drawing.RectangleShape");

            XShape xDrawShape = (XShape)UnoRuntime.queryInterface(XShape.class, drawShape);

            xDrawShape.setSize(new Size(10000, 20000));

            xDrawShape.setPosition(new Point(5000, 5000));

            xDrawPage.add(xDrawShape);

           

            XText xShapeText = (XText)UnoRuntime.queryInterface(XText.class, drawShape);

            XPropertySet xShapeProps = (XPropertySet)UnoRuntime.queryInterface(

                XPropertySet.class, drawShape);

           

            // wrap text inside shape

            xShapeProps.setPropertyValue("TextContourFrame", new Boolean(true));            

           

            manipulateText(xShapeText);

            manipulateShape(xDrawShape);

        }

        catch( com.sun.star.lang.DisposedException e ) { //works from Patch 1

            xRemoteContext = null;

            throw e;

        }

       

           

}

[ Previous document | Content Table | Next document ]