[ Previous document | Content Table | Next document ]

# 6 Office Development

This chapter describes the application environment of the OpenOffice.org application. It assumes that you have read the chapter 2 First Steps, and that you are able to connect to the office and load documents.

In most cases, developers use the functionality of OpenOffice.org by opening and modifying documents. The interfaces and services common to all document types and how documents are embedded in the surrounding application environment are discussed.

It is also possible to extend the functionality of OpenOffice.org by replacing the services mentioned here by intercepting the communication between objects or by creating your own document type and integrating it into the desktop environment. All these things are discussed in this chapter.

## 6.1 OpenOffice.org Application Environment

### 6.1.1 Overview

The OpenOffice.org application environment is made up of the desktop environment and the framework API.

Illustration 6.1: OpenOffice.org Application Environment

The desktop environment consists of the desktop and auxiliary objects. It employs the framework API to carry out its functions. The framework API currently has two parts: the component framework and dispatch framework. The component framework follows a special Frame-Controller-Model paradigm to manage components viewable in OpenOffice.org. The dispatch framework handles command requests sent by the GUI.

#### Desktop Environment

The com.sun.star.frame.Desktop service is the central management instance for the OpenOffice.org application framework. All OpenOffice.org application windows are organized in a hierarchy of frames that contain viewable components. The desktop is the root frame for this hierarchy. From the desktop you can load viewable components, access frames and components, terminate the office, traverse the frame hierarchy and dispatch command requests.

The name of this service originates at StarOffice 5.x, where all document windows were embedded into a common application window that was occupied by the StarOffice desktop, mirroring the Windows desktop. The root frame of this hierarchy was called the desktop frame. The name of this service and the interface name com.sun.star.frame.XDesktop were kept for compatibility reasons.

The desktop object and frame objects use auxiliary services, such as the com.sun.star.document.TypeDetection service and other, opaque implementations that interact with the UNO-based office, but are not accessible through the OpenOffice.org API. Examples for the latter are the global document event handling and its user interface (Tools – Configure – Events), and the menu bars that use the dispatch API without being UNO services themselves. The desktop service, together with these surrounding objects, is called the desktop environment.

Illustration 6.2: The Desktop terminates the office and manages components and frames

The viewable components managed by the desktop can be three different kinds of objects: full-blown office documents with a document model and controllers, components with a controller but no model, such as the bibliography and database browser, or simple windows without API-enabled controllers, for example, preview windows. The commonality between these types of components is the com.sun.star.lang.XComponent interface. Components with controllers are also called office components, whereas simple window components are called trivial components.

Frames in the OpenOffice.org API are the connecting link between windows, components and the desktop environment. The relationship between frames and components are discussed in the next section 6.1.1 Office Development - OpenOffice.org Application Environment - Overview - Framework API.

Like all other services, the com.sun.star.frame.Desktop service can be exchanged by another implementation that extends the functionality of OpenOffice.org. By exchanging the desktop service it is possible to use different kinds of windows or to make OpenOffice.org use MDI instead of SDI. This is not an easy thing to do, but it is possible without changing any code elsewhere in OpenOffice.org.

#### Framework API

The framework API does not define an all-in-one framework with strongly coupled interfaces, but defines specialized frameworks that are grouped together by implementing the relevant interfaces at OpenOffice.org components. Each framework concentrates on a particular aspect, so that each component decides the frameworks it wants to participate in.

Currently, there are two of these frameworks: the component framework that implements the frame-controller-model paradigm and the dispatch framework that handles command requests from and to the application environment. The controller and frame implementations form the bridge between the two frameworks, because controllers and frames implement interfaces from the component framework and dispatch framework.

The framework API is an abstract specification. Its current implementation uses the Abstract Window Toolkit (AWT) specified in com.sun.star.awt, which is an abstract specification as well. The current implementation of the AWT is the Visual Component Library (VCL), a cross-platform toolkit for windows and controls written in C++ created before the specification of com.sun.star.awt and adapted to support com.sun.star.awt.

##### Frame-Controller-Model Paradigm in OpenOffice.org

The well known Model-View-Controller (MVC) paradigm separates three application areas: document data (model), presentation (view) and interaction (controller). OpenOffice.org has a similar abstraction, called the Frame-Controller-Model (FCM) paradigm. The FCM paradigm shares certain aspects with MVC , but it has different purposes, therefore it is best to approach FCM independently from MVC. The model and controller in MVC and FCM are quite different things.

The FCM paradigm in OpenOffice.org separates three application areas: document object (model), screen interaction with the model (controller) and controller-window linkage (frame).

- The model holds the document data and has methods to change these data without using a controller object. Text, drawings, and spreadsheet cells are accessed directly at the model.

- The controller has knowledge about the current view status of the document and manipulates the screen presentation of the document, but not the document data. It observes changes made to the model, and can be duplicated to have multiple controllers for the same model.

- The frame contains the controller for a model and knows the windows that are used with it, but does not have window functionality.

The purpose of FCM is to have three exchangeable parts that are used with an exchangeable window system:

It is possible to write a new controller that presents an existing model in a different manner without changing the model or the frame. A controller depends on the model it presents, therefore a new controller for a new model can be written.

Developers can introduce new models for new document types without taking care of the frame and underlying window management system. However, since there is no default controller, it is necessary to write a suitable controller also.

By keeping all window-related functionality separate from the frame, it is possible to use one single frame implementation for every possible window in the entire OpenOffice.org application. Thus, the presentation of all visible components is customized by exchanging the frame implementation. At runtime you can access a frame and replace the controller, together with the model it controls, by a different controller instance.

##### Frames

###### Linking Components and Windows

The main role of a frame in the Frame-Controller-Model paradigm is to act as a liaison between viewable components and the window system.

Frames can hold one component, or a component and one or more subframes. The following diagrams: and depict both possibilities. The first illustration shows a frame containing only a component. It is connected with two window instances: the container window and component window.

Illustration 6.3: Frame containing a component

When a frame is constructed, the frame must be initialized with a container window using com.sun.star.frame.XFrame:initialize(). This method expects the com.sun.star.awt.XWindow interface of a surrounding window instance, which becomes the container window of the frame. The window instance passed to initialize() must also support com.sun.star.awt.XTopWindow to become a container window. The container window must broadcast window events, such as windowActivated(), and appear in front of other windows or be sent to the background. The fact that container windows support com.sun.star.awt.XTopWindow does not mean the container window is an independent window of the underlying window system with a title bar and a system menu. An XTopWindow acts as a window if necessary, but it can also be docked or depend on a surrounding application window.

After initializing the frame, a component is set into the frame by a frame loader implementation that loads a component into the frame. It calls com.sun.star.frame.XFrame:setComponent() that takes another com.sun.star.awt.XWindow instance and the com.sun.star.frame.XController interface of a controller.Usually the controller is holding a model, therefore the component gets a component window of its own, separate from the container window.

A frame with a component is associated with two windows: the container window which is an XTopWindow and the component window, which is the rectangular area that displays the component and receives GUI events for the component while it is active. When a frame is initialized with an instance of a window in a call to initialize(), this window becomes its container window. When a component is set into a frame using setComponent(), another com.sun.star.awt.XWindow instance is passed becoming the component window.

When a frame is added to the desktop frame hierarchy, the desktop becomes the parent frame of our frame. For this purpose, the com.sun.star.frame.XFramesSupplier interface of the desktop is passed to the method setCreator() at the XFrame interface. This happens internally when the method append() is called at the com.sun.star.frame.XFrames interface supplied by the desktop.

| A component window can have sub-windows, and that is the case with all documents in OpenOffice.org. For instance, a text document has sub-windows for the toolbars and the editable text. Form controls are sub-windows, as well, however, these sub-windows depend on the component window and do not appear in the Frame-Controller-Model paradigm, as discussed above.
--- | ---

The second diagram shows a frame with a component and a sub-frame with another component. Each frame has a container window and component window.

Illustration 6.4: Frame containing a component and a sub-frame

In the OpenOffice.org GUI, sub-frames appear as dependent windows. The sub-frame in could be a dockable window, such as the beamer showing the database browser or a floating frame in a document created with Insert – Frame.

Note that a frame with a component and sub-frame is associated with four windows. The frame and the sub-frame have a container window and a component window for the component.

When a sub-frame is added to a surrounding frame, the frame becomes the parent of the sub-frame by a call to setCreator() at the sub-frame. This happens internally when the method append() is called at the com.sun.star.frame.XFrames interface supplied by the surrounding frame.

The section 6.1.4 Office Development - OpenOffice.org Application Environment - Creating Frames Manually shows examples for the usage of the XFrame interface that creates frames in the desktop environment, constructs dockable and standalone windows, and inserts components into frames.

###### Communication through Dispatch Framework

Besides the main role of frames as expressed in the com.sun.star.frame.XFrame interface, frames play another role by providing a communication context for the component they contain, that is, every communication from a controller to the desktop environment, and the user interface and conversely is done through the frame. This aspect of a frame is published through the com.sun.star.frame.XDispatchProvider interface, that uses special command requests to trigger actions.

The section 6.1.6 Office Development - OpenOffice.org Application Environment - Using the Dispatch Framework discusses the usage of the dispatch API.

###### Components in Frames

The desktop environment section discussed the three kinds of viewable components that can be inserted into a frame. If the component has a controller and a model like a document, or if it has only a controller, such as the bibliography and database browser, it implements the com.sun.star.frame.Controller service represented by the interface com.sun.star.frame.XController . In the call to com.sun.star.frame.XFrame:setComponent(), the controller is passed with the component window instance. If the component has no controller, it directly implements com.sun.star.lang.XComponent and com.sun.star.awt.XWindow . In this case, the component is passed as XWindow parameter, and the XController parameter must be an XController reference set to null .

If the viewable component is a trivial component (implementing XWindow only), the frame holds a reference to the component window, controls the lifetime of the component and propagates certain events from the container window to the component window. If the viewable component is an office component (having a controller), the frame adds to these basic functions a set of features for integration of the component into the environment by supporting additional command URLs for the component at its com.sun.star.frame.XDispatchProvider interface.

##### Controllers

Controllers in OpenOffice.org are between a frame and document model. This is their basic role as expressed in com.sun.star.frame.XController, which has methods getModel() and getFrame(). The method getFrame() provides the frame the controller is attached to. The method getModel() returns a document model, but it may return an empty reference if the component does not have a model.

Usually the controller objects support additional interfaces specific to the document type they control, such as com.sun.star.sheet.XSpreadsheetView for Calc document controllers or com.sun.star.text.XTextViewCursorSupplier for Writer document controllers.

Illustration 6.5: Controller with Model and Frame

There can be more than one controller instance with frames of their own controlling the same document model simultaneously. Multiple controllers and frames are created by OpenOffice.org when the user clicks Window – New Window.

##### Windows

Windows in the OpenOffice.org API are rectangular areas that are positioned and resized, and inform listeners about UI events (com.sun.star.awt.XWindow). They have a platform-specific counterpart that is wrapped in the com.sun.star.awt.XWindowPeer interface, which is invalidated (redrawn), and sets the system pointer and hands out the toolkit for the window. The usage of the window interfaces is outlined in the section 6.1.3 Office Development - OpenOffice.org Application Environment - Using the Component Framework - Window Interfaces below.

##### Dispatch Framework

The dispatch framework is designed to provide a uniform access to components for a GUI by using command URLs that mirror menu items, such as Edit – Select All with various document components. Only the component knows how to execute a command. Similarly, different document components trigger changes in the UI by common commands. For example, a controller might create UI elements like a menu bar, or open a hyperlink.

Command dispatching follows a chain of responsibility. Calls to the dispatch API are moderated by the frame, so all dispatch API calls from the UI to the component and conversely are handled by the frame. The frame passes on the command until an object is found that can handle it. It is possible to restrict, extend or redirect commands at the frame through a different frame implementation or through other components connecting to the frame.

It has already been discussed that frames and controllers have an interface com.sun.star.frame.XDispatchProvider. The interface is used to query a dispatch object for a command URL from a frame and have the dispatch object execute the command. This interface is one element of the dispatch framework.

By offering the interception of dispatches through the interface com.sun.star.frame.XDispatchProviderInterception, the Frame service offers a method to modify a component's handling of GUI event s while keeping its whole API available simultaneously.

| Normally, command URL dispatches go to a target frame which decides what to do with it. A component can use globally accessible objects like the desktop service to bypass restrictions set by a frame, but this is not recommended. It is impossible to prevent a implemention of components against the design principles, because the framework API is made for components that adhere to its design.
--- | ---

The usage of the Dispatch Framework is described in the section 6.1.6 Office Development - OpenOffice.org Application Environment - Using the Dispatch Framework.

### 6.1.2 Using the Desktop

Illustration 6.6: Desktop Service and Component Framework

The com.sun.star.frame.Desktop service available at the global service manager includes the service com.sun.star.frame.Frame. The Desktop service specification provides three interfaces: com.sun.star.frame.XDesktop, com.sun.star.frame.XComponentLoader and com.sun.star.document.XEventBroadcaster, as shown in the following UML chart:

Illustration 6.7: UML description of the desktop service

The interface com.sun.star.frame.XDesktop provides access to frames and components, and controls the termination of the office process. It defines the following methods:

com::sun::star::frame::XFrame getCurrentFrame ()

com::sun::star::container::XEnumerationAccess getComponents ()

com::sun::star::lang::XComponent getCurrentComponent ()

boolean terminate ()

void addTerminateListener ( [in] com::sun::star::frame::XTerminateListener xListener)

void removeTerminateListener ( [in] com::sun::star::frame::XTerminateListener xListener)

The methods getCurrentFrame() and getCurrentComponent() distribute the active frame and document model, whereas getComponents() returns a com.sun.star.container.XEnumerationAccess to all loaded documents. For documents loaded in the desktop environment the methods getComponents() and getCurrentComponent() always return the com.sun.star.lang.XComponent interface of the document model.

| If a specific document component is required, but are not sure whether this component is the current component, use getComponents() to get an enumeration of all document components, check each for the existence of the com.sun.star.frame.XModel interface and use getURL() at XModel to identify your document. Since not all components have to support XModel, test for XModel before calling getURL().
--- | ---

The office process is usually terminated when the user selects File - Exit or after the last application window has been closed. Clients can terminate the office through a call to terminate()and add a terminate listener to veto the shutdown process.

As long as the Windows quickstarter is active, the soffice executable is not terminated.

The following sample shows an com.sun.star.frame.XTerminateListener implementation that prevents the office from being terminated when the class TerminationTest is still active:

import com.sun.star.frame.TerminationVetoException;

import com.sun.star.frame.XTerminateListener;

public class TerminateListener implements XTerminateListener {

public void notifyTermination (com.sun.star.lang.EventObject eventObject) {

System.out.println("about to terminate...");

}

public void queryTermination (com.sun.star.lang.EventObject eventObject)

throws TerminationVetoException {

// test if we can terminate now

if (TerminationTest.isAtWork() == true) {

System.out.println("Terminate while we are at work? No way!");

throw new TerminationVetoException() ; // this will veto the termination,

// a call to terminate() returns false

}

}

public void disposing (com.sun.star.lang.EventObject eventObject) {

}

}

The following class TerminationTest tests the TerminateListener above.

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;

import com.sun.star.beans.PropertyValue;

import com.sun.star.frame.XDesktop;

import com.sun.star.frame.TerminationVetoException;

import com.sun.star.frame.XTerminateListener;

