[ Previous document | Content Table | Next document ]
This chapter shows you the first steps when using the OpenOffice.org API. Following these steps is essential to understand and use the chapters about OpenOffice.org documents such as 8 Text Documents, 9 Spreadsheet Documents and 10 Drawing. After you have successfully done the first steps, you can go directly to the other chapters of this manual.
The focus of the first steps will be Java, but other languages are covered as well. If you want to use OpenOffice.org Basic afterwards, please refer to the chapters 12.1 OpenOffice.org Basic and Dialogs - First Steps with OpenOffice.org Basic and 3.4.3 Professional UNO - UNO Language Bindings - OpenOffice.org Basic. The usage of C++ is described in 3.4.2 Professional UNO - UNO Language Bindings - C++ Language Binding.
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, Windows, Power PC, FreeBSD and Mac OS X. Other ports are still being developed at OpenOffice.org. The supported programming languages are Java, C++ and OpenOffice.org Basic. As well, UNO is available through the component technology Microsoft COM for many other languages. On OpenOffice.org there is also a language binding for Python available.
With OpenOffice.org 2.0, UNO is also programmable with .NET languages using the new Common Language Infrastructure binding. In addition, the new scripting framework offers the use of the API through several scripting languages, such as Javascript or Beanshell. See 19 Scripting Framework for more details.
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.
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, Add-ons, 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.
A number of files and installation sets are required before beginning with the OpenOffice.org API.
These files are required for any of the languages you use.
OpenOffice.org Installation
Install the latest version of OpenOffice.org or StarOffice/StarSuite.
You can download OpenOffice.org from www.openoffice.org. StarOffice can be obtained from Sun Microsystems or through your distributors.
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 found on, or downloaded from, the documents section at api.openoffice.org.
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 or later
Java applications for OpenOffice.org require the Java Development Kit 1.3.1 or later. Download and install a JDK from java.sun.com. To get all features, Java 1.4.1_01 is required. The recommendation is to use always the latest Java version, because of important bug fixes.
Java IDE
Download an Integrated Development Environment (IDE), such as NetBeans from www.netbeans.org or the SunTM One Java Studio from Sun Microsystems. Other IDEs can be used, but NetBeans/Sun One Java Studio 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)
Obtain the OpenOffice.org Software Development Kit (SDK) from www.openoffice.org. It contains the build environment for the examples mentioned in this manual and reference documentation for the OpenOffice.org API, for the Java UNO runtime, and the C++ API. It also offers more example sources. By means of the SDK you can use GNU make to build and run the examples we mention here.
Unpack the SDK somewhere in your file system. The file index.html gives an overview of the SDK. For detailed instructions which compilers to use and how to set up your development environment, please refer to the SDK installation guide.
OpenOffice.org uses a Java Virtual Machine to instantiate components written in Java. From OpenOffice.org 2.0 on, Java is found automatically during startup, or latest when Java functionality is required. If you prefer to preselect a JRE or JDK, or if no Java is found, you can configure Java using the Tools – Options dialog in OpenOffice.org and select the section OpenOffice.org – Java section. In older versions of OpenOffice.org you can also 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.
Next, the OpenOffice.org class files must be made known to the Java IDE. For NetBeans these Java UNO jar files must be mounted to a project. The following steps show how to create a new project and mount class files in NetBeans from version 3.5.1.
From the Project menu, select Project Manager. Click the New... button in the Project Manager window to create a new project. NetBeans uses your new project as the current project.
Activate the NetBeans Explorer window—it should contain a Filesystems item (to display the NetBeans Explorer window, click View - Explorer). Open its context menu and select Mount – Archive Files, navigate to the folder <OfficePath>/program/classes, choose at least jurt.jar, unoil.jar, ridl.jar and juh.jar in that directory and click Finish to mount the OpenOffice.org jars in your project. As an alternative, you can also mount files using File - Mount Filesystem.
Finally you need a folder for the source files of your project. Choose Mount – Local Directory from the context menu of the Filesystems icon and use the file manager dialog to create a new folder somewhere in your file system. Select it without opening it and click Finish to add it to your project.
We recommend to add the API and the Java UNO reference to your Java IDE to get online help for the OpenOffice.org API and the Java UNO runtime. In NetBeans 3.4.1, follow these steps:
Open your project and choose the Tools – Javadoc Manager menu. With the button Add Folder... add the folders docs/common/ref and docs/java/ref of your SDK installation to use the API and the Java UNO reference in your project.
You can now use Alt + F1 to view online help while the cursor is on a OpenOffice.org API or Java UNO identifier in the source editor window.
Since OpenOffice.org 2.0 it is very simple to get a working environment that offers a transparent use of UNO functionality and of office functionality. The following demonstrates how to write a small program that initializes UNO, which means that it internally connects to an office or starts a new office process if necessary and tells you if it was able to get the office component context that provides the office service manager object. Start the Java IDE or source editor, and enter the following source code for the FirstUnoContact class.
To create and run the class in the NetBeans 3.5.1 IDE, use the following steps:
Add a main class to the project. In the NetBeans Explorer window, click the Project <project_name> tab, right click the Project item, select Add New... to display the New Wizard, open the Java Classes folder, highlight the template Main, and click Next.
In the Name field, enter 'FirstUnoContact' as classname for the Main class and select the folder that contains your project files. The FirstUnoContact is added to the default package of your project. Click Finish to create the class.
Enter the source code shown below (FirstSteps/FirstUnoContact.java).
Add a blank ant script to the project. In the NetBeans Explorer window, click the Project <project_name> tab, right click the Project item, select Add New to display the New Wizard, open the Ant Build Scripts folder, highlight the template Blank Ant Project, and click Next.
In the Name field, enter 'build_FirstUnoContact' as script name for the ant build script and select the folder that contains your project files. The build_FirstUnoContact is added to your project. Click Finish to create the script.
Enter the script code shown below (FirstSteps/build_FirstUnoContact.xml).
Select and right click the build_FirstUnoContact script, select Execute to build the example project. Right click the build_FirstUnoContact script again, select Run Target to display more availble targets, select the run target to execute the example.
The FirstUnoContact example (FirstSteps/FirstUnoContact.java):
public class FirstUnoContact {
public static void main(String[] args) {
try {
// get the remote office component context
com.sun.star.uno.XComponentContext xContext =
com.sun.star.comp.helper.Bootstrap.bootstrap();
System.out.println("Connected to a running office ...");
com.sun.star.lang.XMultiComponentFactory xMCF =
xContext.getServiceManager();
String available = (xMCF != null ? "available" : "not available");
System.out.println( "remote ServiceManager is " + available );
}
catch (java.lang.Exception e){
e.printStackTrace();
}
finally {
System.exit(0);
}
}
}
The example ant build script (FirstSteps/build_FirstUnoContact.xml):
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="all" name="FirstUnoContact">
<property environment="env"/>
<property name="OFFICE_HOME" value="${env.OFFICE_HOME}"/>
<property name="OO_SDK_HOME" value="${env.OO_SDK_HOME}"/>
<target name="init">
<property name="OUTDIR" value="${OO_SDK_HOME}/WINExample.out/class/FirstUnoContact"/>
</target>
<path id="office.class.path">
<filelist dir="${OFFICE_HOME}/program/classes"
files="jurt.jar,unoil.jar,ridl.jar,juh.jar"/>
</path>
<fileset id="bootstrap.glue.code" dir="${OO_SDK_HOME}/classes">
<patternset>
<include name="com/sun/star/lib/loader/*.class"/>
<include name="win/unowinreg.dll"/>
</patternset>
</fileset>
<target name="compile" depends="init">
<mkdir dir="${OUTDIR}"/>
<javac debug="true" deprecation="true" destdir="${OUTDIR}" srcdir=".">
<classpath refid="office.class.path"/>
</javac>
</target>
<target name="jar" depends="init,compile">
<jar basedir="${OUTDIR}" compress="true"
jarfile="${OUTDIR}/FirstUnoContact.jar">
<exclude name="**/*.java"/>
<exclude name="*.jar"/>
<fileset refid="bootstrap.glue.code"/>
<manifest>
<attribute name="Main-Class" value="com.sun.star.lib.loader.Loader"/>
<section name="com/sun/star/lib/loader/Loader.class">
<attribute name="Application-Class" value="FirstUnoContact"/>
</section>
</manifest>
</jar>
</target>
<target name="all" description="Build everything." depends="init,compile,jar">
<echo message="Application built. FirstUnoContact!"/>
</target>
<target name="run" description="Try running it." depends="init,all">
<java jar="${OUTDIR}/FirstUnoContact.jar" failonerror="true" fork="true">
</java>
</target>
<target name="clean" description="Clean all build products." depends="init">
<delete>
<fileset dir="${OUTDIR}">
<include name="**/*.class"/>
</fileset>
</delete>
<delete file="${OUTDIR}/FirstUnoContact.jar"/>
</target>
</project>
For an example that connects to the office with C++, see chapter 3.4.2 Professional UNO - UNO Language Bindings - C++ Language Binding. Accessing the office with OpenOffice.org Basic is described in 12.1 OpenOffice.org Basic and Dialogs - First Steps with OpenOffice.org Basic.
The next section describes what happens during the connection between a Java program and OpenOffice.org.
UNO introduces the concept of service managers, which can be considered as “factories” that create services. For now, it is sufficient to see services as UNO objects that can be used to perform specific tasks. Later on we will give a more precise definition for the term service.
For example, the following services are available:
com.sun.star.frame.Desktop
maintains loaded documents: is used to load documents, to get the current document, and access all loaded documents
com.sun.star.configuration.ConfigurationProvider
yields access to the OpenOffice.org configuration, for instance the settings in the Tools - Options dialog
com.sun.star.sdb.DatabaseContext
holds databases registered with OpenOffice.org
com.sun.star.system.SystemShellExecute
executes system commands or documents registered for an application on the current platform
com.sun.star.text.GlobalSettings
manages global view and print settings for text documents
Illustration 2.1: Service manager |
A service always exists in a component context, which consists of the service manager that created the service and other data to be used by the service.
The FirstUnoContact class above is considered a client of the OpenOffice.org process, OpenOffice.org is the server in this respect. The server has its own component context and its own service manager, which can be accessed from client programs to use the office functionality. The client program initializes UNO and gets the component context from the OpenOffice.org process. Internally, this initialization process creates a local service manager, establishes a pipe connection to a running OpenOffice.org process (if necessary a new process is started) and returns the remote component context. In the first step this is the only thing you have to know. The com.sun.star.comp.helper.Bootstrap.bootstrap() method initializes UNO and returns the remote component context of a running OpenOffice.org process. You can find more details about bootstrapping UNO, the opportunities of different connection types and how to establish a connection to a UNO server process in the 3.3 Professional UNO - UNO Concepts.
After this first initialization step, you can use the method getServiceManager() from the component context to get the remote service manager from the OpenOffice.org process, which offers you access to the complete office functionality available through the API.
A remote connection can fail under certain conditions:
Client programs should be able to detect errors. For instance, sometimes the bridge might become unavailable. Simple clients that connect to the office, perform a certain task and exit afterwards should stop their work and inform the user if an error occurred.
Clients that are supposed to run over a long period of time should not assume that a reference to an initial object will be valid over the whole runtime of the client. The client should resume even if the connection goes down for some reason and comes back later on. When the connection fails, a robust, long running client should stop the current work, inform the user that the connection is not available and release the references to the remote process. When the user tries to repeat the last action, the client should try to rebuild the connection. Do not force the user to restart your program just because the connection was temporarily unavailable.
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 this Exception 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.
A more sophisticated way to handle lost connections is be to register a listener at the underlying bridge object. The chapter 3.3.1 Professional UNO - UNO Concepts - UNO Interprocess Connections shows how to write a connection-aware client.
An object in our context is a software artifact that has methods you can call. Objects are required to do something with OpenOffice.org. But where do you obtain them?
New objects
In general, new objects or objects which are necessary for a first access are created by service managers in OpenOffice.org. In the FirstLoadComponent example, 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:
Features which are designed to be an integral part of the object that provides the feature can be obtained by get methods in the OpenOffice.org API. It is common to get an object from a get method. For instance, getSheets() is required for every Calc document, getText() is essential for every Writer Document and getDrawpages() is an essential part of every Draw document. After loading a document, these methods are used to get the Sheets, Text and Drawpages object of the corresponding document. Object-specific get methods are an important technique to get objects.
Features which are not considered integral for the architecture of an object are accessible through a set of universal methods. In the OpenOffice.org API, these features are called properties, and generic methods are used, such as getPropertyValue(String propertyName) to access them. In some cases such a non-integral feature is provided as an object, therefore the method getPropertyValue() can be another source for objects. For instance, page styles for spreadsheets have the properties "RightPageHeaderContent" and "LeftPageHeaderContent", that contain objects for the page header sections of a spreadsheet document. The generic getPropertyValue() method can sometimes provide an object you need.
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.
Working with OpenOffice.org API objects involves the following:
First we will learn the UNO concepts of objects, interfaces, services, attributes, and properties, and we will get acquainted with UNO's method of using them.
After that, we will work with a OpenOffice.org document for the first time, and give some hints for the usage of the most common types in OpenOffice.org API.
Finally we will introduce the common interfaces that allow you to work with text, tables and drawings across all OpenOffice.org document types.
In UNO, an object is a software artifact that has methods that you can call and attributes that you can get and set. Exactly what methods and attributes an object offers is specified by the set of interfaces it supports.
An interface specifies a set of attributes and methods that together define one single aspect of an object. For instance, the interface com.sun.star.resource.XResourceBundle
module com { module sun { module star { module resource {
interface XResourceBundle: com::sun::star::conainer::XNameAccess {
[attribute] XResourceBundle Parent;
com::sun::star::lang::Locale getLocale();
any getDirectElement([in] string key);
};
}; }; }; };
specifies the attribute Parent and the methods getLocale() and getDirectElement(). To allow for reuse of such interface specifications, an interface can inherit one or more other interfaces (as, for example, XResourceBundle inherits all the attributes and methods of com.sun.star.container.XNameAccess). Multiple inheritance, the ability to inherit more than one interface, is new in OpenOffice.org 2.0.
Strictly speaking, interface attributes are not needed in UNO. Each attribute could also be expressed as a combination of one method to get the attribute’s value, and another method to set it (or just one method to get the value for a read-only attribute). However, there are at least two good reasons for the inclusion of interface attributes in UNO: First, the need for such combinations of getting and setting a value seems to be widespread enough to warrant extra support. Second, with attributes, a designer of an interface can better express nuances among the different features of an object. Attributes can be used for those features that are not considered integral or structural parts of an object, while explicit methods are reserved to access the core features.
Historically, a UNO object typically supported a set of many independent interfaces, corresponding to its many different aspects. With multiple-inheritance interfaces, there is less need for this, as an object may now support just one interface that inherits from all the other interfaces that make up the object’s various aspects.
Historically, the term “service” has been used with an unclear meaning in UNO. Starting with OpenOffice.org 2.0, the underlying concepts have been made cleaner. Unfortunately, this leaves two different meanings for the term “service” within UNO. In the following, we will use the term “new-style service” to denote an entity that conforms to the clarified, OpenOffice.org-2.0 service concept, while we use “old-style service” to denote an entity that only conforms to the historical, more vague concept. To make matters even more complicated, the term “service” is often used with still different meanings in contexts outside UNO.
Although technically there should no longer be any need for old-style services, the OpenOffice.org API still uses them extensively, to remain backwards compatible. Therefore, be prepared to encounter uses of both service concepts in parallel when working with the OpenOffice.org API.
A new-style service is of the form
module com { module sun { module star { module bridge {
service UnoUrlResolver: XUnoUrlResolver;
}; }; }; };
and specifies that objects that support a certain interface (for example, com.sun.star.bridge.XUnoUrlResolver) will be available under a certain service name (e.g., "com.sun.star.bridge.UnoUrlResolver") at a component context’s service manager. (Formally, new-style services are called “single-interface–based services.”)
The various UNO language bindings offer special constructs to easily obtain instances of such new-style services, given a suitable component context; see 3.4.1 Professional UNO - UNO Language Bindings - Java Language Binding - Type Mappings - Mapping of Services and 3.4.2 Professional UNO - UNO Language Bindings - C++ Language Binding - Type Mappings - Mapping of Services.
An old-style service (formally called an “accumulation-based service”) is of the form
module com { module sun { module star { module frame {
service Desktop {
service Frame;
interface XDesktop;
interface XComponentLoader;
interface com::sun::star::document::XEventBroadcaster;
};
}; }; }; };
and is used to specify any of the following:
The general contract is, that if an object is documented to support a certain old-style service, then you can expect that object to support all interfaces exported by the service itself and any inherited services. For example, the method com.sun.star.frame.XFrames:queryFrames returns a sequence of objects that should all support the old-style service com.sun.star.frame.Frame, and thus all the interfaces exported by Frame.
Additionally, an old-style service may specify one or more properties, as in
module com { module sun { module star { module frame {
service Frame {
interface com::sun::star::frame::XFrame;
interface com::sun::star::frame::XDispatchProvider;
// ...
[property] string Title;
[property, optional] XDispatchRecorderSupplier RecorderSupplier;
// ...
};
}; }; }; };
Properties, which are explained in detail in the following section, are similar to interface attributes, in that they describe additional features of an object. The main difference is that interface attributes can be accessed directly, while the properties of an old-style service are typically accessed via generic interfaces like com.sun.star.beans.XPropertySet. Often, interface attributes are used to represent integral features of an object, while properties represent additional, more volatile features.
Some old-style services are intended to be available at a component context’s service manager. For example, the service com.sun.star.frame.Desktop can be instantiated at a component context’s service manager under its service name "com.sun.star.frame.Desktop". (The problem is that you cannot tell whether a given old-style service is intended to be available at a component context; using a new-style service instead makes that intent explicit.)
Other old-style services are designed as generic super-services that are inherited by other services. For example, the service com.sun.star.document.OfficeDocument serves as a generic base for all different sorts of concrete document services, like com.sun.star.text.TextDocument and com.sun.star.drawing.DrawingDocument. (Multiple-inheritance interfaces are now the preferred mechanism to express such generic base services.)
Yet other old-style services only list properties, and do not export any interfaces at all. Instead of specifying the interfaces supported by certain objects, as the other kinds of old-style services do, such services are used to document a set of related properties. For example, the service com.sun.star.document.MediaDescriptor lists all the properties that can be passed to com.sun.star.frame.XComponentLoader:loadComponentFromURL.
A property is a feature of an object which is typically not considered an integral or structural part of the object and therefore is handled through generic getPropertyValue()/setPropertyValue() methods instead of specialized get methods, such as getPrinter(). Old-style services offer a special syntax to list all the properties of an object. 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 concepts of interfaces and services were introduced for the following reasons:
Interfaces and services separate specification from implementation
The specification of an interface or service is abstract, that is, it does not define how objects supporting a certain functionality do this internally. Through the abstract specification of the OpenOffice.org API, it is possible to pull the implementation out from under the API and install a different implementation if required.
Service names allow to create instances by specification name, not by class names
In Java or C++ you use the new operator to create a class instance. This approach is restricted: the class you get is hard-coded. You cannot later on exchange it by another class without editing the code. The concept of services solves this. The central object factory in OpenOffice.org, the global service manager, is asked to create an object that can be used for a certain purpose without defining its internal implementation. This is possible, because a service can be ordered from the factory by its service name and the factory decides which service implementation it returns. Which implementation you get makes no difference, you only use the well-defined interface of the service.
Multiple-inheritance interfaces make fine-grained interfaces manageable
Abstract interfaces are more reusable if they are fine-grained, i.e., if they are small and describe only one aspect of an object, not several aspects. But then you need many of them to describe a useful object. Multiple-inheritance interfaces allow to have fine-grained interfaces on the one hand and to manage them easily by forging them into a collection. Since it is quite probable that objects in an office environment will share many aspects, this fine granularity allows the interfaces to be reused and thus to get objects that behave consistently. For instance, it was possible to realize a unified way to handle text, no matter if you are dealing with body text, text frames, header or footer text, footnotes, table cells or text in drawing shapes. It was not necessary to define separate interfaces for all of these purposes.
Let us consider the old-style service com.sun.star.text.TextDocument in UML notation. The UML chart shown in Illustration 2.2 depicts the mandatory interfaces of a TextDocument service. These interfaces express the basic aspects of a text document in OpenOffice.org. It contains text, it is searchable and refreshable. It is a model with URL and controller, and it is modifiable, printable and storable. The UML chart shows how this is specified in the API.
Illustration 2.2: Text Document |
On the left of Illustration 2.2, the services com.sun.star.text.TextDocument and com.sun.star.document.OfficeDocument are shown. Every TextDocument must include these services by definition.
On the right of Illustration 2.2, you find the interfaces, that the services must export. 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 the names of other entities.
Every TextDocument object must support three interfaces: XTextDocument, XSearchable, and XRefreshable. In addition, because a TextDocument is always an OfficeDocument, it must also support the interfaces XPrintable, XStorable, XModifiable and XModel. The methods contained in these interfaces cover these aspects: printing, storing, modification and model handling.
Note that the interfaces shown in Illustration 2.2 are only the mandatory interfaces of a TextDocument. A TextDocument has optional properties and interfaces, among them the properties CharacterCount, ParagraphCount and WordCount and the XPropertySet interface which must be supported if properties are present at all. The current implementation of the TextDocument service in OpenOffice.org does not only support these interfaces, but all optional interfaces as well. The usage of a TextDocument is described thoroughly in 8 Text Documents.
The fact that every UNO object must be accessed through its interfaces 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. Take a look to the second example FirstLoadComponent (FirstSteps/FirstLoadComponent.java) where a new Desktop object is created and afterwards the queryInterface() method is used to get the XComponentLoader interface.
Object desktop = xRemoteServiceManager.createInstanceWithContext(
"com.sun.star.frame.Desktop", xRemoteContext);
XComponentLoader xComponentLoader = (XComponentLoader)
UnoRuntime.queryInterface(XComponentLoader.class, desktop);
O
We asked the service manager to create a com.sun.star.frame.Desktop 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.frame.Desktop service.
The following figure is a simplified specification in UML notation showing the relation to the com.sun.star.frame.Frame service and the supported interfaces.The point is, while we know that the object we ordered at the factory is a DesktopUnoUrlResolver and exports among other interfaces the interface XComponentLoader, the compiler does not. Therefore, we have to use the UNO runtime environment to ask or query for the interface XComponentLoader, since we want to use the loadComponentFromURL() 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:
XComponentLoader xComponentLoader = (XComponentLoader)
UnoRuntime.queryInterface(XComponentLoader.class, desktop);
XComponentLoader is the interface we want to use, so we define a XComponentLoader variable named xComponentLaoder (lower x) to store the interface we expect from queryInterface.
Then we query our desktop object for the XComponentLoader interface, passing in XComponentLoader.class as target interface and desktop as source object. Finally we cast the outcome to XComponentLoader and assign the resulting reference to our variable xComponentLoader.
If the source object does not 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 do not need to 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.
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.frame.Desktop" ),
rComponentContext );
// Query for the XComponentLoader interface
Reference< XComponentLoader > rComponentLoader( rInstance, UNO_QUERY );
In OpenOffice.org Basic, querying for interfaces is not necessary, the Basic runtime engine takes care about that internally.
With the proliferation of multiple-inheritance interfaces in the OpenOffice.org API, there will be less of a demand to explicitly query for specific interfaces in Java or C++. For example, with the hypothetical interfaces
interface XBase1 {
void fun1();
};
interface XBase2 {
void fun2();
};
interface XBoth { // inherits from both XBase1 and XBase2
interface XBase1;
interface XBase2;
};
interface XFactory {
XBoth getBoth();
};
you can directly call both fun1() and fun2() on a reference obtained through XFactory.getBoth(), without querying for either XBase1 or XBase2.
An object 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 FirstLoadComponent example, the XPropertySet interface was used to set the CellStyle property at a cell object. The cell object was a com.sun.star.sheet.SheetCell and therefore supports also the com.sun.star.table.CellProperties service which had a property CellStyle. The following code explains how this property was set:
// query the XPropertySet interface from cell object
XPropertySet xCellProps = (XPropertySet)UnoRuntime.queryInterface(XPropertySet.class, xCell);
// set the CellStyle property
xCellProps.setPropertyValue("CellStyle", "Result");
You are now ready to start working with a OpenOffice.org 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 {
// get the remote office component context
xRemoteContext = com.sun.star.comp.helper.Bootstrap.bootstrap();
System.out.println("Connected to a running office ...");
xRemoteServiceManager = xRemoteContext.getServiceManager();
}
catch( Exception e) {
e.printStackTrace();
System.exit(1);
}
try {
// 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;
}
}
Alternatively, you can add FirstLoadComponent.java from the samples directory to your current project, it contains the changes shown above.
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 UNO is designed to be language independent and therefore has its own set of types which have to be mapped to the proper types for your language binding. The type mappings are briefly described in this section. Refer to 3 Professional UNO for detailed information about type mappings.
The basic UNO types (where the term “basic” has nothing to do with OpenOffice.org Basic) occur as members of structs, as method return types or method parameters. The following table shows the basic UNO types and, if available, their exact mappings to Java, C++, and OpenOffice.org Basic types.
|
UNO |
Type description |
Java |
C++ |
Basic |
|
void |
empty type, used only as method return type and in any |
void |
void |
- |
|
boolean |
Boolean type; true and false |
boolean |
sal_Bool |
Boolean |
|
byte |
signed 8-bit integer type |
byte |
sal_Int8 |
Integer |
|
short |
signed 16-bit integer type |
short |
sal_Int16 |
Integer |
|
unsigned short |
unsigned 16-bit integer type (deprecated) |
- |
sal_uInt16 |
- |
|
long |
signed 32-bit integer type |
int |
sal_Int32 |
Long |
|
unsigned long |
unsigned 32-bit integer type (deprecated) |
- |
sal_uInt32 |
- |
|
hyper |
signed 64-bit integer type |
long |
sal_Int64 |
- |
|
unsigned hyper |
unsigned 64-bit integer type (deprecated) |
- |
sal_uInt64 |
- |
|
float |
IEC 60559 single precision floating point type |
float |
float (if appropriate) |
Single |
|
double |
IEC 60559 double precision floating point type |
double |
double (if appropriate) |
Double |
|
char |
16-bit Unicode character type (more precisely: UTF-16 code units)- |
char |
sal_Unicode |
- |
There are special conditions for types that do not have an exact mapping in this table. Check for details about these types in the corresponding sections about type mappings in 3.4 Professional UNO - UNO Language Bindings.
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 |
Unicode string type (more precisely: strings of Unicode scalar values) |
java.lang.String |
rtl::OUString |
String |
In Java, use UNO strings as if they were native java.lang.String objects.
In C++, native char 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.
The OpenOffice.org API uses 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);
OpenOffice.org Basic understands enumeration types and constant groups. Their usage is straightforward:
'OpenOffice.org Basic
oCellProps.VertJustify = com.sun.star.table.CellVertJustify.TOP
In C++ enums and constant groups are used with the scope operator ::
//C++
rCellProps->setPropertyValue(OUString::createFromAscii( "VertJustify" ),
::com::sun::star::table::CellVertJustify.TOP);
Structs in the OpenOffice.org API are used to create compounds of other UNO types. 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 as a whole, 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 OpenOffice.org Basic, 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
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 especially used in generic UNO interfaces.
Examples for the occurrence of any are the method parameters and return values of the following, frequently used 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 2.3: PropertyValue |
This struct has two member variables, Name and Value, and is ubiquitous 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, and you must be able to interpret the contained any, if you are reading from a PropertyValue. It depends on your language how this is done.
In Java, the any type is mapped to java.lang.Object, but there is also a special Java class com.sun.star.uno.Any, mainly used in those cases where a plain Object would be ambiguous. There are two simple rules of thumb to follow:
When you are supposed to pass in an any value, 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 non-interface type in the target object, you must pass in a java.lang.Object for the new value. If the new value is of a primitive type in Java, use the corresponding Object type for the primitive 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 = Boolean.TRUE;
When you receive an any instance, always use the com.sun.star.uno.AnyConverter to retrieve its value.
The AnyConverter requires a closer look. For instance, if you want to get a property which contains a primitive Java type, you must be aware that getPropertyValue() returns a java.lang.Object containing your primitive type wrapped in an any value. 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 UNO 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(Class clazz, 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)
static int toUnsignedInt(java.lang.Object object)
static long toUnsignedLong(java.lang.Object object)
static short toUnsignedShort(java.lang.Object object)
Its usage is straightforward:
import com.sun.star.uno.AnyConverter;
long cellColor = AnyConverter.toLong(xCellProps.getPropertyValue("CharColor"));
For convenience, for interface types you can directly use UnoRuntime.queryInterface() without first calling AnyConverter.getObject():
import com.sun.star.uno.AnyConverter;
import com.sun.star.uno.UnoRuntime;
Object ranges = xSpreadsheet.getPropertyValue("NamedRanges");
XNamedRanges ranges1 = (XNamedRanges) UnoRuntime.queryInterface(
XNamedRanges.class, AnyConverter.toObject(XNamedRanges.class, r));
XNamedRanges ranges2 = (XNamedRanges) UnoRuntime.queryInterface(
XNamedRanges.class, r);
In OpenOffice.org Basic, the any type becomes a Variant:
'OpenOffice.org Basic
Dim cellColor as Variant
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;
A sequence is a homogeneous collection of values of one UNO type with a variable number of elements. Sequences map to arrays in most current language bindings. Although such collections are sometimes implemented as objects with element access methods in UNO (e.g., via the com.sun.star.container.XEnumeration interface), there is also the sequence type, to be used where remote performance matters. Sequences are always written with pointed brackets in the API reference:
// a sequence of strings is notated as follows in the API reference
sequence< string > aStringSequence;
In Java, you treat sequences as arrays. (But do not use null for empty sequences, use arrays created via new and with a length of zero instead.) Furthermore, keep in mind that you only create an array of references when creating an array of Java objects, 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 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];
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 = Boolean.TRUE;
// assign PropertyValue struct to first element in our array of references to PropertyValue structs
loadProps[0] = asTemplate;
// load calc file as template
XComponent xSpreadsheetComponent = xComponentLoader.loadComponentFromURL(
"file:///X:/share/samples/english/spreadsheets/OfficeSharingAssoc.sxc",
"_blank", 0, loadProps);
In OpenOffice.org Basic, a simple Dim creates an empty array.
'OpenOffice.org Basic
Dim loadProps() 'empty array
A sequence of structs is created using new together with Dim.
'OpenOffice.org Basic
Dim loadProps(0) as new com.sun.star.beans.PropertyValue 'one PropertyValue
In C++, there is a class 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 requested length.
//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);
We have already seen in section 2.4 First Steps - How to get Objects in OpenOffice.org that sets of objects can also 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 API allow consistent object design.
All three interfaces inherit from XElementAccess, i.e., they include the methods:
type getElementType()
boolean hasElements()
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 UNO 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 2.4: Indexed and Named Container |
The XIndexAccess/XNameAccess interfaces are about getting an element. The XIndexReplace/XNameReplace interfaces allow you to replace existing elements without changing the number of elements in the set, whereas the XIndexContainer/XNameContainer 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.
Illustration 2.5: Enumerated Container |
Sets of objects sometimes support all element access methods, some also support only name, index, or 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 the 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.
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, which inherits from XNameAccess. Therefore, you could 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 AnyConverter.toObject() and/or UnoRuntime.queryInterface() before you can call methods at the spreadsheet object.
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 example 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);
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:
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());
}
A common problem is deciding what capabilities an object really has, after you receive it from a method.
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 Alt + F1 in the NetBeans IDE you can 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 (esp