UNO(通用网络对象)
的目标是为跨编程语言和跨平台边界的网络对象提供环境。UNO 对象可在任何地方运行和通信。UNO 通过提供以下基础框架达到此目标:
•
UNO 对象在一种称为 UNOIDL(UNO 接口定义语言)的抽象元语言中指定,这种语言与CORBA IDL 或 MIDL 类似。利用 UNOIDL 规范,可以生成与语言有关的头文件和程序库,用于在目标语言中实现 UNO 对象。在 UNO 对象中,经过编译和绑定程序库的那些对象称为组件。组件必须支持某些基接口才能够在 UNO 环境中运行。
•
为了在目标环境中实例化组件,UNO 使用了工厂概念。该工厂称为服务管理器。它维护一个注册组件数据库,这些组件可通过名称识别,并可按名称创建。服务管理器可能会要求 Linux 加载和实例化用 C++ 编写的共享对象,也可能会调用本地 Java VM 以实例化 Java 类。这对于开发者来说是透明的,无需考虑组件的实现语言。通信是以独占方式通过 UNOIDL 中指定的接口调用来进行的。
•
UNO 提供桥,用于在用不同实现语言编写的进程之间以及对象之间发送方法调用和接收返回值。为此,远程桥使用一种特殊的 UNO 远程协议 (URP) 来支持套接字和管道。桥的两端都必须是 UNO 环境,因此,需要一种特定于语言的 UNO 运行时环境来连接任何受支持语言中的另一个 UNO 进程。这些运行时环境是作为语言绑定提供的。
•
StarSuite 的大多数对象都能够在 UNO 环境中进行通信。OpneOffice 的可编程功能规范称为OpneOffice API。
OpneOffice API
数据类型
简单类型
UNO 提供了一组预定义的简单类型,如下表所示:
void
空类型,仅在 any 中用作方法返回类型。
boolean
可以是 true 或 false。
byte
有符号的 8 位整数类型(范围从 -128 到 127,包括上下限)。
short 有符号的 16 位整数类型(范围从 -32768 到 32767,包括上下限)。
unsigned short 无符号的 16 位整数类型(已不再使用)。
long 有符号的 32 位整数类型(范围从 -2147483648 到 2147483647,包括上下限)。
unsigned long 无符号的 32 位整数类型(已不再使用)。
hyper 有符号的 64 位整数类型(范围从 -9223372036854775808 到 9223372036854775807,包括上下限)。
unsigned hyper 无符号的 64 位整数类型(已不再使用)。
float IEC 60559 单精度浮点类型。
double IEC 60559 双精度浮点类型。
char 表示单个的 Unicode 字符(更确切地说是单个的 UTF-16 代码单元)。
string 表示 Unicode 字符串(更确切地说是 Unicode 标量值的字符串)。
type 说明所有 UNO 类型的元类型。
Any 能够表示其他所有类型值的特殊类型。
Any 类型
特殊类型
any
可以表示其他所有 UNO 类型的值。在目标语言中,
any
类型需要特殊对待。Java 中提供了
AnyConverter
,而 C++ 中提供了特殊的运算符。
接口
UNO 对象之间的通信基于对象接口。接口分对象外部接口和对象内部接口。
从对象外部看,接口提供对象的一种功能或某个特殊方面。通过发布一组有关对象某个特定方面的操作,接口提供对对象的访问,而无需给出对象的任何内部信息。接口是一个十分合乎自然的概念,日常生活中经常用到它。接口允许建立彼此匹配的对象,而无须了解对象的内部细节。
从对象内部或从 UNO 对象实现者的角度来看,接口是抽象规范。OpneOffice API 中所有接口的抽象规范都具有一个优点:用户和实现者可签订同意遵守接口规范的合同。一个严格按照规范使用OpneOffice API 的程序将会始终有效,而对于实现者来说,只要遵守合同,就可以对其对象进行任何操作。
UNO 使用
interface
类型来说明 UNO 对象的这些方面。按照约定,所有接口名称都以字母 X 开头,以将接口类型与其他类型区分开来。所有接口类型都必须继承
com.sun.star.uno.XInterface
根接口,可以直接继承,也可以按层次继承结构继承.
接口通过封装对象数据的专用方法(成员函数)来访问该对象的内部数据。方法通常具有一个参数列表和一个返回值,而且可以定义异常以进行智能错误处理。
服务
一个单继承接口仅说明对象的一个方面。但是,对象通常会包含多个方面。UNO 使用多继承接口和服务来指定包含多个方面的完整对象。
第一步中,对象的所有各个方面(通常用单继承接口表示)被组合到一个多继承接口类型中。如果通过调用特定的工厂方法可获得这样的对象,则此步骤即所需的全部操作。指定工厂方法以返回给定的多继承接口类型的值。但是,如果这样的对象在全局组件上下文中可用作常规服务,则在第二步中必须有服务说明。此服务说明将为新样式,能够将服务名称(在组件上下文中通过它可获得服务)映射成给定的多继承接口类型。
为了实现向后兼容,还存在旧式服务,其中包括一组支持某一功能所需的单继承接口和属性。这样的服务还可以包括其他旧式服务。旧式服务的主要缺点在于,它无法明确指出其说明的是通过特定的工厂方法获得的对象(因此将不存在新式服务说明),还是说明在全局组件上下文中可以获得的常规服务(因此将存在新式服务说明)。
从UNO 对象用户的角度来看,对象提供 API 引用中所述的一项服务,有时甚至提供多项独立的多继承接口或旧式的服务。可以通过接口中分组的方法调用以及也是经过特殊接口处理的属性来使用服务。由于对功能的访问仅由接口提供,因此,希望使用某个对象的用户无需关心如何实现。
从 UNO 对象实现者的角度来看,多继承接口和旧式服务用于定义某项与编程语言无关的功能,且无需提供有关对象内部实现的说明。实现某个对象意味着必须支持所有指定的接口和属性。一个 UNO对象可以实现多项独立的、多继承接口或旧式服务。有时,实现两项或更多独立的、多继承接口或服务非常有用,因为它们具有相关的功能,或者支持对象的不同视图。
接口和服务之间的关系。具有多个接口的旧式服务的与语言无关规范用于实现符合此规范的 UNO 对象。这样的 UNO 对象有时被称为“组件”,尽管该术语用于说明 UNO 环境内的配
置实体更为准确。对于新式服务说明,唯一的区别就是它仅支持一个多继承接口,该接口将继承其他接口。
引用接口
对某项服务定义中接口的引用意味着要实现此服务必须提供指定的接口。此外,还可以提供可选接口。如果某个多继承接口继承可选接口,或者某个旧式服务包含可选接口,则任何给定的 UNO 对象可以支持此接口,也可以不支持。如果您使用某个 UNO 对象的可选接口,通常需要检查
queryInterface()
的结果是否为
null
,并作出相应的反应,否则,您的代码将与不包含可选接口的实现不兼容,并会因为出现空指针异常而结束。以下 UNOIDL 代码段说明了 OpneOffice API 中
com.sun.star.text.TextDocument
的旧式服务的规范片段。请注意方括号中的标志
optional
,此标志表示接口
XFootnotesSupplier
和
XEndnotesSupplier
为可选接口。
// com.sun.star.text.TextDocument
service TextDocument
{
...
interface com::sun::star::text::XTextDocument;
interface com::sun::star::util::XSearchable;
interface com::sun::star::util::XRefreshable;
[optional] interface com::sun::star::text::XFootnotesSupplier;
[optional] interface com::sun::star::text::XEndnotesSupplier;
...
};
服务构造函数
新式服务可以拥有构造函数,与接口方法类似:
service SomeService: XSomeInterface {
create1();
create2([in] long arg1, [in] string arg2);
create3([in] any... rest);
};
在上面的示例中,有三个显式构造函数,名为
create1
、
create2
和
create3
。第一个构造函数没有参数,第二个有两个常规参数,第三个有一个特殊的 rest 参数,该参数可以接受任意数目的
any
值。构造函数参数只能为
[in]
,rest 参数必须是构造函数的唯一参数,并且必须为
any
类型;另外,与接口方法不同,服务构造函数不指定返回类型。
各个语言绑定将 UNO 构造函数映射成特定语言结构,这些结构可用于客户机代码,在给定组件上下文的情况下可得到服务实例。常规约定(例如,后跟 Java 和 C++ 语言绑定)将每个构造函数映射成同名的静态方法(resp. 函数),将 XComponentContext 作为第一个参数,后跟构造函数中指定的所有参数,并返回一个(适当键入的)服务实例。如果无法得到实例,则会抛出
com.sun.star.uno.DeploymentException
。上面的
SomeService
将映射成以下 Java 1.5 类,例
如:
public class SomeService {
public static XSomeInterface create1(
com.sun.star.uno.XComponentContext context) { ... }
public static XSomeInterface create2(
com.sun.star.uno.XComponentContext context, int arg1, String arg2) { ... }
public static XSomeInterface create3(
com.sun.star.uno.XComponentContext context, Object... rest) { ... }
}
服务构造函数还拥有异常规范 (“
raises (Exception1, ...)
”),其处理方法与接口方法的异常规范相同。(如果构造函数没有异常规范,则只能抛出运行时异常,尤其是
com.sun.star.uno.DeploymentException
。)
如果新式服务以简写形式
service SomeService: XSomeInterface;
编写,则会有一个隐式构造函数。隐式构造函数的准确行为是特定于语言绑定的,但其名称通常为
create
,除
XComponentContext
之外不接受任何参数,并且只能抛出运行时异常。
包含属性
建立 OpneOffice API 结构时,设计者发现办公软件环境中的对象具有大量不属于对象结构的属性,更恰当地说,它们似乎是对底层对象的表面更改。同时还发现,并非某种具体类型中的任何对象都具有所有属性。因此,没有为每种属性定义一系列复杂的可选和非可选接口,而是引入了属性的概念。属性是对象中的数据,在普通接口中按名称提供以进行属性访问,接口包含
getPropertyValue()
和
setPropertyValue()
访问方法。属性这一概念还具有其他优点,有许多信息值得了解。
下面是可能的属性标志:
•
optional
实现组件时可以不支持对应的属性。
•
readonly
不能使用
com.sun.star.beans.XPropertySet
更改对应的属性值。
•
bound
如果任何属性值通过
com.sun.star.beans.XPropertySet
注册,则属性值的更改将通知
com.sun.star.beans.XPropertyChangeListener
。
•
constrained
属性在其值被更改之前广播一个事件。侦听器有权禁止更改。
•
maybeambiguous
某些情况下,可能无法确定属性值,例如,具有不同值的多项选择。
•
maybedefault
值可能存储在某个样式工作表中,也可能存储在非对象本身的环境中。
•
maybevoid
除属性类型的范围以外,值也可以为空。它与数据库中的空值类似。
•
removable
对应的属性是可删除的,用于动态属性。
•
transient
如果序列化对象,将不存储对应的属性。
引用其他服务
旧式服务可以包括其他旧式服务。此类引用是可选的。一项服务被另一项服务所包含与实现继承无关,而仅仅是合并规范。由实现者决定是继承还是授权必需的功能,或者决定是否从头实现必需的功能。
以下 UNOIDL 示例中的旧式服务
com.sun.star.text.Paragraph
包含一项必需服务
com.sun.star.text.TextContent
和五项可选服务。每个
Paragraph
都必须是
TextContent
。它同时可以是
TextTable
,而且可用于支持段落和字符的格式化属性:
// com.sun.star.text.Paragraph
service Paragraph
{
service com::sun::star::text::TextContent;
[optional] service com::sun::star::text::TextTable;
[optional] service com::sun::star::style::ParagraphProperties;
[optional] service com::sun::star::style::CharacterProperties;
[optional] service com::sun::star::style::CharacterPropertiesAsian;
[optional] service com::sun::star::style::CharacterPropertiesComplex;
...
};
如果上面示例中的所有旧式服务都使用多继承接口类型代替,则结构类似:多继承接口类型
Paragraph
将继承强制接口
TextContent
和可选接口
TextTable
、
ParagraphProperties
等。
组件中的服务实现
组件是一个共享库或 Java 存档文件,其中包含用 UNO 支持的某种目标编程语言实现的一项或多项服务。这样的组件必须满足基本要求(目标语言不同,通常要求也不同),而且必须支持已实现的服务的规范。这意味着必须实现所有指定的接口和属性。需要在 UNO 运行时系统中注册组件。注册之后,通过在适当的服务工厂对服务实例进行排序以及通过接口访问功能,可以使用所有已实现的服务。
结构
struct
类型定义一条记录中的多个元素。
struct
中的元素是结构中具有唯一名称的 UNO 类型。结构的缺点是不封装数据,但缺少
get ()
和
set()
方法有助于避免通过 UNO 桥进行方法调用所产生的开销。UNO 支持
struct
类型的单继承。派生的
struct
以递归方式继承父对象以及父对象的父对象中的所有元素。
// com.sun.star.lang.EventObject
/** specifies the base for all event objects and identifies the source of the event.
*/
struct EventObject
{
/** refers to the object that fired the event.
*/
com::sun::star::uno::XInterface Source;
};
// com.sun.star.beans.PropertyChangeEvent
struct PropertyChangeEvent : com::sun::star::lang::EventObject {
string PropertyName;
boolean Further;
long PropertyHandle;
any OldValue;
any NewValue;
};
OpneOffice [OO2.0] 的一项新功能是多态结构类型。多态结构类型模板与普通结构类型类似,但它有一个或多个类型参数,并且其成员可以将这些参数作为类型。多态结构类型模板本身不是 UNO 类型—它必须使用实际的类型参数来实例化才能作为一个类型使用。
// A polymorphic struct type template with two type parameters:
struct Poly<T,U> {
T member1;
T member2;
U member3;
long member4;
};
// Using an instantiation of Poly as a UNO type:
下面的Poly<boolean, any>
是一个具有与普通结构类型相同形式的实例化多态结构类型
struct PolyBooleanAny {
boolean member1;
boolean member2;
any member3;
long member4;
};
添加多态结构类型主要用于支持丰富的接口类型属性,这些属性与
maybeambiguous
、
maybedefault
或
maybevoid
属性一样富有表达力但这些类型也可能用于其他环境中。
预定义值
API 提供许多预定义值,用作方法参数,或作为方法的返回值。在 UNO IDL 中,预定义值有两种不
同的数据类型:常数和枚举。
const
const
定义有效 UNO IDL 类型的命名值。值取决于指定的类型,可以是常值(整数、浮点数或字符)、另一个
const
类型的标识符或包含以下运算符的算术项:
+
、
-
、
*
、
/
、
~
、
&
、
|
、
%
、
^
、
<<
、
>>
。
由于可以在 const 中广泛选择类型和值,因此,const 有时用于生成对合并的值进行编码的位矢量。
const short ID = 23;
const boolean ERROR = true;
const double PI = 3.1415;
通常情况下,const 定义是常数组的一部分。
constants
constants
类型定义的是包含多个 const 值的一个命名组。constants 组中的 const 用组名称加上const 名称表示。在下面的 UNO IDL 示例中,
ImageAlign.RIGHT
指的是值 2:
constants ImageAlign {
const short LEFT = 0;
const short TOP = 1;
const short RIGHT = 2;
const short BOTTOM = 3;
};
enum
enum
类型与 C++ 中的枚举类型相当。它包含一个已排序列表,列表中有一个或多个标识符,代表
enum
类型的各个值。默认情况下,值按顺序编号,以 0 开头,每增加一个新值就增加 1。如果给某个
enum
值指定了一个值,则没有预定义值的所有后续
enum
值都以此指定值为基准来获得值。
// com.sun.star.uno.TypeClass
enum TypeClass {
VOID,CHAR,BOOLEAN,BYTE,SHORT,...
};
enum Error {
SYSTEM = 10, // value 10
RUNTIME, // value 11
FATAL, // value 12
USER = 30, // value 30
SOFT // value 31
};
如果调试中使用了 enum,就应该能够通过操作一个 enum 在 API 引用中的位置来获得该 enum 的数值。但是,不要使用文字数值取代程序中的 enum。
序列
sequence
类型是一组相同类型的元素,元素的数量可变。在 UNO IDL 中,使用的元素始终引用某个现有的已知类型或者另一个
sequence
类型。
sequence
可作为其他所有类型定义中的一个一般类型出现。
sequence< com::sun::star::uno::XInterface >
sequence< string > getNamesOfIndex( sequence< long > indexes );
模块
模块是命名空间,与 C++ 中的命名空间或 Java 中的包类似。模块将服务、接口、结构、异常、枚举、类型定义、常数组以及子模块与相关的功能内容或性能组合在一起。使用它们在 API 中指定一致的块,这样可以生成结构良好的 API。例如,模块
com.sun.star.text
包含接口以及其他类型,用于进行文本处理。其他一些典型模块包括
com.sun.star.uno
、
om.sun.star.drawing
、
com.sun.star.sheet
和
com.sun.star.table
。一个模块内的标识符不会与其他模块内的标识符相冲突,因此,同一名称可能多次出现。API 引用的全局索引说明了确实存在这种情况。
尽管模块似乎与 OpneOffice 的各个部分相对应,但是 API 模块与 OpneOffice 应用程序 Writer、Calc 和Draw 之间不存在直接的关系。Calc 和 Draw 中使用模块
com.sun.star.text
的接口。像
com.sun.star.style
或
com.sun.star.document
等模块提供的普通服务和接口不是针对OpneOffice 任何一个部分的。我们在 API 引用中看到的模块是通过在模块说明中嵌套 UNO IDL 类型进行定义的。例如,模块
com.sun.star.uno
包含接口
XInterface
:
module com { module sun { module star { module uno {
interface XInterface {
...
};
};};};};
异常
exception
类型表示函数调用程序发生错误。异常类型给出发生的错误类型的基本说明。另外,UNO IDL
exception
类型包含的元素给出精确规范和错误详细说明。
exception
类型支持继承,这常用于定义错误分层。异常仅用于指出错误,不作为方法参数或返回类型。
UNO IDL 要求所有异常都必须从
com.sun.star.uno.Exception
继承。这是 UNO 运行时的一个
前提。
// com.sun.star.uno.Exception is the base exception for all exceptions
exception Exception {
string Message;
Xinterface Context;
};
// com.sun.star.uno.RuntimeException is the base exception for serious problems
// occuring at runtime, usually programming errors or problems in the runtime environment
exception RuntimeException : com::sun::star::uno::Exception {
};
// com.sun.star.uno.SecurityException is a more specific RuntimeException
exception SecurityException : com::sun::star::uno::RuntimeException {
};
异常只能由指定抛出异常的操作来抛出。而
com.sun.star.uno.RuntimeException
则可以随时发生。
UNO 基接口 com.sun.star.uno.XInterface 的方法 acquire() 和 release 是上述规则的例外。它们是唯一不会抛出运行时异常的操作。但在 Java 和 C++ 程序中,不直接使用这些方法,而是由各自的语言绑定进行处理。
Singleton
Singleton 用于指定已命名对象,在一个 UNO 组件上下文的生存期中恰好可以存在一个实例。singleton 引用一个接口类型,并指定只能在组件上下文中通过使用 singleton 名称来访问 singleton唯一存在的实例。如果该 singleton 不存在实例,组件上下文将实例化一个新的实例。这种新式
singleton 的一个示例如下
module com { module sun { module star { module deployment {
singleton thePackageManagerFactory: XPackageManagerFactory;
}; }; }; };
各个语言绑定提供了在给定组件上下文的情况下,可得到新式 singleton 实例的特定语言方法。例如,在 Java 和 C++ 中,有一个名为
get
的静态方法(resp. 函数),该方法将
XComponentContext
作为其唯一的参数,并返回(适当键入的) singleton 实例。如果无法得到实例,则会抛出
com.sun.star.uno.DeploymentException
。
此外,还有旧式 singleton,这些 singleton 引用的是(旧式)服务而不是接口。但是,对于旧式服务,语言绑定不提供
get
功能。