public class TerminationTest extends java.lang.Object {

private static boolean atWork = false;

public static void main(String[] args) {

XComponentContext xRemoteContext = null;

XMultiComponentFactory xRemoteServiceManager = null;

XDesktop xDesktop = null;

try {

// connect and retrieve a remote service manager and component context

XComponentContext xLocalContext =

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

XMultiComponentFactory xLocalServiceManager = xLocalContext.getServiceManager();

Object urlResolver = xLocalServiceManager.createInstanceWithContext(

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

XUnoUrlResolver xUnoUrlResolver = (XUnoUrlResolver) UnoRuntime.queryInterface(

XUnoUrlResolver.class, urlResolver );

Object initialObject = xUnoUrlResolver.resolve(

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

XPropertySet xPropertySet = (XPropertySet)UnoRuntime.queryInterface(

XPropertySet.class, initialObject);

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

xRemoteContext = (XComponentContext)UnoRuntime.queryInterface(

XComponentContext.class, context);

xRemoteServiceManager = xRemoteContext.getServiceManager();

// get Desktop instance

Object desktop = xRemoteServiceManager.createInstanceWithContext (

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

xDesktop = (XDesktop)UnoRuntime.queryInterface(XDesktop.class, desktop);

TerminateListener terminateListener = new TerminateListener ();

xDesktop.addTerminateListener (terminateListener);

// try to terminate while we are at work

atWork = true;

boolean terminated = xDesktop.terminate ();

System.out.println("The Office " +

(terminated == true ? "has been terminated" : "is still running, we are at work"));

// no longer at work

atWork = false;

// once more: try to terminate

terminated = xDesktop.terminate ();

System.out.println("The Office " +

(terminated == true ? "has been terminated" :

"is still running. Someone else prevents termination, e.g. the quickstarter"));

}

catch (java.lang.Exception e){

e.printStackTrace();

}

finally {

System.exit(0);

}

}

public static boolean isAtWork() {

return atWork;

}

}

The office freezes when terminate() is called if there are unsaved changes. As a workaround set all documents into an unmodified state through their com.sun.star.util.XModifiable interface or store them using com.sun.star.frame.XStorable.

The Desktop offers a facility to load components through its interface com.sun.star.frame.XComponentLoader. It has one method:

com::sun::star::lang::XComponent loadComponentFromURL ( [in] string aURL,

[in] string aTargetFrameName,

[in] long nSearchFlags,

[in] sequence < com::sun::star::beans::PropertyValue aArgs > )

Refer to chapter 6.1.5 Office Development - OpenOffice.org Application Environment - Handling Documents for details about the loading process.

For versions beyond 641, the desktop also provides an interface that allows listeners to be notified about certain document events through its interface com.sun.star.document.XEventBroadcaster.

void addEventListener ( [in] com::sun::star::document::XEventListener xListener)

void removeEventListener ( [in] com::sun::star::document::XEventListener xListener)

The XEventListener must implement a single method (besides disposing()):

[oneway] void notifyEvent ( [in] com::sun::star::document::EventObject Event )

The struct com.sun.star.document.EventObject has a string member EventName that assumes one of the values specified in com.sun.star.document.Events. The corresponding events are found on the Events tab of the Tools – Configure dialog when the option OpenOffice.org is selected.

The desktop broadcasts these events for all loaded documents.

The current version of OpenOffice.org does not have a GUI element as a desktop. The redesign of the OpenOffice.org GUI in StarOffice 5.x and later resulted in the com.sun.star.frame.Frame service part of the desktop service is now non-functional. While the XFrame interface can still be queried from the desktop, almost all of its methods are dummy implementations. The default implementation of the desktop object in OpenOffice.org is not able to contain a component and refuses to be attached to it, because the desktop is still a frame that is the root for the common hierarchy of all frames in OpenOffice.org. The desktop has to be a frame because its com.sun.star.frame.XFramesSupplier interface must be passed to com.sun.star.frame.XFrame:setCreator() at the child frames, therefore the desktop becomes the parent frame. However, the following functionality of com.sun.star.frame.Frame is still in place:

The desktop interface com.sun.star.frame.XFramesSupplier offers methods to access frames. This interface inherits from com.sun.star.frame.XFrame, and introduces the following methods:

com::sun::star::frame::XFrames getFrames ()

com::sun::star::frame::XFrame getActiveFrame ()

void setActiveFrame ( [in] com::sun::star::frame::XFrame xFrame)

The method getFrames() returns a com.sun.star.frame.XFrames container, that is a com.sun.star.container.XIndexAccess, with additional methods to add and remove frames:

void append ( [in] com::sun::star::frame::XFrame xFrame )

sequence < com::sun::star::frame::XFrame > queryFrames ( [in] long nSearchFlags )

void remove ( [in] com::sun::star::frame::XFrame xFrame )

This XFrames collection is used when frames are added to the desktop to become application windows.

Through getActiveFrame(), you access the active sub-frame of the desktop frame, whereas setActiveFrame() is called by a sub-frame to inform the desktop about the active sub-frame.

The object returned by getFrames() does not support XTypeProvider, therefore it cannot be used with OpenOffice.org Basic.

The parent interface of XFramesSupplier, com.sun.star.frame.XFrame is functional by accessing the frame hierarchy below the desktop. These methods are discussed in the section 6.1.3 Office Development - OpenOffice.org Application Environment - Using the Component Framework - Frames below:

com::sun::star::frame::XFrame findFrame ( [in] string aTargetFrameName, [in] long nSearchFlags );

boolean isTop ();

The generic dispatch interface com.sun.star.frame.XDispatchProvider executes functions of the internal Desktop implementation that are not accessible through specialized interfaces. Dispatch functions are described by a command URL. The XDispatchProvider returns a dispatch object that dispatches a given command URL. A reference of command URLs supported by the desktop is available on OpenOffice (http://www.openoffice.org/files/documents/25/60/commands_11beta.html). Through the com.sun.star.frame.XDispatchProviderInterception, client code intercepts thecommand dispatches at the desktop. The dispatching process is described in section 6.1.6 Office Development - OpenOffice.org Application Environment - Using the Dispatch Framework.

### 6.1.3 Using the Component Framework

The component framework comprises the interfaces of frames, controllers and models used to manage components in the OpenOffice.org desktop environment. In our context, everything that "dwells" in a frame of the desktop environment is called a component, because the interface com.sun.star.lang.XComponent is the common denominator for objects that are loaded into frames.

Frames, controllers and models hold references to each other. The frame is by definition the default owner of the controller and the model, that is,. it is responsible to call dispose() on the controller and model when it is destroyed itself. Other objects that are to hold references to the frame, controller, or model must register as listeners to be informed when these references become invalid. Therefore XModel,
XController and XFrame inherit from XComponent:

void dispose ()

void addEventListener ( [in] com::sun::star::lang::XEventListener xListener)

void removeEventListener ( [in] com::sun::star::lang::XEventListener aListener)

The process to resolve the circular dependencies of the component framework is a complex. For instance, the objects involved in the process may be in a condition where they may not be disposed of. Refer to the section 6.1.5 Office Development - OpenOffice.org Application Environment - Handling Documents - Closing Documents for additional details.

Theoretically every UNO object could exist in a frame, as long as it is willing to let the frame control its existence when it ends.

A trivial component (XWindow only) is enough for simple viewing purposes, where no activation of a component and related actions like cursor positioning or user interactions are necessary.

If the component participates in more complex interactions, it must implement the controller service.

Many features of the desktop environment are only available if the URL of a component is known. For example:

- Presenting the URL or title of the document.

- Inserting the document into the autosave queue.

- Preventing the desktop environment from loading documents twice.

- Allow for participation in the global document event handling.

In this case, com.sun.star.frame.XModel comes into operation, since it has methods to handle URLs, among others.

So a complete office component is made up of

- a controller object that presents the model or shows a view to the model that implements the com.sun.star.frame.Controller service, but publishes additional document-specific interfaces. For almost all OpenOffice.org document types there are document specific controller object specifications,such as com.sun.star.sheet.SpreadsheetView, and com.sun.star.drawing.DrawingDocumentDrawView. For controllers, refer to the section 6.1.3 Office Development - OpenOffice.org Application Environment - Using the Component Framework - Controllers.

- a model object implementing the com.sun.star.document.OfficeDocument service. Refer to the section 6.1.3 Office Development - OpenOffice.org Application Environment - Using the Component Framework - Models.

#### Getting Frames, Controllers and Models from Each Other

Usually developers require the controller and frame of an already loaded document model. The com.sun.star.frame.XModel interface of OpenOffice.org document models gets the controller that provides access to the frame through its com.sun.star.frame.XController interface. The following illustration shows the methods that get the controller and frame for a document model and conversely. From the frame , obtain the corresponding component and container window.

Illustration 6.8: Frame-Controller-Model Organization

If the loaded component is a trivial component and implements com.sun.star.awt.XWindow only, the window and the window peer is reached by querying these interfaces from the com.sun.star.lang.XComponent returned by loadComponentFromURL().

#### Frames

Illustration 6.9: Frame Service

##### XFrame

###### Frame Setup

The main role of a frame is to link components into a surrounding window system. This role is expressed by the following methods of the frame's main interface com.sun.star.frame.XFrame:

// methods for container window

void initialize ( [in] com::sun::star::awt::XWindow xWindow);

com::sun::star::awt::XWindow getContainerWindow ();

// methods for component window and controller

boolean setComponent ( [in] com::sun::star::awt::XWindow xComponentWindow,

[in] com::sun::star::frame::XController xController );

com::sun::star::awt::XWindow getComponentWindow ();

com::sun::star::frame::XController getController ();

The first two methods deal with the container window of a frame, the latter three are about linking the component and the component window with the frame. The method initialize() expects a top window that is created by the AWT toolkit that becomes the container window of the frame and is retrieved by getContainerWindow().

###### Frame Hierarchies

When frames link components into a surrounding window system, they build a frame hierarchy. This aspect is covered by the hierarchy-related XFrame methods:

[oneway] void setCreator ( [in] com::sun::star::frame::XFramesSupplier xCreator );

com::sun::star::frame::XFramesSupplier getCreator ();

string getName ();

[oneway] void setName ( [in] string aName );

com::sun::star::frame::XFrame findFrame ( [in] string aTargetFrameName, [in] long nSearchFlags );

boolean isTop ();

The XFrame method setCreator() informs a frame about its parent frame and must be called by a frames container (com.sun.star.frame.XFrames) when a frame is added to it by a call to com.sun.star.frame.XFrames:append(). A frames container is provided by frames supporting the interface com.sun.star.frame.XFramesSupplier. XFramesSupplier is currently supported by the desktop frame and by the default frame implementation used by OpenOffice.org documents. It is described below.

The frame has a custom name that is read through getName() and written through setName(). Frames in the desktop hierarchy created by GUI interaction usually do not have names. The getName() returns an empty string for them, whereas frames that are created for special purposes, such as the beamer frame or the online help, have names. Developers can set a name and use it to address a frame in findFrame() calls or when loading a component into the frame. Custom frame names must not start with an underscore.Leading underscores are reserved for special frame names.See below.

Every frame in the frame hierarchy is accessed through any other frame in this hierarchy by calling the findFrame() method. This method searches for a frame with a given name in five steps: self, children, siblings, parent, and create if not found. The findFrame() checks the called frame, then calls findFrame() at its children, then its siblings and at its parent frame. The fifth step in the search strategy is reached if the search makes it to the desktop without finding a frame with the given name. In this case, a new frame is created and assigned the name that was searched for. If the top frame is outside the desktop hierarchy, a new frame is not created.

The name used with findFrame() can be an arbitrary string without a leading underscore or one of the following reserved frame names. These names are for internal use for loading documents.Some of the reserved names are logical in a findFrame() call, also. A complete list of reserved frame names can be found in section 6.1.5 Office Development - OpenOffice.org Application Environment - Handling Documents - Loading Documents - Target Frame.

_top

Returns the top frame of the called frame, first frame where isTop() returns true when traveling up the hierarchy.

_parent

Returns the next frame above in the frame hierarchy.

_self

Returns the frame itself, same as an empty target frame name. This means you are searching for a frame you already have, but it is legal to do so.

_blank

Creates a new top-level frame whose parent is the desktop frame.

Calls with "_top" or "_parent" return the frame itself if the called frame is a top frame or has no parent. This is compatible to the targetting strategies of web browsers.

We have seen that findFrame() is called recursively. To control the recursion, the search flags parameter specified in the constants group com.sun.star.frame.FrameSearchFlag is used. For all of the five steps mentioned above, a suitable flag exists (SELF, CHILDREN, SIBLINGS, PARENT, CREATE ). Every search step can be prohibited by deleting the appropriate FrameSearchFlag. The search flag parameter can also be used to avoid ambiguities caused by multiple occurrences of a frame name in a hierarchy by excluding parts of the frame tree from the search. If findFrame() is called for a reserved frame name, the search flags are ignored.

| An additional flag can be used to extend a bottom-up search to all OpenOffice.org application windows, no matter where the search starts. Based on the five flags for the five steps, the default frame search stops searching when it reaches a top frame and does not continue with other OpenOffice.org windows. Setting the TASKS flag overrides this.
--- | ---

There are separate frame hierarchies that do not interact with each other. If a frame is created, but not inserted into any hierarchy, it becomes the top frame of its own hierarchy. This frame and its contents can not be accessed from other hierarchies by traversing the frame hierarchies through API calls. , Also, this frame and its content cannot reach frames and their contents in other hierarchies. It is the code that creates a frame and decides if the new frame becomes part of an existing hierarchy, thus enabling it to find other frames ,and making it and its viewable component visible to the other frames. Examples for frames that are not inserted into an existing hierarchy are preview frames in dialogs, such as the document preview in the File – New – Templates and Documents dialog.

| This is the only way the current frame and desktop implementation handle this. If one exchanges either or both of them by another implementation, the treatment of the "_blank" target and the CREATE SearchFlag may differ.
--- | ---

###### Frame Actions

Several actions take place at a frame. The context of viewable components can change, a frame may be activated or the relationship between frame and component may be altered. For instance, when the current selection in a document has been changed, the controller informs the frame about it by calling contextChanged(). The frame then tells its frame action listeners that the context has changed. The frame action listeners are also informed about changes in the relationship between the frame and component, and about frame activation. The corresponding XFrame methods are:

void contextChanged ();

[oneway] void activate ();

[oneway] void deactivate ();

boolean isActive ();

[oneway] void addFrameActionListener ( [in] com::sun::star::frame::XFrameActionListener xListener );

[oneway] void removeFrameActionListener ( [in] com::sun::star::frame::XFrameActionListener xListener );

The method activate() makes the given frame the active frame in its parent container. If the parent is the desktop frame, this makes the associated component the current component. However, this is not reflected in the user interface by making the corresponding window the top window. If the container of the active frame is to be the top window, use setFocus() at the com.sun.star.awt.XWindow interface of the container window.

The interface com.sun.star.frame.XFrameActionListener used with addFrameActionListener() must implement the following method:

Method of com.sun.star.frame.XFrameActionListener
frameAction() | Takes a struct com.sun.star.frame.FrameActionEvent. The struct contains two members: the source com.sun.star.frame.XFrame Frame and an enum com.sun.star.frame.FrameActionEvent Action value with one of the following values: COMPONENT_ATTACHED: a component has been attached to a frame. This is almost the same as the instantiation of the component within that frame. The component is attached to the frame immediately before this event is broadcast. COMPONENT_DETACHING: a component is detaching from a frame. This is the same as the destruction of the component which was in that frame. The moment the event is broadcast the component is still attached to the frame, but in the next moment it will not be.. COMPONENT_REATTACHED: a component has been attached to a new model. In this case, the component remains the same, but operates on a new model component. FRAME_ACTIVATED: a component has been activated. Activations are broadcast from the top component which was not active, down to the innermost component. FRAME_DEACTIVATING: broadcast immediately before the component is deactivated. Deactivations are broadcast from the innermost component which does not stay active up to the outermost component which does not stay active. CONTEXT_CHANGED: a component has changed its internal context, for example, the selection. If the activation status within a frame changes, this is a context change, also. FRAME_UI_ACTIVATED: broadcast by an active frame when it is getting UI control (tool control). FRAME_UI_DEACTIVATING: broadcast by an active frame when it is losing UI control (tool control).

| At this time, the XFrame methods used to build a frame-controller-model relationship can only be fully utilized by frame loader implementations or customized trivial components. Outside a frame loader you can create a frame, but the current implementations cannot create a standalone controller that could be used with setComponent(). Therefore, you can not remove components from one frame and add them to another or create additional controllers for a loaded model using the component framework. This is due to restrictions of the VCL and the C++ implementation of the current document components. Currently, the only way for clients to construct a frame and insert a OpenOffice.org document into it, is to use the com.sun.star.frame.XComponentLoader interface of the com.sun.star.frame.Desktop or the interfaces com.sun.star.frame.XSynchronousFrameLoader, the preferred frame loader interface, and the asynchronous com.sun.star.frame.XFrameLoader of the com.sun.star.frame.FrameLoader service that is available at the global service factory. The recommended method to get additional controllers for loaded models is to use the OpenNewView property with loadComponentFromURL() at the com.sun.star.frame.XComponentLoader interface of the desktop. There is also another possibility: dispatch a “.uno:NewWindow” command to a frame that contains that model.
--- | ---

##### XFramesSupplier

The Frame interface com.sun.star.frame.XFramesSupplier offers methods to access sub-frames of a frame. The frame implementation of OpenOffice.org supports this interface. This interface inherits from com.sun.star.frame.XFrame, and introduces the following methods:

com::sun::star::frame::XFrames getFrames ()

com::sun::star::frame::XFrame getActiveFrame ()

void setActiveFrame ( [in] com::sun::star::frame::XFrame xFrame)

The method getFrames() returns a com.sun.star.frame.XFrames container, that is a com.sun.star.container.XIndexAccess with additional methods to add and remove frames:

void append ( [in] com::sun::star::frame::XFrame xFrame )

sequence < com::sun::star::frame::XFrame > queryFrames ( [in] long nSearchFlags )

void remove ( [in] com::sun::star::frame::XFrame xFrame );

This XFrames collection is used when frames are appended to a frame to become sub-frames. The append() method implementation must extend the existing frame hierarchy by an internal call to setCreator() at the parent frame in the frame hierarchy. The parent frame is always the frame whose XFramesSupplier interface is used to append a new frame.

Through getActiveFrame() access the active sub-frame in a frame with subframes. If there are no sub-frames or a sub-frame is currently non active, the active frame is null. The setActiveFrame() is called by a sub-frame to inform the frame about the activation of the sub-frame. In setActiveFrame(), the method setActiveFrame() at the creator is called, then the registered frame action listeners are notified by an appropriate call to frameAction() with com.sun.star.frame.FrameActionEvent:Action set to FRAME_UI_ACTIVATED.

##### XDispatchProvider and XDispatchProviderInterception

Frame services also support com.sun.star.frame.XDispatchProvider and com.sun.star.frame.XDispatchProviderInterception. The section 6.1.6 Office Development - OpenOffice.org Application Environment - Using the Dispatch Framework explains how these interfaces are used.

##### XStatusIndicatorFactory

The frame implementation supplies a status indicator through its interface com.sun.star.task.XStatusIndicatorFactory. A status indicator can be used by a frame loader to show the loading process for a document. The factory has only one method that returns an object supporting com.sun.star.task.XStatusIndicator:

com::sun::star::task::XStatusIndicator createStatusIndicator ()

The status indicator is displayed by a call to com.sun.star.task.XStatusIndicator:start(). Pass a text and a numeric range, and use setValue() to let the status bar grow until the maximum range is reached. The method end() removes the status indicator.

#### Controllers

Illustration 6.10: Controller Service

##### XController

A com.sun.star.frame.XController inherits from com.sun.star.lang.XComponent and introduces the following methods:

com::sun::star::frame::XFrame getFrame ()

void attachFrame (com::sun::star::frame::XFrame xFrame)

com::sun::star::frame::XModel getModel ()

boolean attachModel (com::sun::star::frame::XModel xModel)

boolean suspend (boolean bSuspend)

any getViewData ()

void restoreViewData (any Data)

The com.sun.star.frame.XController links model and frame through the methods get/attachModel() and get/attachFrame(). These methods and the corresponding methods in the com.sun.star.frame.XModel and com.sun.star.frame.XFrame interfaces act together. calling attachModel() at the controller must be accompanied by a corresponding call of connectController() at the model, and attachFrame() at the controller must have its counterpart setComponent() at the frame.

The controller is asked for permission to dispose of the entire associated component by using suspend(). The suspend() method shows dialogs, for example, to save changes. To avoid the dialog, close the corresponding frame without using suspend() before. The section 6.1.5 Office Development - OpenOffice.org Application Environment - Handling Documents - Closing Documents provides additional information.

Developers retrieve and restore data used to setup the view at the controller by calling get/restoreViewData(). These methods are usually called on loading and saving the document, but they also allow developers to manipulate the state of a view from the outside. The exact content of this data depends on the concrete controller/model pair.

##### XDispatchProvider

Through com.sun.star.frame.XDispatchProvider, the controller participates in the dispatch framework. It is described in section 6.1.6 Office Development - OpenOffice.org Application Environment - Using the Dispatch Framework.

##### XSelectionSupplier

The optional Controller interface com.sun.star.view.XSelectionSupplier accesses the selected object and informs listeners when the selection changes:

boolean select ( [in] any aSelection)

any getSelection ()

void addSelectionChangeListener ( [in] com::sun::star::view::XSelectionChangeListener xListener)

void removeSelectionChangeListener ( [in] com::sun::star::view::XSelectionChangeListener xListener)

The type of selection depends on the type of the document and the selected object. It is also possible to get the current selection in the active or last controller of a model by calling the method getCurrentSelection() in the com.sun.star.frame.XModel interface.

##### XContextMenuInterception

The optional Controller interface com.sun.star.ui.XContextMenuInterception intercepts requests for context menus in the document's window. See chapter 4.7.5 Writing UNO Components - Integrating Components into OpenOffice.org - Intercepting Context Menus.

##### Document Specific Controller Services

The com.sun.star.frame.Controller specification is generic and does not describe additional features required for a fully functional document controller specification, such as the controller specifications for Writer, Calc and Draw documents. The following table shows the controller services specified for OpenOffice.org document components.

Once the reference to a controller is retrieved, you can query for these interfaces. Use the com.sun.star.lang.XServiceInfo interface of the model to ask it for the supported service(s). The component implementations in OpenOffice.org support the following services. Refer to the related chapters for additional information about the interfaces you get from the controllers of OpenOffice.org documents.

Component and Chapter | Specialized Controller Service | General Description
--- | --- | ---
Writer 7.5 Text Documents - Text Document Controller | com.sun.star.text.TextDocumentView | The text view supplies a text view cursor that has knowledge about the current page layout and page number. It can walk through document pages, screen pages and lines. The selected ruby text is also available, a special Asian text formatting, comparable to superscript.
Calc 8.5 Spreadsheet Documents - Controlling Spreadsheet Documents | com.sun.star.sheet.SpreadsheetView | The spreadsheet view is extremely powerful. It includes the services com.sun.star.sheet.SpreadsheetViewPane and com.sun.star.sheet.SpreadsheetViewSettings. The view pane handles the currently visible cell range and provides controllers for form controls in the spreadsheet. The view settings deal with the visibility of spreadsheet elements, such as the grid and current zoom mode. Furthermore, the spreadsheet view provides access to the active sheet in the view and the collection of all view panes, allowing to split and freeze the view, and control the interactive selection of a cell range.
Draw 9.7 Drawing - Drawing and Presentation Document Controller | com.sun.star.drawing.DrawingDocumentDrawView | The drawing document view toggles master page mode and layer mode, controls the current page and supplies the currently visible rectangle.
Impress 9.7 Drawing - Drawing and Presentation Document Controller | com.sun.star.drawing.DrawingDocumentDrawView com.sun.star.presentation.PresentationView | The presentation view does not introduce presentation specific features. Running presentations are controlled by the com.sun.star.presentation.XPresentationSupplier interface of the presentation document model.
DataBaseAccess | com.sun.star.sdb.DataSourceBrowser | This controller has no published functionality that would be useful for developers.
Bibliography | (no special controller specified) | -
Writer (PagePreview) | (no special controller specified) | -
Writer/Webdocument (SourceView) | (no special controller specified) | -
Calc (PagePreview) | (no special controller specified) | -
Chart 10.4 Charts - Chart Document Controller | (no special controller specified) | -
Math | (no special controller specified) | -

#### Models

There is not an independent specification for a model service. The interface com.sun.star.frame.XModel is currently supported by Writer, Calc, Draw and Impress document components. In our context, we call objects supporting com.sun.star.frame.XModel, model objects. All OpenOffice.org document components have the service com.sun.star.document.OfficeDocument in common. An OfficeDocument implements the following interfaces:

##### XModel

The interface com.sun.star.frame.XModel inherits from com.sun.star.lang.XComponent and introduces the following methods, which handle the model's resource description, manage its controllers and retrieves the current selection.

string getURL ()

sequence < com::sun::star::beans::PropertyValue > getArgs ()

boolean attachResource ( [in] string aURL,

[in] sequence < com::sun::star::beans::PropertyValue aArgs > )

com::sun::star::frame::XController getCurrentController ()

void setCurrentController (com::sun::star::frame::XController xController)

void connectController (com::sun::star::frame::XController xController)

void disconnectController (com::sun::star::frame::XController xController)

void lockControllers ()

void unlockControllers ()

boolean hasControllersLocked ()

com::sun::star::uno::XInterface getCurrentSelection ()

The method getURL() provides the URL where a document was loaded from or last stored using storeAsURL(). As long as a new document has not been saved, the URL is an empty string. The method getArgs() returns a sequence of property values that report the resource description according to com.sun.star.document.MediaDescriptor, specified on loading or saving with storeAsURL. The method attachResource() is used by the frame loader implementations to inform the model about its URL and MediaDescriptor.

The current or last active controller for a model isretrieved through getCurrentController(). The corresponding method setCurrentController() sets a different current controller at models where additional controllers are available. However, additional controllers can not be created at this time for OpenOffice.org components using the component API. The method connectController() is used by frame loader implementations and provides the model with a new controller that has been created for it, without making it the current controller. The disconnectController() tells the model that a controller may no longer be used. Finally, the model holds back screen updates using lockControllers() and unlockControllers(). For each call to lockControllers(), there must be a call to unlockControllers() to remove the lock. The method hasControllersLocked() tells if the controllers are locked.

The currently selected object is retrieved by a call to getCurrentSelection(). This method is an alternative to getSelection() at the com.sun.star.view.XSelectionSupplier interface supported by controller services.

##### XModifiable

The interface com.sun.star.util.XModifiable traces the modified status of a document:

void addModifyListener ( [in] com::sun::star::util::XModifyListener aListener)

void removeModifyListener ( [in] com::sun::star::util::XModifyListener aListener)

boolean isModified ()

void setModified ( [in] boolean bModified)

##### XStorable

The interface com.sun.star.frame.XStorable stores a document under an arbitrary URL or its current location. Details about how to use this interface are discussed in the chapter 6.1.5 Office Development - OpenOffice.org Application Environment - Handling Documents

##### XPrintable

The interface com.sun.star.view.XPrintable is used to set and get the printer and its settings, and dispatch print jobs. These methods and special printing features for the various document types are described in the chapters 7.2.3 Text Documents - Handling Text Document Files - Printing Text Documents, 8.2.3 Spreadsheet Documents - Handling Spreadsheet Document Files - Printing Spreadsheet Documents, 9.2.3 Drawing - Handling Drawing Document Files - Printing Drawing Documents and 9.4.2 Drawing - Handling Presentation Document Files - Printing Presentation Documents.

sequence< com::sun::star::beans::PropertyValue > getPrinter ()

void setPrinter ( [in] sequence< com::sun::star::beans::PropertyValue > aPrinter )

void print ( [in] sequence< com::sun::star::beans::PropertyValue > xOptions )

##### XEventBroadcaster

For versions later than 641, the optional interface com.sun.star.document.XEventBroadcaster at office documents enables developers to add listeners for events related to office documents in general, or for events specific for the individual document type.See 6.2.8 Office Development - Common Application Features - Document Events).

void addEventListener ( [in] com::sun::star::document::XEventListener xListener)

void removeEventListener ( [in] com::sun::star::document::XEventListener xListener)

The XEventListener must implement a single method, besides disposing():

[oneway] void notifyEvent ( [in] com::sun::star::document::EventObject Event )

The struct com.sun.star.document.EventObject has a string member EventName, that assumes one of the values specified in com.sun.star.document.Events. These events are also on the Events tab of the Tools – Configure dialog.

The general events are the same events as those provided at the XEventBroadcaster interface of the desktop. While the model is only concerned about its own events, the desktop broadcasts the events for all the loaded documents.

##### XEventsSupplier

The optional interface com.sun.star.document.XEventsSupplier binds the execution of dispatch URLs to document events, thus providing a configurable event listener as a simplification for the more general event broadcaster or listener mechanism of the com.sun.star.document.XEventBroadcaster interface. This is done programmatically versus manually in Tools – Configure – Events.

##### XDocumentInfoSupplier

The optional interface com.sun.star.document.XDocumentInfoSupplier provides access to document information as described in section 6.2.7 Office Development - Common Application Features - Document Info.Document information is presented in the File – Properties dialog in the GUI.

##### XViewDataSupplier

The optional com.sun.star.document.XViewDataSupplier interface sets and restores view data.

com::sun::star::container::XIndexAccess getViewData ()

void setViewData ( [in] com::sun::star::container::XIndexAccess aData)

The view data are a com.sun.star.container.XIndexAccess to sequences of com.sun.star.beans.PropertyValue structs. Each sequence represents the settings of a view to the model that supplies the view data.

##### Document Specific Features

Every service specification for real model objects provides more interfaces that constitute the actual model functionality For example, a text document service com.sun.star.text.TextDocument provides text related interfaces. Having received a reference to a model, developers query for these interfaces. The com.sun.star.lang.XServiceInfo interface of a model can be used to ask for supported services. The OpenOffice.org document types support the following services:

Document | Service | Chapter
--- | --- | ---
Calc | com.sun.star.sheet.SpreadsheetDocument | 8 Spreadsheet Documents
Draw | com.sun.star.drawing.DrawingDocument | 9 Drawing
Impress | com.sun.star.presentation.PresentationDocument | 9 Drawing
Math | com.sun.star.formula.FormulaProperties | -
Writer (all Writer modules) | com.sun.star.text.TextDocument | 7 Text Documents
Chart | com.sun.star.chart.ChartDocument | 10 Charts

Refer to the related chapters for additional information about the interfaces of the documents of OpenOffice.org.

#### Window Interfaces

The window interfaces of the component window and container window control the OpenOffice.org application windows. This chapter provides a short overview.

##### XWindow

The interface com.sun.star.awt.XWindow is supported by the component and controller windows. This interface comprises methods to resize a window, control its visibility, enable and disable it, and make it the focus for input device events. Listeners are informed about window events.

[oneway] void setPosSize ( long X, long Y, long Width, long Height, short Flags );

com::sun::star::awt::Rectangle getPosSize ();

[oneway] void setVisible ( boolean Visible );

[oneway] void setEnable ( boolean Enable );

[oneway] void setFocus ();

[oneway] void addWindowListener ( com::sun::star::awt::XWindowListener xListener );

[oneway] void removeWindowListener ( com::sun::star::awt::XWindowListener xListener );

[oneway] void addFocusListener ( com::sun::star::awt::XFocusListener xListener );

[oneway] void removeFocusListener ( com::sun::star::awt::XFocusListener xListener );

[oneway] void addKeyListener ( com::sun::star::awt::XKeyListener xListener );

[oneway] void removeKeyListener ( com::sun::star::awt::XKeyListener xListener );

[oneway] void addMouseListener ( com::sun::star::awt::XMouseListener xListener );

[oneway] void removeMouseListener ( com::sun::star::awt::XMouseListener xListener );

[oneway] void addMouseMotionListener ( com::sun::star::awt::XMouseMotionListener xListener );

[oneway] void removeMouseMotionListener ( com::sun::star::awt::XMouseMotionListener xListener );

[oneway] void addPaintListener ( com::sun::star::awt::XPaintListener xListener );

[oneway] void removePaintListener ( com::sun::star::awt::XPaintListener xListener );

The com.sun.star.awt.XWindowListener gets the following notifications. The com.sun.star.awt.WindowEvent has members describing the size and position of the window.

[oneway] void windowResized ( [in] com::sun::star::awt::WindowEvent e )

[oneway] void windowMoved ( [in] com::sun::star::awt::WindowEvent e )

[oneway] void windowShown ( [in] com::sun::star::lang::EventObject e )

[oneway] void windowHidden ( [in] com::sun::star::lang::EventObject e );

What the other listeners do are evident by their names.

##### XTopWindow

The interface com.sun.star.awt.XTopWindow is available at container windows. It informs listeners about top window events, and it can put itself in front of other windows or withdraw into the background. It also has a method to control the current menu bar:

[oneway] void addTopWindowListener ( com::sun::star::awt::XTopWindowListener xListener );

[oneway] void removeTopWindowListener ( com::sun::star::awt::XTopWindowListener xListener );

[oneway] void toFront ();

[oneway] void toBack ();

[oneway] void setMenuBar ( com::sun::star::awt::XMenuBar xMenu );

| Although the XTopWindow interface has a method setMenuBar(), this method is not usable at this time. The com.sun.star.awt.XMenuBar interface is deprecated.
--- | ---

The top window listener receives the following messages. All methods take a com.sun.star.awt.WindowEvent with members describing the size and position of the window.

[oneway] void windowOpened ( [in] com::sun::star::awt::WindowEvent e )

[oneway] void windowClosing ( [in] com::sun::star::awt::WindowEvent e )

[oneway] void windowClosed ( [in] com::sun::star::awt::WindowEvent e )

[oneway] void windowMinimized ( [in] com::sun::star::awt::WindowEvent e )

[oneway] void windowNormalized ( [in] com::sun::star::awt::WindowEvent e )

[oneway] void windowActivated ( [in] com::sun::star::awt::WindowEvent e )

[oneway] void windowDeactivated ( [in] com::sun::star::awt::WindowEvent e )

##### XWindowPeer

Each XWindow has a com.sun.star.awt.XWindowPeer. The com.sun.star.awt.XWindowPeer interface accesses the window toolkit implementation used to create it and provides the pointer of the pointing device, and controls the background color. It is also used to invalidate a window or portions of it to trigger a redraw cycle.

com::sun::star::awt::XToolkit getToolkit ()

[oneway] void setPointer ( [in] com::sun::star::awt::XPointer Pointer )

[oneway] void setBackground ( [in] long Color )

[oneway] void invalidate ( [in] short Flags )

[oneway] void invalidateRect ( [in] com::sun::star::awt::Rectangle Rect,

[in] short Flags )

### 6.1.4 Creating Frames Manually

##### Frame Creation

]Every time a frame is needed in OpenOffice.org, the com.sun.star.frame.Frame service is created. OpenOffice.org has an implementation for this service, available at the global service manager.

This service can be replaced by a different implementation, for example, your own implementation in Java, by registering it at the service manager. In special cases, it is possible to use a custom frame implementation instead of the com.sun.star.frame.Frame service by instantiating a specific implementation using the implementation name with the factory methods of the service manager. Both methods can alter the default window and document handling in OpenOffice.org, thus changing or extending its functionality.

##### Assigning Windows to Frames

Every frame can be assigned to any OpenOffice.org window. For instance, the same frame implementation is used to load a component into an application window of the underlying windowing system or into a preview window of a OpenOffice.org dialog. The com.sun.star.frame.Frame service implementation does not depend on the type of the window, although the entirety of the frame and window will be a different object by the user.

If you have a window in your application and want to load a OpenOffice.org document, create a frame and window object, and put them together by a call to initialize(). A default frame is created by instantiating an object implementing the com.sun.star.frame.Frame service at the global service manager. For window creation, the current com.sun.star.awt implementation has to be used to create windows in all languages supporting UNO. This toolkit offers a method to create window objects that wrap a platform specific window, such as a Java AWT window or a Windows system window represented by its window handle. A Java example is given below.

Two conditions apply to windows that are to be used with OpenOffice.org frames.

The first condition is that the window must be created by the current com.sun.star.awt.Toolkit service implementation. Not every object implementing the com.sun.star.awt.XWindow interface is used as an argument in the initialize() method, because it is syntactically correct, but it is restricted to objects created by the current com.sun.star.awt implementation. The insertion of a component into a frame only works if all involved windows are .xbl created by the same toolkit implementation. All internal office components, such as Writer and Calc, are implemented using the Visual Component Library (VCL), so that they do not work if the container window is not implemented by VCL. The current toolkit uses this library internally, so all the windows created by the awt toolkit are passed to a frame.No others work at this time. Using VCL directly is not recommended. The code has to be rewritten, whenever this complication has incurred by the current office implementation and is removed, and the toolkit implementation is exchangeable.

The second condition is that if a frame and its component are supposed to get windowActivated() messages, the window object implements the additional interface com.sun.star.awt.XTopWindow. This is necessary for editing components, because the windowActivated event shows a cursor or a selection in the document. As long as this condition is met, further code is not necessary for the interaction between the frame and window, because the frame gets all the necessary events from the window by registering the appropriate listeners in the call to initialize().

When you use the com.sun.star.awt.Toolkit to create windows, supply a com.sun.star.awt.WindowDescriptor struct to describe what kind of window is required. Set the Type member of this struct to com.sun.star.awt.WindowClass:TOP and the WindowServiceName member to "window" if you want to have an application window, or to "dockingwindow" if a window is need to be inserted in other windows created by the toolkit.

##### Setting Components into Frame Hierarchies

Once a frame has been initialized with a window, it can be added to a frames supplier, such as the desktop using the frames container provided by com.sun.star.frame.XFramesSupplier:getFrames(). Its method com.sun.star.frame.XFrames:append() inserts the new frame into the XFrames container and calls setCreator() at the new frame, passing the XFramesSupplier interface of the parent frame.

| The parent frame must be set as the creator of the newly created frame. The current implementation of the frames container calls setCreator() internally when frames are added to it using append().
--- | ---

The following example creates a new window and a frame, plugs them together, and adds them to the desktop, thus creating a new, empty OpenOffice.org application window. (OfficeDev/DesktopEnvironment/FunctionHelper.java)

// Conditions: xSMGR = m_xServiceManager
// Get access to vcl toolkit of remote office to create
// the container window of new target frame.
com.sun.star.awt.XToolkit xToolkit =
(com.sun.star.awt.XToolkit)UnoRuntime.queryInterface(
com.sun.star.awt.XToolkit.class,
xSMGR.createInstance("com.sun.star.awt.Toolkit") );
// Describe the properties of the container window.
// Tip: It is possible to use native window handle of a java window
// as parent for this. see chapter "OfficeBean" for further informations
com.sun.star.awt.WindowDescriptor aDescriptor =
new com.sun.star.awt.WindowDescriptor();

aDescriptor.Type = com.sun.star.awt.WindowClass.TOP ;
aDescriptor.WindowServiceName = "window" ;
aDescriptor.ParentIndex = -1;
aDescriptor.Parent = null;
aDescriptor.Bounds = new com.sun.star.awt.Rectangle(0,0,0,0);

aDescriptor.WindowAttributes =
com.sun.star.awt.WindowAttribute.BORDER |
com.sun.star.awt.WindowAttribute.MOVEABLE |
com.sun.star.awt.WindowAttribute.SIZEABLE |
com.sun.star.awt.WindowAttribute.CLOSEABLE ;
com.sun.star.awt.XWindowPeer xPeer = xToolkit.createWindow(aDescriptor) ;

com.sun.star.awt.XWindow xWindow = (com.sun.star.awt.XWindow)UnoRuntime.queryInterface (
com.sun.star.awt.XWindow .class, xPeer);
// Create a new empty target frame.
// Attention: Before OpenOffice.org build 643 we must use
// com.sun.star.frame.Task instead of com.sun.star.frame.Frame,

// because the desktop environment accepts only this special frame type
// as direct children. It will be deprecated from build 643
xFrame = (com.sun.star.frame.XFrame)UnoRuntime.queryInterface(
com.sun.star.frame.XFrame.class,

xSMGR.createInstance ("com.sun.star.frame.Task "));
// Set the container window on it.
xFrame.initialize(xWindow) ;

// Insert the new frame in desktop hierarchy.
// Use XFrames interface to do so. It provides access to the
// child frame container of the parent node.
// Note: append(xFrame) calls xFrame.setCreator(Desktop) automaticly.
com.sun.star.frame.XFramesSupplier xTreeRoot =
(com.sun.star.frame.XFramesSupplier)UnoRuntime.queryInterface(
com.sun.star.frame.XFramesSupplier.class,
xSMGR.createInstance("com.sun.star.frame.Desktop") );

com.sun.star.frame.XFrames xChildContainer = xTreeRoot.getFrames ();

xChildContainer.append(xFrame) ;

// Make some other initializations.
xPeer.setBackground(0xFFFFFFFF);
xWindow.setVisible(true);
xFrame.setName("newly created 1") ;

### 6.1.5 Handling Documents

#### Loading Documents

The framework API defines a simple but powerful interface to load viewable components, the com.sun.star.frame.XComponentLoader. This interface is implemented by the globally accessible com.sun.star.frame.Desktop service,to query the XComponentLoader from the desktop.

Illustration 6.11: Services Involved in Document Loading

The interface com.sun.star.frame.XComponentLoader has one method:

com::sun::star::lang::XComponent loadComponentFromURL ( [in] string aURL,

[in] string aTargetFrameName,

[in] long nSearchFlags,

[in] sequence < com::sun::star::beans::PropertyValue aArgs > )

The use fo this method is demonstrated below in the service com.sun.star.document.MediaDescriptor.

##### MediaDescriptor

A call to loadComponentFromURL() receives a sequence of com.sun.star.beans.PropertyValue structs as a parameter, which implements the com.sun.star.document.MediaDescriptor service, consisting of property definitions. It describes where a resource or medium should be loaded from and how this should be done.

The media descriptor is also used for saving a document to a location using the interface com.sun.star.frame.XStorable. It transports the "where to" and the "how" of the storing procedure. The table below shows the properties defined in the media descriptor.

Some properties are used for loading and saving while others apply to one or the other. If a media descriptor is used, only a few of the members are specified. The others assume default values. Strings default to empty strings in general and interface references default to empty references. For all other properties, the default values are specified in the description column of the table.

Some properties are tagged deprecated. There are old implementations that still use these properties. They are supported, but are discouraged to use them. Use the new property that can be found in the description column of the deprecated property.

To develop a UNO component that uses the media descriptor, note that all the properties are under control of the framework API. Never create your own property names for the media descriptor, or name clashes may be induced if the framework defines a property that uses the same name. Instead, use the ComponentData property to transport document specific information. ComponentData is specified to be an any, therefore it can be a sequence of property values by itself. If you do use it. make an appropriate specification available to users of your component.

Properties of com.sun.star.document.MediaDescriptor
AsTemplate | boolean. Setting AsTemplate to true creates a new untitled document out of the loaded document, even if it has no template extension. Loading a template, that is, a document with a template extension, creates a new untitled document by default, but setting the AsTemplate property to false loads a template for editing.
Author | string. Only for storing versions in components supporting versioning: author of version.
CharacterSet | string. Defines the character set for document formats that contain single byte characters, if necessary. Which character set names are valid depends on the filter implementation, but with the current filters you can employ the character sets used for the conversion of byte to unicode strings.
Comment | string. Only for storing versions in components supporting versioning: comment (description) for stored version.
ComponentData | any. This is a parameter that is used for any properties specific for a special office component type.
FileName - deprecated | string. Same as URL (added for compatibility reasons)
FilterData | any. This is a parameter that is used for any properties specific for a special filter type.
FilterName | string. Name of a filter that should be used for loading or storing the component. Names must match the names of the typedetection configuration.Invalid names are ignored. If a name is specified on loading, it will be verified by a filter detection, but in case of doubt it will be preferred.
FilterFlags - deprecated | string. For compatibility reasons: same as FilterOptions
FilterOptions | string. Some filters need additional parameters. Use only together with property FilterName. Details must be documented by the filter. This is an old format for some filters. If a string is not enough, filters can use the property FilterData.
Hidden | boolean. Defines if the loaded component is made visible. If this property is not specified, the component is made visible by default. Making a hidden component visible by calling setVisible() at the container window is not recommended at this time.
InputStream | com.sun.star.io.XInputStream. Used when loading a document. Reading must be done using this stream. If no stream is provided, the loader creates a stream by itself using the URL, version number, readonly flag, password, or anything requiredfor stream creation, given in the media descriptor. The model becomes the final owner of the stream and usually holds the reference to lock the file. Therefore, it is not allowed to keep a reference to this InputStream after loading the component.It is useless, because an InputStream is only usable once for reading. Even if it implements the com.sun.star.io.XSeekable interface, do not interfere with the model's reading process. Consider all the objects involved in the loading process as temporary.
InteractionHandler | com.sun.star.task.XInteractionHandler. Object implementing the com.sun.star.task.InteractionHandler service that handles exceptional situations where proceeding with the task is impossible without additional information or impossible at all. OpenOffice.org provides a default implementation that can handle many situations. If no InteractionHandler is set, a suitable exception is thrown. It is not allowed to keep a reference to this object, not even in the loaded or stored components' copy of the MediaDescriptor provided by its arguments attribute.
JumpMark | string. Jump to a marked position after loading. The office document loaders expect simple strings used like targets in HTML documents.Do not use a leading # character. The meaning of a jump mark depends upon the filter, but in Writer, bookmarks can be used, whereas in Calc cells, cell ranges and named areas are supported.
MediaType (string) | string. Type of the medium to load that must match to one of the types defined in the typedetection configuration, otherwise it is ignored. The typedetection configuration is found in the TypeDetection.xml file in the config/registry/instance/org/openoffice/Office folders of the user or share tree. The MediaType is found in the "type" entries. Here, it is the second member of the "data" value. This parameter bypasses the type detection of the desktop environment, so that passing a wrong MediaType causes load failures.
OpenFlags - deprecated | string. For compatibility reasons: string that summarizes some flags for loading. The string contains capital letters for the flags: "ReadOnly" - "R" "Preview" - "B" "AsTemplate" - "T" "Hidden" - "H" Use the corresponding boolean parameters instead.
OpenNewView | boolean. Affects the behavior of the component loader when a resource is already loaded. If true, the loader tries to open a new view for a document already loaded. For components supporting multiple views, a second window is opened as if the user clicked Window – New Window.Other components are loaded one more time. Without this property, the default behavior of the loader applies, for example, the loader of the desktop activates a document if the user tries to load it a second time.
Overwrite | boolean. For storing only: overwrite existing files with the same name, default is true, so an com.sun.star.io.IOException occurs if the target file already exists. If the default is changed and the file exists, the UCB throws an exception. If the file is loaded through API, this exception is transported to the caller or handled by an interaction handler.
Password | string. A password for loading or storing a component, if necessary. If no password is specified, loading of a password protected document fails, storing is done without encryption.
PostData | reference <XinputStream>. HTTP post data to send to a location described by the media descriptor to get a result that is loaded as a component, usually in webforms. Default is: no PostData.
PostString - deprecated | string. Same as PostData, but the data is transferred as a string (just for compatibility).
Preview | boolean. Setting this to true tells the loaded component that it is loaded as a preview, so that it can optimize loading and viewing for this special purpose. Default is false.
ReadOnly | boolean. Tells if a document is to be loaded in a (logical) readonly or in read/write mode. If opening in the desired mode is impossible, an error occurs. By default, the loaded content decides what to do. If its UCB content supports a "readonly" property, the logical open mode depends on that property, otherwise it is read/write. This property only affects the UI. Opening a document in read only mode does not prevent the component from being modified by API calls, but all modifying functionality in the UI is disabled or removed.
Referer (the wrong spelling is kept for compatibility reasons) | string. A URL describing the environment of the request; for example,. a referrer may be the URL of a document, if a hyperlink inside this document is clicked to load another document. The referrer may be evaluated by the addressed UCB content or the loaded document. Without a referrer, the processing of URLs that require security checks is denied, for instance macro: URLs.
StatusIndicator | com.sun.star.task.XStatusIndicator. Object implementing the com.sun.star.task.XStatusIndicator interface that gives status information, such as text or progress, for the target frame. OpenOffice.org provides a default implementation that is retrieved by calling createStatusIndicator() at the frame you load a component into. Usually you do not need this parameter if you do not want to use any other indicator than the one in the status bar of the document window. It is not allowed to keep a reference to this object, not even in the loaded or stored component's copy of the MediaDescriptor provided by its getArgs() method.
TemplateName | string. The logical name of a template to load. Together with the TemplateRegionName property this is used instead of the URL of the template. The logical names are the template names you see in the templates dialog.
TemplateRegionName | string. See TemplateName. The template region names are the folder names you see in the templates dialog.
Unpacked | boolean. For storing: Setting this to true means that a zip file is not used to save the document. Use a folder instead for UCB contents that support folders, such as file, WebDAV, and ftp. Default is false.
URL | string. The location of the component in URL syntax.
Version | short. For components supporting versioning: the number of the version to be loaded or saved. Default is zero and means that no version is created or loaded, and the main document is processed.
ViewData | any. Data to set a special view state after loading. The type depends on the component and is retrieved from a controller object by its com.sun.star.document.XViewDataSupplier interface. Default is: no ViewData.
ViewId | short. For components supporting different views: a number to define the view that should be constructed after loading. Default is: zero, and this should be treated by the component as the default view.
MacroExecutionMode | short. How should the macro be executed - the value should be one from com.sun.star.document.MacroExecMode constants group
UpdateDocMode | short. Can the document be updated depending on links. The value should be one from com.sun.star.document.UpdateDocMode constant group

The media descriptor used for loading and storing components is passed as an in/out parameter to some objects that participate in the loading or storing process, that is, the com.sun.star.document.TypeDetection service or a com.sun.star.document.ExtendedTypeDetection service. These objects add additional information they have gathered to the media descriptor, so that other objects called later do not have to reinvestigate this.

The first object that gets the media descriptor might need an input stream, but assume that there is currently none. The object creates one and uses it. If the stream happens to be seekable (usually it is), the object puts the stream into the media descriptor, so that it passes it to other objects that need the stream as well. They do not have to create it again. It is important for streams created for a remote resource, such as http contents.

If the stream, provided from the outside or created by the first consumer, is not seekable, every consumer creates one. It creates a buffering stream component that reads in the original stream and provides a seekable stream for all further consumers. This buffered stream can be put into the media descriptor.

As previously mentioned, the easiest way to load a document is to call loadComponentFromURL() at the desktop service, but any other object could implement this interface.

##### URL Parameter

The URL is part of the media descriptor and also an explicit parameter for loadComponentFromURL(). This enables script code to load a document without creating a media descriptor at the cost of code redundancy. The URL parameter of loadComponentFromURL() overrides a possible URL property passed in the media descriptor. Aside from valid URLs that describe an existing file, the following URLs are used to open viewable components in OpenOffice.org:

Component | URL
--- | ---
Writer | private:factory/swriter
Calc | private:factory/scalc
Draw | private:factory/sdraw
Impress | private:factory/simpress
Database | .component:DB/QueryDesign .component:DB/TableDesign .component:DB/RelationDesign .component:DB/DataSourceBrowser .component:DB/FormGridView
Bibliography | .component:Bibliography/View1

##### Target Frame

The URL and media descriptor loadComponentFromURL() have two additional arguments, the target frame name and search flags. The method loadComponentFromURL() looks for a frame in the frame hierarchy and loads the component into the frame it finds. It uses the same algorithm as findFrame() at the com.sun.star.frame.XFrame interface, described in section 6.1.3 Office Development - OpenOffice.org Application Environment - Using the Component Framework - Frames - XFrame - Frame Hierarchies.

The target frame name is a reserved name starting with an underscore or arbitrary name. The reserved names denote frequently used frames in the frame hierarchy or special functions, whereas an arbitrary name is searched recursively. If a reserved name is used, the search flags are ignored and set to 0. The following reserved names are supported:

_self

Returns the frame itself. The same as with an empty target frame name. This means to search for a frame you already have, but it is legal.

_top

Returns the top frame of the called frame .,The first frame where isTop() returns true when traveling up the hierarchy. If the starting frame does not have a parent frame, the call is treated as a search for "_self". This behavior is compatible to the frame targeting in a web browser.

_parent

Returns the next frame above in the frame hierarchy. If the starting frame does not have a parent frame, the call is treated as a search for "_self". This behavior is compatible to the frame targeting in a web browser.

_blank

Creates a new top-level frame as a child frame of the desktop. If the called frame is not part of the desktop hierarchy, this call fails. Using the "_blank" target loads open documents again that result in a read-only document, depending on the UCB content provider for the component. If loading is done as a result of a user action, this becomes confusing to theusers, therefore the "_default" target is recommended in calls from a user interface, instead of "_blank". Refer to the next section for a discussion about the _default target..

_default

Similar to "_blank", but the implementation defines further behavior that has to be documented by the implementer. The com.sun.star.frame.XComponentLoader implemented at the desktop object shows the following default behavior.

First, it checks if the component to load is already loaded in another top-level frame. If this is the case, the frame is activated and brought to the foreground. When the OpenNewView property is set to true in the media descriptor, the loader creates a second controller to show another view for the loaded document. For components supporting this, a second window is opened as if the user clicked Window – New Window. The other components are loaded one more time, as if the "_blank" target had been used. Currently, almost all office components implementing com.sun.star.frame.XModel have multiple controllers, except for HTML and writer documents in the online view. The database and bibliography components have no model, therefore they cannot open a second view at all and OpenNewView leads to an exception with them.

Next, the loader checks if the active frame contains an unmodified, empty document of the same document type as the component that is being loaded. If so, the component is loaded into that frame, replacing the empty document, otherwise a new top-level frame is created similar to a call with "_blank".

Names starting with an underscore must not be used as real names for a frame.

If the given frame name is an arbitrary string, the loader searches for this frame in the frame hierarchy. The search is done in the following order: self, children, siblings, parent, create if not found. Each of these search steps can be skipped by deleting it from the com.sun.star.frame.FrameSearchFlag bit vector:

Constants in com.sun.star.frame.FrameSearchFlag group
SELF | search current frame
CHILDREN | search children recursively
SIBLINGS | search frames on the same level
PARENT | search frame above the current frame in the hierarchy
CREATE | create new frame if not found
TASKS | do not stop searching when a top frame is reached, but continue with other top frames
ALL | search the frame hierarchy below the current top frame, do not create new frame: SELF | CHILDREN | SIBLINGS | PARENT
GLOBAL | search all frames, do not create new frame: SELF | CHILDREN | SIBLINGS | PARENT | TASKS

A typical case for a named frame is a situation where a frame is needed to be reused for subsequent loading of components, for example, a frame attached to a preview window or a docked frame, such as the frame in OpenOffice.org that opens the address book when the F4 key is pressed.

The frame names "_self", "_top" and "_parent" define a frame target relative to a starting frame. They can only be used if the component loader interface finds the frame and the setComponent() can be used with the frame. The desktop frame is the root, therefore it does not have a top and parent frame. The component loader of the desktop cannot use these names, because the desktop refuses to have a component set into it.. However, if a frame implemented com.sun.star.frame.XComponentLoader, these names could be used.

| OpenOffice.org 1.1.0 will have a frame implementation that supports XComponentLoader.
--- | ---

The reserved frame names are also used as a targeting mechanism in the dispatch framework with regard to as far as the relative frame names being resolved. For additional information, see chapter 6.1.6 Office Development - OpenOffice.org Application Environment - Using the Dispatch Framework.

The example below creates a frame, and uses the target frame and search flag parameters of loadComponentFromURL() to load a document into it. (OfficeDev/DesktopEnvironment/FunctionHelper.java)

// Conditions: sURL = "private:factory/swriter"
// xSMGR = m_xServiceManager
// xFrame = reference to a frame

// lProperties[] = new com.sun.star.beans.PropertyValue[0]

// First prepare frame for loading.
// We must adress it inside the frame tree without any complications.
// To do so we set an unambiguous name and use it later.
// Don't forget to reset the name to the original name after that.

String sOldName = xFrame.getName();
String sTarget = "odk_officedev_desk";
xFrame.setName(sTarget);
// Get access to the global component loader of the office
// for synchronous loading of documents.
com.sun.star.frame.XComponentLoader xLoader =
(com.sun.star.frame.XComponentLoader)UnoRuntime.queryInterface(
com.sun.star.frame.XComponentLoader.class,
xSMGR.createInstance("com.sun.star.frame.Desktop"));
// Load the document into the target frame by using our unambigous name
// and special search flags.
xDocument = xLoader.loadComponentFromURL(
sURL, sTarget, com.sun.star.frame.FrameSearchFlag.CHILDREN, lProperties);
// dont forget to restore old frame name ...
xFrame.setName(sOldName);

The loadComponentFromURL() call returns a reference to a com.sun.star.lang.XComponent interface. The object belonging to this interface depends on the loaded component. If it is a component that only provides a component window, but not a controller, the returned component is this window. If it is an office component that provides a controller, the returned component is the controller or its model, if these is one. All Writer, Calc, Draw, Impress or Math documents in OpenOffice.org support a model, therefore the loadComponentFromURL() call returns it. The database and bibliography components however, return a controller, because they do not have a model.

#### Closing Documents

The loadComponentFromURL() returns a com.sun.star.lang.XComponent interface has previously been discussed. The return value is a reference to a com.sun.star.lang.XComponent interface, the corresponding object is a disposable component, and the caller must take care of lifetime problems. An XComponent supports the following methods:

void dispose ()

void addEventListener ( [in] com::sun::star::lang::XEventListener xListener)

void removeEventListener ( [in] com::sun::star::lang::XEventListener aListener)

In principle, there is a simple rule. The documentation of a com.sun.star.lang.XComponent specifies the objects that can own a component. Normally, a client using an XComponent is the owner of the XComponent and has the responsibility to dispose of it or it is not the owner. If it is not the owner, it may add itself as a com.sun.star.lang.XEventListener at the XComponent and not call dispose() on it. This type of XEventListener supports one method in which a component reacts upon the fact that another component is about to be disposed of:

void disposing ( [in] com::sun::star::lang::EventObject Source )

However, the frame, controller and model are interwoven tightly, and situations do occur in which there are several owners, for example, if there is more than one view for one model, or one of these components is in use and cannot be disposed of, for example, while a print job is running or a modal dialog is open. Therefore, developers must cope with these situations and remember a few things concerning the deletion of components.

Closing a document has two aspects. It is possible that someone else wants to close a document being currently worked on And you may want to close a component someone else is using at the same time. Both aspects are discussed in the following sections. A code example that closes a document is provided at the end of this section.

##### Reacting Upon Closing

The first aspect is that someone else wants to close a component for which you hold a reference. In the current version of OpenOffice.org, there are three possibilities.

- If the component is used briefly as a stack variable, you do not care about the component after loading, or you are sure there will be no interference, it is justifiable to load the component without taking further measures. If the user is going to close the component, let the reference go out of scope, or release the reference when no longer required.

- If a reference is used, but it is not necessary to react when it becomes invalid and the object supports com.sun.star.uno.XWeak, you can hold a weak reference instead of a hard reference. Weak references are automatically converted to null if the object they reference is going to be disposed. Because the generic frame implementation, and also the controllers and models of all standard document types implement XWeak, it is recommended to use it when possible.

- If a hard reference is held or you want to know that the component has been closed and the new situation has to be accommodated, add a com.sun.star.lang.XEventListener at the com.sun.star.lang.XComponent interface. In this case, release the reference on a disposing() notification.

Sometimes it is necessary to exercise more control over the closing process, therefore a new, optional interface com.sun.star.util.XCloseable has been introduced whichis supported in versions beyond 641. If the object you are referencing is a com.sun.star.util.XCloseable , register it as a com.sun.star.util.XCloseListener and throw a com.sun.star.util.CloseVetoException when prompted to close. Since XCloseable is specified as an optional interface for frames and models, do not assume that this interface is supported. It is possible that the code runs with a OpenOffice.org version where frames and models do not implement XCloseable . Therefore ,be prepared for the case when you receive null when you try to query XCloseable . The XCloseable interface is described in more detail below.

##### How to Trigger Closing

The second aspect – to close a view of a component or the entire viewable component yourself – is more complex. The necessary steps depend on how you want to treat modified documents. Besides you have to prepare for the new com.sun.star.util.XCloseable interface, which will be implemented in future versions of OpenOffice.org.

| Although XCloseable is not supported in the current version of OpenOffice.org, you already have to check for this interface to write compatible code. Not checking for XCloseable will be illegal in future versions. If a component supports this interface, you must not use any closing procedure other than calling close() at that interface.
--- | ---

The following three diagrams show the decisions to be made when closing a frame or a document model. The important points are: if you expect modifications, you must either handle them using com.sun.star.util.XModifiable and com.sun.star.frame.XStorable, or let the user do the necessary interaction by calling suspend() on the controller. In any case, check if the frame or model is an XCloseable and prefer com.sun.star.util.XCloseable:close() over a call to dispose(). The first two diagrams illustrate the separate closing process for frames and models, the third diagram covers the actual termination of frames and models.

Illustration 6.12: Closing a Frame

Illustration 6.13: Closing a Model

Illustration 6.14: Terminate Frame/Model

##### XCloseable

The dispose mechanism has shortcomings in complex situations, such as the frame-controller-model interaction. The dispose call cannot be rejected, but as shown above, sometimes it is necessary to prevent destruction of objects due to shared ownership or a state of the documents that forbids destruction.

A closing mechanism is required that enables all involved objects to negotiate if deletion is possible and to veto, if necessary. By offering the interface com.sun.star.util.XCloseable, a component tells it must be destroyed by calling close(). Calling dispose() on an XCloseable might lead to deadlocks or crash the entire application.

In OpenOffice.org, model or frame objects are possible candidates for implementing the interface XCloseable, therefore query for that interface before destroying the object. Call dispose() directly if the model or frame does not support the interface, thus declaring that it handles all the problems.

An object implementing XCloseable registers close listeners. When a close request is received, all listeners are asked for permission. If a listener wants to deprecate, it throws an exception derived from com.sun.star.util.CloseVetoException containing the reason why the component can not be closed. This exception is passed to the close requester. The XCloseable itself can veto the destruction by throwing an exception. If there is no veto, the XCloseable calls dispose() on itself and returns.

The XCloseable handles problems that occur if a component rejects destruction. A script programmer usually can not cope with a component not used anymore and refuses to be destroyed. Ensure that the component is destroyed to avoid a memory leak. The close() method offers a method to pass the responsibility to close the object to any possible close listener that vetoes closing or to the XCloseable if the initial caller is not able to stay in memory to try again later. This responsibility is referred to as delivered ownership. The mechanism sets some constraints on the possible reasons for an objection against a close request.

A close listener that is asked for permission can object for any reason if the close call does not force it to assume ownership of the closeable object.The close requester is aware of a possible failure. If the close call forces the ownership, the close listener must be careful. An objection is only allowed if the reason is temporary. As soon as the reason no longer exists, the owner automatically calls close on the object that should be closed, now being in the same situation as the initial close requester.

A permanent reason for objection is not allowed. For example,. the document is modified is not a valid reason to object, because it is unlikely that the document becomes unmodified by itself. Consequently, it could never be closed. Therefore, if an API programmer wants to avoid data loss, he must use the com.sun.star.util.XModifiable and com.sun.star.frame.XStorable interfaces of the document. The fact that a model refuses to be closed if it is modified is not dependable.

The interface com.sun.star.util.XCloseable inherits from com.sun.star.util.XCloseBroadcaster and has the following methods:

[oneway] void addCloseListener ( [in] com::sun::star::util::XCloseListener Listener );

[oneway] void removeCloseListener ( [in] com::sun::star::util::XCloseListener Listener );

void close ( [in] boolean DeliverOwnership )

The com.sun.star.util.XCloseListener is notified twice when close() is called on an XClosable :

void queryClosing ( [in] com::sun::star::lang::EventObject Source,

[in] boolean GetsOwnership )

void notifyClosing ( [in] com::sun::star::lang::EventObject Source )

Both com.sun.star.util.XCloseable:close() and com.sun.star.util.XCloseListener:queryClosing() throw a com.sun.star.util.CloseVetoException.

In the closing negotiations, an XClosable is asked to close itself. In the call to close(), the caller passes a boolean parameter DeliverOwnership to tell the XClosable that it will give up ownership in favor of an XCloseListener, or the XCloseable that might have to finish a job first, but will close the XClosable immediately when the job is completed.

After a call to close(), the XClosable notifies its listeners twice. First, it checks if itcan be closed. If not, it throws a CloseVetoException, otherwise it uses queryClosing() to see if a listener has any objections against closing. The value of DeliverOwnership is conveyed in the GetsOwnership parameter of queryClosing(). If no listener disapproves of closing, the XClosable exercises notifyClosing() on the listeners and disposes itself. The result of a call to close() on a model is that all frames, controllers and the model itself are destroyed. The result of a call to close() on a frame is that this frame is closed, but the model stays alive if there are other controllers.

If an XCloseListener does not agree on closing, it throws a CloseVetoException, and the XClosable lets the exception pass in close(), so that the caller receives the exception. The CloseVetoException tells the caller that closing failed. If the caller delegated its ownership in the call to close() by setting the DeliverOwnership parameter to true, an XCloseListener knows that it automatically assumes ownership by throwing a CloseVetoException.The caller knows that someone else is now the owner if it receives a CloseVetoException. The new owner is compelled to close the XClosable as soon as possible. If the XCloseable was the object that threw an exception, it is compelled also to close itself as soon as possible.

| No API exists for trivial components. As a consequence, components are not allowed to do anything that prevents them from being destroyed. For example, since the office crashes when a container window or component window has an open modal dialog, every component that wants to open a modal dialog must implement the com.sun.star.frame.XController interface.
--- | ---

If a model object supports XCloseable, calling dispose() on it is forbidden, try to close() the XCloseable and catch a possible CloseVetoException. Components that cannot cope with a destroyed model add a close listener at the model. This enables them to object when the model receives a close() request. They also add as a close listener if they are not already added as an (dispose) event listener. This can be done by every controller object that uses that model. Tt is also possible to let the model iterate through its controllers and call their suspend() methods explicitly as a part of its implementation of the close method. It is only necessary to know that a method close() must be called to close the model with its controllers. The method the model chooses is an implementation detail.

The example below closes a loaded document component. It does not save modified documents or prompts the user to save. (OfficeDev/DesktopEnvironment/FunctionHelper.java)

// Conditions: xDocument = m_xLoadedDocument
// Check supported functionality of the document (model or controller).
com.sun.star.frame.XModel xModel =
(com.sun.star.frame.XModel)UnoRuntime.queryInterface(
com.sun.star.frame.XModel.class,xDocument);
if(xModel!=null)
{
// It is a full featured office document.
// Try to use close mechanism instead of a hard dispose().
// But maybe such service is not available on this model.
com.sun.star.util.XCloseable xCloseable =
(com.sun.star.util.XCloseable)UnoRuntime.queryInterface(
com.sun.star.util.XCloseable.class,xModel);

if(xCloseable!=null)
{
try
{
// use close(boolean DeliverOwnership)

// The boolean parameter DeliverOwnership tells objects vetoing the close process that they may

// assume ownership if they object the closure by throwing a CloseVetoException

// Here we give up ownership. To be on the safe side, catch possible veto exception anyway.
xCloseable.close(true);
}
catch(com.sun.star.util.CloseVetoException exCloseVeto)
{
}
}
// If close is not supported by this model - try to dispose it.
// But if the model disagree with a reset request for the modify state
// we shouldn't do so. Otherwhise some strange things can happen.
else
{
com.sun.star.lang.XComponent xDisposeable =
(com.sun.star.lang.XComponent)UnoRuntime.queryInterface(
com.sun.star.lang.XComponent.class,xModel);
xDisposeable.dispose();
}
catch(com.sun.star.beans.PropertyVetoException exModifyVeto)
{
}
}
}
}

