Работа с объектами
Объекты, интерфейсы и сервисы
Объекты
В UNO объект – программный артефакт, имеющий методы, которые можно вызывать и атрибуты, которые можно получать и устанавливать. Конкретный набор методов и атрибутов, предоставляемых объектом, определяется интерфейсами, реализуемыми объектом.
Интерфейсы
Интерфейс описывает набор атрибутов и методов, которые вместе определяют некоторый единичный аспект объекта. Например, интерфейс
com.sun.star.resource.XResourceBundle:
module com
{
module sun
{
module star
{
module resource
{
interface XResourceBundle: com::sun::star::container::XNameAccess
{
[attribute] XResourceBundle Parent;
com::sun::star::lang::Locale getLocale();
any getDirectElement([in] string key);
};
};
};
};
}; |
определяет атрибут Parent и методы getLocale() и getDirectElement().
Для обеспечения многократного использования подобных интерфейсных спецификаций, интерфейс может наследовать один или более других интерфейсов (например, XResourceBundle наследует все атрибуты и методы интерфейса com.sun.star.container.XNameAccess). Множественное наследование интерфейсов – новая возможность OpenOffice.org 2.0.
Строго говоря, интерфейсные атрибуты не необходимы в UNO. Каждый атрибут может быть представлен сочетанием get-метода для получения значения атрибута и set-метода для установки значения (или одного из этих методов). Но с использованием атрибутов возможно лучше выразить нюансы особенностей объекта. Атрибуты могут использоваться для не неотъемлемых особенностей объекта, а методы доступа резервируются для манипуляции с необходимыми аспектами объектов.
Исторически типичный объект UNO поддерживал набор из нескольких независимых интерфейсов, соответствующих различным ракурсам объекта. С множественным наследованием ситуация изменилась, объект может поддерживать один интерфейс, наследующий все остальные.
Сервисы
Изначально используемый в UNO термин "сервис" был не вполне ясен. Начиная с версии OpenOffice.org 2.0 ситуация прояснилась, но до сих пор остаются разночтения для термина "сервис". В дальнейшем будет использоваться термин в новом значении, соответствующем разъясненной концепции OpenOffice.org 2.0. Словосочетание "сервис старого стиля" будет применяться для обозначения объекта, соответствующего старой, неопределенной концепции. Затрудняет ситуацию и то, что слово "сервис" применяется и помимо UNO в самых разных значениях.
Хотя сейчас нет надобности в сервисах старого стиля, OpenOffice.org API продолжает поддерживать их для сохранения обратной совместимости.
Сервис "нового стиля" выглядит так:
module com
{
module sun
{
module star
{
module bridge
{
service UnoUrlResolver: XUnoUrlResolver;
};
};
};
}; |
Это определяет, что объекты, поддерживающие некоторый интерфейс (например, здесь com.sun.star.bridge.XUnoUrlResolver) будут доступны под некоторым именем сервиса – здесь com.sun.star.bridge.UnoUrlResolver, – в компонентном контексте менеджера сервисов. Иначе сервисы "нового стиля" еще называют сервисами, основанными на одиночном интерфейсе.
Сервис старого стиля – иначе называемый накопительным сервисом, – выглядит так:
module com
{
module sun
{
module star
{
module frame
{
service Desktop
{
service Frame;
interface XDesktop;
interface XComponentLoader;
interface com::sun::star::document::XEventBroadcaster;
};
};
};
};
}; |
В общем, если объект определен как поддерживающий какой-то сервис старого стиля, то следует ожидать, что этот объект будет поддерживать все интерфейсы, экспортируемые этим сервисом – и всеми унаследованными сервисами.
Например, метод com.sun.star.frame.XFrames.queryFrames() вернет последовательность объектов, все они будут поддерживать сервис старого стиля com.sun.star.frame.Frame, и, следовательно, все интерфейсы, экспортируемые Frame.
Сервис старого стиля может содержать одно (или более) свойств:
module com
{
module sun
{
module star
{
module frame
{
service Frame
{
interface com::sun::star::frame::XFrame;
interface com::sun::star::frame::XDispatchProvider;
// ...
[property] string Title;
[property, optional] XDispatchRecorderSupplier RecorderSupplier;
// ...
};
};
};
};
}; |
Свойства (подробнее рассматриваемые ниже) подобны интерфейсным атрибутам, и описывают некоторые дополнительные особенности объекта. Основное отличие от атрибутов – обращение к атрибутам производится непосредственно, тогда как к свойствам сервисы старого стиля обращаются при помощи родовых интерфейсов, таких, как com.sun.star.beans.XPropertySet. Интерфейсные атрибуты чаще применяются для представления неотъемлемых особенностей объектов, тогда как свойства определяют дополнительные, более изменчивые аспекты.
Некоторые сервисы старого стиля подразумевают доступность в компонентном контексте менеджера сервисов. Например, экземпляр сервиса com.sun.star.frame.Desktop может быть создан в компонентном контексте менеджера сервисов под собственным именем "com.sun.star.frame.Desktop". Но нельзя определить, предназначен ли какой-то сервис старого стиля для подобного использования, тогда как применение сервиса "нового стиля" делает намерение подобного использования более прозрачным.
Другие сервисы старого стиля проектируются как родовые "супер-сервисы", предназначенные для наследования другими сервисами. Например, сервис com.sun.star.document.OfficeDocument служит родовой основой для многих конкретных сервисов документа, таких, как com.sun.star.text.TextDocument, или com.sun.star.drawing.DrawingDocument. Для определения подобных базовых родовых сервисов предпочтителен механизм множественного наследования.
Другие сервисы старого стиля проектируются как родовые "супер-сервисы", предназначенные для наследования другими сервисами. Например, сервис com.sun.star.document.OfficeDocument служит родовой основой для многих конкретных сервисов документа, таких, как com.sun.star.text.TextDocument, или com.sun.star.drawing.DrawingDocument. Для определения подобных базовых родовых сервисов предпочтителен механизм множественного наследования.
Другие сервисы старого стиля лишь перечисляют свойства, не экспортируя каких-либо интерфейсов. Подобные сервисы используются для описания набора связанных между собою свойств. Например, сервис com.sun.star.document.MediaDescriptor перечисляет все свойства, которые могут быть переданы методу интерфейса
com.sun.star.frame.XComponentLoader.loadComponentFromURL.
Свойства определяют такие аспекты объекта, которые не являются его неотъемлемой или структурной частью, и поэтому обрабатываются при помощи родовых методов getPropertyValue() и setPropertyValue() вместо специализированных методов, наподобие getPrinter(). Сервисами старого стиля предлагается специальный синтаксис для перечисления свойств объекта. Объект, содержащий свойства, должен поддерживать интерфейс com.sun.star.beans.XPropertySet, что дает возможность работать со всеми разновидностями свойств. Например – свойства, содержащие какие-то параметры форматирования символа или абзаца. При помощи свойств можно определить несколько параметров одним вызовом метода setPropertyValues(), что весьма повышает производительность при удаленной работе. К примеру, объекты-абзацы поддерживают метод setPropertyValues() благодаря реализуемому ими интерфейсу
com.sun.star.beans.XMultiPropertySet.
Использование сервисов
Причины появления концепций интерфейсов и сервисов.
-
Интерфейсы и сервисы отделяют спецификацию от реализации.
Спецификация интерфейса или сервиса абстрактна, то есть не определяет, как объект, обеспечивающий некую функциональность, делает это. Благодаря этому можно менять реализацию компонентов, не изменяя другие части программы.
-
Сервисы позволяют создавать экземпляры по имени спецификации – не по имени класса.
В языках Java или C++ для создания экземпляра класса используется оператор new. При таком подходе получаемый класс жестко предопределен, нельзя создать экземпляр другого класса без изменения кода. Применение сервисов решает эту проблему. Глобальный менеджер сервисов (центральную "фабрику" объектов OpenOffice.org) клиенты запрашивают создать объект для какой-то цели – без какого-то указания на внутреннюю реализацию запрашиваемого объекта. Это возможно потому, что создание сервиса запрашивается согласно имени сервиса, и уже менеджер сервисов ("фабрика" объектов) будет решать, какую именно реализацию сервиса создавать. Для клиента нет разницы, какую реализацию он получил, если полученный объект поддерживает нужные интерфейсы.
-
Множественное наследование облегчает управление малоразмерными интерфейсами.
Абстрактные интерфейсы проще повторно использовать, если они малоразмерны – то есть если они малы по размерам и описывают только какую-то один аспект объекта. В таком случае требуется много подобных интерфейсов для описания большого объекта. Множественное наследование упрощает задачу манипулирования большим количеством малоразмерных интерфейсов.
Рассмотрим сервис старого стиля com.sun.star.text.TextDocument в нотации UML.
Как видно, здесь схематически изображены сервисы com.sun.star.text.TextDocument и com.sun.star.document.OfficeDocument.
Также видно, какие интерфейсы экспортирует каждый из этих сервисов. В OpenOffice.org API принято начинать имя интерфейса с буквы X.
Каждый объект TextDocument должен поддерживать интерфейсы XTextDocument, XSearchable и XRefreshable. Так как TextDocument наследует OfficeDocument, он также поддерживает XPrintable, XStorable, XModel и XModifiable. Методы этих интерфейсов обеспечивают различные аспекты поведения объекта, как видно из названия: печать, сохранение, модификацию, управление моделью и др.
Не следует забывать, что на схеме отображены лишь основные интерфейсы. Реальное устройство объектов намного сложнее.
Использование интерфейсов
Обращение к объектам UNO через его интерфейсы накладывает особенность при кодировании на Java или C++. В этих языках компилятор нуждается в правильном типе ссылки для того, чтобы можно было вызывать методы объекта, и обычно перед обращением к интерфейсу следует произвести приведение типа. При работе с объектами UNO дело обстоит иначе. Следует запросить UNO о соответствующей ссылке каждый раз, когда производится обращение к методам интерфейса, поддерживаемого объектом.
Для этого существует метод UNO queryInterface(). Это выглядит довольно усложненно, но позволяет безопасное (в том числе межпроцессное) приведение типов. В примере FirstloadComponent создается новый объект Desktop, и метод queryInterface() используется для получения ссылки на интерфейс XComponentLoader.
Object desktop = xRemoteServiceManager.createInstanceWithContext( "com.sun.star.frame.Desktop", xRemoteContext);
XComponentLoader xComponentLoader = (XComponentLoader)
UnoRuntime.queryInterface(XComponentLoader.class, desktop); |
Здесь делается запрос методу менеджера сервисов (объект xRemoteServiceManager) createInstanceWithContext() для создания экземпляра com.sun.star.frame.Desktop. Этот метод возвращает Java-тип Object, на самом же деле этот объект типа com.sun.star.frame.Desktop.
Вот спецификация этого метода:
java.lang.Object createInstanceWithContext(String serviceName, XComponentContext context) |
Далее создается ссылка на интерфейс XComponentLoader объекта desktop. Хотя мы и знаем, что этот объект экспортирует требуемый интерфейс, компилятор не имеет информации об этом. Поэтому делается запрос методом UNO queryInterface() чтобы получить ссылку требуемого типа. При этом для компилятора нет разницы, локальный или удаленный объект используется.
В языке Java есть два вида спецификации этого метода:
java.lang.Object UnoRuntime.queryInterface(java.lang.Class targetInterface, Object sourceObject)
java.lang.Object UnoRuntime.queryInterface(com.sun.star.uno.Type targetInterface, Object sourceObject) |
Так как метод queryInterface() возвращает значение типа java.lang.Object, следует все же принудительно привести тип. Однако (в отличие от метода createInstanceWithContext()) здесь можно безопасно приводить тип, и ссылка будет работать – даже с объектом другого процесса.
Еще раз подробно разберем запрос интерфейса.
XComponentLoader xComponentLoader = (XComponentLoader)UnoRuntime.queryInterface(XComponentLoader.class, desktop); |
XComponentLoader – тип интерфейса, который мы хотим использовать, для чего создаем переменную по имени xComponentLoader. В ней будет храниться результат запроса к queryInterface().
Аргументы здесь – требуемый интерфейс XComponentLoader.class и объект desktop, у которого этот интерфейс запрашивается.
Перед присвоением значения происходит приведение типа результата к типу XComponentLoader. Если объект не поддерживает запрашиваемый интерфейс, метод вернет null.
В Java подобный запрос делается каждый раз, когда нужна ссылка на объект, поддерживающий нужный интерфейс. Можно сделать запрос интерфейса не только от объекта, но и от другой интерфейсной ссылки:
// загружаем пустой документ, полученный при помощи интерфейса XComponent:
XComponent xComponent = xComponentLoader.loadComponentFromURL("private:factory/scalc", "_blank", 0, loadProps);
//а теперь запрашиваем ссылку на XSpreadsheetDocument:
XSpreadsheetDocument xSpreadsheetDocument =
(XSpreadsheetDocument)UnoRuntime.queryInterface(XSpreadsheetDocument.class, xComponent); |
Если метод определен так, что сразу возвращает интерфейсный тип – нет нужды в запросе интерфейса, можно сразу использовать его методы.
В приведенном выше отрывке метод loadComponentFromURL() определен так, что возвращает интерфейсный тип com.sun.star.lang.XComponent – и можно сразу вызывать методы addEventListener() и removeEventListener(), используя переменную xComponent – если нужно получать уведомление о закрытии документа.
Вот так это делается в C++:
//получаем экземпляр сервиса от менеждера сервисов
Reference < XInterface > rInstance =
rServiceManager->createInstanceWithContext(
OUString::createFromAscii("com.sun.star.frame.Desktop" ),
rComponentContext );
//запрос интерфейса XComponentLoader
Reference < XComponentLoader > rComponentLoader( rInstance, UNO_QUERY ); |
В OpenOffice.org Basic нет необходимости в запросе интерфейсов, Basic делает это самостоятельно.
С увеличением числа множественно-унаследованных интерфейсов уменьшается число запросов интерфейсов в Java и C++.
Предположим, есть интерфейсы:
interface XBase1 {
void fun1();
};
interface XBase2 {
void fun2();
};
interface XBoth { // наследует оба интерфейса: XBase1 и XBase2
interface XBase1;
interface XBase2;
};
interface XFactory {
XBoth getBoth();
}; |
Используя интерфейс, полученный при помощи XFactory.getBoth(), поддерживает методы fun1() и fun2() одновременно, нет надобности создавать ссылки на интерфейсы XBase1 и XBase2.
Использование свойств
Объект предлагает клиенту свойства при помощи интерфейсов, которые позволяют работать со свойствами. Основной интерфейс, используемый для этого – com.sun.star.beans.XPropertySet. Есть и другие интерфейсы, такие, как com.sun.star.beans.XMultiPropertySet, работающий с несколькими свойствами путем одиночного вызова метода. XPropertySet поддерживается всегда, если у сервиса есть свойства.
Два метода XPropertySet осуществляют доступ к свойству. Определение методов в Java таково:
void setPropertyValue(String propertyName, Object propertyValue)
Object getPropertyValue(String propertyName) |
Пример: работа с электронной таблицей
В примере FirstLoadComponent интерфейс XPropertySet используется для установки свойства CellStyle объекта ячейки Объект ячейки – экземпляр com.sun.star.sheet.SheetCell, и поэтому поддерживает сервис com.sun.star.table.CellProperties, который имеет свойство CellStyle.
//запрос к интерфейсу XPropertySet объекта ячейки
XPropertySet xCellProps = (XPropertySet)UnoRuntime.queryInterface(XPropertySet.class, xCell);
//установка значения свойства CellStyle
xCellProps.setPropertyValue("CellStyle", "Result"); |
В примере FirstLoadComponent мы делаем запрос удаленному менеджеру сервисов для получения удаленного объекта Desktop и используем метод loadComponentFromURL() для создания нового документа-электронной таблицы. В этом документе мы получаем контейнер листов таблицы и осуществляем доступ к новому листу по имени. В новом листе мы вводим значения в ячейки A1 и A2, суммируя их в ячейке A3. Ячейка-результат получает стиль Result – подчеркнутый полужирный курсив. Наконец, этот лист делается активным – чтобы с ним мог работать пользователь.
Общие типы
До сих пор литеральные и общие типы для параметров методов и возвращаемых значений использовались нами так, будто бы OpenOffice.org API написан специально для Java. Однако следует помнить, что UNO задуман как независимый язык и имеет собственный набор типов, которые следует как-то соотносить с типами используемого языка.
Основные типы
Основные типы UNO обычно выступают типами членов структур, параметров и возвращаемых значений методов. Вот сводная таблица основных типов OpenOffice.org UNO:
Строки
UNO считает строки простыми типами, но в некоторых языках они обрабатываются особым образом.
Java обрабатывает строки как объекты java.lang.String.
В C++ символьные строки должны быть преобразованы в UNO-строки Unicode с помощью функций преобразования, обычно – функции createFromAscii() в объект класса rtl::OUString:
//C++
static OUString createFromAscii(const sal_Char * value) throw(); |
OpenOffice.org Basic работает со строками без дополнительных преобразований.
Перечисления и группы констант
В OpenOffice.org API используются типы перечисления (enum) и группы констант. Перечисления используются для создания списков допустимых в данном контексте именованных значений. Группы констант определяют допустимые значения для свойств, параметров, возвращаемых значений и членов структур.
Например, перечисление com.sun.star.table.CellVertJustify описывает допустимые значения вертикального выравнивания содержимого ячейки таблицы. За вертикальное выравнивание ячейки таблицы отвечает свойство com.sun.star.table.CellProperties.VertJustify. Допустимые значения этого свойства, согласно перечислению CellVertJustify – STANDARD, TOP, CENTER и BOTTOM.
//выравнивание содержимого ячейки по верхней границе ячейки.
//
//сервис com.sun.star.table.Cell включает в себя сервис com.sun.star.table.CellProperties
//и имеет свойство VertJustify, управляющее вертикальным выравниванием.
//для установки свойства используем интерфейс XPropertySet
xCellProps.setPropertyValue("VertJustify", com.sun.star.table.CellVertJustify.TOP); |
OpenOffice.org Basic может использовать перечисления и группы констант напрямую:
oCellProps.VertJustify = com.sun.star.table.CellVertJustify.TOP
|
В C++ эти типы используются так:
rCellProps->setPropertyValue(OUString::createFromAscii( "VertJustify" ),
::com::sun::star::table::CellVertJustify.TOP); |
Структуры
Структуры в OpenOffice.org API используются для создания составных типов из уже существующих. Структуры OpenOffice.org соответствуют структурам C++ или Java-классам, содержащим только публичные члены.
Структуры не инкапсулируют данные, они упрощаю передачу данных "целиком", вместо выстраивания ряда запросов get/set. Это дает преимущества, к примеру, при связи с удаленными компонентами.
Доступ к членам структур производится оператором-точкой ".", например:
aProperty.Name = "ReadOnly"; |
В Java, C++ и Basic при создании экземпляра структуры используется слово new. Применяя автоматизацию OLE (ActiveX), для получения экземпляра структуры следует использовать com.sun.star.reflection.CoreReflection. Для создания структур не следует применять менеджер сервисов. Например:
//В Java:
com.sun.star.beans.PropertyValue aProperty = new com.sun.star.beans.PropertyValue(); |
'В Basic
Dim aProperty As New com.sun.star.beans.PropertyValue
|
Тип any
В OpenOffice.org API часто используется тип any, соответствующий типу Variant, широко известному благодаря некоторым средам разработки...
Тип any может содержать один (произвольный) тип UNO. Особенно часто тип any используется в родовых интерфейсах UNO.
Тип any входит в структуру com.sun.star.beans.PropertyValue:
Эта структура имеет два члена: Name и Value, которые в паре описывают свойство по имени и значению. Для работы с такими структурами следует назначать тип any – и быть в состоянии интерпретировать полученный тип any.
В Java тип any соответствует java.lang.Object, однако есть и специальный класс com.sun.star.uno.Any, используемый тогда, когда применение обычного Object было бы неоднозначно. Когда предполагается передача значения типа any, всегда передается java.lang.Object или объект UNO.
Например, если используется метод setPropertyValue() для установки значения неинтерфейсного типа, следует передавать java.lang.Object. Если значение имеет примитивный тип Java, используется соответствующий ему объектный тип:
xCellProps.setPropertyValue("CharWeight", new Double(200.0));
//...
// Или так:
//...
com.sun.star.beans.PropertyValue aProperty = new com.sun.star.beans.PropertyValue();
aProperty.Name = "ReadOnly";
aProperty.Value = Boolean.TRUE; |
При получении значения типа any следует использовать com.sun.star.uno.AnyConverter.
Например, если нужно получить значение свойства, которое имеет примитивный тип Java, следует иметь в виду, что метод getPropertyValue() вернет java.lang.Object, содержащий примитивный тип, "обернутый" в тип any.
com.sun.star.uno.AnyConverter служит преобразователем для таких объектов.
Вот список его функций:
static java.lang.Object toArray(java.lang.Object object)
static boolean toBoolean(java.lang.Object object)
static byte toByte(java.lang.Object object)
static char toChar(java.lang.Object object)
static double toDouble(java.lang.Object object)
static float toFloat(java.lang.Object object)
static int toInt(java.lang.Object object)
static long toLong(java.lang.Object object)
static java.lang.Object toObject(Class clazz, java.lang.Object object)
static java.lang.Object toObject(Type type, java.lang.Object object)
static short toShort(java.lang.Object object)
static java.lang.String toString(java.lang.Object object)
static Type toType(java.lang.Object object)
static int toUnsignedInt(java.lang.Object object)
static long toUnsignedLong(java.lang.Object object)
static short toUnsignedShort(java.lang.Object object) |
При этом использовать его следует явно, например:
import com.sun.star.uno.AnyConverter;
long cellColor = AnyConverter.toLong(xCellProps.getPropertyValue("CharColor")); |
Для интерфейсных типов можно сразу использовать UnoRuntime.queryInterface без предварительного вызова AnyConverter.getObject():
import com.sun.star.uno.AnyConverter;
import com.sun.star.uno.UnoRuntime;
Object ranges = xSpreadsheet.getPropertyValue("NamedRanges");
XNamedRanges ranges1 = (XNamedRanges) UnoRuntime.queryInterface(
XNamedRanges.class, AnyConverter.toObject(XNamedRanges.class, r));
XNamedRanges ranges2 = (XNamedRanges) UnoRuntime.queryInterface(
XNamedRanges.class, r); |
В OpenOffice.org Basic тип any соответствует типу Variant:
'OpenOffice.org Basic
Dim cellColor As Variant
cellColor = oCellProps.CharColor
|
В C++ используем специальный оператор:
//C++ имеет специальные операторы <<= и >>= для типа Any (угловые скобки всегда располагаются слева)
sal_Int32 cellColor;
Any any;
any = rCellProps->getPropertyValue(OUString::createFromAscii( "CharColor" ));
// извлекаем значение из any
any >>= cellColor; |
Последовательность
Последовательность – однородный набор значений одного и того же типа UNO, который может содержать различное число элементов. В других распространенных языках подобные сущности называются массивами (имеются в виду одномерные массивы-векторы). Хотя подобные наборы иногда реализуются как объекты с методами доступа, принятыми в UNO (например, при помощи интерфейса com.sun.star.container.XEnumeration), они также выступают как последовательности, которые могут использоваться в целях повышения удаленной производительности. Последовательности изображаются с угловыми скобками.
//последовательность строк
sequence< string > aStringSequence; |
В Java последовательности обрабатываются как массивы (не следует использовать null для пустых последовательностей, надо создавать массив при помощи слова new и определять ему нулевую длину). Следует также помнить, что при создании массива объектов Java будет создан массив ссылок, а не экземпляров объектов. Поэтому после создания массива надо будет создавать каждый объект в отдельности и присваивать его члену массива (в цикле).
Пустая последовательность структур PropertyValue чаще всего применяется с loadComponentFromURL.
// создаем пустой массив структур PropertyValue для loadComponentFromURL
PropertyValue[] emptyProps = new PropertyValue[0]; |
Последовательность структур PropertyValue нужна для использования загрузки параметров с loadComponentFromURL(). Допустимые значения параметров для loadComponentFromURL() и для интерфейса com.sun.star.frame.XStorage можно найти в сервисе
com.sun.star.document.MediaDescriptor.
//создаем массив с одной структурой PropertyValue для loadComponentFromURL, он содержит ссылку
PropertyValue[] loadProps = new PropertyValue[1];
//создаем экземпляр структуры PropertyValue и инициализируем поля
PropertyValue asTemplate = new PropertyValue();
asTemplate.Name = "AsTemplate";
asTemplate.Value = Boolean.TRUE;
//присваиваем полученную структуру PropertyValue первому элементу массива
loadProps[0] = asTemplate;
//загружаем файл Calc как шаблон
XComponent xSpreadsheetComponent = xComponentLoader.loadComponentFromURL
(
"file:///X:/share/samples/english/spreadsheets/OfficeSharingAssoc.sxc",
"_blank", 0, loadProps
); |
В OpenOffice.org Basic пустой массив создается просто:
Dim loadProps() 'пустой массив
|
Последовательность структур создается с использованием new:
Dim loadProps(0) As New com.sun.star.beans.PropertyValue 'одна структура PropertyValue
|
В C++ есть шаблон класса для последовательностей. Пустая последовательность создается без указания числа элементов:
Sequence< ::com::sun::star::beans::PropertyValue > loadProperties; //пустая последовательность
Если передается число элементов, создается массив элементов нужной длины:
Sequence< ::com::sun::star::beans::PropertyValue > loadProps( 1 );
//структура создается с конструктором по умолчанию
loadProps[0].Name = OUString::createFromAscii( "AsTemplate" );
loadProps[0].Handle <<= true;
Reference< XComponent > rComponent = rComponentLoader->loadComponentFromURL
(
OUString::createFromAscii("private:factory/swriter"),
OUString::createFromAscii("_blank"),
0,
loadProps
); |
Доступ к элементу набора
Наборы объектов должны быть обеспечены методами доступа к элементам. Есть три основных метода доступа: посредством интерфейсов com.sun.star.container.XNameContainer, com.sun.star.container.XIndexContainer и com.sun.star.container.XEnumeration.
Три интерфейса доступа к элементу – пример того, как малоразмерные интерфейсы OpenOffice.org API позволяют создать связный дизайн объекта.
Все три интерфейса наследуют от XElementAccess, то есть включают методы
type getElementType()
boolean hasElements() |
для получения основной информации о наборе элементов. Метод hasElements() определяет, содержит ли набор элементы вообще, и какого типа. В Java и C++ можно получить информацию о типе UNO при помощи com.sun.star.uno.Type.
Интерфейсы com.sun.star.container.XIndexContainer и com.sun.star.container.XNameContainer имеют сходный дизайн.
Интерфейсы XIndexAccess/XNameAccess предназначены для получения элемента по индексу и по имени соответственно, XIndexReplace/XNameReplace позволяют заменять существующие элементы без изменения числа элементов в наборе, и, наконец, XIndexContainer/XNameContainer предоставляют возможность вставлять и удалять элементы, изменяя их число в наборе.
Некоторые именованные или индексированные наборы не полностью поддерживают эту иерархию наследования XIndex(Name)Container – потому что возможности подклассов не всегда применимы для элементов набора.
Например, интерфейс XEnumerationAccess работает по-другому, нежели именованные и индексированные контейнеры-наследники XElementAcces. Интерфейс XEnumerationAccess не позволяет работу с единичными элементами наподобие XIndex(Name)Access, но создает перечисление объектов, имеющее методы для перехода на следующий элемент набора – если указатель находится не на последнем элементе (то есть для перебора элементов набора). Вот схема:
Наборы объектов могут поддерживать доступ к элементам по имени, индексу или доступ по типу перечисления – в различных сочетаниях. В каждом конкретном случае следует обращаться к справке API.
Например, метод getSheets() интерфейса com.sun.star.sheet.XSpreadsheetDocument определен так, чтобы возвращать интерфейс com.sun.star.sheet.XSpreadsheets, унаследованный от XNameContainer. В справке API можно узнать также, что реализующий эти интерфейсы объект также поддерживает сервис com.sun.star.sheet.Spreadsheets, определяющий дополнительные интерфейсы доступа к элементам, помимо XSpreadsheets.
Ниже приведены примеры работы с XNameAccess, XIndexAccess и XEnumerationAccess.
Доступ по имени
Основной интерфейс, предоставляющий доступ к элементу по имени – com.sun.star.container.XNameAccess. Он имеет три метода:
any getByName( [in] string name)
sequence< string > getElementNames()
boolean hasByName( [in] string name) |
В примере FirstLoadComponent метод getSheets() возвращает интерфейс com.sun.star.sheet.XSpreadsheets, унаследованный от XNameAccess. Поэтому возможно использовать метод getByName() для получения объекта листа от контейнера XSpreadsheets.
XSpreadsheets xSpreadsheets = xSpreadsheetDocument.getSheets();
Object sheet = xSpreadsheets.getByName("Новый лист");
XSpreadsheet xSpreadsheet = (XSpreadsheet)UnoRuntime.queryInterface(
XSpreadsheet.class, sheet);
// используется интерфейс XSpreadsheet для получения ячейки A1 в позиции 0,0 и ввода значения 42
XCell xCell = xSpreadsheet.getCellByPosition(0, 0); |
Так как getByName() возвращает тип any, следует использовать AnyConverter.toObject() и(или) UnoRuntime.queryInterface() для возможности обращения к методам объекта листа.
Доступ по индексу
Интерфейс, осуществляющий доступ к элементу набора по индексу – com.sun.star.container.XIndexAccess. Он имеет два метода:
any getByIndex( [in] long index)
long getCount() |
Пример FirstLoadComponent демонстрирует применение этого интерфейса. Из справки API можно узнать, что сервис, возвращаемый методом getSheets() – com.sun.star.sheet.Spreadsheet, – поддерживает не только интерфейс com.sun.star.sheet.XSpreadsheets, но и XIndexAccess.
Поэтому становится возможным обращение к листам не только по имени, но и по индексу:
XIndexAccess xSheetIndexAccess = (XIndexAccess)UnoRuntime.queryInterface(
XIndexAccess.class, xSpreadsheets);
Object sheet = XSheetIndexAccess.getByIndex(0); |
Доступ к перечислению
Интерфейс com.sun.star.container.XEnumerationAccess создает перечисления, позволяющие перебор набора объектов. У него один метод:
com.sun.star.container.XEnumeration createEnumeration()
Интерфейс com.sun.star.container.XEnumerationAccess поддерживает интерфейс com.sun.star.container.XEnumeration. С его помощью можно осуществлять перебор перечисления.
XEnumeration имеет методы:
boolean hasMoreElements()
any nextElement() |
используемые для построения циклов типа:
while (xCells.hasMoreElements())
{
Object cell = xCells.nextElement();
//что-то делаем с объектом cell
} |
Например, так можно определить, какие ячейки таблицы содержат формулы. Результирующий набор объектов cell рассматривается как XEnumerationAccess.
Интерфейс, запрашивающий ячейки с формулами – com.sun.star.sheet.XCellRangesQuery, имеющий, в частности, метод
XSheetCellRanges queryContentCells(short cellFlags)
определяющий тип содержимого ячейки в соответствии с группой констант com.sun.star.sheet.CellFlags. Один из этих флагов – FORMULA.
Метод queryContentCells() возвращает объект, поддерживающий интерфейс com.sun.star.sheet.XSheetCellRanges, имеющий три метода:
XEnumerationAccess getCells()
String getRangeAddressesAsString()
sequence< com.sun.star.table.CellRangeAddress > getRangeAddresses() |
Метод getCells() может использоваться для перечисления всех ячеек с формулами и содержащиеся в них формулы.
XCellRangesQuery xCellQuery =
(XCellRangesQuery)UnoRuntime.queryInterface(
XCellRangesQuery.class, sheet);
XSheetCellRanges xFormulaCells = xCellQuery.queryContentCells(
(short)com.sun.star.sheet.CellFlags.FORMULA);
XEnumerationAccess xFormulas = xFormulaCells.getCells();
XEnumeration xFormulaEnum = xFormulas.createEnumeration();
while (xFormulaEnum.hasMoreElements())
{
Object formulaCell = xFormulaEnum.nextElement();
// do something with formulaCell
xCell = (XCell)UnoRuntime.queryInterface(XCell.class, formulaCell);
XCellAddressable xCellAddress = (XCellAddressable)UnoRuntime.queryInterface(
XCellAddressable.class, xCell);
System.out.println("Ячейке в строке " + xCellAddress.getCellAddress().Column +
", ряду " + xCellAddress.getCellAddress().Row +
" содержит формулу " + xCell.getFormula());
} |
|