#### Storing Documents

After loading an office component successfully, the returned interface cis used to manipulate the component. Document specific interfaces, such as the interfaces com.sun.star.text.XTextDocument, com.sun.star.sheet.XSpreadsheetDocument or com.sun.star.drawing.XDrawPagesSupplier are retrieved using queryInterface().

If the office component supports the com.sun.star.frame.XStorable interface applying to every component implementing the service com.sun.star.document.OfficeDocument, it can be stored:

void store ( )

void storeAsURL ( [in] string sURL,

[in] sequence< com::sun::star::beans::PropertyValue > lArguments )

void storeToURL ( [in] string sURL,

[in] sequence< com::sun::star::beans::PropertyValue > lArguments )

boolean hasLocation ()

string getLocation ()

boolean isReadonly ()

The XStorable offers the methods store(), storeAsURL() and storeToURL() for storing. The latter two methods are called with a media descriptor.

The method store() overwrites an existing file. Calling this method on a document that was created from scratch using a private:factory/... URL leads to an exception.

The other two methods storeAsURL() and storeToURL() leave the original file untouched and differ after the storing procedure. The storeToURL() method saves the current document to the desired location without touching the internal state of the document. The method storeAsURL sets the Modified attribute of the document, accessible through its com.sun.star.util.XModifiable interface, to false and updates the internal media descriptor of the document with the parameters passed in the call. This changes the document URL.

The following example exports a Writer document, Writer/Web document or Calc sheet to HTML. (OfficeDev/DesktopEnvironment/FunctionHelper.java)

// Conditions: sURL = "file:///home/target.htm"
// xDocument = m_xLoadedDocument
// Export can be achieved by saving the document and using
// a special filter which can write the desired format.
// Normally this filter should be searched inside the filter
// configuration (using service com.sun.star.document.FilterFactory)
// but here we use well known filter names directly.

String sFilter = null;

// Detect document type by asking XServiceInfo

com.sun.star.lang.XServiceInfo xInfo = (com.sun.star.lang.XServiceInfo)UnoRuntime.queryInterface (
com.sun.star.lang.XServiceInfo .class, xDocument);

// Determine suitable HTML filter name for export.

if(xInfo!=null)
{
if(xInfo.supportsService ("com.sun.star.text.TextDocument ") == true)
sFilter = new String("HTML (StarWriter) ");
else
if(xInfo.supportsService ("com.sun.star.text.WebDocument ") == true)
sFilter = new String("HTML ");
else
if(xInfo.supportsService ("com.sun.star.sheet.SpreadsheetDocument ") == true)
sFilter = new String("HTML (StarCalc) ");
}

if(sFilter!=null)
{
// Build necessary argument list for store properties.
// Use flag "Overwrite" to prevent exceptions, if file already exists.

com.sun.star.beans.PropertyValue[] lProperties =
new com.sun.star.beans.PropertyValue[2];
lProperties[0] = new com.sun.star.beans.PropertyValue();
lProperties[0].Name = "FilterName ";
lProperties[0].Value = sFilter;
lProperties[1] = new com.sun.star.beans.PropertyValue();
lProperties[1].Name = "Overwrite ";
lProperties[1].Value = new Boolean(true);
com.sun.star.frame.XStorable xStore = (com.sun.star.frame.XStorable)UnoRuntime.queryInterface (

com.sun.star.frame.XStorable .class, xDocument);

xStore.storeAsURL (sURL, lProperties);
}

If a model is loaded or stored successfully, all parts of the media descriptor not explicitly excluded according to the media descriptor table in section 6.1.5 Office Development - OpenOffice.org Application Environment - Handling Documents - Loading Documents - MediaDescriptor must be provided by the methods getURL() and getArgs() in thecom.sun.star.frame.XModel interface. The separation of the URL and the other arguments is used, because the URL is the often the most wanted part for itsperformance optimized access.

| The XModel offers a method attachResource() that changes the media descriptor of the document, but this method should only be used in special cases, for example, by the implementer of a new document model and controller. The method attachResource() does not force reloading of the document. Validation checks are done when a document is loaded through MediaDescriptor. For example, if the resource is write protected, add Readonly to the MediaDescriptor and the filter name must match the data. A possible use for attachResource() could be creating a document from a template, where after loading successfully, the document's resource is changed to an "unnamed" state by deleting the URL.
--- | ---

#### Printing Documents

Printing revolves around the interface com.sun.star.view.XPrintable. Its methods and special printing features for the various document types are described in the document chapters 7.2.3 Text Documents - Handling Text Document Files - Printing Text Documents, 8.2.3 Spreadsheet Documents - Handling Spreadsheet Document Files - Printing Spreadsheet Documents, 9.2.3 Drawing - Handling Drawing Document Files - Printing Drawing Documents and 9.4.2 Drawing - Handling Presentation Document Files - Printing Presentation Documents.

### 6.1.6 Using the Dispatch Framework

]The component framework with the Frame-Controller-Model paradigm builds the skeleton of the global object structure. Other frameworks are defined that enrich the communication between an office component and the desktop environment. Usually they start at a frame object for the frame anchors an office component in the desktop environment.

One framework is the dispatch framework. Its main purpose defines interfaces for a generic communication between an office component and a user interface. This communication process handles requests for command executions and gives information about the various attributes of an office component. Generic means that the user interface does not have to know all the interfaces supported by the office component.The user interfaces sends messages to the office component and receives notifications.Tthe messages use a simple format. The entire negotiation about supported commands and parameters can happen at runtime while an application built on the specialized interfaces of the component are created at compile or interpret time. This generic approach is achieved by looking at an office component differently, not as objects with method-based interfaces, but as slot machines that take standardized command tokens.

We have discussed the differences between the different document types. The common functionality covers the generic features, that is, an office component is considered to be the entirety of its controller, its model and many document-specific interfaces. To implement a user interface for a component, it would be closely bound to the component and its specialized interfaces. If different components use different interfaces and methods for their implementations, similar functions cannot be visualized by the same user interface implementation. For instance, an action like Edit – Select All leads to different interface calls depending on the document type it is sent to. From a user interface perspective, it would be better to define abstract descriptions of the actions to be performed and let the components decide how to handle these actions, or not to handle . These abstract descriptions and how to handle them is specified in the dispatch framework.

#### Command URL

In the dispatch framework, every possible user action is defined as an executable command, and every possible visualization as a reflection of something that is exposed by the component is defined as an attribute. Every executable command and every attribute is a feature of the office component, and the dispatch framework gives every feature a name called command URL. It is represented by a com.sun.star.util.URL struct.

Command URLs are strings that follow the protocol_scheme:protocol_specific_part pattern. Public URL schemes, such as file: or http can be used here.Executing a request with a URL that points to a location of a document means that this document is loaded. In general, both parts of the command URL can be arbitrary strings, but a request cannot be executed if there is an object that does not know how to handle its command URL.

#### Processing Chain

A request is created by any object.User interface objects can create requests. Consider a toolbox where different functions acting on the office component are presented as buttons. When a button is clicked, the desired functionality is executed. If the code assigned to the button is provided with a suitable command URL, it handles the user action by creating the request and finding a component that can handle it. The button handler does not require any prior knowledge of the component and how it would go about its task.

This situation is handled by the design pattern chain of responsibility. Everything a component needs to know to execute a request is the last link of a chain of objects capable of executing requests. If this object gets the request, it checks if it can handle it or passes it to the next chain member until the request is executed, or the end of the chain is reached.

The chain members in the dispatch framework are objects implementing the interface com.sun.star.frame.XDispatchProvider. Every frame and controller supports it.In the simplest case, the chain consists of two members, a frame and its controller, but concatenating several chain parts on demand of a frame or a controller is possible. A controller once called, passes on the call, that is, it can use internal frames created by its implementation. A frame also passes the call to other objects, for example, its parent frame.

The current implementation of the chain is different from a simple chain.A frame is always the leading chain member and must be called initially, but in the default implementation used in OpenOffice.org, the frame first(!) asks its controller before it goes on with the request. Other frame implementations handle this in a different way. Other chain members are inserted into the call sequence before the controller uses the dispatch interception capability of a frame. The developers should not rely on any particular order inside the chain.

The dispatch framework uses a generic approach to describe and handle requests with a lose coupling between the participating objects. To work correctly, it is necessary to follow certain rules:

1. Every chain starts at a frame, and this object decides if it passes on the call to its controller. The controller is not called directly from the outside. This is not compulsory for internal usage of the dispatch API inside an office component implementation. Ther two reasons for this rule are:

- - A frame providesa com.sun.star.frame.XDispatchProviderInterception interface, where other dispatch providers dock. The frame implementation guarantees that these interceptors are called before the frame handles the request or passes it to the controller. This allows a sophisticated customization of the dispatch handling.

  - If a component is placed into a context where parts of its functionality are not be exposed to the outside, a special frame implementation is used to suppress or handle requests before they are passed to the controller. This frame can add or remove arguments to requests and exchange them.

1. A command URL isparsed into a com.sun.star.util.URL struct before passing it to a dispatch provider, because it is assumed that the call is passed on to several objects. Having a preparsed URL saves parsing the command string repeatedly. Parsing means that the members Complete, Main, Protocol and at least one more member of the com.sun.star.util.URL struct, depending on the given protocol scheme have to be set. Additional members are set if the concrete URL protocol supports them. For well known protocol schemes and protocol schemes specific to OpenOffice.org, the service com.sun.star.util.URLTransformer is used to fill the struct from a command URL string. For other protocols, the members are set explicitly, but it is also possible to write an extended version of the URLTransformer service to carry out URL parsing. An extended URLTransformer must support all protocols supported by the default URLTransformer implementation, for example, by instantiating the old implementation by its implementation name and forwarding all known URLs to it, except URLs with new protocols.

The dispatch framework connects an object that creates a request with another object that reacts on the request. In addition, it provides feedback to the requester. It can tell if the request is currently allowed or not. If the request acts on a specific attribute of an object, it c provides the current status of this attribute. Altogether, this is called status information, represented by a com.sun.star.frame.FeatureStateEvent struct. This information is reflected in a user interface by enabling or disabling controls to show their availability, or by displaying the status of objects. For example, a pressed button for the bold attribute of text, or a numeric value for the text height in a combo box.

The com.sun.star.frame.XDispatchProvider interface does not handle requests, but delegates every request to an individual dispatch object implementing com.sun.star.frame.XDispatch.

| This is the concept, but the implementation is not forced and it may decide to return the same object for every request. It is not recommened to use the dispatch provider object as a dispatch object.
--- | ---

#### Dispatch Process

This section describes the necessary steps to handle dispatch providers and dispatch objects. The illustration below shows the services and interfaces of the the Dispatch framework.

Illustration 6.15: Dispatch Framework

##### Getting a Dispatch Object

First, create a command URL that represents the desired functionality ensuring that it is parsed as described above. Tables with possible command URLs for the default office components of OpenOffice.org are located in the appendix.

Request the com.sun.star.frame.XDispatchProvider interface of the frame that contains the office component for a dispatch object for the command URL by calling its queryDispatch() method.

com::sun::star::frame::XDispatch queryDispatch ( [in] com::sun::star::util::URL URL,

[in] string TargetFrameName,

[in] long SearchFlags )

sequence< com::sun::star::frame::XDispatch > queryDispatches (

[in] sequence< com::sun::star::frame::DispatchDescriptor > Requests )

The additional parameters (TargetFrameName, SearchFlags) of this call are only used for dispatching public URL schemes, because they specify a target frame and frame search mode to the loading process. Valid target names and search flags are described in the section 6.1.5 Office Development - OpenOffice.org Application Environment - Handling Documents - Loading Documents - Target Frame. The targets "_self", "_parent" and "_top" are well defined, so that they can be used, because a queryDispatch() call starts at a frame object. Using frame names or search flags with command URLs does not have any meaning in the office components in OpenOffice.org.

You receive a dispatch object that supports at least com.sun.star.frame.XDispatch:

[oneway] void dispatch ( [in] com::sun::star::util::URL URL,

[in] sequence< com::sun::star::beans::PropertyValue > Arguments )

[oneway] void addStatusListener ( [in] com::sun::star::frame::XStatusListener Control,

[in] com::sun::star::util::URL URL )

[oneway] void removeStatusListener ( [in] com::sun::star::frame::XStatusListener Control,

[in] com::sun::star::util::URL URL )

##### Listening for Status Information

If a dispatch object is received, add a listener for status events by calling its addStatusListener() method. A com.sun.star.frame.XStatusListener implements:

[oneway] void statusChanged ( [in] com::sun::star::frame::FeatureStateEvent Event )

Keep a reference to the dispatch object until you call the removeStatusListener() method, because it is not sure that any other object will keep it alive. If a status listener is not registered, because you want to dispatch a command,and are not interested in status events, release all references to the dispatch object immediately after usage. If a dispatch object is not received, the desired functionality is not available. If you have a visual user interface element that represents that functionality, disable it.

If a status listener is registered and there is status information, a com.sun.star.frame.FeatureStateEvent is received immediately after registering the listener. Status information is still received later if the status changes and you are still listening. The IsEnabled member of the com.sun.star.frame.FeatureStateEvent tells you if the functionality is currently available, and the State member holds information about a status that could be represented by UI elements. Its type depends on the command URL. A boolean status information is visualized in a pressed or not pressed look of a toolbox button. Other types need complex elements, such as combo boxes or spinfields embedded in a toolbox that show the current font and font size. If the State member is empty, the action does not have an explicit status, such as the menu item File – Print. The current status can be ambiguous, because more than one object is selected and the objects are in a different status, for example. selected text that is partly formatted bold and partly regular.

A special event is a status event where the Requery flag is set. This is a request to release all references to the dispatch object and to ask the dispatch provider for a new object, because the old one has become invalid. This allows the office components to accommodate internal context changes. It is possible that a dispatch object is not received, because the desired functionality has become unavailable.

If you do not get any status information in your statusChanged() implementation, assume that the functionality is always available, but has no explicit status.

If you are no longer interested in status events, use the removeStatusListener() method and release all references to the dispatch object. You may get a disposing() callback from the dispatch object when it is going to be destroyed. It is not necessary to call removeStatusListener(). Ensure that you do not hold any references to the dispatch object anymore.

##### Listening for Context Changes

Sometimes internal changes, for example, travelling from a text paragraph to a text table, or selecting a different type of object, force an office component to invalidate all referenced dispatch objects and provides other dispatch objects, including dispatches for command URLs it could not handle before. The component then calls the contextChanged() method of its frame, and the frame broadcasts the corresponding com.sun.star.frame.FrameActionEvent. For this reason, register a frame action listener using addFrameActionListener() at frames you want dispatch objects. Refer to section 6.1.3 Office Development - OpenOffice.org Application Environment - Using the Component Framework - Frames - XFrame - Frame Actions for additional information. If the listener is called back with a CONTEXT_CHANGED event, release all dispatch objects and query new dispatch objects for every command URL you require. You can also try command URLs that did not get a dispatch object before.

If you are no longer interested in context changes of a frame, use the removeFrameActionListener() method of the frame to deregister and release all references to the frame. If you get a disposing() request from the frame in between, it is not necessary to call removeFrameActionListener(), but you must release all frame references you are currently holding.

##### Dispatching a Command

If the desired functionality is available, execute it by calling the dispatch() method of the dispatch object. This method is called with the same command URL you used to get it, and optionally with a sequence of arguments of type com.sun.star.beans.PropertyValue that depend on the command. It is not redundant that supplied the URL again, because it is allowed to use one dispatch object for many command URLs. The appendix shows the names and types for the parameters. However, the command URLs for simple user interface elements, such as menu entries or toolbox buttons send no parameters. Complex user interface elements use parameters, for example, a combo box in a toolbar that changes the font height. (OfficeDev/DesktopEnvironment/FunctionHelper.java)

// Conditions: sURL = "private:factory/swriter"
// lProperties = new com.sun.star.beans.PropertyValue[0]
// xSMGR = m_xServiceManager
// xListener = this

// xFrame = a given frame
// Query the frame for right interface which provides access to all
// available dispatch objects.
com.sun.star.frame.XDispatchProvider xProvider =
(com.sun.star.frame.XDispatchProvider)UnoRuntime.queryInterface (
com.sun.star.frame.XDispatchProvider .class, xFrame );

// Create and parse a valid URL
// Note: because it is an in/out parameter we must use an array of URLs
com.sun.star.util.XURLTransformer xParser =
(com.sun.star.util.XURLTransformer)UnoRuntime.queryInterface (
com.sun.star.util.XURLTransformer .class,

xSMGR.createInstance("com.sun.star.util.URLTransformer" ));
com.sun.star.util.URL[] aParseURL = new com.sun.star.util.URL[1];
aParseURL[0] = new com.sun.star.util.URL();
aParseURL[0].Complete = sURL;

xParser.parseStrict (aParseURL);
// Ask for dispatch object for requested URL and use it.
// Force given frame as target "" which means the same like "_self".
xDispatcher = xProvider.queryDispatch(aParseURL[0],"",0);

if(xDispatcher!=null)
{
xDispatcher.addStatusListener (xListener,aParseURL[0]);
xDispatcher.dispatch (aParseURL[0],lProperties);
}

#### Dispatch Results

Every dispatch object implement optional interfaces. An important extension is the com.sun.star.frame.XNotifyingDispatch interface for dispatch results. The dispatch() call is a void method and should be treated as an asynchronous or oneway call, therefore a dispatch result can not be passed as a return value, rather, a callback interface is necessary. The interface that provides dispatch results by a callback is the com.sun.star.frame.XNotifyingDispatch interface:

[oneway] void dispatchWithNotification ( [in] com::sun::star::util::URL URL,

[in] sequence< com::sun::star::beans::PropertyValue > Arguments,

[in] com::sun::star::frame::XDispatchResultListener Listener )

Its method dispatchWithNotification() takes a com.sun.star.frame.XDispatchResultListener interface that is called after a dispatched URL has been executed.

| Although the dispatch process is considered to be asynchronous, this is not necessarily so. Therefore, be prepared to get the dispatch result notification before the dispatch call returns.
--- | ---

The dispatch result is transferred as a com.sun.star.frame.DispatchResultEvent struct in the callback method dispatchFinished(). The State member of this struct tells if the dispatch was successful or not, while the Result member contains the value that would be returned if the call had been executed as a synchronous function call. The appendix shows the types of return values. If a public URL is dispatched, the dispatch result is a reference to the frame the component was loaded into. (OfficeDev/DesktopEnvironment/FunctionHelper.java)

// Conditions: sURL = "private:factory/swriter"
// lProperties = new com.sun.star.beans.PropertyValue[0]
// xSMGR = m_xServiceManager
// xListener = this
// Query the frame for right interface which provides access to all
// available dispatch objects.
com.sun.star.frame.XDispatchProvider xProvider =
(com.sun.star.frame.XDispatchProvider)UnoRuntime.queryInterface (
com.sun.star.frame.XDispatchProvider .class, xFrame);

// Create and parse a valid URL
// Note: because it is an in/out parameter we must use an array of URLs
com.sun.star.util.XURLTransformer xParser =
(com.sun.star.util.XURLTransformer)UnoRuntime.queryInterface(
com.sun.star.util.XURLTransformer .class,
xSMGR.createInstance ("com.sun.star.util.URLTransformer "));

// Ask for right dispatch object for requested URL and use it.
// Force given frame as target "" which means the same like "_self".
// Attention: The interface XNotifyingDispatch is an optional one!

com.sun.star.frame.XDispatch xDispatcher =
xProvider.queryDispatch (aURL,"",0);

com.sun.star.frame.XNotifyingDispatch xNotifyingDispatcher =
(com.sun.star.frame.XNotifyingDispatch)UnoRuntime.queryInterface (
com.sun.star.frame.XNotifyingDispatch.class , xDispatcher );

if(xNotifyingDispatcher!=null)
xNotifyingDispatcher.dispatchWithNotification (aURL, lProperties, xListener);

#### Dispatch Interception

The dispatch framework described in the last chapter establishes a communication between a user interfaces and an office component. Both can be OpenOffice.org default components or custom components. Sometimes it is not necessary to replace a UI element by a new implementation. It can be sufficient to influence its visualized state or to redirect user interactions to external code. This is the typical use for dispatch interception.

The dispatch communication works in two directions: status information is transferred from the office component to the UI elements and user requests travel from the UI element to the office component. Both go through the same switching center that is, an object implementing com.sun.star.frame.XDispatch. The UI element gets this object by calling queryDispatch() at the frame containing the office component, and usually receives an object that connects to code inside the frame, the office component or global services in OpenOffice.org. The frame offers an interface that is used to return third-party dispatch objects that provide the UI element with status updates. For example, it is possible to disable a UI element that would not be disabled otherwise. Another possibility is to write replacement code that is called by the UI element if the user performs a suitable action.

Dispatch objects are provided by objects implementing the com.sun.star.frame.XDispatchProvider interface, and that is the interface you are required to implement. There is an extra step where the dispatch provider must be attached to the frame to intercept the dispatching communication, therefore the dispatch provider becomes a part of the chain of responsibility described in the previous section. This is accomplished by implementing com.sun.star.frame.XDispatchProviderInterceptor.

This chain usually only consists of the frame and the controller of the office component it contains, but the frame offers the com.sun.star.frame.XDispatchProviderInterception interface where other providers are inserted. They are called before the frame tries to find a dispatch object for a command URL, so that it is possible to put the complete dispatch communication in a frame under external control. More than one interceptor can be registered, thus building a bigger chain.

Routing every dispatch through the whole chain becomes a performance problem, because could be more than a hundred possible clients asking for a dispatch object. For this reason there is also an API that limits the routing procedure to particular commands or command groups. This is described below.

Once the connection is established, the dispatch interceptor decides how requests for a dispatch object are dealt with. When asked for a dispatch object for a Command URL, it can:

- Return an empty interface that disables the corresponding functionality.

  There's a bug in Ooo1.0/SO6.0 that this does not work, so disabling must be done explicitly (see below). It will be fixed in Ooo1.02/SO6.02.

- Pass the request to the next chain member, called slave dispatcher provider described below if it is not interested in that functionality.

- Handle the request and return an object implementing com.sun.star.frame.XDispatch. As described in the previous chapter, client objects may register at this object as status event listeners. The dispatch object returns any possible status information as long as the type of the "State" member in the com.sun.star.frame.FeatureStateEvent struct has one of the expected types, otherwise the client requesting the status information can not handle it properly. The expected types must be documented together with the existing commands.For example, if a menu entry wants status information, it handles a void, that is, do nothing special or a boolean state by displaying a check mark, but nothing else.
  The status information could contain a disable directive. Note that a dispatch object returns status information immediately when a listener registers. Any , events change can be broadcasted at arbitrary points in time.

- The returned dispatch object is also used by client objects to dispatch the command that matches the command URL. The dispatch object receiving this request checks if the code it wants to execute is valid under the current conditions. It is not sufficient to rely on disable requests, because a client is not forced to register as a status listener if it wants to dispatch a request.

The slave dispatch provider and master dispatch provider in the com.sun.star.frame.XDispatchProviderInterceptor interface are a bit obscure at first. They are two pointers to chain members in both directions, next and previous, where the first and last member in the chain have special meanings and responsibilities.

The command dispatching passes through a chain of dispatch providers, starting at the frame. If the frame is answered to include an interceptor in this chain, the frame inserts the interceptor in the chain and passes the following chain member to the new chain member, so that calls are passed along the chain if it does not want to handle them.

If any interceptor is deregistered, the frame puts the lose ends together by adjusting the master and slave pointer of the chain successor and predecessor of the element that is going to be removed from the chain. All of them are interceptors, so only the last slave is a dispatch provider.

The frame takes care of the whole chain in the register or deregister of calls in the dispatch provider interceptor, so that the implementer of an interceptor does not have to be concerned with the chain construction.

### 6.1.7 Java Window Integration

This section discusses experiences obtained during the development of Java-OpenOffice.org integration. Usually, developers use the OfficeBean for this purpose. The following provides background information about possible strategies to reach this goal.

There are multiple possibilities to integrate local windows with OpenOffice.org windows. This chapter shows the integration of OpenOffice.org windows into a Java bean environment. Some of this information maybe helpful with other local window integrations.

#### The Window Handle

An important precondition is the existence of a system window handle of the own Java window. For this, use a java.awt.Canvas and the following JNI methods:

- a method to query the window handle (HWND on Windows, X11 ID on UNIX)

- a method to identify the operating system, for example, UNIX, Windows, or Macintosh

For an example, see bean/com/sun/star/beans/LocalOfficeWindow.java

The two methods getNativeWindow() and getNativeWindowSystemType() are declared and exported, but implemented for windows in bean/native/win32/com_sun_star_beans_LocalOfficeWindow.c through JNI

| It has to be a java.awt.Cavans. These JNI methods cannot be implemented at a Swing control, because it does not have its own system window. You can use a java.awt.Canvas in a Swing container environment.
--- | ---

| The handle is not available before the window is visible, otherwise the JNI function does not work. One possibility is to cache the handle and set it in show() or setVisible().
--- | ---

#### Using the Window Handle

The window handle create the OpenOffice.org window. There are two ways to accomplish this:

##### A Hack

This option is mentioned because there are situations where this is the only feasible method. The knowledge of this option can help in other situations.

Add the UNO interface com.sun.star.awt.XWindowPeer so that it is usable for the OpenOffice.org window toolkit. This interface can have an empty implementation. In com.sun.star.awt.XToolkit:createWindow(), another interface com.sun.star.awt.XSystemDependentWindowPeer is expected that queries the HWND. Thus, XWindowPeer is for transporting and com.sun.star.awt.XSystemDependentWindowPeer queries the HWND.

This method getsa com.sun.star.awt.XWindow as a child of your own Java window, that is used to initialize a com.sun.star.frame.XFrame. (OfficeDev/DesktopEnvironment/FunctionHelper.java)

com.sun.star.awt.XToolkit xToolkit =
(com.sun.star.awt.XToolkit)UnoRuntime.queryInterface(
com.sun.star.awt.XToolkit.class,
xSMGR.createInstance("com.sun.star.awt.Toolkit"));

// this is the canvas object with the JNI methods
aParentView = ...
// some JNI methods cannot work before this

aParentView.setVisible(true);

// now wrap the canvas (JavaWindowPeerFake) and add the necessary interfaces

com.sun.star.awt.XWindowPeer xParentPeer =
(com.sun.star.awt.XWindowPeer)UnoRuntime.queryInterface(

com.sun.star.awt.XWindowPeer.class,

new JavaWindowPeerFake(aParentView));

com.sun.star.awt.WindowDescriptor aDescriptor = new com.sun.star.awt.WindowDescriptor();

aDescriptor.Type = com.sun.star.awt.WindowClass.TOP;

aDescriptor.WindowServiceName = "workwindow";

aDescriptor.ParentIndex = 1;

aDescriptor.Parent = xParentPeer;

aDescriptor.Bounds = new com.sun.star.awt.Rectangle(0,0,0,0);

if (aParentView.getNativeWindowSystemType()==com.sun.star.lang.SystemDependent.SYSTEM_WIN32)
aDescriptor.WindowAttributes = com.sun.star.awt.WindowAttribute.SHOW;

else

aDescriptor.WindowAttributes = com.sun.star.awt.WindowAttribute.SYSTEMDEPENDENT;

// now the toolkit can create an com.sun.star.awt.XWindow
com.sun.star.awt.XWindowPeer xPeer = xToolkit.createWindow( aDescriptor );

com.sun.star.awt.XWindow xWindow =
(com.sun.star.awt.XWindow)UnoRuntime.queryInterface(

com.sun.star.awt.XWindow.class,

xPeer);

##### Legal Solution

The com.sun.star.awt.Toolkit service has a method com.sun.star.awt.XSystemChildFactory with a method createSystemChild(). This accepts an any with a wrapped HWND or X Window ID, as long and the system type, such as Windows, Java, and UNIX directly. Here you create an com.sun.star.awt.XWindow. This method cannot be used in OpenOffice.org build versions before src642, because the process ID parameter is unknown to the Java environment. Newer versions do not check this parameter, thus this new, method works.

| As a user of com.sun.star.awt.XSystemChildFactory:createSystemChild() ensure that your client (Java application) and your server (OpenOffice.org) use the same display. Otherwise the window handle is not interchangeable.
--- | ---

(OfficeDev/DesktopEnvironment/FunctionHelper.java)

com.sun.star.awt.XToolkit xToolkit =
(com.sun.star.awt.XToolkit)UnoRuntime.queryInterface(

com.sun.star.awt.XToolkit.class,

xSMGR.createInstance("com.sun.star.awt.Toolkit"));

// this is the canvas with the JNI functions
aParentView = ...
// some JNI funtions will not work withouth this

aParentView.setVisible(true);

// no wrapping necessary, simply use the HWND

com.sun.star.awt.XSystemChildFactory xFac =
(com.sun.star.awt.XSystemChildFactory)UnoRuntime.queryInterface(

com.sun.star.awt.XSystemChildFactory.class,

xToolkit);

Integer nHandle = aParentView.getHWND();

byte[] lIgnoredProcessID = new byte[0];

com.sun.star.awt.XWindowPeer xPeer =
xFac.createSystemChild(
(Object)nHandle,
lIgnoredProcessID,
com.sun.star.lang.SystemDependent.SYSTEM_WIN32);

com.sun.star.awt.XWindow xWindow =
(com.sun.star.awt.XWindow)UnoRuntime.queryInterface(

com.sun.star.awt.XWindow.class,

xPeer);

| The old method still works and can be used, but it should be considered deprecated. If in doubt, implement both and try the new method at runtime. If it does not work, try the hack.
--- | ---

Resizing

Another difficulty is resizing the window. Normally, the child window expects resize events of the parent. The child does not resize it window, because it must know the layout of the parent window. The VCL, OpenOffice.org's windowing engine creates a special system child window, thus we can resize windows.

The parent window can be filled "full size" with the child window, but only for UNIX and not for Windows. The VCL's implementation is system dependent.

The bean deals with this issue by adding another function to the local library. Windows adds arbitrary properties to an HWND. You can also subclass the window, that is, each Windows window has a function pointer or callback to the function that performs the event handling (WindowProcedure). Using this, it is possible to treat events by calling your own methods. This is useful whenever the window is not created by you and you need to influence the behavior of the window.

In this case, the Java window has not been created by us, but we need to learn about resize events to forward these to the OpenOffice.org window. Look at the file bean/native/win32/com_sun_star_beans_LocalOfficeWindow.c , and find the method OpenOfficeWndProc(). In the first call of the JNI function Java_com_sun_star_beans_LocalOfficeWindow_getNativeWindow() of this file, the own handler is applied to the foreign window.

| The old bean implementation had a bug that is fixed in newer versions. If you did not check if the function pointer was set, and called Java_com_sun_star_beans_LocalOfficeWindow_getNativeWindow() multiple times, you created a chain of functions that called each other with the result of an endless recursion leading to a stack overflow. If the own handler is already registered, it is now marked in one of the previously mentioned properties registered with an HWND:
--- | ---

In the future, VCL will do this sub-classing by itself, even on Windows. This will lead to equal behavior between Windows and UNIX.

The initial size of the window is a related problem. If a canvas is connected with a OpenOffice.org window, set both sizes to a valid, positive value, otherwise the OpenOffice.org window will not be visible. If you are using a non-product build of OpenOffice.org, you see an assertion failed "small world isn't it". This might change when the sub-classing is done by VCL in the future.

There is still one unresolved problem. The code mentioned above works with Java 1.3, but not for Java 1.4. There, the behavior of windows is changed. Where Java 1.3 sends real resize events from the own WindowProc, Java 1.4 does a re-parenting. The canvas window is destroyed and created again. This leads to an empty window with no OpenOffice.org window. This problem is under investigation.

#### More Remote Problems

There are additional difficulties to window handles and local window handles. Some personal experiences of one of the OpenOffice.org authors are provided:

- Listeners in Java should be implemented in a thread. The problem is that SolarMutex, a mutex semaphore of OpenOffice.org, one-way UNO methods and the global Java GUI thread do not work together.

- The Java applet should release its listeners. If they stay in the containers of OpenOffice.org after the Java process ends, UNO throws a com.sun.star.lang.DisposedException, which are not caught correctly. Java does not know destructors, therefore it is a difficult to follow this advice. One possibility is to register a Thread object at java.Runtime as a ShutDownHook. This is called even when CTRL-C is pressed on the command line where you can deregister the listeners. Because listeners are threads, there is some effort.

## 6.2 Common Application Features

### 6.2.1 Clipboard

This chapter introduces the usage of the clipboard service com.sun.star.datatransfer.clipboard.SystemClipboard. The clipboard serves as a data exchange mechanism between OpenOffice.org custom components, or between custom components and external applications. It is usually used for copy and paste operations.

| Note: The architecture of the OpenOffice.org clipboard service is strongly conforming to the Java clipboard specification.
--- | ---

Different platforms use different methods for describing data formats available on the clipboard. Under Windows, clipboard formats are identified by unique numbers, for example, under X11, a clipboard format is identified by an ATOM. To have a platform independent mechanism, the OpenOffice.org clipboard supports the concept of DataFlavors. Each instance of a DataFlavor represents the opaque concept of a data format as it would appear on a clipboard. A DataFlavor defined in com.sun.star.datatransfer.DataFlavor has three members:

Members of com.sun.star.datatransfer.DataFlavor
MimeType | A string that describes the data. This string must conform to Rfc2045 and Rfc2046 with one exception. The quoted parameter may contain spaces. In section 6.2.1 Office Development - Common Application Features - Clipboard - OpenOffice.org Clipboard Data Formats, a list of common DataFlavors supported by OpenOffice.org is provided.
HumanPresentableName | The human presentable name for the data format that this DataFlavor represents.
DataType | The type of the data. In section 6.2.1 Office Development - Common Application Features - Clipboard - OpenOffice.org Clipboard Data Formats there is a list of common DataFlavors supported by OpenOffice.org and their corresponding DataType.

The carrier of the clipboard data is a transferable object that implements the interface com.sun.star.datatransfer.XTransferable. A transferable object offers one or many different DataFlavors.

#### Using the Clipboard

##### Pasting Data

The following Java example demonstrates the use of the clipboard service to paste from the clipboard. (OfficeDev/Clipboard/Clipboard.java)

import com.sun.star.datatransfer.*;
import com.sun.star.datatransfer.clipboard.*;
import com.sun.star.uno.AnyConverter;

...

// instantiate the clipboard service

Object oClipboard =

xMultiComponentFactory.createInstanceWithContext(

"com.sun.star.datatransfer.clipboard.SystemClipboard",

xComponentContext);

// query for the interface XClipboard

XClipboard xClipboard = (XClipboard)

UnoRuntime.queryInterface(XClipboard.class, oClipboard);

//---------------------------------------------------

// get a list of formats currently on the clipboard

//---------------------------------------------------

XTransferable xTransferable = xClipboard.getContents();

DataFlavor[] aDflvArr = xTransferable.getTransferDataFlavors();

// print all available formats

System.out.println("Reading the clipboard...");

System.out.println("Available clipboard formats:");

DataFlavor aUniFlv = null;

for (int i=0;i<aDflvArr.length;i++)

{

System.out.println( "MimeType: " +

aDflvArr[i].MimeType +

" HumanPresentableName: " +

aDflvArr[i].HumanPresentableName );

// if there is the format unicode text on the clipboard save the

// corresponding DataFlavor so that we can later output the string

if (aDflvArr[i].MimeType.equals("text/plain;charset=utf-16"))

{

aUniFlv = aDflvArr[i];

}

}

System.out.println("");

try

{

if (aUniFlv != null)

{

System.out.println("Unicode text on the clipboard...");

Object aData = xTransferable.getTransferData(aUniFlv); System.out.println(AnyConverter.toString(aData));

}

}

catch(UnsupportedFlavorException ex)

{

System.err.println( "Requested format is not available" );

}

...

##### Copying Data

To copy to the clipboard, implement a transferable object that supports the interface com.sun.star.datatransfer.XTransferable. The transferable object offers arbitrary formats described by DataFlavors.

The following Java example demonstrates the implementation of a transferable object. This transferable object contains only one format, unicode text. (OfficeDev/Clipboard/TextTransferable.java)

//---------------------------------------
// A simple transferable containing only
// one format, unicode text
//---------------------------------------

public class TextTransferable implements XTransferable
{
public TextTransferable(String aText)
{
text = aText;
}
// XTransferable methods

public Object getTransferData(DataFlavor aFlavor) throws UnsupportedFlavorException
{
if ( !aFlavor.MimeType.equalsIgnoreCase( UNICODE_CONTENT_TYPE ) )
throw new UnsupportedFlavorException();

return text;
}

public DataFlavor[] getTransferDataFlavors()
{
DataFlavor[] adf = new DataFlavor[1];
DataFlavor uniflv = new DataFlavor(
UNICODE_CONTENT_TYPE,
"Unicode Text",
new Type(String.class) );

adf[0] = uniflv;
return adf;
}

public boolean isDataFlavorSupported(DataFlavor aFlavor)
{
return aFlavor.MimeType.equalsIgnoreCase(UNICODE_CONTENT_TYPE);
}
// members
private final String text;
private final String UNICODE_CONTENT_TYPE = "text/plain;charset=utf-16";
}

Everyone providing data to the clipboard becomes a clipboard owner. A clipboard owner is an object that implements the interface com.sun.star.datatransfer.clipboard.XClipboardOwner. If the current clipboard owner loses ownership of the clipboard, it receives a notification from the clipboard service. The clipboard owner can use this notification to destroy the transferable object that was formerly on the clipboard. If the transferable object is a self-destroying object, destroying clears all references to the object. If the clipboard service is the last client, clearing the reference to the transferable object leads to destruction.

All data types except for text have to be transferred as byte array. The next example shows this for a bitmap.

public class BmpTransferable implements XTransferable

{

public BmpTransferable(byte[] aBitmap)

{

mBitmapData = aBitmap;

}

// XTransferable methods

public Object getTransferData(DataFlavor aFlavor) throws UnsupportedFlavorException

{

if ( !aFlavor.MimeType.equalsIgnoreCase(BITMAP_CONTENT_TYPE ) )

throw new UnsupportedFlavorException();

return mBitmapData;

}

public DataFlavor[] getTransferDataFlavors()

{

DataFlavor[] adf = new DataFlavor[1];

DataFlavor bmpflv= new DataFlavor(

BITMAP_CONTENT_TYPE,

"Bitmap",

new Type(byte[].class) );

adf[0] = bmpflv;

return adf;

}

public boolean isDataFlavorSupported(DataFlavor aFlavor)

{

return aFlavor.MimeType.equalsIgnoreCase(BITMAP_CONTENT_TYPE);

}

// members

private byte[] mBitmapData;

private final String BITMAP_CONTENT_TYPE = "application/x-openoffice;windows_formatname="Bitmap"";

}

The following Java example shows an implementation of the interface com.sun.star.datatransfer.clipboard.XClipboardOwner. (OfficeDev/Clipboard/ClipboardOwner.java)

...

//----------------------------------------

// A simple clipboard owner implementation

//----------------------------------------

public class ClipboardOwner implements XClipboardOwner

{

public void lostOwnership(

XClipboard xClipboard,

XTransferable xTransferable )

{

System.out.println("");

System.out.println( "Lost clipboard ownership..." );

System.out.println("");

isowner = false;

}

public boolean isClipboardOwner()

{

return isowner;

}

private boolean isowner = true;

}

...

The last two samples combined show how it is possible to copy data to the clipboard as demonstrated in the following Java example. (OfficeDev/Clipboard/Clipboard.java)

import com.sun.star.datatransfer.*;
import com.sun.star.datatransfer.clipboard.*;
import com.sun.star.uno.AnyConverter;

...

// instantiate the clipboard service

Object oClipboard =

xMultiComponentFactory.createInstanceWithContext(

"com.sun.star.datatransfer.clipboard.SystemClipboard",

xComponentContext);

// query for the interface XClipboard

XClipboard xClipboard = (Xclipboard)UnoRuntime.queryInterface(XClipboard.class, oClipboard);

//---------------------------------------------------
// becoming a clipboard owner
//---------------------------------------------------

System.out.println("Becoming a clipboard owner...");
System.out.println("");

ClipboardOwner aClipOwner = new ClipboardOwner();
xClipboard.setContents(new TextTransferable("Hello World!"), aClipOwner);

while (aClipOwner.isClipboardOwner())
{
System.out.println("Still clipboard owner...");
Thread.sleep(1000);
}

...

##### Becoming a Clipboard Viewer

It is useful to listen to clipboard changes. User interface controls may change their visible appearance depending on the current clipboard content. To avoid polling on the clipboard, the clipboard service supports an asynchronous notification mechanism. Every client that needs notification about clipboard changes implements the interface com.sun.star.datatransfer.clipboard.XClipboardListener and registers as a clipboard listener.
Implementing the interface com.sun.star.datatransfer.clipboard.XClipboardListener is simple as the next Java example demonstrates. (OfficeDev/Clipboard/ClipboardListener.java)

//----------------------------
// A simple clipboard listener
//----------------------------

public class ClipboardListener implements XClipboardListener
{
public void disposing(EventObject event)
{
}
public void changedContents(ClipboardEvent event)
{
System.out.println("");
System.out.println("Clipboard content has changed!");
System.out.println("");
}
}

If the interface was implemented by the object, it registers as a clipboard listener. A clipboard listener deregisters if clipboard notifications are no longer necessary. Both aspects are demonstrated in the next example. (OfficeDev/Clipboard/Clipboard.java)

// instantiate the clipboard service

Object oClipboard =
xMultiComponentFactory.createInstanceWithContext(
"com.sun.star.datatransfer.clipboard.SystemClipboard",
xComponentContext);

// query for the interface XClipboard

XClipboard xClipboard = (XClipboard)
UnoRuntime.queryInterface(XClipboard.class, oClipboard);

//---------------------------------------------------
// registering as clipboard listener
//---------------------------------------------------

XClipboardNotifier xClipNotifier = (XClipboardNotifier)
UnoRuntime.queryInterface(XClipboardNotifier.class, oClipboard);

ClipboardListener aClipListener= new ClipboardListener();

xClipNotifier.addClipboardListener(aClipListener);

...

//---------------------------------------------------
// unregistering as clipboard listener
//---------------------------------------------------

xClipNotifier.removeClipboardListener(aClipListener);

...

#### OpenOffice.org Clipboard Data Formats

This section describes common clipboard data formats that OpenOffice.org supports and their corresponding DataType.
As previously mentioned, data formats are described by DataFlavors. The important characteristics of a DataFlavor are the MimeType and DataType. The OpenOffice.org clipboard service uses a standard MimeType for different data formats if there is one registered at Iana. For example, for HTML text, the MimeType "text/html" is used, Rich Text uses the MimeType "text/richtext", and text uses "text/plain". If there is no corresponding MimeType registered at Iana, OpenOffice.org defines a private MimeType. Private OpenOffice.org MimeType always has the MimeType "application/x-openoffice". Each private OpenOffice.org MimeType has a parameter "windows_formatname" identifying the clipboard format name used under Windows. The used Windows format names are the format names used with older OpenOffice.org versions. Common Windows format names are "Bitmap", "GDIMetaFile", "FileName", "FileList", and "DIF".
The DataType of a DataFlavor identifies how the data are exchanged. There are only two DataTypes that can be used. The DataType for Unicode text is a string, and in Java, String.class, For all other data formats, the DataType is a sequence of bytes in Java byte[].class.

The following table lists common data formats, and their corresponding MimeType and DataTypes:

Form | MimeType | DataType (in Java) | Description
--- | --- | --- | ---
Unicode Text | text/plain;charset=utf-16 | String.class | Unicode Text
Richtext | text/richtext | byte[].class | Richtext
Bitmap | application/x-openoffice;windows_formatname="Bitmap" | byte[].class | A bitmap in OpenOffice bitmap format.
HTML Text | text/html | byte[].class | HTML Text

### 6.2.2 Internationalization

The I18N framework provides interfaces to access locale-dependent data (e.g. calendar data, currency) and methods (e.g. collation and transliteration). The I18N framework offers full-featured internationalization functionality that covers a range of geographic locations that include South Asia (China, Japan, and Korea, or CJK), Europe, Middle East (Hebrew, Arabic) and South-East Asia (Thai, Indian). Also, the I18N framework builds on the component model UNO, thus making the addition of new internationalization components easy.

#### Introduction

The I18N framework contains a lot of data and many interfaces and methods not important to developers of external code using the OpenOffice.org API, but only for developers of the OpenOffice.org application itself. This chapter is split into two parts, one that gives a short overview on using the API and is restricted to what is useful to external developers, and a second part that focuses on how to implement a new locale supporting the API (Note that this section does not cover how to translate and localize the OpenOffice.org resources).

#### Overview and Using the API

##### XLocaleData

The com.sun.star.i18n.XLocaleData interface provides access to locale-specific information, such as decimal separators, group (thousands) separators, currency information, calendar data, and number format codes. No further functionality is discussed.

##### XCharacterClassification

The com.sun.star.i18n.XCharacterClassification interface is used to determine the Unicode type of a character (such as uppercase, lowercase, letter, digit, punctuation) or the script type. It also provides methods to perform simple uppercase to lowercase and lowercase to upper case conversions that are locale-dependent but do not need real transliteration. An example of locale-dependent case conversion is the Turkish lowercase i to uppercase I-dot and lowercase i-dotless to uppercase I conversion, as opposed to the western lowercase i to uppercase I conversion.

| There was a bug in OpenOffice.org 1.0.2 that prevents this special example of Turkish case conversion to work properly. The issue is resolved for OpenOffice.org 1.1.0.
--- | ---

Another provided functionality is parsing methods to isolate and determine identifiers, numbers, and quoted strings in a given string. See the description of com.sun.star.i18n.XCharacterClassification methods parseAnyToken() and parsePredefinedToken(). The parser uses com.sun.star.i18n.XLocaleData to obtain the locale-dependent decimal and group separators.

##### XCalendar

The com.sun.star.i18n.XCalendar interface enables the application to use any calendar available for a given locale, not being restricted to the Gregorian calendar. You may query the interface for the available calendars for a given locale with method getAllCalendars() and load one of the available calendars using method loadCalendar(), or you may use the default calendar loaded with method loadDefaultCalendar(). Normally, a Gregorian calendar is available with the name "gregorian" in the Name field of com.sun.star.i18n.Calendar even if the default calendar is not a Gregorian calendar, but this is not mandatory. Available calendars are obtained through the com.sun.star.i18n.XLocaleData interface.

| You must initially load a calendar before using any of the interface methods that perform calendar calculations.
--- | ---

##### XExtendedCalendar

The com.sun.star.i18n.XExtendedCalendar interface was introduced with OpenOffice.org 1.1.0 and provides additional functionality to display locale and calendar dependent calendar values. This interface is derived from com.sun.star.i18n.XCalendar. The interface provides a method to obtain display strings of date parts for specific calendars of a specific locale.

##### XNumberFormatCode

The com.sun.star.i18n.XNumberFormatCode interface provides access to predefined number format codes for a given locale, which in turn are obtained through the com.sun.star.i18n.XLocaleData interface. Normally you do not need to bother with it because the application's number formatter 6.2.5 Office Development - Common Application Features - Number Formats manages the codes. It just might serve to get the available codes and determine default format codes of a specific category.

##### XNativeNumberSupplier

The com.sun.star.i18n.XNativeNumberSupplier interface was introduced with OpenOffice.org 1.1.0 and provides functionality to convert between ASCII Arabic digits/numeric strings and native numeral strings, such as Korean number symbols.

##### XCollator

The com.sun.star.i18n.XCollator interface provides locale-dependent collation algorithms for sorting purposes. There is at least one collator algorithm available per locale, though there may be more than one, for example dictionary and telephone algorithms, or stroke, radical, pinyin in Chinese locales. There is always one default algorithm for each locale that may be loaded using method loadDefaultCollator(), and all available algorithms may be queried with method listCollatorAlgorithms() of those a selected algorithm may be loaded using loadCollatorAlgorithm(). The available collator implementations and options are obtained through the com.sun.star.i18n.XLocaleData interface.

| You must initially load an algorithm prior to using any of the compare...() methods, otherwise the result will be 0 indicating any comparison being equal.
--- | ---

| Since collation may be a very time consuming procedure, use it only for user-visible data, for example for sorted lists. If, for example, you only need a case insensitive comparison without displaying the results to the user, use the com.sun.star.i18n.XTransliteration interface instead.
--- | ---

##### XTransliteration

The com.sun.star.i18n.XTransliteration interface provides methods to perform locale-dependent character conversions, such as case conversions, conversions between Hiragana and Katakana, and Half-width and Full-width. Transliteration is also used by the collators if, for example, a case insensitive sort is to be performed. The available transliteration implementations are obtained through the com.sun.star.i18n.XLocaleData interface.

| You must initially load a transliteration module prior to using any of the transliterating or comparing methods, otherwise the result is unpredictable.
--- | ---

| If you only need to determine if two strings are equal for a specific transliteration (for example a case insensitive comparison) use the equals() method instead of the compare...() methods, it may have a faster implementation.
--- | ---

##### XBreakIterator

The com.sun.star.i18n.XBreakIterator interface may be used to traverse the text in character mode or word mode, to jump to the beginning or to the end of a sentence, to find the beginning or the end of a given script type, and, as the name suggests, to determine a line break position, optionally using a com.sun.star.linguistic2.XHyphenator. The service implementation obtains lists of forbidden characters (characters that are not allowed at the beginning or the end of a line in certain locales) through the com.sun.star.i18n.XLocaleData interface. The XBreakIterator interface also offers methods to determine the script type of a character or to find the beginning or end of a script type along a sequence of characters.

##### XIndexEntrySupplier

The com.sun.star.i18n.XIndexEntrySupplier interface may be used to obtain information on index entries to generate a "table of alphabetical index" for a given locale. Since not all languages are alphabetical in the western sense (for example, CJK languages), different methods are needed.

##### XExtendedIndexEntrySupplier

The com.sun.star.i18n.XExtendedIndexEntrySupplier interface was introduced with OpenOffice.org 1.1.0 and provides additional functionality to generate index entries for languages that need phonetically sorted indexes, such as Japanese. The interface is derived from com.sun.star.i18n.XIndexEntrySupplier.

##### XInputSequenceChecker

The com.sun.star.i18n.XInputSequenceChecker interface was introduced with OpenOffice.org 1.1.0 and provides input sequence checking for Thai and Hindi.

#### Implementing a New Locale

| The procedures, directory layout, and file contents described here reflect the structure of the i18npool module as of OpenOffice.org version 1.1.0, and not the i18n module for OpenOffice.org 1.0.2.
--- | ---

##### XLocaleData

One of the most important tasks in implementing a new locale is to define all the locale data to be used, listed in the following table as types returned by the com.sun.star.i18n.XLocaleData interface methods:

Type | Count
--- | ---
com.sun.star.i18n.LanguageCountryInfo | exactly 1
com.sun.star.i18n.LocaleDataItem | exactly 1
sequence<com.sun.star.i18n.Calendar> | 1 or more
sequence<com.sun.star.i18n.Currency> | 1 or more
sequence<com.sun.star.i18n.FormatElement> | at least all com.sun.star.i18n.NumberFormatIndex format codes (see below)
sequence<com.sun.star.i18n.Implementation> collator implementations | 0 or more, if none specified the ICU collator will be called for the language given in <LanguageCountryInfo>
sequence<string> search options (transliteration modules) | 0 or more
sequence<string> collation options (transliteration modules) | 0 or more
sequence<string> names of supported transliterations (transliteration modules) | 0 or more
com.sun.star.i18n.ForbiddenCharacters | exactly 1, though may have empty elements
sequence<string> reserved words | all words of com.sun.star.i18n.reservedWords
sequence<com.sun.star.beans.PropertyValues> numbering levels (no public XLocaleData API method available, used by and accessible through com.sun.star.text.XDefaultNumberingProvider method getDefaultContinuousNumberingLevels() implemented in i18npool) | exactly 8 <NumberingLevel> entities
sequence<com.sun.star.container.XIndexAccess> outline styles (no public XLocaleData API method available, used by and accessible through com.sun.star.text.XDefaultNumberingProvider method getDefaultOutlineNumberings() implemented in i18npool ) | exactly 8 <OutlineStyle> entities consisting of 5 <OutlineNumberingLevel> entities each

Locale data is defined in an XML file. It is translated into a C++ source file during the build process, which is compiled and linked together with other compiled locale data files into shared libraries. The contents of the XML file, their elements, and how they are to be defined are described in i18npool/source/localedata/data/locale.dtd. The latest revision available for a specific CVS branch of that file provides up-to-date information about the definitions, as well as additional information.

If the language-country combination is not already listed in tools/inc/lang.hxx and tools/source/intntl/isolang.cxx and svx/source/dialog/langtab.src, OpenOffice.org is probably not prepared to deal with your specific locale. For assistance, you can consult http://l10n.openoffice.org/adding_language.html#step1 (Add the New Language to the Resource System) and join the dev@l10n.openoffice.org mailing list (see also http://l10n.openoffice.org/servlets/ProjectMailingListList ).

In order to conform with the available build infrastructure, the name of your locale data file should follow the conventions used in the i18npool/source/localedata/data directory: <language>_<country>.xml, where language is a lowercase, two letter ISO-639 code, and country is an uppercase two letter ISO-3166 code. Start by copying the en_US.xml file to your <language>_<country>.xml file and adopt the entries to suit your needs. Add the corresponding *.cxx and *.obj target file name to the i18npool/source/localedata/data/makefile.mk. Note that there is an explicit rule defined, so that you do not need to add the *.xml file name anywhere. You must also add the locale to the aDllsTable structure located in i18npool/source/localedata/data/localedata.cxx. Make sure to specify the correct library name, since it must correspond to the library name used in the makefile. Finally, the public symbols to be exported must be added to the linker map file corresponding to the library. You can use the i18npool/source/localedata/data/linkermapfile-check.awk script to assist you. Instructions for how to use the script are located the header comments of the file.

<LC_FORMAT><FormatElement>

To be able to load documents of versions up to and including StarOffice 5.2 (old binary file format), each locale must define all number formats mentioned in com.sun.star.i18n.NumberFormatIndex and assign the proper formatindex="..." attribute.
Failing to do so may result in data not properly displayed or not displayed at all if a built-in "System" or "Default" format code was used (as generally done by the average user) and the document is loaded under a locale not having those formats defined. Since old versions did merge some format information of the [Windows] Regional Settings, it might be necessary to define some duplicated codes to fill all positions. To verify that all necessary elements are defined, use a non-product build of OpenOffice.org and open a number formatting dialog, and select your locale from the Language list box. An assertion message box appears if there are any missing elements. The errors are only shown the very first time the locale is selected in a given document.

<LC_FORMAT><FormatElement><FormatCode>

In general, definition of number format codes follows the user visible rules, apart from that any non-ASCII character must be entered using UTF-8 encoding. For a detailed description of codes and a list of possible keywords please consult the OpenOffice.org English online help on section "number format codes".
Be sure to use the separators you declared in the <LC_CTYPE> section in the number format codes, for example <DecimalSeparator>, <ThousandSeparator>, otherwise the number formatter generates incorrect formats.
Verify the defined codes again by using the number formatter dialog of a non-product OpenOffice.org build. If anything is incorrect, an assertion message box appears containing information about the error.
The format indices 1..49 are reserved and, for backward compatibility, must be used as stated in offapi/com/sun/star/i18n/NumberFormatIndex.idl. Note that 48 and 49 are used internally and must not be used in locale data XML files. All other formats must be present.

<FormatCode usage="DATE"> and <FormatCode usage="DATE_TIME">

Characters of date and time keywords, such as YYYY for year, had previously been localized for a few locales (for example, JJJJ in German). The new I18N framework no longer follows that approach, because it may lead to ambiguous and case insensitive character combinations that cannot be resolved at runtime. Localized keyword support is only given for some old locales, other locales must define their codes using English notation.
The table below shows the localized keyword codes:

| DayOfWeek | Era | Year | Month | Day | Hour
--- | --- | --- | --- | --- | --- | ---
English (and all other locales not mentioned) | A | G | Y | M | D | H
de_AT, de_CH, de_DE, de_LI, de_LU |  |  | J |  | T |
nl_BE, nl_NL |  |  | J |  |  | U
fr_BE, fr_CA, fr_CH, fr_FR, fr_LU, fr_MC | O |  | A |  | J |
it_CH, it_IT | O | X | A |  | G |
pt_BR, pt_PT | O |  | A |  |  |
es_AR, es_BO, es_CL, es_CO, es_CR, es_DO, es_EC, es_ES, es_GT, es_HN, es_MX, es_NI, es_PA, es_PE, es_PR, es_PY, es_SV, es_UY, es_VE | O |  | A |  |  |
da_DK |  |  |  |  |  | T
nb_NO, nn_NO, no_NO |  |  |  |  |  | T
sv_FI, sv_SE |  |  |  |  |  | T
fi_FI |  |  | V | K | P | T

<FormatCode usage="DATE" formatindex="21"> and
<FormatCode usage="DATE_TIME" formatindex="47">

The formatindex="21" com.sun.star.i18n.NumberFormatIndex DATE_SYS_DDMMYYYY format code is used to edit date formatted data. It represents a date using the most detailed information available, for example, a 4-digit year and instead of a 2-digit year. The YMD default order (how a date is assembled) is determined from the order encountered in this format.
Similarly, the formatindex="47" com.sun.star.i18n.NumberFormatIndex DATETIME_SYS_DDMMYYYY_HHMMSS format code is used to edit date-time data. Both format codes must display data in a way that is parable by the application, in order to be able to reassemble edited data. This generally means using only YYYY,MM,DD,HH,MM,SS keywords and <DateSeparator> and <TimeSeparator>.

<FormatCode usage="CURRENCY">

The [$xxx-yyy] notation is needed for compatibility reasons. The xxx part denotes the currency symbol, and the yyy part specifies the locale identifier in Microsoft Language ID hexadecimal notation. For example, having “409” as the locale identifier (English-US) and “$” as the currency symbol results in [$$-409]. A list of available Language IDs known to the OpenOffice.org application can be found at project util module tools in file tools/inc/lang.hxx. Format indices 12, 13, 14, 15, 17 with [$xxx-yyy] notation must use the xxx currency symbol that has the attribute usedInCompatibleFormatCodes="true" (see element <LC_CURRENCY> in the locale.dtd file).

##### XCalendar

The interface com.sun.star.i18n.XCalendar provides a general calendar service. All calendar implementations are managed by a class CalendarImpl, the front-end, which dynamically calls a language-specific implementation.

Calendar_gregorian is a wrapper to ICU's Calendar class.

If you need to implement a locale-specific calendar, you can choose to either derive your class from Calendar_gregorian or to write your own class.

There are three steps needed to create a locale-specific calendar:

1. Name your calendar < name > (for example, 'gengou' for Japanese Calendar) and add it to the locale data XML file with proper day/month/era names.

2. Derive a class either from Calendar_gregorian or XCalendar, name it as Calendar_<name>, which will be loaded by CalendarImpl when the calendar is specified.

3. Add your new calendar as a service in i18npool/source/registerservices/registerservices.cxx.

If you plan to derive from the Gregorian calendar, you need to know the mapping between your new calendar and the Gregorian calendar. For example, the Japanese Emperor Era calendar has a starting year offset to Gregorian calendar for each era. You will need to override the method Calendar_gregorian::mapToGregorian() and Calendar_gregorian::mapFromGregorian() to map the Era/Year/Month/Day between the Gregorian calendar and the calendar for your language.

##### XCharacterClassification

The interface com.sun.star.i18n.XCharacterClassification provides toUpper(), toLower(), toTitle() and methods to get various character attributes defined by Unicode. These functions are implemented by the cclass_unicode class. If you need language specific requirements for these functions, you can derive a language specific class cclass_<locale_name> from cclass_unicode and overwrite the methods. In most cases, the attributes are well defined by Unicode, so you do not need to create your own class.

The class also provides a generic parser. If a particular language needs special number parsing, detected non-ASCII numbers are fed to the com.sun.star.i18n.NativeNumberSupplier service to obtain the ASCII representation, which in turn is interpreted and converted to a double precision floating point value.

A manager class CharacterClassificationImpl will handle the loading of language specific implementations of CharacterClassification on the fly. If no implementation is provided, the implementation defaults to class cclass_unicode.

##### XBreakIterator

The interface com.sun.star.i18n.XBreakIterator provides support for Character(Cell)/Word/Sentence/Line-break services. For example, BreakIterator provides the APIs to iterate a string by character, word, line and sentence. The interface is used by the Output layer for the following operations:

- Cursor positioning and selection: Since a character or cell can take more than one code point, cursor movement cannot be done by simply incrementing or decrementing the index.

- Complex Text Layout Languages (CTL): In CTL languages (such as Thai, Hebrew, Arabic and Indian), multiple characters can combine to form a display cell. Cursor movement must traverse a display cell instead of a single character.

Line breaking must be highly configurable in desktop publishing applications. The line breaking algorithm should be able to find a line break with or without a hyphenator. Additionally, it should be able to parse special characters that are illegal if they occur at the end or beginning of a line.

Both requirements are locale-sensitive.

The BreakIterator components are managed by the class BreakIteratorImpl, which will load the language-specific component in service name BreakIterator_<language> dynamically.

The base break iterator class BreakIterator_Unicode is a wrapper to the ICU BreakIterator class. While this class meets the requirements for western languages, it does not meet the requirements for other languages, such as those of South Asia (CJK) and South East Asia (Indian, Thai, Arabic), where enhanced functionality is required, as described previously.

Thus the current BreakIterator base class has two derived classes, BreakIterator_CJK and BreakIterator_CTL. BreakIterator_CJK provides a dictionary based word break for Chinese and Japanese, and a forbidden rule driven line break for Chinese, Japanese and Korean. BreakIterator_CTL provides a more specific definition of character/cell/cluster grouping for languages like Thai and Arabic.

Use the following steps to create a language-specific BreakIterator service:

1. Derive a class either from BreakIterator_CJK or BreakIterator_CTL, name it as BreakIterator_<language>.

2. Add new service in registerservices.cxx

There are three methods for word breaking: nextWord(), previousWord(), getWordBoundary(). You can overwrite them with your own language rules.

BreakIterator_CJK provides input string caching and dictionary searching for longest matching. You can provide a sorted dictionary (the encoding must be UTF-8) by creating the following file: i18npool/source/breakiterator/data/<language>.dict.

The utility gendict will convert the file to C code, which will be compiled into a shared library for dynamic loading.

All dictionary searching and loading is performed in the xdictionary class. The only thing you need to do is to derive your class from BreakIterator_CJK and create an instance of the xdictionary with the language name and pass it to the parent class.

##### XCollator

The interface com.sun.star.i18n.XCollator must be used to provide text collation for the new locale. There are two types of collations, single level and multiple level collation.

Most European and English locales need multiple level collation. OpenOffice.org uses the ICU collator to cover these needs.

Most CJK languages only require single level collation. There is a two step lookup table that performs the collation for these languages. If you have a new language or algorithm in this category, you can derive a new service from Collator_CJK and provide index and weight tables. Here is a sample implementation:

#include <collator_CJK.hxx>
static sal_uInt16 index[] = {
...
};
static sal_uInt16 weight[] = {
...
};
sal_Int32 SAL_CALL Collator_zh_CN_pinyin::compareSubstring(
const ::rtl::OUString& str1, sal_Int32 off1, sal_Int32 len1,
const ::rtl::OUString& str2, sal_Int32 off2, sal_Int32 len2)
throw (::com::sun::star::uno::RuntimeException)
{
return compare(str1, off1, len1, str2, off2, len2, index, weight);
}
sal_Int32 SAL_CALL Collator_zh_CN_pinyin::compareString(
const ::rtl::OUString& str1,
const ::rtl::OUString& str2)
throw (::com::sun::star::uno::RuntimeException)
{
return compare(str1, 0, str1.getLength(), str2, 0, str2.getLength(),
index, weight);
}

Front end implementation Collator will load and cache the language-specific service on the name Collator_<locale> dynamically.

The steps to add new services:

1. Derive the new service from the above class

2. Provide the index and weight tables

3. Register the new service in registerservices.cxx

4. Add the new service in the collation section in the locale data file.

##### XTransliteration

The interface com.sun.star.i18n.XTransliteration can be used for string conversion. The front end implementation TransliterationImpl will load and cache specific transliteration services by a predefined enum in com.sun.star.i18n.TransliterationModules or com.sun.star.i18n.TransliterationModulesNew, or dynamically by implementation name.

Transliterations have been defined in three categories: Ignore, OneToOne and Numeric. All of them are derived from transliteration_commonclass.

Ignore services are for ignore case, half/full width, and Katakana/Hiragana. You can derive your new service from it, and overwrite folding/transliteration methods.

OneToOne services are for one to one mapping, such as converting lowercase to uppercase. The class provides two more services, to take a mapping table or mapping function to do folding and transliteration. You can derive a class from it and provide a table or function for the parent class to do the transliteration.

Numeric services are used to convert a number to a number string in specific languages. It can be used to format Date string and other types of strings.

To add a new transliteration

1. Derive a new class from the three classes previously mentioned.

2. Overwrite folding/transliteration methods or provide a table for the parent to perform the transliteration.

3. Register the new service in registerservices.cxx

4. Add the new service in the transliteration section in the locale data file

##### XNativeNumberSupplier

The interface com.sun.star.i18n.XNativeNumberSupplier provides the functionality to convert between ASCII Arabic digit numbers and locale-dependent numeral representations. It performs the conversion by implementing special transliteration services. The interface also provides a mechanism to generate attributes to be stored in the XML file format (see the XML file format documentation, section "Common Data Style Attributes", "number:transliteration-..."), as well as a conversion of those XML attributes needed to map back to a specific representation style. If you add a number transliteration for a specific locale and reuse one of the com.sun.star.i18n.NativeNumberMode constants, please add the description to com.sun.star.i18n.NativeNumberMode if your changes are to be added back to the OpenOffice.org code repository.

##### XIndexEntrySupplier

The interface com.sun.star.i18n.XIndexEntrySupplier can be used to provide the functionality to generate index pages. The main method of this interface is getIndexCharacter(). Front end implementation IndexEntrySupplier will dynamically load and cache language specific service based on the name IndexEntrySupplier_<locale>.

Languages to be indexed have been divided into two sets. The first set contains Latin1 languages, which can be covered by 256 Unicode code points. A one step lookup table is used to generate index characters. An alphabetic and numeric table has been generated, which covers most Latin1 languages. But if you need another algorithm or have a conflict with the table, you can create your own table and derive a new class from IndexEntrySupplier_Euro. Here is a sample implementation:

#include <sal/types.h>
#include <indexentrysupplier_euro.hxx>
#include <indexdata_alphanumeric.h>
OUString SAL_CALL i18n::IndexEntrySupplier_alphanumeric::getIndexCharacter(
const OUString& rIndexEntry,
const lang::Locale& rLocale, const OUString& rSortAlgorithm )
throw (uno::RuntimeException)
{
return getIndexString(rIndexEntry, idxStr);
}

where idxStr is the table.

For the languages that could not be covered in the first set, such as CJK, a two step lookup table is used. Here is a sample implementation:

#include <indexentrysupplier_cjk.hxx>
#include <indexdata_zh_pinyin.h>
OUString SAL_CALL i18n::IndexEntrySupplier_zh_pinyin::getIndexCharacter(
const OUString& rIndexEntry,
const lang::Locale& rLocale, const OUString& rSortAlgorithm )
throw (uno::RuntimeException)
{
return getIndexString(rIndexEntry, idxStr, idx1, idx2);
}

where idx1 and idx2 are two step tables and idxStr contains all the index keys that will be returned. If you have a new language or algorithm, you can derive a new service from IndexEntrySupplier_CJK and provide tables for the parent class to generate the index.

Note that the index depends on collation, therefore, each index algorithm should have a collation algorithm to support it.

To add new service:

1. Derive the new service from IndexEntrySupplier_Euro.

2. Provide a table for the lookup

3. Register new service in registerservices.cxx

##### A Comment on Search and Replace

Search and replace is also locale-dependent because there may be special search options that are only available for a particular locale. For instance, if the Asian languages support is enabled, you'll see an additional option for "Sounds like (Japanese)" in the Edit - Find & Replace dialog box. With this option, you can turn on or off certain options specific to Japanese in the search and replace process.

Search and replace relies on the transliteration modules for various search options. The transliteration modules are loaded and the search string is converted before the search process.

### 6.2.3 Linguistics

The Linguistic API provides a set of UNO services used for spell checking, hyphenation or accessing a thesaurus. Through the Linguistic API, developers add new implementations and integrate them into OpenOffice.org. Users of the Linguistic API call its methods Usually this functionality is used by one or more clients, that is, applications or components, to process documents , such as text documents or spreadsheets.

#### Services Overview

The services provided by the Linguistic API are:

- com.sun.star.linguistic2.LinguServiceManager

- com.sun.star.linguistic2.DictionaryList

- com.sun.star.linguistic2.LinguProperties

Also there is at least one or more implementation for each of the following services:

- com.sun.star.linguistic2.SpellChecker

- com.sun.star.linguistic2.Hyphenator

- com.sun.star.linguistic2.Thesaurus

The service implementations for spell checker, thesaurus and hyphenator supply the respective functionality. Each of the implementations support a different set of languages. Refer to com.sun.star.linguistic2.XSupportedLocales.

For example, there could be two implementations for a spell checker, usually from different supporting parties: the first supporting English, French and German, and the second supporting Russian and English. Similar settings occur for the hyphenator and thesaurus.

It is not convenient for each application or component to know all these implementations and to choose the appropriate implementation for the specific purpose and language, therefore a mediating instance is required.

This instance is the LinguServiceManager. Spell checking, hyphenation and thesaurus functionality is accessed from a client by using the respective interfaces from the LinguServiceManager.

The LinguServiceManager dispatches the interface calls from the client to a specific service implementation,if any, of the respective type that supports the required language.
For example, if the client requires spell checking of a French word, the first spell checker implementations from those mentioned above are called.

If there is more than one spell checker available for one language, as in the above example for the English language, the LinguServiceManager starts with the first one that was supplied in the setConfiguredServices() method of its interface. The thesaurus behaves in a similar manner.
For more details, refer to the interface description com.sun.star.linguistic2.XLinguServiceManager.

The LinguProperties service provides, among others, properties that are required by the spell checker, hyphenator and thesaurus that are modified by the client. Refer to thecom.sun.star.linguistic2.LinguProperties.

The DictionaryList (see com.sun.star.linguistic2.DictionaryList) provides a set of user defined or predefined dictionaries for languages that are activated and deactivated. If they are active, they are used by the spell checker and hyphenator. These are used by the user to override results from the spell checker and hyphenator implementations, thus allowing the user to customize spell checking and hyphenation.

In the code snippets and examples in the following chapters, we will use the following members and interfaces: (OfficeDev/Linguistic/LinguisticExamples.java)

// used interfaces

import com.sun.star.lang.XMultiServiceFactory;

import com.sun.star.linguistic2.XLinguServiceManager;

import com.sun.star.linguistic2.XSpellChecker;

import com.sun.star.linguistic2.XHyphenator;

import com.sun.star.linguistic2.XThesaurus;

import com.sun.star.linguistic2.XSpellAlternatives;

import com.sun.star.linguistic2.XHyphenatedWord;

import com.sun.star.linguistic2.XPossibleHyphens;

import com.sun.star.linguistic2.XMeaning;

import com.sun.star.linguistic2.XSearchableDictionaryList;

import com.sun.star.linguistic2.XLinguServiceEventListener;

import com.sun.star.linguistic2.LinguServiceEvent;

import com.sun.star.beans.XPropertySet;

import com.sun.star.beans.PropertyValue;

import com.sun.star.uno.XComponentContext;

import com.sun.star.uno.XNamingService;

import com.sun.star.lang.XMultiComponentFactory;

import com.sun.star.lang.EventObject;

import com.sun.star.lang.Locale;

import com.sun.star.bridge.XUnoUrlResolver;

import com.sun.star.uno.UnoRuntime;

import com.sun.star.uno.Any;

import com.sun.star.lang.XComponent;

//

// members for commonly used interfaces

//

// The MultiServiceFactory interface of the Office

protected XMultiServiceFactory mxFactory = null;

// The LinguServiceManager interface

protected XLinguServiceManager mxLinguSvcMgr = null;

// The SpellChecker interface

protected XSpellChecker mxSpell = null;

// The Hyphenator interface

protected XHyphenator mxHyph = null;

// The Thesaurus interface

protected XThesaurus mxThes = null;

// The DictionaryList interface

protected XSearchableDictionaryList mxDicList = null;

// The LinguProperties interface

protected XPropertySet mxLinguProps = null;

To establish a connection to the office and have our mxFactory object initialized with its XMultiServiceFactory, the following code is used: (OfficeDev/Linguistic/LinguisticExamples.java)

public void Connect( String sConnection )

throws com.sun.star.uno.Exception,

com.sun.star.uno.RuntimeException,

Exception

{

XComponentContext xContext =

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

XMultiComponentFactory xLocalServiceManager = xContext.getServiceManager();

Object xUrlResolver = xLocalServiceManager.createInstanceWithContext(

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

XUnoUrlResolver urlResolver = (XUnoUrlResolver)UnoRuntime.queryInterface(

XUnoUrlResolver.class, xUrlResolver );

Object rInitialObject = urlResolver.resolve( "uno:" + sConnection +

";urp;StarOffice.NamingService" );

XNamingService rName = (XNamingService)UnoRuntime.queryInterface(XNamingService.class,

rInitialObject );

if( rName != null )

{

Object rXsmgr = rName.getRegisteredObject( "StarOffice.ServiceManager" );

mxFactory = (XMultiServiceFactory)

UnoRuntime.queryInterface( XMultiServiceFactory.class, rXsmgr );

}

}

And the LinguServiceManager object mxLinguSvcMgr is initialized like similar to the following snippet: (OfficeDev/Linguistic/LinguisticExamples.java)

/** Get the LinguServiceManager to be used. For example to access spell checker,

thesaurus and hyphenator, also the component may choose to register itself

as listener to it in order to get notified of relevant events. */

public boolean GetLinguSvcMgr()

throws com.sun.star.uno.Exception

{

if (mxFactory != null) {

Object aObj = mxFactory.createInstance(

"com.sun.star.linguistic2.LinguServiceManager" );

mxLinguSvcMgr = (XLinguServiceManager)

UnoRuntime.queryInterface(XLinguServiceManager.class, aObj);

}

return mxLinguSvcMgr != null;

}

The empty list of temporary property values used for the current function call only and the language used may look like the following:

// list of property values to used in function calls below.

// Only properties with values different from the (default) values

// in the LinguProperties property set need to be supllied.

// Thus we may stay with an empty list in order to use the ones

// form the property set.

PropertyValue[] aEmptyProps = new PropertyValue[0];

// use american english as language

Locale aLocale = new Locale("en","US","");

Using temporary property values:

To change a value for the example IsGermanPreReform to a different value for one or a limited number of calls without modifying the default values, provide this value as a member of the last function argument used in the examples below before calling the respective functions.

// another list of property values to used in function calls below.

// Only properties with values different from the (default) values

// in the LinguProperties property set need to be supllied.

PropertyValue[] aProps = new PropertyValue[1];

aProps[0] = new PropertyValue();

aProps[0].Name = "IsGermanPreReform";

aProps[0].Value = new Boolean( true );

Replace the aEmptyProps argument in the function calls with aProps to override the value of IsGermanPreReform from the LinguProperties. Other properties are overridden by adding them to the aProps object.

#### Using Spellchecker

The interface used for spell checking is com.sun.star.linguistic2.XSpellChecker. Accessing the spell checker through the LinguServiceManager and initializing the mxSpell object is done by: (OfficeDev/Linguistic/LinguisticExamples.java)

/** Get the SpellChecker to be used.

*/

public boolean GetSpell()

throws com.sun.star.uno.Exception,

com.sun.star.uno.RuntimeException

{

if (mxLinguSvcMgr != null)

mxSpell = mxLinguSvcMgr.getSpellChecker();

return mxSpell != null;

}

Relevant properties

The properties of the LinguProperties service evaluated by the spell checker are:

Spell-checking Properties of com.sun.star.linguistic2.LinguProperties Description
IsIgnoreControlCharacters | Defines if control characters should be ignored or not.
IsUseDictionaryList | Defines if the dictionary-list should be used or not.
IsGermanPreReform | Defines if the new German spelling rules should be used for German language text or not.
IsSpellUpperCase | Defines if words with only uppercase letters should be subject to spellchecking or not.
IsSpellWithDigits | Defines if words containing digits or numbers should be subject to spellchecking or not.
IsSpellCapitalization | dDefines if the captitalization of words should be checked or not.

Changing the values of these properties in the LinguProperties affect all subsequent calls to the spell checker. Instantiate a com.sun.star.linguistic2.LinguProperties instance and change it by calling com.sun.star.beans.XPropertySet:setPropertyValue(). The changes affect the whole office unless another modifies the properties again. This is done implicitly when changing the linguistic settings through Tools - Options - Language Settings - Writing Aids.

The following example shows verifying single words: (OfficeDev/Linguistic/LinguisticExamples.java)

// test with correct word

String aWord = "horseback";

boolean bIsCorrect = mxSpell.isValid( aWord, aLocale, aEmptyProps );

System.out.println( aWord + ": " + bIsCorrect );

// test with incorrect word

aWord = "course";

bIsCorrect = mxSpell.isValid( aWord, aLocale , aEmptyProps );

System.out.println( aWord + ": " + bIsCorrect );

Tne following example shows spelling a single word and retrieving possible corrections:

aWord = "house";

XSpellAlternatives xAlt = mxSpell.spell( aWord, aLocale, aEmptyProps );

if (xAlt == null)

System.out.println( aWord + " is correct." );

else

{

System.out.println( aWord + " is not correct. A list of proposals follows." );

String[] aAlternatives = xAlt.getAlternatives();

if (aAlternatives.length == 0)

System.out.println( "no proposal found." );

else

{

for (int i = 0; i < aAlternatives.length; ++i)

System.out.println( aAlternatives[i] );

}

}

For a description of the return types interface, refer to com.sun.star.linguistic2.XSpellAlternatives.

#### Using Hyphenator

The interface used for hyphenation is com.sun.star.linguistic2.XHyphenator. Accessing the hyphenator through the LinguServiceManager and initializing the mxHyph object is done by: (OfficeDev/Linguistic/LinguisticExamples.java)

/** Get the Hyphenator to be used.

*/

public boolean GetHyph()

throws com.sun.star.uno.Exception,

com.sun.star.uno.RuntimeException

{

if (mxLinguSvcMgr != null)

mxHyph = mxLinguSvcMgr.getHyphenator();

return mxHyph != null;

}

Relevant properties

The properties of the LinguProperties service evaluated by the hyphenator are:

Hyphenating Properties of com.sun.star.linguistic2.LinguProperties
IsIgnoreControlCharacters | Defines if control characters should be ignored or not.
IsUseDictionaryList | Defines if the dictionary-list should be used or not.
IsGermanPreReform | Defines if the new German spelling rules should be used for German language text or not.
HyphMinLeading | The minimum number of characters of a hyphenated word to remain before the hyphenation character.
HyphMinTrailing | The minimum number of characters of a hyphenated word to remain after the hyphenation character.
HyphMinWordLength | The minimum length of a word to be hyphenated.

Changing the values of these properties in the Lingu-Properties affect all subsequent calls to the hyphenator.

A valid hyphenation position is a possible one that meets the restrictions given by the HyphMinLeading, HyphMinTrailing and HyphMinWordLength values.

For example, if HyphMinWordLength is 7, "remove" does not have a valid hyphenation position. Also, this is the case when HyphMinLeading is 3 or HyphMinTrailing is 5.

The following example shows a word hypenated: (OfficeDev/Linguistic/LinguisticExamples.java)

// maximum number of characters to remain before the hyphen

// character in the resulting word of the hyphenation

short nMaxLeading = 6;

XHyphenatedWord xHyphWord = mxHyph.hyphenate( "horseback", aLocale, nMaxLeading , aEmptyProps );

if (xHyphWord == null)

System.out.println( "no valid hyphenation position found" );

else

{

System.out.println( "valid hyphenation pos found at " + xHyphWord.getHyphenationPos()

+ " in " + xHyphWord.getWord() );

System.out.println( "hyphenation char will be after char " + xHyphWord.getHyphenPos()

+ " in " + xHyphWord.getHyphenatedWord() );

}

If the hyphenator implementation is working correctly, it reports a valid hyphenation position of 4 that is after the 'horse' part. Experiment with other values for nMaxLeading and other words. For example, if you set it to 4, no valid hyphenation position is found since there is no hyphenation position in the word 'horseback' before and including the 's'.

For a description of the return types interface, refer tocom.sun.star.linguistic2.XHyphenatedWord.

The example below shows querying for an alternative spelling. In some languages, for example German in the old (pre-reform) spelling, there are words where the spelling of changes when they are hyphenated at specific positions. To inquire about the existence of alternative spellings, the queryAlternativeSpelling() function is used: (OfficeDev/Linguistic/LinguisticExamples.java)

//! Note: 'aProps' needs to have set 'IsGermanPreReform' to true!

xHyphWord = mxHyph.queryAlternativeSpelling( "Schiffahrt",

new Locale("de","DE",""), (short)4, aProps );

if (xHyphWord == null)

System.out.println( "no alternative spelling found at specified position." );

else

{

if (xHyphWord.isAlternativeSpelling())

System.out.println( "alternative spelling detectetd!" );

System.out.println( "valid hyphenation pos found at " + xHyphWord.getHyphenationPos()

+ " in " + xHyphWord.getWord() );

System.out.println( "hyphenation char will be after char " + xHyphWord.getHyphenPos()

+ " in " + xHyphWord.getHyphenatedWord() );

}

The return types interface is the same as in the above example (com.sun.star.linguistic2.XHyphenatedWord).

The next example demonstrates getting possible hyphenation positions. To determine all possible hyphenation positions in a word, do this: (OfficeDev/Linguistic/LinguisticExamples.java)

XPossibleHyphens xPossHyph = mxHyph.createPossibleHyphens( "waterfall", aLocale, aEmptyProps );

if (xPossHyph == null)

System.out.println( "no hyphenation positions found." );

else

System.out.println( xPossHyph.getPossibleHyphens() );

For a description of the return types interface, refer to com.sun.star.linguistic2.XPossibleHyphens.

#### Using Thesaurus

The interface used for the thesaurus is com.sun.star.linguistic2.XThesaurus. Accessing the thesaurus through the LinguServiceManager and initializing the mxThes object is done by: (OfficeDev/Linguistic/LinguisticExamples.java)

/** Get the Thesaurus to be used.

*/

public boolean GetThes()

throws com.sun.star.uno.Exception,

com.sun.star.uno.RuntimeException

{

if (mxLinguSvcMgr != null)

mxThes = mxLinguSvcMgr.getThesaurus();

return mxThes != null;

}

The properties of the LinguProperties service evaluated by the thesaurus are:

Thesaurus related Properties of com.sun.star.linguistic2.LinguProperties
IsIgnoreControlCharacters | Defines if control characters should be ignored or not.
IsGermanPreReform | Defines if the new German spelling rules should be used for German language text or not.

Changing the values of these properties in the LinguProperties affect all subsequent calls to the thesaurus. The following example about retrieving synonyms shows this: (OfficeDev/Linguistic/LinguisticExamples.java)

XMeaning[] xMeanings = mxThes.queryMeanings( "house", aLocale, aEmptyProps );

if (xMeanings == null)

System.out.println( "nothing found." );

else

{

for (int i = 0; i < xMeanings.length; ++i)

{

System.out.println( "Meaning: " + xMeanings[i].getMeaning() );

String[] aSynonyms = xMeanings[i].querySynonyms();

for (int k = 0; k < aSynonyms.length; ++k)

System.out.println( " Synonym: " + aSynonyms[k] );

}

}

The reason to subdivide synonyms into different meanings is becausethere are different synonyms for some words that are not even closely related. For example, the word 'house' has the synonyms 'home', 'place', 'dwelling', 'family', 'clan', 'kindred', 'room', 'board', and 'put up'.

The first three in the aboce list have the meaning of 'building where one lives' where the next three mean that of 'a group of people sharing common ancestry' and the last three means that of 'to provide with lodging'. Thus, having meanings is a way to group large sets of synonyms into smaller ones with approximately the same definition.

#### Events

There are several types of events. For example, all user dictionaries com.sun.star.linguistic2.XDictionary report their status changes as events com.sun.star.linguistic2.DictionaryEvent to the DictionaryList, which collects and transforms their information into DictionaryList events com.sun.star.linguistic2.DictionaryListEvent, and passes those on to its own listeners.

Thus, it is possible to register to the DictionaryList as a listener to be informed about relevant changes in the dictionaries., There is no need to register as a listener for each dictionary.

The spell checker and hyphenator implementations monitor the changes in the LinguProperties for changes of their relevant properties. If such a property changes its value, the implementation launches an event com.sun.star.linguistic2.LinguServiceEventthat hints to its listeners that spelling or hyphenation should be reevaluated. For this purpose, those implementations support the com.sun.star.linguistic2.XLinguServiceEventBroadcaster interface.

The LinguServiceManager acts as a listener for com.sun.star.linguistic2.DictionaryListEvent and com.sun.star.linguistic2.LinguServiceEvent events. The respective interfaces are com.sun.star.linguistic2.XDictionaryListEventListener] and com.sun.star.linguistic2.XLinguServiceEventListener. The events from the DictionaryList are transformed into com.sun.star.linguistic2.LinguServiceEvent events and passed to the listeners of the LinguServiceManager, along with the received events from the spell checkers and hyphenators.

Therefore, a client that wants to be notified when spell checking or hyphenation changes, for example, when it features automatic spell checking or automatic hyphenation, needs to be registered as com.sun.star.linguistic2.XLinguServiceEventListener to the LinguServiceManager only.

Implementing the com.sun.star.linguistic2.XLinguServiceEventListener interface is similar to the following snippet: (OfficeDev/Linguistic/LinguisticExamples.java)

/** simple sample implementation of a clients XLinguServiceEventListener

* interface implementation

*/

public class Client

implements XLinguServiceEventListener

{

public void disposing ( EventObject aEventObj )

{

//! any references to the EventObjects source have to be

//! released here now!

System.out.println("object listened to will be disposed");

}

public void processLinguServiceEvent( LinguServiceEvent aServiceEvent )

{

//! do here whatever you think needs to be done depending

//! on the event recieved (e.g. trigger background spellchecking

//! or hyphenation again.)

System.out.println("Listener called");

}

};

After the client has been instantiated, it needs to register as com.sun.star.linguistic2.XLinguServiceEventListener. For the sample client above, this looks like: (OfficeDev/Linguistic/LinguisticExamples.java)

XLinguServiceEventListener aClient = new Client();

// now add the client as listener to the service manager to

// get informed when spellchecking or hyphenation may produce

// different results then before.

mxLinguSvcMgr.addLinguServiceManagerListener( aClient );

This enables the sample client to receive com.sun.star.linguistic2.LinguServiceEvents and act accordingly. Before the sample client terminates, it has to stop listening for events from the LinguServiceManager:

//! remove listener before programm termination.

//! should not be omitted.

mxLinguSvcMgr.removeLinguServiceManagerListener(aClient);

In the LinguisticExamples.java sample, a property is modified for the listener to be called.

#### Implementing a Spell Checker

A sample implementation of a spell checker isfound in the (OfficeDev/Linguistic/SampleSpellChecker.java) file from the examples for linguistics.

The spell checker implements the following interfaces:

- com.sun.star.linguistic2.XSpellChecker

- com.sun.star.linguistic2.XLinguServiceEventBroadcaster

- com.sun.star.lang.XInitialization

- com.sun.star.lang.XServiceDisplayName

- com.sun.star.lang.XServiceInfo

- com.sun.star.lang.XComponent

and

- com.sun.star.lang.XTypeProvider, to access your add-in interfaces from OpenOffice.org Basic, otherwise, this interface is not mandatory.

To implement a spell checker of your own, modify the sample in the following ways:

Choose a unique service implementation nameto distinguish your service implementation from any other. To do this, edit the string in the line

public static String _aSvcImplName = "com.sun.star.linguistic2.JavaSamples.SampleSpellChecker";

Then, specify the list of languages supported by your service. Edit the

public Locale[] getLocales()

function and modify the

public boolean hasLocale( Locale aLocale )

function accordingly. The next step is to change the

private short GetSpellFailure(...)

as required. This function determines if a word is spelled correctly in a given language. If the word is OK return -1, otherwise return an appropriate value of the type com.sun.star.linguistic2.SpellFailure.

Check if you need to edit or remove the

private boolean IsUpper(...)

and

private boolean HasDigits(...)

functions. Consider this only if you are planning to support non-western languages and need sophisticated versions of those, or do not need them at all. Do not forget to change the code at the end of

public boolean isValid(...)

accordingly.

Supply your own version of

private XSpellAlternatives GetProposals(...)

It provides the return value for the

public XSpellAlternatives spell(...)

function call if the word was found to be incorrect. The main purpose is to provide proposals for how the word might be written correctly. Note the list ay be empty.

Next, edit the text in

public String getServiceDisplayName(...)

It should be unique but it is not necessary. If you are developing a set of services, that is, spellchecker, hyphenator and thesaurus, it should be the same for all of them. This text is displayed in dialogs to show a more meaningful text than the service implementation name.

Now, have a look in the constructor

public SampleSpellChecker()

at the property names. Remove the entries for the properties that are not relevant to your service implementation. If you make modification, also look in the file PropChgHelper_Spell.java in the function

public void propertyChange(...)

and change it accordingly.

Set the values of bSCWA and bSWWA to true only for those properties that are relevant to your implementation, thus avoiding sending unnecessary com.sun.star.linguistic2.LinguServiceEvent events, that is, avoid triggering spell-checking in clients if there is no requirement.

Finally, after registration of the service (see 4.9 Writing UNO Components - Deployment Options for Components) it has to be activated to be used by the LinguServiceManager. After restarting OpenOffice.org, this is done in the following manner:

Open the dialog Tools – Options – Language Settings – Writing Aids. In the section Writing Aids, in the box Available Language Modules, a new entry with text of the Service Display Name that you chose is displayed in the implementation. Check the empty checkbox to the left of that entry. If you want to use your module, uncheck any other listed entry. If you want to make more specific settings per language, press the Edit button next to the modules box and use that dialog.

The Context menu of the Writer that pops up when pressing the right-mouse button over an incorrectly spelled word currently has a bug that may crash the program when the Java implementation of a spell checker is used. The spell check dialog is functioning.

#### Implementing a Hyphenator

A sample implementation of a hyphenator is found in the (OfficeDev/Linguistic/SampleHyphenator.java) file from the examples for linguistic.

The hyphenator implements the following interfaces:

- com.sun.star.linguistic2.XHyphenator

- com.sun.star.linguistic2.XLinguServiceEventBroadcaster

- com.sun.star.lang.XInitialization

- com.sun.star.lang.XServiceDisplayName

- com.sun.star.lang.XServiceInfo

- com.sun.star.lang.XComponent

and

- com.sun.star.lang.XTypeProvider, if you want to access your add-in interfaces from OpenOffice.org Basic, otherwise, this interface is not mandatory.

Aside from choosing a new service implementation name, the process of implementing the hyphenator is the same as implementing the spell checker, except that you need to implement the com.sun.star.linguistic2.XHyphenator interface instead of the com.sun.star.linguistic2.XSpellChecker interface.

You can choose a different set of languages to be supported. When editing the sample code, modify the hasLocale() and getLocales() methods to reflect the set of languages your implementation supports.

To implement the com.sun.star.linguistic2.XHyphenator interface, modify the functions

public XHyphenatedWord hyphenate(...)

public XHyphenatedWord queryAlternativeSpelling(...)

public XPossibleHyphens createPossibleHyphens(...)<

in the sample hyphenator source file at the stated positions.

Look in the constructor

public SampleHyphenator()

at the relevant properties and modify the

public void propertyChange(...)

function in the file (OfficeDev/Linguistic/PropChgHelper_Hyph.java) accordingly.

The rest, registration and activation is again the same as for the spell checker.

#### Implementing a Thesaurus

A sample implementation of a thesaurus is found in the (OfficeDev/Linguistic/SampleThesaurus.java) file from the examples for linguistic.

The thesaurus implements the following interfaces:

- com.sun.star.linguistic2.XThesaurus

- com.sun.star.lang.XInitialization

- com.sun.star.lang.XServiceDisplayName

- com.sun.star.lang.XServiceInfo

- com.sun.star.lang.XComponent

and

- com.sun.star.lang.XTypeProvider, if you want to access your add-in interfaces from OpenOffice.org Basic, otherwise, this interface is not mandatory.

For the implementation of the thesaurus, modify the sample thesaurus by following the same procedure as for the spell checker and thesaurus:

Choose a different implementation name for the service and modify the

public Locale[] getLocales()

and

public boolean hasLocale(...)

functions.

The only function to be modified at the stated position to implement the com.sun.star.linguistic2.XThesaurus interface is

public XMeaning[] queryMeanings(...)

Look in the constructor

public SampleThesaurus()

to see if there are properties you do not require.

Registration and activation is the same as for the spell checker and hyphenator.

### 6.2.4 Integrating Import and Export Filters

This section explains the implementation of OpenOffice.org import and export filter components, focussing on filter components. It is intended as a brief introduction for developers who want to implement OpenOffice.org filters for foreign file formats.

#### Approaches

They are several ways to get information into or out of OpenOffice.org: You can

- link against the application core

- use the document API

- use the XML file format

Each method has unique advantages and disadvantages, that are summarized briefly:

Using the core data structure and linking against the application core is the traditional way to implement filters in OpenOffice.org. The advantages of this method are efficiency and direct access to the document. However, the core implementation provides an implementation centric view of the applications. Additionally, there are a number of technical disadvantages. Every change in the core data structures or objects must be followed by corresponding changes in code that uses them. Consequently, filters need to be recompiled to match the binary layout of the application core objects. While these are manageable, albeit cumbersome, for closed source applications, this method is expected to create a maintenance nightmare if application and filters are developed separately as is customary in open source applications. Simultaneous delivery of a new application build and the corresponding filters developed by third parties looks challenging.

Using the OpenOffice.org API based on UNO is more advantageous, since it solves the technical problems indicated in the above paragraph. The idea is to read data from a file on loading and build up a document using the OpenOffice.org API, and to iterate over a document model and write the corresponding data to a file on storing. The UNO component technology insulates the filter from binary layout, and other compiler and version dependent issues. Additionally, the API is expected to be more stable than the core interfaces, and provides an abstraction from the core applications. In fact, the example filter implementation of this section makes use of this strategy and is based on the OpenOffice.org API.

The third is to import and export documents using the XML-based file format. UNO-based XML import and export components feature all of the advantages of the previous method, but additionally provide the filter implementer with a clean, structured, and fully documented view of the document. A significant difficulty in conversion between formats is the conceptual mapping from the one format to the other. From OpenOffice.org 1.1.0 there are XML filter components that carry out the mapping at runtime, so that filter implementers can read from XML streams when exporting and write to XML streams when importing.

The following section describes the second method using the UNO-based API. Further details on the third method, based on the generic XML format are found in the xml project of OpenOffice.org under http://xml.openoffice.org/filter/ . The third method to create XML based filters is described afterwards.

#### Document API Filter Development

First, we provide an overview of the import and export process using a document API, and gain an understanding of the general concepts.

##### Introduction

Inside OpenOffice.org a document is represented by its document service, called model. On disk, the same document is represented as a file or possibly as a dynamic generated output, for example, of a database statement. We cannot assign it to a file on disk, so we call it content to describe it. A filter component is used to convert between these different formats.

Illustration 6.16: Import/Export Filter Process

If you make use of UNO, this above diagram can be turned into programming reality quite easily. The three entities in the diagram, content, model, and filter, all have direct counterparts in UNO services. The services consist of several interfaces that map to a specific implementation, for example, using C++ or Java.

The filter writer creates a class that implements the com.sun.star.document.ExportFilter or com.sun.star.document.ImportFilter services, or both. To achieve this, the corresponding stream or URL is obtained from the com.sun.star.document.MediaDescriptor. The incoming data is then interpreted and the model is used by calling the appropriate methods. The available methods depend on the type of document as described by the document service.

For a list of available document services, refer to the section 6.1.3 Office Development - OpenOffice.org Application Environment - Using the Component Framework - Models - Document Specific Features.

##### Filtering Process

Inside OpenOffice.org, the whole process of loading or saving contents is realized as a modular system that is based on UNO services. It functions generically in many components and is easily adapted to the developer's needs through the addition of custom modules or the removal of others.

Loading:

A URL or a stream is passed to com.sun.star.frame.XComponentLoader:loadComponentFromURL(). The load properties create a com.sun.star.document.MediaDescriptor that is filled with the URL or stream, and the load properties. The component loader implementation passes the information about the resource to the TypeDetection.

The com.sun.star.document.TypeDetection uses the MediaDescriptor to determine a unique type name that is necessary to create a filter instance at the com.sun.star.document.FilterFactory.

The TypeDetection also employs the com.sun.star.document.ExtendedTypeDetectionthat examines the given resource and confirms the unique type name determined by TypeDetection. The MediaDescriptor is updated, if necessary, and a unique type name is returned.

Finally, the component loader ensures there is a frame, or creates a new one, if necessary, and asks a frame loader service (com.sun.star.frame.FrameLoader or com.sun.star.frame.SynchronousFrameLoader) to load the resource into the frame. Its interface com.sun.star.frame.XFrameLoader has a method load() that takes a frame, the MediaDescriptor and an event listener, and creates a com.sun.star.document.ImportFilter instance at the FilterFactory to load the resource into the given frame. For this purpose, it calls createInstance() with the filter implementation name (such as com.sun.star.comp.Writer.GenericXMLFilter) or createInstanceWithArguments() with the implementation name and additional arguments used to initialize the filter.

Then, the loader calls setTargetDocument() and filter() on the ImportFilter service. The ImportFilter creates its results in the given target document.

Storing to a URL:

A URL or a stream is passed to storeToURL() or storeAsURL() in the interface com.sun.star.frame.XStorable, implemented by office documents. The store properties create a media descriptor that is filled with the URL or stream, and the store properties. The TypeDetection provides a unique type name that is used with the FilterFactory to create a com.sun.star.document.ExportFilter .

The XStorable implementation calls setSourceDocument() and filter() at the filter, which writes the results to the storage specified in the MediaDescriptor passed to filter().

| Many existing filters are legacy filters. The XStorable implementation does not use the FilterFactory to create them, but triggers filtering by internal calls.
--- | ---

If a URL or an already open stream takes part in the load or save process of the OpenOffice.org, the following services and operations are involved:

Illustration 6.17: General Filtering Process

In the following, the modules that participate in the loading process are discussed in detail.

###### MediaDescriptor

The media descriptor is an abstract description of a content specifying the where from and the how for the handling of the content to be performed. A content is also called a medium. Refer to 6.1.5 Office Development - OpenOffice.org Application Environment - Handling Documents - Loading Documents - MediaDescriptor for further information. Inside the OpenOffice.org, it is realized as a sequence of com.sun.star.beans.PropertyValue structs as a parameter.

A descriptor is passed to various methods which are involved in the load and save process.

Every member of the process can use this descriptor and change it to update the information about the document. This descriptor is used as an [inout] parameter by com.sun.star.document.XTypeDetection:queryTypeByDescriptor() and com.sun.star.document.XExtendedFilterDetection:detect(). The MediaDescriptor is [in] only in com.sun.star.frame.XComponentLoader:loadComponentFromURL(), com.sun.star.frame.XFrameLoader:load() and com.sun.star.document.XFilter:filter(). With methods that take the MediaDescriptor as [in] parameter only, a manual synchronization must be done by the outside code. The caller of a method that accepts the MediaDescriptor as [in] parameter only merges the results, for example, return values, manually into the original descriptor. The model is not available at loading time. It is the result of the load request.

| It is not allowed to hold a member of this descriptor by reference longer than it is used, especially a possible stream item. For example, it would not be possible to close a stream that is still referenced by others. It is only allowed to use it directly or as a copy.
--- | ---

| The stream part of the MediaDescriptor is a special item. If a stream exists, it must be used. Only if a stream does not exist, is it allowed to open a new one using the URL. The stream should be set in the MediaDescriptor to provide it for following users of the descriptor. One rule exists for all: the stream inside the descriptor should be seekable. In case it is not, it makes no sense to provide it to the other members of the whole process, especially used sub-modules. On the other hand, a module can be called with a non-seekable stream from outside to perform the operation. For example, for detection or loading it should be no problem. In case a non-seekable stream comes in, but seeking is important, it must be used buffered. Another central question is: who controls the lifetime of the stream or the stream position ? The lifetime of a non-seekable stream is controlled by the creator everytime. It has to be deleted after using. Seekable streams should be added to the MediaDescriptor and will be released by the creator of the MediaDescriptor. Every (sub-) module must be called with a stream seeked to position 0. Of course, non-seekable streams must be newly created and unused. Internally it can do anything with this stream. Furthermore it is not necessary (or even impossible) to restore any positions. The user of the module has to do such things.
--- | ---

###### TypeDetection

Every content to be loaded must be specified, that is, the type of content represented in the OpenOffice.org must be well known in OpenOffice.org. The type is usually document type,.however, the results of active contents, for example, macros, or database contents are also described here.

A special service com.sun.star.document.TypeDetection is used to accomplish this. It provides an API to associate, for example, a URL or a stream with the extensions well known to OpenOffice.org, MIME types or clipboard formats. The resulting value is an internal unique type name used for further operations by using other services, for example, com.sun.star.frame.FrameLoaderFactory. This type name can be a part of the already mentioned MediaDescriptor.

It is not necessary or useful to replace this service by custom implementations.,It works in a generic method on top of a special configuration. Extending the type detection is done by changing the configuration and is described later. It is required to make these changes if new content formats are provided for OpenOffice.org, because this is the reason to integrate custom filters into the product.

###### ExtendedTypeDetection

Based on the registered types, flat detection is already possible, that is,. the assignment of types, for example, to a URL, on the basis of configuration data only. Tlat detection cannot always get a correct result if you imagine someone modifying the file extension of a text document from .sxw to .txt.. To ensure correct results, we need deep detection, that is, the content has to be examinedThe com.sun.star.document.ExtendedTypeDetection service performs this task. It is called detector. It gets all the information collected on a document and decides the type to assign it to. In the new modular type detection, the detector is meant as a UNO service that registers itself in the OpenOffice.org and is requested by the generic TypeDetection mechanism, if necessary.

To extend the list of the known content types of OpenOffice.org, we suggest implementing a detector component in addition to a filter. It improves the generic detection of OpenOffice.org and makes the results more secure.

Inside OpenOffice.org, a detector service is called with an already opened stream that is used to find out the content type. In case no stream is given, it indicates that someone else uses this service, for example, outside OpenOffice.org). It is then allowed to open your own stream by using the URL part of the MediaDescriptor. If the resulting stream is seekable, it should be set inside the descriptor after its position is reset to 0. If the stream is not seekable, it is not allowed to set it. Please follow the already mentioned rules for handling streams.

###### FrameLoader

Frame loaders load a detected type. A visual component is expected as the result. Such visual components are:

- trivial components only implementing com.sun.star.awt.XWindow

- simple office components implementing the com.sun.star.frame.Controller service

- full featured office components implementing the com.sun.star.document.OfficeDocument service.

Further details are found in section 6.1.1 Office Development - OpenOffice.org Application Environment - Overview - Framework API.

A frame loader service exist in different versions:

- com.sun.star.frame.FrameLoader for asynchronous

- com.sun.star.frame.SynchronousFrameLoader for synchronous load processes.

It can be searched or created by another service com.sun.star.frame.FrameLoaderFactorythat is described below. The synchronous version is optional. Both services can be implemented at the same component, but the synchronous version is preferred, if it is supported.

There are two ways to extend OpenOffice.org to load a new content format:

- implementing a frame loader that uses its own internal mechanism to create the expected visual component, for example, . local file access.

- implementing a filter that does the same,but isused by a generic frame loader implementation.

Note that the first method does not work for exporting, because a loader service can not be used at save timeTo enable a content format for import and export is to provide a filter service. A generic frame loader implementation already exists in OpenOffice.orgthat uses all well known registered filters in a uniform way. So the second method is preferred.

##### Filter

Most of the services described before are used for loading. Normally, they are not necessary for saving, except the MediaDescriptor. Only filters are fixed members of both processes.

These objects also represent a service. Their task is to import or export the content of a type into or from a model. Accordingly, import filters are distinguished from export filters. It is possible to provide both functionality in the same implementation.

A filter is acquired from the factory service com.sun.star.document.FilterFactory. It provides a low-level access to the configuration that knows all registered filters of OpenOffice.org, supports search functionality, and creates and initializes filter components. The description of this factory and its configuration are provided below.

If a filter wants to be initialized with its own configuration data or get existing parameters of the corresponding create request, it implements the interface com.sun.star.lang.XInitialization. The method initialize() is used directly after creation by the factory and is the first request on a new filter instance. The parameter list of initialize() uses the following protocol:

- The first item in the list is a sequence of com.sun.star.beans.PropertyValue structs, that describe the configuration properties of the filter.

- All other items are directly copied from the parameter Arguments of the factory interface method com.sun.star.lang.XMultiServiceFactory:createWithArguments().

A filter should be initialized, because one generic implementation is registered to handle different types, it must know which specialization is required. The simplest way to achieve this for the filter is to know its own configuration data, especially the unique internal name.

This information is used internally then, or it is provided by the interface com.sun.star.container.XNamed . An owner of a filter uses the provided name to find specific information about this component by using the FilterFactory service.

| The interface provides functionality for reading and writing of this name. It is not allowed to change an internal filter name during runtime of OpenOffice.org, because all filter names must be unique and it is not possible for a filter instance to alter its name. Calls to com.sun.star.container.XNamed:setName() should be ignored or forwarded to the FilterFactory service, which knows all unique names and can solve ambigities!
--- | ---

This code snippet initializes a filter instance:

private String m_sInternalName;

public void initialize( Object[] lArguments )
throws com.sun.star.uno.Exception
{
// no arguments – no initialization
if (lArguments.length<1)
return;

// Arguments[0] = own configuration data
com.sun.star.beans.PropertyValue[] lConfig =
(com.sun.star.beans.PropertyValue[])lArguments[0];
// Arguments[1..n] = optional arguments of create request
for (int n=1; n<lArguments.length; ++n)
{
...
}
// analyze own configuration data for our own internal
// filter name! Important for generic filter services,
// which are registered more then once. They can use this
// information to find out, which specialization of it
// is required.
for (int i=0; i<lConfig.length; ++i)
{
if (lConfig[i].Name.equals("Name"))
{
m_sInternalName =
AnyConverter.toString(lConfig[i].Value);
// Tip: A generic filter implementation can use this internal
// name at runtime, to detect which specialization of it is required.
if (m_sInternalName==”filter_format_1”)
m_eHandle = E_FORMAT_1;
else
if (m_sInternalName==”filter_format_2”)
...
}
}
}

Furthermore, depending on its action a filter supports the services com.sun.star.document.ImportFilter for import or com.sun.star.document.ExportFilter for export functionality.

The common interface of both services is com.sun.star.document.XFilter starts or cancels the filter process. How the cancelling is implemented is an internal detail of the filter implementation, however a thread is a good solution.

On calling com.sun.star.document.XFilter:filter(), the already mentioned MediaDescriptor is passed to the service. It includes the necessary information about the content, for example, the URL or the stream, but not the source or the target model for the filter process.

Additional interfaces are part of the service description, com.sun.star.document.XImporter and com.sun.star.document.XExporter to get this information. These interfaces are used directly before the filter operation is started. A filter saves the model set by setTargetDocument() and setSourceDocument(), and uses it inside its filter operation.

| The filter() method does not include any information about the required import or export functionality. It seems that it is not possible to implement both at the same object. The interfaces XImporter/XExporter are used to solve this conflict. Only one of them is called for one filter() request. So an internal flag that indicates the using of an interface helps.
--- | ---

This example code detects the required filter operation: (OfficeDev/FilterDevelopment/AsciiFilter/AsciiReplaceFilter.java)

private boolean m_bImport;
// used to tell us: "you will be used for import"
public void setTargetDocument(
com.sun.star.lang.XComponent xDocument )
throws com.sun.star.lang.IllegalArgumentException
{
m_bImport = true;
}
// used to tell us: "you will be used for export"
public void setSourceDocument(
com.sun.star.lang.XComponent xDocument )
throws com.sun.star.lang.IllegalArgumentException
{
m_bImport = false;
}
// detect required type of filter operation
public boolean filter(
com.sun.star.beans.PropertyValue[] lDescriptor )
{
boolean bState = false;
if (m_bImport==true)
bState = impl_import( lDescriptor );
else
bState = impl_export( lDescriptor );
return bState;
}

The MediaDescriptor does not include the model, but it should include the already opened stream, true for the current implementation in OpenOffice.org. If it is there, it must be used. Only if a stream does not exist, it indicates that someone else uses this filter service, for example, outside OpenOffice.org, it creates a stream of your own by using the URL parameter of the descriptor.

In general, a filter must not change the position of an incoming stream without reading or writing data. The position inside the stream is 0. Follow the previouslymentioned rules for handling streams of the section about the MediaDescriptor above. We can make these rules easier, because currently there are no external filters used inside office. See descriptions of the chapter “MediaDescriptor” before ... )).

##### Filter Options

It is possible to parameterize a filter component. For example, the OpenOffice.org filter "Text - txt - csv (StarCalc)" needs a separator used to detect columns. This information is transported inside the MediaDescriptor. A special property named FilterData of type any exists. The value depends on the filter implementation and is not specified.

| There is another string property named FilterOptions. It should be used if the flexibility of an any is not required. For historical reasons, a third-string property FilterFlags exists. It is deprecated, so it is not recommend for use.
--- | ---

A generic UI that uses a filter as one part of a load request does not know about special parameters. Normally, the FilterData are not set inside the media descriptor, therefore a filter should use default values. It should be possible to prompt the user for better values by registering another component that implements the service com.sun.star.ui.dialogs.FilterOptionsDialog. It is called UIComponent. It enables a filter developer to query for user options before the filter operation is performed. It does not show this dialog inside the filter, because any UI can be suppressed, for example, an external application uses the API of OpenOffice.org for scripting running in a hidden mode. The code that uses the filter decides if it is necessary and allowed to use the dialog. If not, the filter lives with missing parameters and uses default values. If it is not possible to have defaults, it aborts the filter() request returning false.

The UIComponent provides an interface com.sun.star.beans.XPropertyAccess used to set the whole MediaDescriptor before executing the dialog using the FilterOptionsDialog interface com.sun.star.ui.dialogs.XExecutableDialog and retrieves the changes. The user of the dialog decides if the changes are merged with the original ones or replaced. Using the whole descriptor provides the informtion about the environment in which the filter works, for example, the URL or information about preview mode. The parameters of a filter depend on it. Normally a UIComponent is shown if no FilterData or FilterOptions are part of the descriptor, so that they are added. In the case where they exist, it is necessary to change it.

| If the filter programmer wants to implement a generic dialog for different filters, then he must know which of these filters the UIComponent is shown. This information exists inside the MediaDescriptor, called FilterName. The outside code which uses the dialog knows this filter alsoand should set it in the descriptor, because the implementation name of the component must be known to create the dialog. This information exists inside the configuration where it is registered for a filter.
--- | ---

##### Configuring a Filter in OpenOffice.org

As previously discussed, the whole process of loading and saving content works generically in many components and can be adapted to the needs of a user through the addition of custom modules or the removal of others. All this information about services and parameters are organized in a special configuration branch of OpenOffice.org called org.openoffice.Office.TypeDetection. The principal structure is shown below:

Illustration 6.18: Structure of org.openoffice.Office.TypeDetection Configuration Branch

As shown on the left, the file consists of lists called sets. The list items are described by the structures shown on the right to which the arrows point. It works similar to 1:n relations in a database. Every filter, frame loader, detector is registered for one or multiple types. The detection of the proper type is important for the functionality of the whole system. If the right loader or filter cannot be found, the load or save request does not produce the right results.

To extend OpenOffice.org to load or save new content formats, a new type entry is added describing the new content. Furthermore, a filter item is registered for this new type. An optional and recommended change for a detector can be done.

| It is not a good idea to edit the configuration branch files directly to make these changes. It is better to use the configuration API to do so, because the format of the file may be changed in the future. The properties describing the components, such as types and filters, are always the same and are not likely to be changed or in an incompatible manner. It is better to add entries by specifying their properties using the API only. To make this easier for external programmers, this manual provides a OpenOffice.org Basic script that is used for that purpose called regfilter.bas. The work to be done by the filter programmer is to provide an ini file that includes the properties and start the basic script inside OpenOffice.org. The script reads the file and uses it to change the configuration package. These changes are done for the user layer of the configuration, so it is possible to restore the original state. There is also an example ini file in the samples folder for this manual that can be used for your own purposes called regfilter.ini.
--- | ---

##### General Notes

In OpenOffice.org, there are services providing a special API to access the underlying configuration repository. Most of these services support container functionality and allow read access whereas some services offer write access also . During runtime, every configuration item, such as type, filter, and detector, is represented as a sequence of com.sun.star.beans.PropertyValue structs. The next sections describe the names and values of those structures.

##### Necessary Steps

To extend OpenOffice.org by new content formats, use the following steps:

1. Implement a filter component. It must be able to load or save the type it is registered for. For access to the office, only the API of the document service or universal content provider keeps the filter compatible with new versions of OpenOffice.org.

2. Provide an implementation of a com.sun.star.document.ExtendedTypeDetection service to analyze a given content. It must return an internal type name representing the type or an empty value for unknown formats.

3. Add a filter options dialog if the implemented filter requires additional parameters. Keep it separate from the filter and change the given MediaDescriptor based on user input.
   Document the parameters so that an external script programmer can use this information to provide proper values to the MediaDescriptor.

4. Register the component libraries as UNO services inside OpenOffice.org. This is done by the mechanism described in the chapter 4.9 Writing UNO Components - Deployment Options for Components.

5. Adapt the configuration branch org.openoffice.Office.TypeDetection so that it knows these new components. Use OpenOffice.org Basic script regfilter.bas that is provided as an additional tool in this chapter. It requires an ini file that is specified inside the subroutine Main of the script and has to be adjusted for your own purposes. It is well documented, and uses the names and value types described in this manual.

##### Properties of a Type

Every type inside OpenOffice.org is specified by the properties shown in the table below. These values are accessible at the previously mentioned service com.sun.star.document.TypeDetection using the interface com.sun.star.container.XNameAccess. Write access is not available here. All types are addressed by their internal names.

Properties of a Document Type, available at TypeDetection
Name | string. The internal name of a type must be unique and is also used as a list entry. It contains any special characters, but they must been coded.
UIName | string. Displays the type at the user interface under a localized name. You must assign a value for a language, thus supporting CJK versions. All Unicode characters are permitted here.
MediaType | string. Describes the MIME type of the contents. The reason is that the internal names can be altered at any time without affecting the process.
ClipboardFormat | string. The format is a unique description of this type for use in clipboards.
URLPattern | sequence<string>. Important components of a type are the patterns. They enable the support of your own URL schemata, for example, in OpenOffice.org "private:factory/swriter" for opening an empty text document. The wildcards '*' or '?' are supported here.
Extensions | sequence<string>. The type of a content can be derived from its URL by its extension. In most cases, the flat detection depends on them alone.
Preferred | boolean. Since file extensions cannot always be assigned to a unique type, this flag was introduced. It indicates the preferred type for a group of types with similar properties, otherwise, the first match is used.
DocumentIconID | int. You can assign an icon to a type. To do this, the ID is used as reference to a resource. This feature is currently not supported in OpenOffice.org.

##### Properties of an ExtendedTypeDetection Service

In contrast to filters or frame loaders, the ExtendedTypeDetection has no configuration API on top of its configuration data. The normal configuration API of OpenOffice.org has to be used, as described in 15 Configuration Management . The configuration set org.openoffice.Office.TypeDetection/DetectServices could be used, but it is better to use the already mentioned basic macro regfilter.bas in combination with regfilter.ini . Such detector services are used automatically during type detection of content. A detector service is addressed by its UNO implementation name.

Property Name | Description
ServiceName | string. This must be a valid UNO implementation name. This field cannot contain the service name, because this value must be unique, otherwise it would be impossible to distinguish more than one registered entry, for a service name is not unique. This value is also an entry in the corresponding configuration list.
Types | sequence<string>. A list of type names recognized by this service that makes it possible to write a servicethat detects more than one type.

##### Properties of a Filter

Every filter is registered for only one type . Multiple registrations are to be done by multiple configuration entries. One type is handled by more than one filter. Flags also regulate the use of the preferred filter. A filter is described by the following properties:

Property Name | Description
--- | ---
Name | string. The internal name of a filter must be unique and is also used as list entry. It contains special characters, but they must be encoded.
UIName | string. A filter should be able to show a localized name in selection dialogs. You must assign a value for a language, thus supporting CJK versions. All Unicode characters are permitted here.
Installed | boolean. This flag indicates the installation status of a filter. A filter is generally registered equally for all users. In a network installation you should deactivate this for certain groups or single users. Note: A filter works only if the component library has already been registered in OpenOffice.org.
Order | int. This number shows filters in a user defined order. Valid values are greater then 0. If the number is set to 0, sorting is done alphabetically by the UIName property of the filter. The same applies to filters that have the same Order value.
Type | string. A filter must register itself for the type it can handle. Multiple assignments are not allowed. Multiple configuration entries must be created, one for every supported type.
DocumentService | string. Describes the component for which the filter operates,For example, "com.sun.star.text.TextDocument", depending upon the use., This is considered the output or goal of the filter process. A UNO service name is expected. Note: The implementation name cannot be used here, the generic type of the document is needed.
FilterService | string. This is the UNO implementation name of the filter. It should be clear that this field can not contain the service name of a filter, otherwise OpenOffice.org could not distinguish more then one registered filter.
UIComponent | string. Describes an implementation of a UI dialog used by the filter to let the user modify certain properties for filtering. For example, the "Text - txt - csv (StarCalc)" needs information about the used column separators to load data. To distinguish between different implementations, it must be the real UNO implementation name, not a service name.
Flags | int. Describes the filter, as shown in the table below. This is where, the organization into import and export filters takes place. Note that external filters must set the ThirdParty flag to be detected.
UserData | sequence<string>. Some filters need to store more configuration data than usual. This is realized through this entry. The format of the string list is not restricted.
FileFormatVersion | int. Indicates a version number of a document that can be edited by this filter.
TemplateName | string. The name of a template file for importing styles. It is a special feature for importing documents only and not useable for export. Every OpenOffice.org document service knows default styles. If this TemplateName is set, it merges these default styles with the styles of the template, and the template styles are merged with all styles of a document that is imported by this filter.

Most functionality of a Filter is listed by its flags. They are necessary to prevent a filter from being displayed in a UI, and to classify import and export, or internal a nd external filters, and prefer some filters to others. Currently supported flags are:

Name | Value | Description
--- | --- | ---
Import | 0x00000001 h | This filter supports the specification of a com.sun.star.document.ImportFilter and is used for loading content.
Export | 0x00000002 h | This filter supports the specification of a com.sun.star.document.ExportFilter and is used for saving content.
Template | 0x00000004 h | These filters are specialized to handle template formats. By default, a filtered document is used as a template to create a new document .
Internal | 0x00000008 h | This filter should never be shown on any UI and not be available.
OwnTemplate | 0x00000010 h | Templates used with the template API of OpenOffice.org and it supports the internal template features. For older versions, it is useable for internal content formats only.
Own | 0x00000020 h | Tag the intrinsic content formats of OpenOffice.org based on OLE storage or zip packages.
Alien | 0x00000040 h | A filter with this flag is not fully compatible with the current document format. It is unclear what document features will be lost during saving. This flag decides if a warning box on saving has to be shown.
UsesOptions (deprecated) | 0x00000080 h | This filter could be customized during processing. Older versions of OpenOffice.org used it to customize the "SaveAs" dialog. Newer versions uses the filter property "UIComponent" to tell if a filter provides filter options.
Default | 0x00000100 h | Mark a filter as the default filter for saving. Only one filter in an application module, distinguished through the DocumentService property, has this flag set.
NotInFileDialog | 0x00001000 h | Suppress display of a filter in file open and save dialogs.
NotInChooser | 0x00002000 h | Suppress display of a filter in UI elements for choosing filters.
ThirdParty | 0x00080000 h | These filters are developed by external parties. For historical reasons, the filter detection of OpenOffice.org differentiates between old internal and new external ones, because the former are not UNO based and are used differently.
Preferred | 0x10000000 h | If more than one filter is registered for the same type, this flag prefers one of them at loading time if the user does not select a specific filter. In contrast to the Default flag, it does not depend on the application module, but there can only be one preferred filter for a type.

| Besides these filter flags there are other flags existing that are used currently, but are not documented here. Use documented flags only.
--- | ---

The service com.sun.star.document.FilterFactory provides these data. It supports read access by using the interface com.sun.star.container.XNameAccess. All items are addressed by their internal names. The return value is represented as a list of type com.sun.star.beans.PropertyValue structures. It uses the filter properties shown above.

Another aspect of this service is the factory interface com.sun.star.lang.XMultiServiceFactory. It creates filter instances using an internal type, or an internal filter name directly. Using a type name searches for a suitable filter and creates, initializes and returns it. Using a filter name directly follows the algorithm shown in the box below. Note that creation of filters is possible for external ones only that have set the FilterService property. Most of the current filters of OpenOffice.org are internal filters, implemented as local code, but not as a UNO service. They can not be created by this FilterFactory. It is possible to ask only for their properties.

| Direct creation of a filter instance is only possible using a special argument in the createInstanceWithArguments () call of the interface XMultiServiceFactory. To do so, a com.sun.star.beans.PropertyValue FilterName with the internal name of the requested filter as value must be used. Otherwise, the service specifier, that is, the first argument of the create call, is interpreted as an internal type name. It will be used to search a suitable, preferred filter that will be created. It is a combination of searching and creation. Future implementations will split that to make it clearer. In future implementations, a registered filter must be searched through the provided query mechanism and created by using this factory interface.
--- | ---

##### Properties of a FrameLoader

OpenOffice.org distinguishes asynchronous (com.sun.star.frame.FrameLoader) and synchronous (com.sun.star.frame.SynchronousFrameLoader) frame loader implementations, but the configuration does not recognize that. The interface is supported by the loader is detected at runtime , the synchronous interface being preferred. The following properties describe a loader:

Properties of a FrameLoader
Name | string. This must be a valid UNO implementation name. It should be obvious that this field can not contain the service name, because this value must be unique. Otherwise OpenOffice.org could not distinguish more than one registered entry, for there can be several implementations for a service name. This value is also an entry in the corresponding configuration list.
UIName | string. Displays the loader at a localized user interface. You must assign a value for a language, thus supporting CJK versions. All Unicode characters are permitted.
Types | sequence<string>. A list of type names recognized by this service You can also implement and register loader for groups of types.

The service com.sun.star.frame.FrameLoaderFactory makes this data available. It uses the same mechanism as the com.sun.star.document.FilterFactory, that is, an interface for data access, com.sun.star.container.XNameAccess, and another one for creation of such a FrameLoader, com.sun.star.lang.XMultiServiceFactory.

There are other properties than the properties described, for example, for the ContentHandler. They are not necessary for the environment of filters, or loading and saving documents, so they are not described. Additional information is found at http://framework.openoffice.org .

There is one entry in the configuration, used as a fallback if a registered item is not found, the generic FrameLoader. It is not necessary for an external developer to provide a frame loader to add support for an unknown document format to OpenOffice.org. It is enough to register a new filter component that is used by this special loader in a generic manner.

#### XML Based Filter Development

##### Introduction

This chapter outlines the development of XML based filtering components that use the XML filter adaptor framework. The XML filter adaptor is a generic com.sun.star.document.XFilter implementation. It has been designed to be reusable, and to supply a standard method of designing and referencing XML based import and export filter s. The XML filter adaptor does not perform any of the filtering functionality itself, but instead is used to instantiate a filtering component.

The advantage of the XML filter adaptor framework is that you do not have to work with document models to create a document from an import file, nor do you have to iterate over a document model to export it to a different file format. Rather, you can use the OpenOffice.org XML file format to import and export. When importing, you parse your import file and send OpenOffice.org XML to the filter adaptor, which creates a document for you in the GUI. When exporting, the office sends a description of the current document as OpenOffice.org XML, so that you can export without having to iterate over a document model.

The course of action during export and import can be described as follows: when a user clicks File-Open, or some UNO code calls loadComponentFromURL(), the office looks in the type detection configuration to identify an import filter and optionally checks the file format by doing some deep detection. It instantiates the import filter it finds and uses its method importer() to pass a MediaDescriptor for the source, a specialized XML document handler for OpenOffice.org XML, and user data. The import filter has to read the import source and deliver OpenOffice.org XML to the document handler received in the call to importer(), emulating a SAX parser that calls the parser callback functions.

Similarly, the office instantiates an export filter after clicking File-Save (As) or a call to storeXXX() , and uses its method exporter() to pass a target location and user data. In this case, the office expects the export filter to be a com.sun.star.xml.sax.XDocumentHandler , which is able to handle OpenOffice.org XML. The office creates an export stream with OpenOffice.org XML, and parses this XML so that the export filter receives the SAX callbacks and can translate them to whatever is necessary, writing the result to the target received in the call to com.sun.star.xml.XExportFilter:exporter() .

##### Components of a Filter

For a filter of this type to operate, three things are necessary.

1. The XML filter adaptor.

2. A filtering component that implements the required interfaces.

3. A valid filter and type definition.

Both the XML filter adaptor and the filtering component are UNO components that can be instantiated through the com.sun.star.lang.XMultiServiceFactory:createInstance() method. Since the XML filter adaptor is generic, the filtering component is all that needs to be implemented. Once this has been done, the TypeDetection.xcu file can be expanded to include the newly created filter definition.

##### Writing the Filtering Component

The filtering component must implement the following interfaces as described by the com.sun.star.xml.ImportFilter service and the com.sun.star.xml.ExportFilter service:

Importer :

com.sun.star.xml.XMLImportFilter

Exporter :

com.sun.star.xml.XMLExportFilter and com.sun.star.xml.sax.XDocumentHandler

###### XImportFilter

The servicecom.sun.star.xml.XMLImportFilter defines an interface with the following method:

boolean importer(
[in] sequence< com::sun::star::beans::PropertyValue > aSourceData,
[in] com::sun::star::xml::sax::XDocumentHandler xDocHandler,
[in] sequence< string > msUserData )

aSourceData is a MediaDescriptor, which can be used to obtain the following information:

- An XInputStream
  This is a stream that is attached to the source to be read. This can be a file, or some other data source.

- Filename
  This is the name of the file on the disk, that the input stream comes from.

- Url
  This is a url describing the location being read.

xDocHandler is a SAX event handler that can be used when parsing an XInputStream, which may or may not contain OpenOffice.org XML. Before this stream can be read by OpenOffice.org, it will need to be transformed into OpenOffice.org XML.

msUserData is an array of Strings , that contains the information supplied in the UserData section of the Filter definition in the TypeDetection.xcu file.

###### XExportFilter

The com.sun.star.xml.XExportFilter defines an interface with the following method:

boolean exporter(

[in] sequence< com::sun::star::beans::PropertyValue > aSourceData,

[in] sequence< string > msUserData )

aSourceData and msUserData contain the same type of information as in the importer, except that the MediaDescriptor contains an XOutputStream , which can be used to write to.

###### XDocumentHandler

When the export takes place, the new Filtering component must also be an XDocumentHandler , to allow the output based on SAX events to be filtered, if required. For this reason, an XDocumentHandler is not passed to the exporter, and any exporter that is used by the XML filter adaptor must implement the com.sun.star.xml.sax.XDocumentHandler interface.

##### The Importer

###### Evaluating XImportFilter Parameters

The writing of an importer usually starts with extracting the required variables from the Mediadescriptor and the userData . These variables are required for the filtering component to operate correctly. Depending on the requirements of the individual filter, the first thing to do is to extract the information from the MediaDescriptor, referred to as aSourceData in the interface definition. This can be achieved as follows:

Get the number of elements in the MediaDescriptor

sal_Int32 nLength = aSourceData.getLength();

Iterate through the MediaDescriptor to find the information needed: an input stream, a file name, or a URL.

for (sal_Int32 i = 0; i < nLength; i++) {

if (pValue[i].Name.equalsAsciiL (RTL_CONSTASCII_STRINGPARAM("InputStream")))
pValue[i].Value >>= xInputStream;

else if (pValue[i].Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("FileName")
pValue[i].Value >>= sFileName;

else if (pValue[i].Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("URL")))
pValue[i].Value >>= sURL;

}

The msUserData parameter passed to importer() contains information that defines how the filter operates, so this information must be referenced as required.

###### Importer Filtering

An XInputStream implementation has now been obtained that contains all of the information you want to process. From the filtering perspective, you can just read from this stream and carry out whatever processing is required in order for the input to be transformed into OpenOffice.org XML. Once this has been done, however, you need to write the result to where it can be parsed into OpenOffice.org's internal format. A Pipe can be used to achieve this. A Pipe is a form of buffer that can be written to and read from. For the importer, read from the XInputStream that was extracted from the MediaDescriptor, and once the filtering has taken place, write to a Pipe that has been created. This Pipe can be read from when it comes to parsing. This is how the Pipe is created:

Reference <XInterface> xPipe;

// We Create our pipe

xPipe= XflatXml::xMSF->createInstance(OUString::createFromAscii("com.sun.star.io.Pipe"));

// We get an inputStream to our Pipe

Reference< com::sun::star::io::XInputStream > xPipeInput (xPipe,UNO_QUERY);

// We get an OutputStream to our Pipe

Reference< com::sun::star::io::XOutputStream > xTmpOutputStream (xPipe,UNO_QUERY);

The XInputStream can be read from, and the XOutputstream can be written to.

###### Parsing the Result

Once the desired OpenOffice.org XML has been produced and written to the XOutputStream of the Pipe, the XinputStream of the Pipe can be parsed with the aid of the XdocumentHandler.

// Create a Parser

const OUString sSaxParser(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.xml.sax.Parser"));

Reference < com::sun::star::xml::sax::XParser > xSaxParser(xMSF->createInstance(sSaxParser), UNO_QUERY);

// Create an InputSource using the Pipe

com::sun::star::xml::sax::InputSource aInput;

aInput.sSystemId = sFileName; // File Name

aInput.aInputStream = xPipeInput; // Pipe InputStream

// Set the SAX Event Handler

xSaxParser->setDocumentHandler(xHandler);

// Parse the result

try {
xSaxParser->parseStream(aInput);

}

catch( Exception &exc){
// Perform exception handling

}

Assuming that the XML was valid, no exceptions will be thrown and the importer will return true. At this stage, the filtering is complete and the imported document will be displayed.

##### The Exporter

###### Evaluating XExportFilter Parameters

The exporter() method operates in much the same way as importer(), except that instead of the exporter using a provided XDocumentHandler, it is itself a com.sun.star.xml.sax.XDocumentHandler implementation.

When the exporter() method is invoked, the necessary variables need to be extracted for use by the filter. This is the same thing that happens with the importer, except that the MediaDescriptor contains an XOutputStream , instead of the importer's XInputStream . Once the variables have been extracted (and—in some cases—a Pipe has been created) the exporter() method returns. It does not carry out the filtering at this stage.

| The pipe is only necessary if the output needs to be processed further after being processed by the XDocumentHandler. Otherwise, the result from the XDocumentHandler implementation can be written directly to the XOutputStream provided. For instance, this is the case with a FlatXML filter.
--- | ---

###### Exporter Filtering

After the exporter() method returns, the XML filter adaptor then invokes the com.sun.star.xml.sax.XDocumentHandler methods to parse the XML output.

For the filtering, the com.sun.star.xml.sax.XDocumentHandler implementation is used. This consists of a set of SAX event handling methods, which define how particular XML tags are handled. These methods are:

startDocument(){
}

endDocument(){
}

startElement(){
}

endElement(){
}

charactors(){
}

ignorableWhitespace(){
}

processingInstruction(){
}

setDocumentLocator(){
}

The result of this event handling can be processed and written to the XOutputStream that was extracted from the MediaDescriptor .

##### Configuration

For OpenOffice.org to be able to make use of this filtering component, the filter and the type that it handles must be defined in the TypeDetection.xcu file.

The type section defines certain file types and their extensions. The filter section contains the actual filter definition.

Below is an example of a type and filter definition in the TypeDetection.xcu file. It describes a PocketWord filter.

<!-- Type section -->

<node oor:name="writer_PocketWord_File" oor:op="replace">

<prop oor:name="UIName">

<value xml:lang="en-US">Pocket Word</value>

</prop>

<prop oor:name="Data">

<value>0,,,,psw,20002,</value>

</prop>

</node>

<!-- Filter section -->

<node oor:name="PocketWord File" oor:op="replace">

<prop oor:name="UIName">

<value xml:lang="en-US">Pocket Word</value>

</prop>

<prop oor:name="Data">

<value>

0,

writer_PocketWord_File,

com.sun.star.text.TextDocument ,

com.sun.star.comp.Writer.XmlFilterAdaptor,

524355,

com.sun.star.documentconversion.XMergeBridge;

classes/pocketword.jar;

com.sun.star.comp.Writer.XMLImporter;

com.sun.star.comp.Writer.XMLExporter;

staroffice/sxw;application/x-pocket-word,

0,

,

</value>

</prop>

<prop oor:name="Installed" oor:type="xs:boolean">

<value>true</value>

</prop>

</node>

The type section defines a type writer_PocketWord_File with "Pocket Word" as UIName in File - Open . The file extension of this type is specified as .psw in the Data property.

The filter section defines the same UI name "Pocket Word" for the filter, and a number of settings in the Data property, simply separated by commas:

Type

This is the id of the file t ype definition that defines the type of file that this filter can handle, in this case "writer_PocketWord_File ” . This value refers to the oor:name of the type section.

Office application

This is the application that will be used to open the document, in this case Writer (“ com.sun.star.text.TextDocument ”).

Filter Component

This is the component that OpenOffice.org will initialize when importing or exporting, in this case “ com.sun.star.comp.Writer.XmlFilterAdaptor “.

User Data

Section containing the filtering component that the XML filter adaptor will initialize and use for filtering. In this example “ com.sun.star.documentconversion.XMergeBridge;classes/pocketword.jar;com.sun.star.comp.Writer.XMLImporter;
com.sun.star.comp.Writer.XMLExporter;staroffice/sxw;application/x-pocket-word ”

From this example, you see that this Filter uses the XML filter adaptor. When the XML filter adaptor initializes, it initializes the XMergeBridge that is specified in the UserData section. The rest of the information in the UserData section has been included for use by the XMergeBridge filtering component, including the com.sun.star.comp.Writer.XMLImporter, which will be used by the XML filter adaptor to create the XDocumentHandler when importing.

##### Sample Filter Component Implementations

There are currently three filtering components which use the XML filter adaptor.

The first one is the XMergeBridge . This has been created as a means of linking the XMerge Small Device filter framework with OpenOffice.org. This means that any available XMerge plugin, can also be used as a OpenOffice.org filter. This is currently hosted within the XMerge project in openoffice cvs at

xml/xmerge/java/org/openoffice/xmerge/xmergebridge

The final two are a Java and a C++ implementation of a Flat OpenOffice.org XML reader and writer. These are intended to be sample filter component implementations, and offer a skeleton filter component that can be expanded upon by developers wishing to create their own filtering components. These are temporarily hosted in cvs at

xml/xmerge/java/org/openoffice/xmerge/xmergebridge/FlatXml

##### Additional Components

In order for Java based components to operate effectively, a set of wrapper classes have been added to the javaunohelper package. These files allow for an XInputStream or an XOutputStream to be accessed using the same methods as a normal Java InputStream or OutputStream. These classes are located in the javaunohelper package at

com.sun.star.lib.uno.adapter.XInputStreamToInputStreamAdapter

com.sun.star.lib.uno.adapter.XInputStreamToInputStreamAdapter

For more information on the use of these helper classes, see the flatxmljava example.

##### XML Filter Detection

The number of XML files that conform to differing DTD specifications means that a single filter and file type definition is insufficient to handle all of the possible formats available. In order to allow OpenOffice.org to handle multiple filter definitions and implementations, it is necessary to implement an additional filter detection module that is capable of determining the type of XML file being read, based on its DocType declaration.

To accomplish this, a filter detection service com.sun.star.document.ExtendedTypeDetection can be implemented, which is capable of handling and distinguishing between many different XML based file formats. This type of service supersedes the basic flat detection , which uses the file's suffix to determine the Type, and instead, carries out a deep detection which uses the file's internal structure and content to detect its true type.

###### Requirements for Deep Detection

There are three requirements for implementing a deep detection module that is capable of identifying one or more unique XML types. These include:

- An extended type definition for describing the format in more detail (TypeDetection.xcu).

- A DetectService implementation.

- A DetectService definition (TypeDetection.xcu).

###### Extending the File Type Definition

Since many different XML files can conform to different DTDs, the type definition of a particular XML file needs to be extended. To do this, some or all of the DocType information can be contained as part of the file type definition. This information is held as part of the ClipboardFormat property of the type node. A unique namespace or preface identifies the String at this point in the sequence as being a DocType declaration.

Illustration 6.19

###### Sample Type definition:

<node oor:name="writer_DocBook_File" oor:op="replace">

<prop oor:name="UIName">

<value XML:lang="en-US">DocBook</value>

</prop>

<prop oor:name="Data">

<value> 0,

,

doctype:-//OASIS//DTD DocBook XML V4.1.2//EN,

,

XML,

20002,

</value>

</prop>

</node>

###### The ExtendedTypeDetection Service Implementation

In order for the type detection code to function as an ExtendedTypeDetection service, you must implement the detect() method as defined by the com.sun.star.document.XExtendedFilterDetection interface definition:

string detect( [inout]sequence<com::sun::star::beans::PropertyValue > Descriptor );

This method supplies you with a sequence of ProptertyValues from which you can use to extract the current TypeName and the URL of the file being loaded:

::rtl::OUString SAL_CALL FilterDetect::detect(com::sun::star::uno::Sequence< com::sun::star::beans::PropertyValue >& aArguments ) throw (com::sun::star::uno::RuntimeException) {

const PropertyValue * pValue = aArguments.getConstArray();

sal_Int32 nLength;

::rtl::OString resultString;

nLength = aArguments.getLength();

for (sal_Int32 i = 0; i < nLength; i++) {

if (pValue[i].Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("TypeName"))) {

}

else if (pValue[i].Name.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("URL"))) {

pValue[i].Value >>= sUrl;

}

}

Once you have the URL of the file, you can then use it to create a ::ucb::Content from which you can open an XInputStream to the file:

Reference< com::sun::star::ucb::XCommandEnvironment > xEnv;

::ucb::Content aContent(sUrl,xEnv);

xInStream = aContent.openStream();

You can now use this XInputStream to read the header of the file being loaded. Because the exact location of the DocType information within the file is not known, the first 1000 bytes of information will be read:

::rtl::OString resultString;

com::sun::star::uno::Sequence< sal_Int8 > aData;

long bytestRead =xInStream->readBytes (aData, 1000);

resultString=::rtl::OString(

(const sal_Char *)aData.getConstArray(),bytestRead) ;

Once you have this information, you can start looking for a type that describes the file being loaded. In order to do this, you need to get a list of the types currently supported:

Reference <XNameAccess> xTypeCont(mxMSF->createInstance(OUString::createFromAscii(

"com.sun.star.document.TypeDetection" )),UNO_QUERY);

Sequence <::rtl::OUString> myTypes= xTypeCont->getElementNames();

nLength = myTypes.getLength();

For each of these types, you must first determine whether the ClipboardFormat property contains a DocType:

Loc_of_ClipboardFormat=...;

Sequence<::rtl::OUString> ClipboardFormatSeq;

Type_Props[Loc_of_ClipboardFormat].Value >>=ClipboardFormatSeq ;

while() {

if(ClipboardFormatSeq.match(OUString::createFromAscii("doctype:") {

//if it contains a DocType, start to compare to header

}

}

All of the possible DocType declarations of the file types can be checked to determine a match. If a match is found, the type corresponding to the match is returned. If no match is found, an empty string is returned. This will force OpenOffice.org into flat detection mode.

###### TypeDetection.xcu DetectServices Entry

Now that you have created the ExtendedTypeDetection service implementation, you need to tell OpenOffice.org when to use this service.

First create a DetectServices node, unless one already exists, and then add the information specific to the detection service that has been implemented, that is, the name of the service and the file types that use it.

<node oor:name="DetectServices">

<node oor:name="com.sun.star.comp.filters.XMLDetect" oor:op="replace">

<prop oor:name="ServiceName">

<value XML:lang="en-US">com.sun.star.comp.filters.XMLDetect</value>

</prop>

<prop oor:name="Types">

<value>writer_DocBook_File</value>

<value>writer_Flat_XML_File</value>

</prop>

</node>

</node>

### 6.2.5 Number Formats

Number formats are template strings consisting of format codes defining how numbers or text appear, for example,, whether or not to display trailing zeroes, group by thousands, separators, colors, and how many decimals are displayed. This does not include any font attributes, except for colors. They are found wherever number formats are applied, for example, on the Numbers tab of the Format – Cells dialog in spreadsheets.

Number formats are defined on the document level. A document displaying formatted values has a collection of number formats, each with a unique index key within that document. Identical formats are not necessarily represented by the same index key in different documents.

#### Managing Number Formats

Documents provide their formats through the interface com.sun.star.util.XNumberFormatsSupplierthat has one method getNumberFormats() that returns com.sun.star.util.NumberFormats. Using NumberFormats, developers can read and modify number formats in documents, and also add new formats.

You have to retrieve the NumberFormatsSupplier as a property at a few objects from their com.sun.star.beans.XPropertySet interface, for example, from data sources supporting the com.sun.star.sdb.DataSource service and from database connections supporting the service com.sun.star.sdb.DatabaseEnvironment, or com.sun.star.sdb.DatabaseAccess. In addition, all UNO controls offering the service com.sun.star.awt.UnoControlFormattedFieldModel have a NumberFormatsSupplier property.

##### NumberFormats Service

The com.sun.star.util.NumberFormats service specifies a container of number formats and implements the interfaces com.sun.star.util.XNumberFormatTypes and com.sun.star.util.XNumberFormats.

###### XNumberFormats

NumberFormats supports the interface com.sun.star.util.XNumberFormats. This interface provides access to the number formats of a container. It is used to query the properties of a number format by an index key, retrieve a list of available number format keys of a given type for a given locale, query the key for a user-defined format string, or add new format codes into the list or to remove formats.

com::sun::star::beans::XPropertySet getByKey ( [in] long nKey )

sequence< long > queryKeys ( [in] short nType,

[in] com::sun::star::lang::Locale nLocale,

[in] boolean bCreate )

long queryKey ( [in] string aFormat,

[in] com::sun::star::lang::Locale nLocale,

[in] boolean bScan )

long addNew ( [in] string aFormat, [in] com::sun::star::lang::Locale nLocale )

long addNewConverted ( [in] string aFormat, [in] com::sun::star::lang::Locale nLocale,

[in] com::sun::star::lang::Locale nNewLocale )

void removeByKey ( [in] long nKey )

string generateFormat ( [in] long nBaseKey, [in] com::sun::star::lang::Locale nLocale,

[in] boolean bThousands, [in] boolean bRed, [in] short nDecimals, [in] short nLeading )

The important methods are probably queryKey() and addNew(). The method queryKey() finds the key for a given format string and locale, whereas addNew() creates a new format in the container and returns its key for immediate use. The bScan is reserved for future use and should be set to false.

The properties of a single number format are obtained by a call to getByKey() which returns a com.sun.star.util.NumberFormatProperties service for the given index key.

###### XNumberFormatTypes

The interface com.sun.star.util.XNumberFormatTypes offers functions to retreive the index keys of specific predefined number format types. The predefined types are addressed by constants from com.sun.star.util.NumberFormat.The NumberFormat contains values for predefined format types, such as PERCENT, TIME, CURRENCY, and TEXT.

long getStandardIndex ( [in] com::sun::star::lang::Locale nLocale )

long getStandardFormat ( [in] short nType,

[in] com::sun::star::lang::Locale nLocale )

long getFormatIndex ( [in] short nIndex,

[in] com::sun::star::lang::Locale nLocale )

boolean isTypeCompatible ( [in] short nOldType, [in] short nNewType )

long getFormatForLocale ( [in] long nKey,

[in] com::sun::star::lang::Locale nLocale )

In most cases you will need getStandardFormat(). It expects a type constant from the NumberFormat group and the locale t to use, and returns the key of the corresponding predefined format.

#### Applying Number Formats

To format numeric values, an XNumberFormatsSupplier is attached to an instance of a com.sun.star.util.NumberFormatter, available at the global service manager. For this purpose, its main interface com.sun.star.util.XNumberFormatter has a method attachNumberFormatsSupplier(). When the XNumberFormatsSupplier is attached, strings and numeric values are formatted using the methods of the NumberFormatter. To specify the format to apply, you have to get the unique index key for one of the formats defined in NumberFormats. These keys are available at the XNumberFormats and XNumberFormatTypes interface of NumberFormats.

Numbers in documents, such as in table cells, formulas, and text fields, are formatted by applying the format key to the NumberFormat property of the appropriate element.

##### NumberFormatter Service

The service com.sun.star.util.NumberFormatter implements the interfaces com.sun.star.util.XNumberFormatter and com.sun.star.util.XNumberFormatPreviewer.

###### XNumberformatter

The interface com.sun.star.util.XNumberFormatter converts numbers to strings, or strings to numbers, or detects a number format matching a given string.

void attachNumberFormatsSupplier ( [in] com::sun::star::util::XNumberFormatsSupplier xSupplier )

com::sun::star::util::XNumberFormatsSupplier getNumberFormatsSupplier ()

long detectNumberFormat ( [in] long nKey, [in] string aString )

double convertStringToNumber ( [in] long nKey, [in] string aString )

string convertNumberToString ( [in] long nKey, [in] double fValue );

com::sun::star::util::color queryColorForNumber ( [in] long nKey, [in] double fValue,

[in] com::sun::star::util::color aDefaultColor )

string formatString ( [in] long nKey, [in] string aString );

com::sun::star::util::color queryColorForString ( [in] long nKey, [in] string aString,

[in] com::sun::star::util::color aDefaultColor )

string getInputString ( [in] long nKey, [in] double fValue )

###### XNumberformatPreviewer

string convertNumberToPreviewString ( [in] string aFormat, [in] double fValue,

[in] com::sun::star::lang::Locale nLocale,

[in] boolean bAllowEnglish )

com::sun::star::util::color queryPreviewColorForNumber ( [in] string aFormat, [in] double fValue,

[in] com::sun::star::lang::Locale nLocale,

[in] boolean bAllowEnglish,

[in] com::sun::star::util::color aDefaultColor )

This interface com.sun.star.util.XNumberFormatPreviewerconverts values to strings according to a given format code without inserting the format code into the underlying com.sun.star.util.NumberFormats collection.

The example below demonstrates the usage of these interfaces. (OfficeDev/Number_Formats.java)

public void doSampleFunction() throws RuntimeException, Exception

{

// Assume:

// com.sun.star.sheet.XSpreadsheetDocument maSpreadsheetDoc;

// com.sun.star.sheet.XSpreadsheet maSheet;

// Query the number formats supplier of the spreadsheet document

com.sun.star.util.XNumberFormatsSupplier xNumberFormatsSupplier =

(com.sun.star.util.XNumberFormatsSupplier)

UnoRuntime.queryInterface(

com.sun.star.util.XNumberFormatsSupplier.class, maSpreadsheetDoc );

// Get the number formats from the supplier

com.sun.star.util.XNumberFormats xNumberFormats =

xNumberFormatsSupplier.getNumberFormats();

// Query the XNumberFormatTypes interface

com.sun.star.util.XNumberFormatTypes xNumberFormatTypes =

(com.sun.star.util.XNumberFormatTypes)

UnoRuntime.queryInterface(

com.sun.star.util.XNumberFormatTypes.class, xNumberFormats );

// Get the number format index key of the default currency format,

// note the empty locale for default locale

com.sun.star.lang.Locale aLocale = new com.sun.star.lang.Locale();

int nCurrencyKey = xNumberFormatTypes.getStandardFormat(

com.sun.star.util.NumberFormat.CURRENCY, aLocale );

// Get cell range B3:B11

com.sun.star.table.XCellRange xCellRange =

maSheet.getCellRangeByPosition( 1, 2, 1, 10 );

// Query the property set of the cell range

com.sun.star.beans.XPropertySet xCellProp =

(com.sun.star.beans.XPropertySet)

UnoRuntime.queryInterface(

com.sun.star.beans.XPropertySet.class, xCellRange );

// Set number format to default currency

xCellProp.setPropertyValue( "NumberFormat", new Integer(nCurrencyKey) );

// Get cell B3

com.sun.star.table.XCell xCell = maSheet.getCellByPosition( 1, 2 );

// Query the property set of the cell

xCellProp = (com.sun.star.beans.XPropertySet)

UnoRuntime.queryInterface(

com.sun.star.beans.XPropertySet.class, xCell );

// Get the number format index key of the cell's properties

int nIndexKey = ((Integer) xCellProp.getPropertyValue( "NumberFormat" )).intValue();

// Get the properties of the number format

com.sun.star.beans.XPropertySet xProp = xNumberFormats.getByKey( nIndexKey );

// Get the format code string of the number format's properties

String aFormatCode = (String) xProp.getPropertyValue( "FormatString" );

System.out.println( "FormatString: `" + aFormatCode + "'" );

// Create an arbitrary format code

aFormatCode = "\"wonderful \"" + aFormatCode;

// Test if it is already present

nIndexKey = xNumberFormats.queryKey( aFormatCode, aLocale, false );

// If not, add to number formats collection

if ( nIndexKey == -1 )

{

try

{

nIndexKey = xNumberFormats.addNew( aFormatCode, aLocale );

}

catch( com.sun.star.util.MalformedNumberFormatException ex )

{

System.out.println( "Bad number format code: " + ex );

nIndexKey = -1;

}

}

// Set the new format at the cell

if ( nIndexKey != -1 )

xCellProp.setPropertyValue( "NumberFormat", new Integer(nIndexKey) );

}

### 6.2.6 Document Events

Recurring actions, such as loading, printing or saving, that occur when working with documents, are document events, and all documents in OpenOffice.org offer an interface that sends notifications when these events take place.

There are general events common every document, such as loading, printing, or saving, and there are other events that are specific to a particular document type. Both can be accessed through the same interface.

In the document events API, these events are represented by an event name. The following table shows a list of all general document event names:

General Document Event Names
OnNew | New Document was created
OnLoad | Document has been loaded
OnSaveAs | Document is going to be saved under a new name
OnSaveAsDone | Document was saved under a new name
OnSave | Document is going to be saved
OnSaveDone | Document was saved
OnPrepareUnload | Document is going to be removed, but still fully available
OnUnload | Document has been removed, document ist still valid, but closing can no longer be prevented
OnFocus | Document was activated
OnUnfocus | Document was deactivated
OnPrint | Document will be printed
OnModifyChange | Modified state of the document has changed

These event names are documented in the com.sun.star.document.Events service. Note that this service description exceeds the scope of events that happen on the document as a whole—so it also contains events that can only be accessed by finding the part of the document where the event occurred, for example, a button in a form. This list of events can also be extended by new events, so that future versions of OpenOffice.org can support new types of events through the same API. Therefore, every client that wants to deal with a particular document event must check if this event is supported, or whether it should be prepared to catch an exception.

Every client that is interested in document events can register for being notified. The necessary interface for notification is com.sun.star.document.XEventBroadcaster, which is an optional interface of the servicecom.sun.star.document.OfficeDocument. All document objects in OpenOffice.org implement this interface. It has two methods to add and remove listeners for document events:

[oneway] void addEventListener( [in] ::com::sun::star::document::XEventListener Listener );

[oneway] void remove EventListener( [in] ::com::sun::star::document::XEventListener Listener );

The listeners must implement the interface com.sun.star.document.XEventListener and get a notification through a call of their method:

[oneway] void notifyEvent( [in] ::com::sun::star::document::EventObject Event );

The argument of this call is a com.sun.star.document.EventObject struct, which is derived from the usual com.sun.star.lang.EventObject and contains two members: the member Source, which contains an interface pointer to the event source (here the com.sun.star.document.OfficeDocument service) and the member EventName which can be one of the names shown in the preceding table.

Both methods in the interface com.sun.star.document.XEventBroadcaster can cause problems in scripting languages if the object that implements this interface also implements com.sun.star.lang.XComponent, because it has two very similar methods:

[oneway] void addEventListener( [in] ::com::sun::star::lang::XEventListener Listener );

[oneway] void removeEventListener( [in] ::com::sun::star::lang::XEventListener Listener );

Unfortunately this applies to all OpenOffice.org documents.

In C++ and Java this is no problem, because the complete signature of a method, including the arguments, is used to identify it.

In OpenOffice.org Basic, the fully qualified name including the interface can be used from version 1.1.0:

Sub RegisterListener

oListener = CreateUnoListener( "DocumentListener_","com.sun.star.document.XEventListener" )

ThisComponent.com_sun_star_document_XEventBroadcaster_addEventListener( oListener )

End Sub

Sub DocumentListener_notifyEvent( o as object )

IF o.EventName = "OnPrepareUnload" THEN

print o.Source.URL

ENDIF

end sub

Sub DocumentListener_disposing()

End Sub

But the OLE automation bridge, and possibly other scripting language bindings, are unable to distinguish between both addEventListener() and removeEventListener() methods based on the method signature and must be told which interface you want to use.

You must use the core reflection to get access to either method. The following code shows an example in VBScript, which registers a document event listener at the current document.

set xContext = objServiceManager.getPropertyValue( "DefaultContext" )

set xCoreReflection = xContext.getValueByName( "/singletons/com.sun.star.reflection.theCoreReflection" )

set xClass = xCoreReflection.forName( "com.sun.star.document.XEventBroadcaster" )

set xMethod = xClass.getMethod( "addEventListener" )

dim invokeargs(0)

invokeargs(0) = myListener

set value = objServiceManager.Bridge_GetValueObject()

call value.InitInOutParam("[]any", invokeargs)

call xMethod.invoke( objDocument, value )

The C++ code below uses OLE Automation. Two helper functions are provided that help to execute UNO operations.

// helper function to execute UNO operations via IDispatch

HRESULT ExecuteFunc( IDispatch* idispUnoObject,
OLECHAR* sFuncName,
CComVariant* params,
unsigned int count,
CComVariant* pResult )
{
if( !idispUnoObject )
return E_FAIL;
DISPID id;
HRESULT hr = idispUnoObject->GetIDsOfNames( IID_NULL, &sFuncName, 1, LOCALE_USER_DEFAULT, &id);
if( !SUCCEEDED( hr ) ) return hr;
DISPPARAMS dispparams= { params, 0, count, 0};
// DEBUG
EXCEPINFO myInfo;
return idispUnoObject->Invoke( id, IID_NULL,LOCALE_USER_DEFAULT, DISPATCH_METHOD,
&dispparams, pResult, &myInfo, 0);
}

// helper function to execute UNO methods that return interfaces
HRESULT GetIDispByFunc( IDispatch* idispUnoObject,
OLECHAR* sFuncName,
CComVariant* params,
unsigned int count,
CComPtr<IDispatch>& pdispResult )
{
if( !idispUnoObject )
return E_FAIL;
CComVariant result;
HRESULT hr = ExecuteFunc( idispUnoObject, sFuncName, params, count, &result );
if( !SUCCEEDED( hr ) ) return hr;
if( result.vt != VT_DISPATCH || result.pdispVal == NULL )
return E_FAIL;
pdispResult = CComPtr<IDispatch>( result.pdispVal );
return S_OK;
}

// it's assumed that pServiceManager (by creating it as a COM object), pDocument (f.e. by loading it) // and pListener (the listener we want to add) are passed as parameters

HRESULT AddDocumentEventListener(

CComPtr<IDispatch> pServiceManager, CComPtr<IDispatch> pDocument, CComPtr<IDispatch> pListener)

{

CComPtr<IDispatch> pdispContext;
hr = GetIDispByFunc( pServiceManager, L"getPropertyValue", &CComVariant( L"DefaultContext" ), 1, pdispContext );
if( !SUCCEEDED( hr ) ) return hr;
CComPtr<IDispatch> pdispCoreReflection;
hr = GetIDispByFunc( pdispContext,
L"getValueByName",
&CcomVariant( L"/singletons/com.sun.star.reflection.theCoreReflection" ),
1,
pdispCoreReflection );
if( !SUCCEEDED( hr ) ) return hr;
CComPtr<IDispatch> pdispClass;
hr = GetIDispByFunc( pdispCoreReflection,
L"forName",
&CComVariant( L"com.sun.star.document.XEventBroadcaster" ),
1,
pdispClass );
if( !SUCCEEDED( hr ) ) return hr;
CComPtr<IDispatch> pdispMethod;
hr = GetIDispByFunc( pdispClass, L"getMethod", &CComVariant( L"addEventListener" ), 1, pdispMethod );
if( !SUCCEEDED( hr ) ) return hr;
CComPtr<IDispatch> pdispListener;
CComPtr<IDispatch> pdispValueObj;

hr = GetIDispByFunc( mpDispFactory, L"Bridge_GetValueObject", NULL, 0, pdispValueObj );

if( !SUCCEEDED( hr ) ) return hr;

CComVariant pValParams[2];

pValParams[1] = CComVariant( L"com.sun.star.document.XEventListener" );

pValParams[0] = CComVariant( pdispListener );

CComVariant dummyResult;

hr = ExecuteFunc( pdispValueObj, L"Set", pValParams, 2, &dummyResult );

if( !SUCCEEDED( hr ) ) return hr;

SAFEARRAY FAR* pPropVal = SafeArrayCreateVector( VT_VARIANT, 0, 1 );

long ix1 = 0;

CComVariant aArgs( pdispValueObj );

SafeArrayPutElement( pPropVal, &ix, &aArgs );

CComVariant aDoc( pdispDocument );

CComVariant pParams[2];

pParams[1] = aDoc;

pParams[0].vt = VT_ARRAY | VT_VARIANT; pParams[0].parray = pPropVal;

CComVariant result;

//invoking the method addeventlistner

hr = ExecuteFunc( pdispMethod, L"invoke", pParams, 2, &result );

if( !SUCCEEDED( hr ) ) return hr;

return S_OK;

}

Another way to react to document events is to bind a macro to it—a process called event binding. From OpenOffice.org 1.1.0 you can also use scripts in other languages, provided that a corresponding scripting framework implementation is present.

All document objects in OpenOffice.org support event binding through an interface com.sun.star.document.XEventsSupplier. This interface has only one method:

::com::sun::star::container::XNameReplace getEvents();

This method gives access to a container of event bindings. The container is represented by a com.sun.star.container.XNameReplace interface that, together with the methods of its base interfaces, offers the following methods:

void replaceByName( [in] string aName, [in] any aElement );

any getByName( [in] string aName );

sequence< string > getElementNames();

boolean hasByName( [in] string aName );

type getElementType();

boolean hasElements();

Each container element represents an event binding. By default, all bindings are empty. The element names are the event names shown in the preceding table. In addition, there are document type-specific events. The method getElementNames() yields all possible events that are supported by the object and hasByName() checks for the existence of a particular event.

For every supported event name you can use getByName() to query for the current event binding or replaceByName() to set a new one. Both methods may throw a com.sun.star.container.NoSuchElementException exception if an unsupported event name is used.

The type of an event binding, which is wrapped in the any returned by getByName(), is a sequence of com.sun.star.beans.PropertyValue that describes the event binding.

PropertyValue structs in the event binding description
EventType | string. Can assume the values "StarBasic" or "Script" . The event type "Script" describes the location as URL. The event type "StarBasic" is provided for compatibility reasons and describes the location of the macro through the properties Library and MacroName, in addition to URL.
Script | string. Available for the event types Script and StarBasic. Describes the location of the macro/script routine which is bound. For the URL property, a command URL is expected (see 6.1.6 Office Development - OpenOffice.org Application Environment - Using the Dispatch Framework). OpenOffice.org will execute this command when the event occurs. For the event type StarBasic, the URL uses the macro: protocol. For the event type Script, other protocols are possible, especially the script: protocol. The macro protocol has two forms: macro:///<Library>.<Module>.<Method(args)> macro://./<Library>.<Module>.<Method(args)> The first form points to a method in the global basic storage, while the second one points to a method embedded in the current document. <Library>.<Module>.<Method(args)> represent the names of the library, the module and the method. Currently, for args only string arguments (separated by comma) are possible. If no args exist, empty brackets must be used, because the brackets are part of the scheme. An example URL could look like: macro:///MyLib.MyModule.MyMethod(foo,bar) The exact form of the script: command URL protocol depends on the installed scripting module. They will be available later as additional components for OpenOffice.org 1.1.0.
Library | string. Deprecated. Available for EventType "StarBasic". Can assume the values "application" or empty string for the global basic storage, and "document" for the document where the code is embedded.
MacroName | string. Deprecated. Available for EventType "StarBasic". Describes the macro location as <Library>.<MyModule>.<MyMethod>.

| In OpenOffice.org 1.1.0 all properties (URL, Library, MacroName) will be returned for event bindings of type StarBasic, regardless if the binding was created with a URL property only or with the Library and MacroName property. The internal implementation does the necessary conversion. Older versions of OpenOffice.org always returned only Library and MacroName, even if the binding was created with the URL property.
--- | ---

In OpenOffice.org 1.1.0 there is another important extension in the area of document events and event bindings. This version has a new service com.sun.star.document.GlobalEventBroadcaster that offers the same document-event-related functionality as described previously (interfaces com.sun.star.document.XEventBroadcaster, com.sun.star.document.XEventsSupplier), but it allows you to register for events that happen in any document and also allows you to set bindings for all documents that are stored in the global UI configuration of OpenOffice.org. Using this services frees you from registering at every single document that has been created or loaded.

Though a potential listener registers for event notifications at this global service and not at any document itself, the received event source in the event notification is the document, not the GlobalEventBroadcaster. The reason for this is that usually a listener contains code that works on the document, so it needs a reference to it.

The service com.sun.star.document.GlobalEventBroadcaster also supports two more events that do not occur in any document but are useful for working with document events:

Global Event Names
OnStartApp | Application has been started
OnCloseApp | Application is going to be closed. This event is fired after all documents have been closed and nobody objected to the shutdown.

The event source in the notifications is NULL (empty).

All event bindings can be seen or set in the OpenOffice.org UI in the Tools-Configure dialog on Events page. Two radio buttons on the right side of the dialog toggle between OpenOffice.org and Document binding. In OpenOffice.org 1.1.0, you can still only bind to OpenOffice.org Basic macros in the dialog. Bindings to script: URLs can only be set using the API, but the dialog is at least able to display them. If, in OpenOffice.org 1.1.0, a global and a document binding are set for the same event, first the global and then the document binding is executed. With older versions, only the document binding was executed, and the global binding was only executed if no document binding was set.

### 6.2.7 Path Organization

The path settings service is the central service that manages the paths of OpenOffice.org. Almost every component inside OpenOffice.org uses one or more of the paths to access its resources located on the file system.

Users can customize most of the paths in OpenOffice.org by choosing Tools – Options – OpenOffice.org – Paths.

#### Path Settings

The com.sun.star.util.PathSettings service supports a number of properties which store the OpenOffice.org predefined paths. There are two different groups of properties. One group stores only a single path and the other group stores two or more paths - separated by a semicolon.

Properties of com.sun.star.util.PathSettings
Addin | Single path | Specifies the directory that contains spreadsheet add-ins which use the old add-in API.
AutoCorrect | Multi path | Specifies the directories that contain the settings for the AutoCorrect dialog.
AutoText | Multi path | Specifies the directories that contain the AutoText modules.
Backup | Single path | Specifies the directory for storing automatic backup copies of documents.
Basic | Multi path | Specifies the location of the Basic files that are used by the AutoPilots.
Bitmap | Single path | Specifies the directory that contains the external icons for the toolbars.
Config | Single path | Specifies the location of the configuration files. This property is not visible in the OpenOffice.org path options dialog and cannot be changed by users.
Dictionary | Single path | Specifies the location of the OpenOffice.org dictionaries.
Favorite | Single path | Specifies the directory that contains the saved folder bookmarks.
Filter | Single path | Specifies the directory where the filters are stored.
Gallery | Multi path | Specifies the directories that contain the Gallery database and multimedia files.
Graphic | Single path | Specifies the directory that is displayed when the dialog for opening a graphic or for saving a new graphic is called.
Help | Single path | Specifies the location of the Office help files.
Linguistic | Single path | Specifies the directory where the spellcheck files are stored.
Module | Single path | Specifies the directory where the modules are stored.
Palette | Single path | Specifies the location of the palette files that contain user-defined colors and patterns (*.SOB and *.SOF).
Plugin | Multi path | Specifies the directories where the Plugins are stored.
Storage | Single path | Specifies the directory where mail and news files as well as other information (for example, about FTP Server) are stored. This property is not visible in the OpenOffice.org path options dialog and cannot be changed by users.
Temp | Single path | Specifies the directory for the office temp-files.
Template | Multi path | Specifies the directory for the OpenOffice.org document templates.
UIConfig | Multi path | Specifies the location of global directories when looking for user interface configuration files. The user interface configuration is merged with the user settings that are stored in the directory specified by UserConfig.
UserConfig | Single path | Specifies the directory that contains the user settings, including the user interface configuration files for menus, toolbars, accelerators and status bars.
UserDictionary | Single path | Specifies the directory for the custom dictionaries.
Work | Single path | Specifies the location of the work folder. This path can be modified according to the user's needs and can be seen in the Open or Save dialog.

##### Configuration

The path settings service uses the group Path in the org.Openoffice.Office.Common branch to read and store paths. The Current and Default groups in the share layer of the configuration branch store the path settings properties. The Current group initialize the properties of the path settings service during startup. If the user activates the Default button in the path options dialog, the Default group values are copied to the current ones.

| Note: The configuration branch separates the paths of a property with a colon (:), whereas the path settings service separates multiple paths with a semicolon (;).
--- | ---

<?xml version='1.0' encoding='UTF-8'?>

<oor:component-schema oor:name="Common" oor:package="org.openoffice.Office" xml:lang="en-US" xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://wwww.w3.org/2001/XMLSchema-instance”>

<component>

<group oor:name="Path">

<group oor:name="Current">

<prop oor:name="OfficeInstall" oor:type="xs:string">

<value/>

</prop>

<prop oor:name="OfficeInstallURL" oor:type="xs:string">

<value/>

</prop>

<prop oor:name="Addin" oor:type="xs:string">

<value>$(progpath)/addin</value>

</prop>

<prop oor:name="AutoCorrect" oor:type="oor:string-list">

<value oor:separator=":">$(insturl)/share/autocorr:$(userurl)/autocorr</value>

</prop>

<prop oor:name="AutoText" oor:type="oor:string-list">

<value oor:separator=":">

$(insturl)/share/autotext/$(vlang):$(userurl)/autotext

</value>

</prop>

<prop oor:name="Backup" oor:type="xs:string">

<value>$(userurl)/backup</value>

</prop>

<prop oor:name="Basic" oor:type="oor:string-list">

<value oor:separator=":">$(insturl)/share/basic:$(userurl)/basic</value>

</prop>

<prop oor:name="Bitmap" oor:type="xs:string">

<value>$(insturl)/share/config/symbol</value>

</prop>

<prop oor:name="Config" oor:type="xs:string">

<value>$(insturl)/share/config</value>

</prop>

<prop oor:name="Dictionary" oor:type="xs:string">

<value>$(insturl)/share/wordbook/$(vlang)</value>

</prop>

<prop oor:name="Favorite" oor:type="xs:string">

<value>$(userurl)/config/folders</value>

</prop>

<prop oor:name="Filter" oor:type="xs:string">

<value>$(progpath)/filter</value>

</prop>

<prop oor:name="Gallery" oor:type="oor:string-list">

<value oor:separator=":">$(insturl)/share/gallery:$(userurl)/gallery</value>

</prop>

<prop oor:name="Graphic" oor:type="xs:string">

<value>$(insturl)/share/gallery</value>

</prop>

<prop oor:name="Help" oor:type="xs:string">

<value>$(instpath)/help</value>

</prop>

<prop oor:name="Linguistic" oor:type="xs:string">

<value>$(insturl)/share/dict</value>

</prop>

<prop oor:name="Module" oor:type="xs:string">

<value>$(progpath)</value>

</prop>

<prop oor:name="Palette" oor:type="xs:string">

<value>$(userurl)/config</value>

</prop>

<prop oor:name="Plugin" oor:type="oor:string-list">

<value oor:separator=":">$(userpath)/plugin</value>

</prop>

<prop oor:name="Storage" oor:type="xs:string">

<value>$(userpath)/store</value>

</prop>

<prop oor:name="Temp" oor:type="xs:string">

<value>$(temp)</value>

</prop>

<prop oor:name="Template" oor:type="oor:string-list">

<value oor:separator=":">

$(insturl)/share/template/$(vlang):$(userurl)/template

</value>

</prop>

<prop oor:name="UIConfig" oor:type="oor:string-list">

<value oor:separator=":"/>

</prop>

<prop oor:name="UserConfig" oor:type="xs:string">

<value>$(userurl)/config</value>

</prop>

<prop oor:name="UserDictionary" oor:type="xs:string">

<value>$(userurl)/wordbook</value>

</prop>

<prop oor:name="Work" oor:type="xs:string">

<value>$(work)</value>

</prop>

</group>

<group oor:name="Default">

<prop oor:name="Addin" oor:type="xs:string">

<value>$(progpath)/addin</value>

</prop>

<prop oor:name="AutoCorrect" oor:type="oor:string-list">

<value oor:separator=":">

$(insturl)/share/autocorr:$(userurl)/autocorr

</value>

</prop>

<prop oor:name="AutoText" oor:type="oor:string-list">

<value oor:separator=":">

$(insturl)/share/autotext/$(vlang):$(userurl)/autotext

</value>

</prop>

<prop oor:name="Backup" oor:type="xs:string">

<value>$(userurl)/backup</value>

</prop>

<prop oor:name="Basic" oor:type="oor:string-list">

<value oor:separator=":">$(insturl)/share/basic:$(userurl)/basic</value>

</prop>

<prop oor:name="Bitmap" oor:type="xs:string">

<value>$(insturl)/share/config/symbol</value>

</prop>

<prop oor:name="Config" oor:type="xs:string">

<value>$(insturl)/share/config</value>

</prop>

<prop oor:name="Dictionary" oor:type="xs:string">

<value>$(insturl)/share/wordbook/$(vlang)</value>

</prop>

<prop oor:name="Favorite" oor:type="xs:string">

<value>$(userurl)/config/folders</value>

</prop>

<prop oor:name="Filter" oor:type="xs:string">

<value>$(progpath)/filter</value>

</prop>

<prop oor:name="Gallery" oor:type="oor:string-list">

<value oor:separator=":">$(insturl)/share/gallery:$(userurl)/gallery</value>

</prop>

<prop oor:name="Graphic" oor:type="xs:string">

<value>$(insturl)/share/gallery</value>

</prop>

<prop oor:name="Help" oor:type="xs:string">

<value>$(instpath)/help</value>

</prop>

<prop oor:name="Linguistic" oor:type="xs:string">

<value>$(insturl)/share/dict</value>

</prop>

<prop oor:name="Module" oor:type="xs:string">

<value>$(progpath)</value>

</prop>

<prop oor:name="Palette" oor:type="xs:string">

<value>$(userurl)/config</value>

</prop>

<prop oor:name="Plugin" oor:type="oor:string-list">

<value oor:separator=":">$(userpath)/plugin</value>

</prop>

<prop oor:name="Temp" oor:type="xs:string">

<value>$(temp)</value>

</prop>

<prop oor:name="Template" oor:type="oor:string-list">

<value oor:separator=":">

$(insturl)/share/template/$(vlang):$(userurl)/template

</value>

</prop>

<prop oor:name="UIConfig" oor:type="oor:string-list">

<value oor:separator=":"/>

</prop>

<prop oor:name="UserConfig" oor:type="xs:string">

<value>$(userurl)/config</value>

</prop>

<prop oor:name="UserDictionary" oor:type="xs:string">

<value>$(userurl)/wordbook</value>

</prop>

<prop oor:name="Work" oor:type="xs:string">

<value>$(work)</value>

</prop>

</group>

</group>

</component>

</oor:component-schema>

##### Accessing Path Settings

The path settings service is a one-instance service that supports the com.sun.star.beans.XPropertySet, com.sun.star.beans.XFastPropertySet and com.sun.star.beans.XMultiPropertySet interfaces for access to the properties.

The service can be created using the service manager of OpenOffice.org and the service name com.sun.star.util.PathSettings. The following example creates the path settings service.

import com.sun.star.lang.XMultiServiceFactory;

import com.sun.star.uno.Exception;

import com.sun.star.uno.XInterface;

import com.sun.star.beans.XPropertySet

XPropertySet createPathSettings() {

// Obtain Process Service Manager.

XMultiServiceFactory xServiceFactory = ...

// Create Path settings service. Needs to be done only once per process.

XInterface xPathSettings;

try {

xPathSettings = xServiceFactory.createInstance("com.sun.star.util.PathSettings" );

}

catch (com.sun.star.uno.Exception e) {

}

if (xPathSettings != null)

return (XpropertySet) UnoRuntime.queryInterface(XPropertySet.class, xPathSettings);

else

return null;

}

The main interface of the path settings service is com.sun.star.beans.XPropertySet. You can retrieve and write properties with this interface. It also supports getting information about the properties themselves.

- com::sun::star::beans::XPropertySetInfo getPropertySetInfo();
  The path settings service returns an XPropertySetInfo interface where more information about the path properties can be retrieved. The information includes the name of the property, a handle for faster access with XFastPropertySet, the type of the property value and attribute values.

- void setPropertyValue( [in] string aPropertyName, [in] any aValue );
  This function can set the path properties to a new value. The path settings service expects that a value of type string is provided. The string must be a correctly encoded file URL. If the path property supports multiple paths, each path must be separated by a semicolon (;). Path variables are also allowed, so long as they can be resolved to a valid file URL.

- any getPropertyValue( [in] string PropertyName );
  This function retrieves the value of a path property. The property name must be provided and the path is returned. The path settings service always returns the path as a file URL. If the property value includes multiple paths, each path is separated by a semicolon (;).

| Note: The path settings service always provides property values as file URLs. Properties which are marked as multi path (see table above) use a semicolon (;) as a separator for the different paths. The service also expects that a new value for a path property is provided as a file URL or has a preceding path variable, otherwise a com.sun.star.lang.IllegalArgumentException is thrown.
--- | ---

shows how the path settings, path substitution, and configuration service work together to read or write path properties.

Illustration 6.20: Interaction of path settings, path substitution and configuration

| Important: Keep in mind that the paths managed by the path settings service are vital for almost all of the functions in OpenOffice.org. Entering a wrong path can result in minor malfunctions or break the complete OpenOffice.org installation. Although the path settings service performs a validity check on the provided URL, this cannot prevent all problems.
--- | ---

The following code example uses the path settings service to retrieve and set the path properties.

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;

import com.sun.star.beans.PropertyValue;

import com.sun.star.beans.UnknownPropertyException;

/* Provides example code how to access and use the

* path pathsettings servce.

*/

public class PathSettingsTest extends java.lang.Object {

/*

* List of predefined path variables supported by

* the path settings service.

*/

private static String[] predefinedPathProperties = {

"Addin",

"AutoCorrect",

"AutoText",

"Backup",

"Basic",

"Bitmap",

"Config",

"Dictionary",

"Favorite",

"Filter",

"Gallery",

"Graphic",

"Help",

"Linguistic",

"Module",

"Palette",

"Plugin",

"Storage",

"Temp",

"Template",

"UIConfig",

"UserConfig",

"UserDictionary",

"Work"

};

/*

* @param args the command line arguments

*/

public static void main(String[] args) {

XComponentContext xRemoteContext = null;

XMultiComponentFactory xRemoteServiceManager = null;

XPropertySet xPathSettingsService = null;

try {

// connect

XComponentContext xLocalContext =

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

XMultiComponentFactory xLocalServiceManager = xLocalContext.getServiceManager();

Object urlResolver = xLocalServiceManager.createInstanceWithContext(

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

XUnoUrlResolver xUnoUrlResolver = (XUnoUrlResolver) UnoRuntime.queryInterface(

XUnoUrlResolver.class, urlResolver );

Object initialObject = xUnoUrlResolver.resolve(

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

XPropertySet xPropertySet = (XPropertySet)UnoRuntime.queryInterface(

XPropertySet.class, initialObject);

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

xRemoteContext = (XComponentContext)UnoRuntime.queryInterface(

XComponentContext.class, context);

xRemoteServiceManager = xRemoteContext.getServiceManager();

Object pathSubst = xRemoteServiceManager.createInstanceWithContext(

"com.sun.star.comp.framework.PathSettings", xRemoteContext );

xPathSettingsService = (XPropertySet)UnoRuntime.queryInterface(

XPropertySet.class, pathSubst);

/* Work with path settings */

workWithPathSettings( xPathSettingsService );

}

catch (java.lang.Exception e){

e.printStackTrace();

}

finally {

System.exit(0);

}

}

/*

* Retrieve and set path properties from path settings service

* @param xPathSettingsService the path settings service

*/

public static void workWithPathSettings(XPropertySet xPathSettingsService) {

if (xPathSettingsService != null) {

for (int i=0; i<predefinedPathProperties.length; i++) {

try {

/* Retrieve values for path properties from path settings service*/

Object aValue = xPathSettingsService.getPropertyValue(predefinedPathProperties[i]);

// getPropertyValue returns an Object, you have to cast it to type that you need

String aPath = (String)aValue;

System.out.println("Property="+ predefinedPathProperties[i] + " Path=" + aPath);

}

catch (com.sun.star.beans.UnknownPropertyException e) {

System.out.println("UnknownPropertyException has been thrown accessing "+predefinedPathProperties[i]);

}

catch (com.sun.star.lang.WrappedTargetException e) {

System.out.println("WrappedTargetException has been thrown accessing "+predefinedPathProperties[i]);

}

}

// Try to modfiy the work path property. After running this example

// you should see the new value of "My Documents" in the path options

// tab page, accessible via "Tools - Options – OpenOffice.org - Paths".

// If you want to revert the changes, you can also do it with the path tab page.

try {

xPathSettingsService.setPropertyValue("Work", "$(temp)");

String aValue = (String)xPathSettingsService.getPropertyValue("Work");

System.out.println("The work path should now be " + aValue);

}

catch (com.sun.star.beans.UnknownPropertyException e) {

System.out.println("UnknownPropertyException has been thrown accessing PathSettings service");

}

catch (com.sun.star.lang.WrappedTargetException e) {

System.out.println("WrappedTargetException has been thrown accessing PathSettings service");

}

catch (com.sun.star.beans.PropertyVetoException e) {

System.out.println("PropertyVetoException has been thrown accessing PathSettings service");

}

catch (com.sun.star.lang.IllegalArgumentException e) {

System.out.println("IllegalArgumentException has been thrown accessing PathSettings service");

}

}

}

}

#### Path Variables

Path variables are used as placeholders for system-dependent paths or parts of paths which are only known during the runtime of OpenOffice.org. The path substitution service com.sun.star.util.PathSubstitution - which manages all path variables of OpenOffice.org - checks the runtime environment during startup and sets the values of the path variables. The path substitution service supports a number of predefined path variables. They provide information about important paths that OpenOffice.org currently uses. They are implemented as read-only values and cannot be changed.

OpenOffice.org is a multi-platform solution that runs on different file systems. Obviously users want to have a single user configuration on all workstations across all platforms in a networked installation. For example, a user wants to use both the Windows and Unix version of OpenOffice.org. The home directory and the working directory are located on a central file server that uses Samba to provide access for Windows systems. The user only wants to have one user installation for both systems, so that individual settings only need to be specified once.

The path settings service described in 6.2.11 Office Development - Common Application Features - Path Organization - Path Settings utilizes the path substitution service. In the configuration of OpenOffice.org, path variables describe the path settings, and these variables can be substituted by platform-specific paths during startup. That way, path substitution gives users the power to apply path settings only once, while the system takes care of the necessary platform-dependent and environment adaptations.

shows how a path variable can resolve the path problem that arises when you use the same user directory on different platforms.

Illustration 6.21: Path variables solve problems in heterogeneous environments

The following sections describe predefined variables, how to define your own variables, and how to resolve path variables with respect to paths in your programs.

##### Predefined Variables

The path substitution service supports a number of predefined path variables. They provide information about the paths that OpenOffice.org currently uses. They are implemented as read-only values and cannot be modified.

The predefined path variables can be separated into three distinct groups. The first group of variables specifies a single path, the second group specifies a list of paths that are separated by the shell or operating system dependent character, and the third group specifies only a part of a path.

All predefined variable names are case insensitive, as opposed to the user-defined variables that are described below.

Predefined variables supported by service com.sun.star.util.PathSubstitution
$(home) | Single path | The absolute path to the home directory of the current user. Under Windows this depends on the specific versions: usually the <drive>:\Documents and Settings\<username>\Application Data under Windows 2000/XP and <drive>:\Windows\Profiles\<username>\Application Data under Windows NT and Win9x, ME with multi user support. Windows 9x and ME without multi-user support <drive>:\Windows\Application Data.
$(inst) $(instpath) $(insturl) | Single path | The absolute installation path of OpenOffice.org. Normally the share and program folders are located inside the installation folder. The $(instpath) and $(insturl) variables are aliases to $(inst)— they are included for downward compatibility and should not be used.
$(prog) $(progpath) $(progurl) | Single path | The absolute path of the program folder of OpenOffice.org. Normally the executable and the shared libraries are located in this folder. The $(progpath) and $(progurl) variables are aliases to $(prog)— they are supported for downward compatibility and should not be used.
$(temp) | Single path | The absolute path of the current temporary directory used by OpenOffice.org.
$(user) $(userpath) $(userurl) | Single path | The absolute path to the user installation folder of OpenOffice.org. The $(userpath) and $(userurl) variables are aliases to $(user)— they are supported for downward compatibility and should not be used.
$(work) | Single path | The absolute path of the working directory of the user. Under Windows this is the My Documents folder. Under Unix this is the home directory of the user.
$(path) | List of paths | The value of the PATH environment variable of the OpenOffice.org process. The single paths are separated by a ';' character independent of the system.
$(lang) | Part of a path | The country code used by OpenOffice.org, see the table Mapping ISO 639/3166 to $(lang) below for examples.
$(langid) | Part of a path | The language identifier used by OpenOffice.org. An identifier is composed of a primary language identifier and a sublanguage identifier such as 0x0009=English (primary language identifier), 0x0409=English US (composed language code).The language identifier is based on the Microsoft language identifiers, for further information please see: Table of Language Identifiers http://msdn.microsoft.com/library/en-us/intl/nls_238z.asp Primary Language Identifiers http://msdn.microsoft.com/library/en-us/intl/nls_61df.asp SubLanguage Identifiers http://msdn.microsoft.com/library/en-us/intl/nls_19ir.asp
$(vlang) | Part of a path | The language used by OpenOffice.org as an English string, for example, "german" for a German version of OpenOffice.org.

The values of $(lang), $(langid) and $(vlang) are based on the property ooLocale in the configuration branch org.openoffice.Setup/L10N , that is normally located in the share directory. This property follows the ISO 639-1/ISO3166 standards that define identification codes for languages and countries. The ooLocale property is written by the setup application during installation time. The following are examples of table Mapping ISO 639/3166 to $(vlang):

Mapping from ISO639-1/ISO3166 to $(lang) and $(vlang)
ISO 639-1 | ISO 3166 | $(lang) | $(vlang)
ar | * | 96 | arabic
ca | AD | 37 | catalan
ca | ES | 37 | catalan
cs | * | 42 | czech
cz | * | 42 | czech
da | DK | 45 | danish
de | * | 49 | german
el | * | 30 | greek
en | * | 1 | english
en | GB | 1 | english_uk
es | * | 34 | spanish
fi | FI | 35 | finnish
fr | * | 33 | french
he | * | 97 | hebrew
hu | HU | 36 | hungarian
it | * | 39 | italian
ja | JP | 81 | japanese
ko | * | 82 | korean
nb | NO | 47 | norwegian
nl | * | 31 | dutch
nn | NO | 47 | norwegian
no | NO | 47 | norwegian
pl | PL | 48 | polish
pt | BR | 55 | portuguese_brazilian
pt | PT | 3 | portuguese
ru | RU | 7 | russian
sk | SK | 43 | slovak
sv | * | 46 | swedish
th | TH | 66 | thai
tr | TR | 90 | turkish
zh | CN | 86 | chinese_simplified
zh | TW | 88 | chinese_traditional

##### Custom Path Variables

###### Syntax

The path substitution service supports the definition and usage of user-defined path variables. The variable names must use this syntax:

variable ::= “$(“ letter { letter | digit } “)”

letter ::= "A"-"Z"|"a"-"z"

digit ::= "0"-"9"

The user-defined variables must be defined in the configuration branch org.openoffice.Office.Substitution. OpenOffice.org employs a rule-based system to evaluate which definition of a user-defined variable is chosen. The following sections describe the different parts of this rule-based system and the configuration settings that are required for defining new path variables.

###### Environment Values

To bind a specific value to a user-defined path variable, the path substitution service uses environment values. The path substitution service chooses a variable definition based on the values of these environment parameters. The following table describes which parameters can be used:

Environment parameters
Host | This value can be a host name or an IP address , depending on the network configuration (DNS server available). A host name is case insensitive and can also use the asterisk (*) wildcard to represent match zero or more characters.
YPDomain | The yellow pages domain or NIS domain. The value is case insensitive and can use the asterisk (*) wildcard to represent match zero or more characters.
DNSDomain | The domain name service. The value is case insensitive and can use the asterisk (*) wildcard to represent match zero or more characters.
NTDomain | Windows NT domain. The value is case insensitive and can use the asterisk (*) wildcard to represent match zero or more characters.
OS | The operating system parameter supports the following values: WINDOWS (all windows versions including Win9x, WinME, and WinXP) UNIX (includes LINUX and SOLARIS) SOLARIS LINUX

###### Rules

The user can define the mapping of environment parameter values to variable values. Each definition is called a rule and all rules for a particular variable are the rule set. You can only have one environment parameter value for each rule.

The following example rules specify that the user-defined variable called devdoc is bound to the directory s:\develop\documentation if OpenOffice.org is running under Windows. The second rule binds devdoc to /net/develop/documentation if OpenOffice.org is running under Solaris.

Variable name=devdoc
Environment parameter=OS
Value=file:///s:/develop/documentation

Variable name=devdoc
Environment parameter=SOLARIS
Value=file:///net/develop/documentation

###### Analyzing User-Defined Rules

OpenOffice.org uses matching rules to find the active rule inside a provided rule set.

1. Tries to match with the Host environment parameter. If more than one rule matches—this can be possible if you use the asterisk (*) wildcard character - the first matching rule is applied.

2. Tries to match with the different Domain parameters. There is no predefined order for the domain parameters - the first matching rule is applied.

3. Try to match with the OS parameter. The specialized values have a higher priority than generic ones, for example, LINUX has a higher priority than UNIX.

shows the analyzing and matching of user-defined rules.

Illustration 6.22: Process of the rule set analyzing

The analyzing and matching process is done whenever a rule set has changed. Afterwards the values of the user-defined path variables are set and can be retrieved using the interface com.sun.star.util.XStringSubstitution.

###### Configuration

The path substitution service uses the org.openoffice.Office.Substitution configuration branch for the rule set definitions, which adhere to this schema:

<?xml version='1.0' encoding='UTF-8'?>

<oor:component-schema oor:name="Substitution" oor:package="org.openoffice.Office" xml:lang="en-US" xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<templates>

<group oor:name="SharePointMapping">

<prop oor:name="Directory" oor:type="xs:string" oor:nillable="false"/>

<group oor:name="Environment">

<prop oor:name="OS" oor:type="xs:string"/>

<prop oor:name="Host" oor:type="xs:string"/>

<prop oor:name="DNSDomain" oor:type="xs:string"/>

<prop oor:name="YPDomain" oor:type="xs:string"/>

<prop oor:name="NTDomain" oor:type="xs:string"/>

</group>

</group>

<set oor:name="SharePoint" oor:node-type="SharePointMapping"/>

</templates>

<component>

<set oor:name="SharePoints" oor:node-type="SharePoint"/>

</component>

</oor:component-schema>

The SharePoints set is the root container that store the definition of the different user-defined path variables. The SharePoint set uses nodes of type SharePoint which defines a single user-defined path variable.

Properties of the SharePoint set nodes
oor:component-data | String. The name of the user-defined path variable. It must be unique inside the SharePoints set. The name must meet the requirements for path variable names, see 6.2.11 Office Development - Common Application Features - Path Organization - Path Variables. The preceding characters “$(“ and the succeeding “)” must be omitted, for example, the node string for the path variable $(devdoc) must be devdoc.

A SharePoint set is a container for the different rules, called SharePointMapping in the configuration.

Properties of the SharePointMapping group
oor:component-data | String - must be unique inside the SharePoint set, but with no additional meaning for user-defined path variables. Use a consecutive numbering scheme - even numbers are permitted.
Directory | String - must be set and contain a valid and encoded file URL that represents the value of the user-defined path variable for the rule.
Environment | Group - contains a set of properties that define the environment parameter that this rule must match. You can only use one environment in a rule.
| OS | The operating system. The following values are supported: WINDOWS = Matches all Windows OS from Win 98 and higher. LINUX = Matches all supported Linux systems. SOLARIS = Matches all supported Solaris systems. UNIX = Matches all supported Unix systems (Linux,Solaris)
| Host | The host name or IP address. The name or address can include the asterisk (*) wildcard to match with zero or more characters. For example, dev*.local.de refers to all systems where the host name starts with “dev” and ends with “.local.de”
| DNSDomain | The domain name service. The value is case insensitive and can use the asterisk (*) wildcard for zero or more characters.
| YPDomain | The yellow pages domain or NIS domain. The value is case insensitive and can use the asterisk (*) wildcard for zero or more characters.
| NTDomain | Windows NT domain. The value is case insensitive and can use the asterisk (*) wildcard for zero or more characters.

The following example uses two rules to map a Windows and Unix specific path to the user-defined path variable MyDocuments .

<?xml version="1.0" encoding="utf-8"?>

<oor:component-data oor:name="Substitution" oor:context="org.openoffice.Office"

xsi:schemaLocation="http://openoffice.org/2001/registry component-update.xsd"

xmlns:xs="http://www.w3.org/2001/XMLSchema"

xmlns:oor="http://openoffice.org/2001/registry"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<node oor:name="SharePoints">

<node oor:name="MyDocuments" oor:op=”replace”>

<node oor:name="1" oor:op=”replace”>

<prop oor:name="Directory"><value>file:///H:/documents</value></prop>

<node oor:name="Environment">

<prop oor:name="OS"><value>Windows</value></prop>

</node>

</node>

<node oor:name="2" oor:op=”replace”>

<prop oor:name="Directory"><value>file:///net/home/user/documents</value></prop>

<node oor:name="Environment">

<prop oor:name="OS"><value>UNIX</value></prop>

</node>

</node>

</node>

</node>

</oor:component-data>

##### Resolving Path Variables

This section explains how to use the OpenOffice.org implementation of the path substitution service. The following code snippet creates a path substitution service.

import com.sun.star.lang.XMultiServiceFactory;

import com.sun.star.uno.Exception;

import com.sun.star.uno.XInterface;

import com.sun.star.util.XStringSubstitution

XStringSubstitution createPathSubstitution() {

/////////////////////////////////////////////////////////////////////

// Obtain Process Service Manager.

/////////////////////////////////////////////////////////////////////

XMultiServiceFactory xServiceFactory = ...

/////////////////////////////////////////////////////////////////////

// Create Path Substitution. This needs to be done only once per process.

/////////////////////////////////////////////////////////////////////

XInterface xPathSubst;

try {

xPathSubst = xServiceFactory.createInstance(

"com.sun.star.util.PathSubstitution" );

}

catch (com.sun.star.uno.Exception e) {

}

if (xPathSubst != null)

return (XStringSubstitution)UnoRuntime.queryInterface(

XStringSubstitution.class, xPathSubst );

else

return null;

}

The service is implemented as a one-instance service and supports the interface com.sun.star.util.XStringSubstitution. The interface has three methods:

string substituteVariables ( [in] string aText, [in] boolean bSubstRequired )

string reSubstituteVariables ( [in] string aText )

string getSubstituteVariableValue ( [in] string variable )

The method substituteVariables() returns a string where all known variables are replaced by their value. Unknown variables are not replaced. The argument bSubstRequired can be used to indicate that the client needs a full substitution—otherwise the function fails and throws a com.sun.star.container.NoSuchElementException. For example: $(inst)/share/autotext/$(vlang) could be substituted to file:///c:/OpenOffice.org1.0.2/share/autotext/english.

The method reSubstituteVariables() returns a string where parts of the provided path aText are replaced by variables that represent this part of the path. If a matching variable is not found, the path is not modified.

The predefined variable $(path) is not used for substitution. Instead, it is a placeholder for the path environment variable does not have a static value during runtime. The path variables $(lang), $(langid) and $(vlang), which represent a directory or a filename in a path, only match inside or at the end of a provided path. For example: english is not replaced by $(vlang), whereas file:///c:/english is replaced by file:///c:/$(vlang).

The method getSubstituteVariableValue()returns the current value of the provided path variable as a predefined or a user-defined value. If an unknown variable name is provided, a com.sun.star.container.NoSuchElementException is thrown. The argument variable can be provided with preceding "$(" and succeeding ")" or without them. So both $(work) and work can be used.

This code example shows how to access, substitute, and resubstitute path variables by means of the OpenOffice.org API.

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;

import com.sun.star.beans.PropertyValue;

import com.sun.star.util.XStringSubstitution;

import com.sun.star.frame.TerminationVetoException;

import com.sun.star.frame.XTerminateListener;

/*

* Provides example code how to access and use the

* path substitution sercvice.

*/

public class PathSubstitutionTest extends java.lang.Object {

/*

* List of predefined path variables supported by

* the path substitution service.

*/

private static String[] predefinedPathVariables = {

"$(home)","$(inst)","$(prog)","$(temp)","$(user)",

"$(work)","$(path)","$(lang)","$(langid)","$(vlang)"

};

/*

* @param args the command line arguments

*/

public static void main(String[] args) {

XComponentContext xRemoteContext = null;

XMultiComponentFactory xRemoteServiceManager = null;

XStringSubstitution xPathSubstService = null;

try {

// connect

XComponentContext xLocalContext =

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

XMultiComponentFactory xLocalServiceManager = xLocalContext.getServiceManager();

Object urlResolver = xLocalServiceManager.createInstanceWithContext(

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

XUnoUrlResolver xUnoUrlResolver = (XUnoUrlResolver) UnoRuntime.queryInterface(

XUnoUrlResolver.class, urlResolver );

Object initialObject = xUnoUrlResolver.resolve(

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

XPropertySet xPropertySet = (XPropertySet)UnoRuntime.queryInterface(

XPropertySet.class, initialObject);

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

xRemoteContext = (XComponentContext)UnoRuntime.queryInterface(

XComponentContext.class, context);

xRemoteServiceManager = xRemoteContext.getServiceManager();

Object pathSubst = xRemoteServiceManager.createInstanceWithContext(

"com.sun.star.comp.framework.PathSubstitution", xRemoteContext );

xPathSubstService = (XStringSubstitution)UnoRuntime.queryInterface(

XStringSubstitution.class, pathSubst);

/* Work with path variables */

workWithPathVariables( xPathSubstService );

}

catch (java.lang.Exception e){

e.printStackTrace();

}

finally {

System.exit(0);

}

}

/*

* Retrieve, resubstitute path variables

* @param xPathSubstService the path substitution service

*/

public static void workWithPathVariables( XStringSubstitution xPathSubstService )

{

if ( xPathSubstService != null ) {

for ( int i=0; i<predefinedPathVariables.length; i++ ) {

try {

/* Retrieve values for predefined path variables */

String aPath = xPathSubstService.getSubstituteVariableValue(

predefinedPathVariables[i] );

System.out.println( "Variable="+ predefinedPathVariables[i] +

" Path=" + aPath );

/* Check resubstitute */

String aValue = xPathSubstService.reSubstituteVariables( aPath );

System.out.println( "Path=" + aPath +

" Variable=" + aValue );

}

catch ( com.sun.star.container.NoSuchElementException e) {

System.out.println( "NoSuchElementExceptio has been thrown accessing"

+ predefinedPathVariables[i]);

}

}

}

}

}

### 6.2.8 OpenOffice.org Single Sign-On API

#### Overview

Users of a client application that can communicate with a variety of services on a network may need to enter several passwords during a single session to access different services. This situation can be further exacerbated if the client application also requires the user to enter a password each time a particular network service is accessed during a session.

As most network users must authenticate to an OS at login time, it would make sense to access some of the required network services at this time as well. A solution to this problem is provided by the Single Sign-On (SSO) methodology, which is the ability to login in once and access several protected network services.

The best known SSO is the Kerberos network authentication protocol (see rfc1510 ). Kerberos functionality is commonly accessed through the Generic Security Service Application Program Interface (GSS-API, see rfc2743 ). Central to GSS-API is the concept of a security context, which is the "state of trust" that is initiated when a client (also known as source or initiator) identifies itself to a network service (also known as target or acceptor). If mutual authentication is supported, then the service can also authenticate itself to the client. To establish a security context, security tokens are exchanged, processed, and verified between the client and the service. The client always initiates this exchange. Once established, a security context can be used to encrypt or decrypt subsequent client-service communications.

The OpenOffice.org SSO API is based on GSS-API. The SSO API supports the creation of security contexts on the client and the service side as well as the generation of the security tokens that are required for the exchange to complete the security context based authentication. The SSO API does not support the actual exchange of security tokens or the encryption or decryption of client-service communications in an established security context.

OpenOffice.org implements SSO in two different ways to authenticate with an LDAP server for configuration purposes. The first is Kerberos based and the second is a simple non-standard "cached username/password" SSO. The latter is provided as a fallback to support scenarios where no Kerberos server is available.

#### Implementing the OpenOffice.org SSO API

Implementing the OpenOffice.org SSO API involves creating security context instances (see XSSOInitiatorContext and XSSOAcceptorContext below) and using these instances to create and process security tokens. All of the OpenOffice.org SSO interfaces are available from the ::com::sun::star::auth namespace. The major interfaces are shown in and described below.

XSSOManagerFactory

Represents the starting point for interaction with the SSO API. This interface is responsible for providing XSSOManager (described below) instances based on the user's configured security mechanism e.g. "KERBEROS".

XSSOManager

This interface is responsible for the creation of unestablished security contexts for clients (XSSOInitiatorContext) and services (XSSOAcceptorContext). An XSSOManager instance "supports" a single security mechanism, that is, the context instances that are created by an XSSOManager instance only interact with a single security mechanism implementation.

XSSOInitiatorContext

This interface represents a client-side security context that is unestablished when it is created. A single method, init(), is provided so that you can create an initial client-side security token that can be delivered to the relevant service and for processing or validating returned service-side security tokens (if mutual authentication is supported). The expected sequence of events for this client-side security context is:

- The client calls init(), passes NULL as the parameter, receives an appropriate client-side security token in return.

- The client sends the security token to the relevant service.

- If the service successfully processes this token, the client is authenticated.

- If mutual authentication is not supported, the client-side authentication sequence is now complete.

- If mutual authentication is supported, the service sends a service-side security token to the client.

- The client calls init() a second time and passes the returned service-side security token as a parameter. If the token is successfully passed, the service is authenticated.

XSSOAcceptorContext

This interface represents a service-side security context that is not established when it is created. A single method, accept(), is provided and is responsible for processing an initial client-side security token. If mutual authentication is supported, the method also generates a service-side security token for the client. The expected sequence of events for this service-side security context is:

- The service receives the client-side security token.

- The service calls accept(), passes the client-side security token as a parameter, and if successful, the client is authenticated.

- If mutual authentication is not supported, the service-side authentication sequence is now complete.

- If mutual authentication is supported, accept() returns a non-zero length service-side security token.

- The service sends the service-side security token to the client to authenticate the service.

Illustration 6.23: Major Interfaces of the OpenOffice.org SSO

The following example is a sample usage of the OpenOffice.org SSO API that provides the authenticate() method of the fictitious client--side MySSO class. For simplicity, assume that MySSO has the following members:

- mSourceName identifies a client-side user that needs to authenticate to a network service.

- mTargetName identifies the service to which the user needs to authenticate.

- mTargetHost identifies the network host where the service of interest is running.

namespace auth = ::com::sun::star::auth;

namespace lang = ::com::sun::star::lang;

namespace uno = ::com::sun::star::uno;

void MySSO::authenticate(void) {

static const rtl::OUString kSSOService(

RTL_CONSTASCII_USTRINGPARAM("com.sun.star.auth.SSOManagerFactory"));

uno::Reference< lang::XMultiServiceFactory > theServiceFactory =

::comphelper::getProcessServiceFactory();

// Create an SSO Manager Factory.

uno::Reference< auth::XSSOManagerFactory > theSSOFactory(

theServiceFactory->createInstance(kSSOService), uno::UNO_QUERY);

if (!theSSOFactory.is()) {

throw;

}

// Ask the SSO Manager Factory for an SSO Manager.

uno::Reference<auth::XSSOManager> theSSOManager =

theSSOFactory->getSSOManager();

if (!theSSOManager.is()) {

throw;

}

// Ask the SSO Manager to create an unestablished client/initiator side

// security context based on user name, service name and service host.

uno::Reference<auth::XSSOInitiatorContext> theInitiatorContext =

theSSOManager->createInitiatorContext(mSourceName, mTargetName, mTargetHost);

// Now create the client side security token to send to the service.

uno::Sequence<sal_Int8> theClientToken = theInitiatorContext->init(NULL);

// The client should now send 'theClientToken' to the service.

// If mutual authentication is supported, the service will return a service

// side security token.

uno::Sequence<sal_Int8> theServerToken = sendToken(theClientToken);

if (theInitiatorContext->getMutual()) {

theInitiatorContext->init(theServerToken);

}

}

##### The SSO Password Cache

When you implement the SSO API, you may require access to user passwords, especially if you are relying on a preexisting underlying security mechanism. If you do not know how to gain such access, you can use the OpenOffice.org SSO password cache. This cache provides basic support for maintaining a list of user name or password entries. Individual entries have a default lifetime corresponding to a single user session, but can optionally exist for multiple sessions. Support is provided for adding, retrieving, and deleting cache entries. Only one entry per user name can exist in the cache at any time. If you add an entry for an existing user name, the new entry replaces the original entry.
The SSO password cache is represented by a single interface, namely the XSSOPasswordCache interface, available in the ::com::sun::star::auth namespace.

[ Previous document | Content Table | Next document ]