ЦИФРОВАЯ БИБЛИОТЕКА GREENSTONE РУКОВОДСТВОChapter 3 Работа системы GreenstoneContentsЭта глава описывает работу системы Greenstone, чтобы вы могли понять, увеличить и расширить ее возможности. Программное обеспечение написано на С ++ и предоставляет большие возможности использования виртуального наследия. Если вы незнакомы с этим языком, вы должны изучить его перед тем, как приступить к работе. Дейтель и Дейтель (1994) предоставили всестороннюю обучающую программу, а Страуструп (1997) - лаконичный справочник. Мы начинаем рассказ о философии дизайна после описания работы системы, так как он будет напрямую зависеть от этого описания. Теперь приступим к деталям, из которых состоит основная часть главы. Версия Greenstone, описанная здесь - это CGI-версия (Web Library if for Windows users/Web -библиотека для пользователей Windows). Windows Local Library использует ту же самую исходную программу, но имеет встроенный web-сервер. Плюс ко всему, Local Library - постоянно продолжающийся процесс. 3.1 Структура процесса
Figure 20
Краткий обзор работы системы Greenstone
На рисунке 20 показано, как несколько пользователей (они представлены в виде компьютерных терминалов наверху диаграммы) обращаяются к трем коллекциям Greenstone. Перед тем, как предстать в режиме on-line, эти коллекции проходят процессы импорта и формирования, которые были описаны в предыдущих главах. Сначала документы, показанные внизу рисунка, импортированы в XML-совместимый Формат Архива Greenstone. Теперь для файлов созданы различные доступные для поиска индексы и информационная база данных коллекции, которая включает иерархические структуры поддержки просмотра. Конечная коллекция готова к работе в режиме on-line и способна предоставлять информацию по запросам. Два центральных компонента обеспечивают работу системы: "receptionists" (регистраторы) и "collection servers" (серверы коллекции). С точки зрения пользователя, регистраторы - точка контакта с цифровой библиотекой. Они принимают запрос пользователя в форме клавиатурного ввода и щелчков мыши, анализируют их, а затем посылают запрос соответствующему серверу (или серверам) коллекции. Здесь определяется местонахождение требуемой части информации и возвращается регистратору для предоставления пользователю. Серверы коллекции действуют как абстрактный механизм, который обрабатывает содержание коллекции, в то время как регистраторы ответственны за интерфейс пользователя.
Figure 21
Система Greenstone, использующая нулевой протокол
Как показано на рисунке 20, регистраторы связываются с серверами коллекции, используя определенный протокол. Выполнение этого протокола зависит от конфигурации компьютера, на котором работает система цифровых библиотек. Наиболее простой и подходящий выбор - это один регистратор и один сервер коллекции, которые оба установлены на одном компьютере. Вы получаете именно такую модификацию, когда устанавливаете систему Greenstone с параметрами по умолчанию. В этом случае оба процесса объединены в один запускаемый файл (называемый library), и следовательно, использование протокола сводится к созданияю функциональных запросов. Мы называем это null protocol (нулевым протоколом). Он формирует основу для стандарта out-of-the-box (из блока) цифровой системы библиотек Greenstone. Ее упрощенная конфигурация представлена на рисунке 21 с регистратором, протоколом и сервером совокупности, связанными вместе в один объект программы библиотеки. Цель этой главы состоит в том, чтобы объяснить вам, как все это работает. Обычно "сервер" - постоянный процесс: единожды запущенный, работает постоянно, отвечая на любые входящие запросы. Несмотря на название, сервер коллекции в конфигурации нулевого протокола не является сервером в привычном смысле слова. Фактически, каждый раз, когда вызывают любую web-страницу Greenstone, запускается программа library (CGI механизмом), которая отвечает на запрос и затем выгружается. Это мы называем "сервером", потому что дополнительно он также предназначен для работы с более общей конфигурацией (см. рисунок 20). Удивительно, но цикл запуска, обработки и выхода работает не так медленно, как можно было ожидать. Однако, это абсолютно неэффективно. Существует механизм Fast-CGI (www.fastcgi.com), который обеспечивает средний уровень. Используя его, программа library может остаться в памяти в конце первого выполнения и накапливать последующие наборы параметров CGI, таким образом избегая повторных инициализаций и достигая почти такого же уровня работы, как сервер. Fast-CGI в Greenstone используется как опция, допуская повторную компиляцию исходного текста с соответствующими библиотеками. Как альтернатива нулевому протоколу, протокол Greenstone был также создан с использованием известной схемы CORBA (Slama et al, 1999). Он использует объединенную объектно-ориентированную парадигму, чтобы разрешить различным процессам, выполняющимся на различных компьютерных платформах и созданных нп различных языках программирования, обращаться к одному и тому же набору распределенных объектов в Internet (или любой другой сети). Тогда сценарии, подобно изображенному на рисунке 20, могут быть полностью осуществлены со всеми регистраторами и серверами коллекции, работающими на различных компьютерах.
Figure 22
Графический интерфейс запроса Greenstone
Это позволяет устанавливать для работы с теми же самыми цифровыми библиотечными коллекциями намного более сложные интерфейсы. В качестве примера рассмотрим один рисунок (см. рисунок 22), который показывает графический интерфейс запроса, основанный на диаграммах Венна, позволяющий пользователям непосредственно управлять Булевыми запросами. Написанный на Java, интерфейс запускается локально на собственном компьютере пользователя. Используя CORBA, он обращается к отдаленному серверу коллекции Greenstone, написанному на C++. Распределенный протокол все еще совершенствуется и готовится для использования, так что в этом руководстве мы более не станем его обсуждать (для получения дополнительной информации см. Bainbridge et al., 2001). 3.2 Концептуальная структура
Figure 23
Генерация страницы "about this collection"
Рисунок 23 показывает страницу "about this collection" (о коллекции) специфической коллекции Greenstone (коллекция Проекта Gutenberg). Смотрите URL наверху. Страница сгенерирована в результате выполнения CGI-программы library, которая, как говорилось выше,является выполняемой программой, включающей и регистратор и сервер коллекции, связанный в соответствии с нулевым протоколом. Аргументы library - c=gberg, a=p, и p=about. Они могут интерпретироваться следующим образом:
Для коллекции Проект Gutenberg (c=gberg) действие генерирует страницу (а=р), сгенрированная страница названа "about" (p=about).
Figure 24
Работа системы Greenstone
Рисунок 24 иллюстрирует основные части работы системы Greenstone. Наверху регистратор сначала инициализирует свои компоненты, затем анализирует CGI-аргументы, чтобы решить, какое действие запустить. При запуске действия (которое включает в дальнейшем обработку CGI аргументов), программное обеспечение использует протокол, чтобы обратиться к содержанию коллекции. Ответ используется для генерации web-страницы с помощью компонента формата и макроязыка. Макроязык, который мы виделив Разделе 2.4, используется, чтобы обеспечить цифровую библиотечную систему Greenstone лаконичным стилем и создавать интерфейсы на различных языках. Взаимодействие с библиотекой генерирует скелет web-страниц; макрос в GSDLHOME/macros наращивает на этот скелет плоть. Объект Macro Language (макроязык) на рисунке 24 ответственен за чтение файлов и сохранение результата анализа этих файлов в памяти. Любое действие может использовать этот объект, чтобы развернуть макрокоманду. Оно может даже создать новые макросы и отменить существующие, добавляя динамическое распределение в использовании макросов. Внешний вид страницы "about this collection" (рисунок 23) известен до момента запуска и закодирован в макрофайле about.dm. Заголовки, нижние колонтитулы и фоновая заливка даже не упомянуты, потому что они расположены в пакете макрокоманды Global. Однако, определенный "about" текст (текст для страницы "О коллекции") для специфической коллекции не известен заранее, он хранится в информационной базе данных коллекции в течение процесса формирования. Эта информация вызывается использованием протокола и хранится как _collectionextra_ в пакете макрокоманды Global. Обратите внимание на то, что имя макроса - по существу имя, используемое для описания этой информации в файле конфигурации коллекции, описанном в Разделе 1.5. Чтобы сгенерировать содержание страницы, была расширена макрокоманда _content_ в пакете about (рисунок 19) . Она в свою очередь запускает _textabout _, который непосредственно обращается к _collectionextra _, только что динамически помещенной туда. Следующий важный компонент - объект Format. Операторы задания формата в файле конфигурации коллекции затрагивают представление специфических частей информации, как описано в Разделе 2.3. Они обработаны объектом Format (см. рисунок 24). Основная задача этого объекта состоит в том, чтобы анализировать и оценивать инструкции типа строк формата (рисунок 16). Как стало ясно из Раздела 2.3, они могут включать ссылки на метаданные в квадратных скобках (например, [Title]), которые должены быть найдены сервером коллекции. Взаимодействие происходит между объектом Format и объектом Macro Language, потому что операторы задания формата могут включать макросы, которые в качестве расширения включают метаданные, которые в качестве расширения включают макросы и так далее. Внизу рисунка 24, сервер коллекции также проходит процесс инициализации, устанавливая объекты Filter и Source, чтобы ответить на входящие запросы протокола, и объект Search, чтобы облегчить выполнение задачи. В конечном счете они обращаются к индексам и информационной базе данных коллекции, которые сформировались в процессе формирования коллекции. Игнорируя пустые строки, регистратор содержит 15 000 строк программы. Сервер коллекции содержит только 5 000 строк (75 % которого занимают файлы заголовка). Сервер коллекции более компактен, потому что релевантный поиск проходит через две предоткомпиляционные программы. Для поиска используется полнотекстовая поисковая система MG, а для поддержки информационной базы даннх коллекции используется СУБД GDBM. Для обеспечения расширяемости и гибкости Greenstone широко использует порядок следования в пределах Action, Filter, Source и Search. Для простой цифровой библиотеки, специализированной на текстовой коллекции, это означает, что вы должны узнать немного больше, чтобы программировать систему. Однако, это также означает, что MG и GDBM могут быть легко заменены, если возникнет потребность. Кроме того, программная архитектура достаточно богата, чтобы поддержать полные возможности мультимедиа, типа управления интерфейсом через устройство речевого ввода или передачи запросов в виде графическх изображений. 3.3 Совместимость концептуальной структурыРазделы 3.6 и 3.8 объясняют взаимодействие сервера коллекции и регистратора более подробно, останавливаясь на каждом модуле рисунка 24 и описывая, как это работает. Полезно сначала разобраться на примере пользователя, взаимодействующего с Greenstone, и описывать то, что происходит. Предполагается, что к настоящему моменту все объекты правильно инициализированы. Инициализация - запутанная процедура, к которой мы вернемся в Разделе 3.9. Поиск
Figure 25
Поиск по запросу Darcy в коллекции Gutenberg
Когда пользователь вводит запрос, нажимая кнопку Begin search на странице поиска, запускается новое действие Greenstone, которое заканчиваясь, генерирует новую HTML-страницу, используя макроязык. На рисунке 25 показан результат поиска в коллекции Project Gutenberg для поискового выражения Darcy. В пределах первоначальной HTML -страницы скрыта инструкция поиска a=q. Когда кнопка поиска нажата, эта инструкция активизируется и устанавливает новое действие - queryaction. Запуск queryaction направляет запрос к объекту Filter/Фильтр (c=gberg), определяемый для коллекции через протокол. Фильтры - важная основная функция серверов коллекции. Приспособленные для действий поиска и просмотра, они обеспечивают способы выбора подмножества информации из коллекции. В этом случае, queryaction устанавливает запрос фильтра:
Запросы к протоколу синхронны. Регистратор эффективно блокируется, пока запрос фильтра не будет обработан сервером коллекции и все сгенерированные данные будут отображены. Когда запрос протокола типа QueryFilter сделан, объект Filter (см. рисунок 24) декодирует варианты и запрашивает объект Search, который использует MG для фактического поиска. Роль объекта Search состоит в том, что он должен обеспечить абстрактный интерфейс программы, который поддерживает поиск, независимо от основного используемого средства поиска. Формат, используемый для того, чтобы возвращать результаты, также предписывает абстракцию, требуя от объекта Search транслировать данные, сгенерированные средством поиска в стандартную форму. Как только результаты поиска возвращены регистратору, дальнейшим действием становится форматирование результатов для отображения, используя объект Format и Макроязык. Как показано на рисунке 25, он выглядит следующим образом: стандартный заголовок Greenstone, нижний колонтитул, навигационная область и фон; повторение основной части страницы запроса только ниже навигационной области; отображение книжного значка, заголовка и автора для каждого найденного документа соответственно. Формат последней части управляется инструкцией format SearchVList в файле конфигурации коллекции. Прежде чем заголовок и метаданные автора будут отображены, они должны быть найдены сервером коллекции. Тут требуется запрос к протоколу, на сей раз используя BrowseFilter. Retrieving a documentПосле вышеупомянутого запроса для Darcy, рассмотрим отображаемый документ. Рисунок 26 показывает результат щелчка по иконке около The Golf Course Mystery на рисунке 25.
Figure 26
The Golf Course Mystery
Исходный текст для коллекции Gutenberg включает один длинный файл книги. Во время компоновки, этот файл разбит на отдельные страницы, каждая по 200 строк или около этого, и необходимая информация для каждой страницы сохранена в индексах и информационной базе данных коллекции. В верхней части рисунка 26 указано, что эта книга содержит 104 создаваемых компьютером страницы, и ниже - начало страницы один: кто ввел информацию, заголовок, автор, начало оглавления (это часть табличной формы исходного текста Gutenberg, она не была сгенерирована Greenstone). В верхней левой части находятся кнопки, которые управляют внешним видом документа: только одна страница или целый документ; подсветка термина запроса вкл. или выкл.; действительно ли книга должна быть отображена в ее собственном окне; отделение поисковой части от окна просмотра. В верхней правой части - навигационная помощь, которая обеспечивает прямой доступ к любой странице в книге: просто напечатайте номер страницы и нажмите кнопку "go to page" (перейти к странице). Дополнительно можно использовать для перехода по страницам стрелки next/previous pages (следующая/предыдущая страница). Действие поиска документов documentaction определено установкой a=d и имеет несколько дополнительных параметров. Наиболее важный - дпоиск документа: это определено через переменную d. На рисунке 26 это установлено как d=HASH51e598821ed6cbbdf0942b. 1 найти первую страницу документа с идентификатором HASH51e598821ed6cbbdf0942b, зная более дружественый термин The Golf Course Mystery. Остальные переменные определяют: вкл. или выкл. подсветку термина запроса (hi), отобразить информацию о том, какая страница книги отображена (gt). Эти переменные используются для поддержки действий, предлагаемых кнопками на странице (см. рисунок 26), описанной выше. Значения по умолчанию используются, если любая из этих переменных опущена. Действие следует за процедурой queryaction: оценка CGI -аргумента, доступ к серверу коллекции, используя протокол и результат для генерации web-страницы. Варианты, касающиеся документа, декодированы CGI-аргументом и сохранены в объекте для дальнейшей работы. Чтобы отыскать документ на сервере коллекции, необходим только идентификатор документа, чтобы установить запрос протокола к get_document (). Как только текст найден, должно быть произведено значительное форматирование. Для этого программа доступа к documentaction сохраняет аргумент и использует объект Format и Макроязык. Просмотр иерархического классификатораНа рисунке 27 показан пример просмотра, где пользователь выбрал Titles А-Z (Заголовки A-Z) и обратился к гиперсвязи для символа К. Это происходит благодаря действию documentaction, которое задается CGI -аргументом a=d. Однако, до подключения переменной d стоит оговориться, что в данном случае она не была использована. Вместо этого использовался узел в пределах доступной для просмотра иерархии классификации, чтобы отобразить определенную переменную cl. В данном примере она представляет заголовки, сгруппированные символом К. Этот список был сформирован во время компановки и сохранен в информационной базе данных коллекции.
Figure 27
Просмотр заголовков коллекции Gutenberg
Записи, которые представляют узлы классификатора в базе данных, используют префикс CL, сопровождаемый числами, отделенными периодами (.), определяющими их нахождение в пределах вложенной структуры. Игнорируя кнопку поиска (левый край в навигационной области), классификаторы пронумерованы последовательно в порядке возрастания, слева направо, начиная с 1. Таким образом, узел классификатора верхнего уровня для заголовков в нашем примере - CL1, а разыскиваемая страница сгенерирована установкой cl=CLl.ll. Вы можете увидеть это в строке URL наверху рисунка 27. Для обработки запроса документа cl используется объект Filter, который отыскивает узел по протоколу. возвращенных данных, дальнейшие запросы протокола сделаны для того, чтобы отыскать метаданные документа. В данном случае найдены заголовки книг. Однако, если узел был внутренний, дочерние записи которого - самостоятельные узлы, то были бы найдены заголовки дочерних вершин. С точки зрения программирования, это тоже самое и обработано тем же самым механизмом. Наконец, вся найденная информация связана с использованием макроязыка и отображена на web-странице, показанной на рисунке 27. Generating the home page
Figure 28
Генерация домашней страницы
В качестве последнего примера мы рассмотрим создание домашней страницы Greenstone. Рисунок 28 демонстрирует домашнюю страницу Greenstone, установленную со значениями по умолчанию. Ее URL, который вы можете видеть наверху экрана, включает аргументы а=р np=home. Таким образом, подобно странице "about this collection" (об этой коллекции), она была сгенерированара^еасй'ои (а=р), но на сей раз применялось home (p=home). Поэтому макроязык обращается к содержанию home.dm. В данном случае нет никакой набодности определять коллекцию (с переменной с). Цель главной страница показать, какие коллекции доступны. Щелчок по иконке приведёт пользователя на страницу “о коллекции” для этой коллекции. Меню создаётся динамически каждый раз, как только страница загружена, и базировано на коллекциях, находящихся в файлах в данное время . Каждая новая коллекция автоматически отображается на главной странице, как только страница перезагружается (если предусмотрено, что коллекция “публичная”) Для этого регистратор использует протокол (конечно). Как часть оценки CGI аргументов, pageaction запрограммирована для обнаружения особого случая когда p=home. Тогда это действие пользуется протокольным запросом get_collection_list(), чтобы установить текущий набор коллекций. Он вызывает get_collectinfo() для получения информации о каждом из них. Эта информация включает: доступность коллекции публично, URL иконки коллекции (если есть какая-нибудь), и полное название коллекции. Эта информация используется, чтобы сгенерировать правильный вход в коллекцию с главной страницы. 3.4 Исходный код
Table 17
Независимые программы, включённые в Гринстоун
Исходный код для работающей системы находится в GSDLHOME/src. Он занимает две поддиректории, recpt для регистрационного сервера и colservr для коллекционного сервера. Гринстоун работает во всех версиях Windows вплоть до Windows 3.1, и к несчастью это налагает восьмизнаковый лимит на файл и название директории. Это объясняет почему используются такие загодачные сокращение, как recpt и colservr. Остальные директории включают отдельностоящие утилиты, в основном для порддержки процесса построения. Они перечислены в Таблице17. Другая директория, GSDLHOME/lib, включает подуровневые объекты, которые используются обоими, регистрационным и коллекционным, серверами . Этот код описан в Секции 3.5. Greenstone широко использует Standard Template Library (STL)/ Стандартную библиотеку шаблонов С ++,- созданную Silicon Graphics (www.sgi.com), которая является результатом многих лет разработки и развития. Подобно всем библиотекам программирования, она требует некоторого времени на изучение. Приложение А дает краткий обзор ключевых частей, которые используются всюду в программе Greenstone. Для более полного ознакомления обращайтесь к официальному STL справочному руководству, доступному на сайте www.sgi.com, или к одному из многих учебников STL, например Josuttis (1999). 3.5 Общие типы GreenstoneОбъекты, определенные в GSDLHOME/lib, являются объектами Greenstone нижнего уровня, основываясь на вершине STL, которые входят в исходную программу. Сначала мы детально рассмотрим бъект text_t, который используется для представления текста в формате Unicode. После чего мы сможем кратко изложить цель каждого файла библиотеки. Объект text_tGreenstone работает с многими языками и для поддержки коллекции, и пользовательского интерфейса. Для этого исходная программа повсеместно использует Unicode. Основным объектом, который понимает строку Unicode является text_t.
Figure 29
text_t API (в сокращены)
Unicode использует два байта для хранения каждого символа. На рисунке 29 показаны главные особенности text_t Application Program Interface/ Интерфейс прикладной программы (API). Он позволяет выполнить двухбайтовое требование, используя С ++ встроенный тип short, который определен как двухбайтовое целое число. Центральный тип данных объекта text_t - это динамический массив, сформированный short, используя STL описание vector<unsigned short> и полученное сокращенное название usvector. Функции конструктора (строки 10-12) явно поддерживают три формы инициализации: конструкция без параметров, которая генерирует пустую строку Unicode; конструкция с целочисленным параметром, которая генерирует версию текста Unicode числового обеспеченного значения; конструкция с параметром char*, который обрабатывает аргумент как строку С ++ с нулевым символом в конце и генерирует версию Unicode. После этого, большинство элементов (строки 17-28), переходят на обслуживание STL векторного контейнера: beginQ, endQ,push_back(), emptyQ и т.д. Здесь имеется поддержка очистки и добавления строки, а также преобразования целочисленного значения в строку текста Unicode, и возвращения соответствующего целочисленного значения текста, который представляет номер.
Figure 30
Перегруженные операторы text_t
Существует множество перегруженных операторов , которые не показаны на рисунке 29. Особо значимые операторы представлены на рисунке 30. Строка 4 поддерживает назначение одного объекта text_t другому, а строка 5 перегружает оператор+ = , чтобы обеспечить более естественный способ добавления одного объекта text_t в конец другого. Также возможно через строку 6 обратиться к специфическому символу Unicode (представленному как short), используя знак массива []. Далее необходимо назначить и добавить в конец операторы, предназначенные для строк C++ и целых чисел. Строки 12-18 представляют Булевы операторы для того, чтобы сравнить два объекта text_t: равно, не равно, предшествует в алфавитном порядке и так далее. Функция-член, которая берет аргумент const вместо не-const, также имеется (но на рисунке не показана). Такое повторение стандартно для объектов C++, делая API более весомым, но не более концептуальным. В действительности, многие из этих функций представлены как отдельные действующие инструкции. Для более подробного изучения обратитесь к исходному файлу GSDLHOME/lib/text_t.h. Программа библиотеки GreenstoneФайлы заголовка в GSDLHOME/lib включают смесь функций и объектов, что обеспечивает поддержку работы системы Greenstone. Для большей эффективности отношения, функции и функции-члены объявлены как inline. Для большей части подробности выполнения содержатся в копии файла заголовка .срр.
3.6 Collection serverТеперь детально объясним все объекты в концептуальной структуре рисунка 24. Мы начнем с низа диаграммы, который является также основой системы, и с Search (Поиск), Source (Источник), РШег(Филыр) и продолжим наш путь через уровень протокола к центральным компонентам в регистраторе: Actions (Действия), Format (Формат) и Macro Language (Макроязык). Затем мы сосредоточимся на объектной инициализации, так как это будет проще понять, если уже известна роль различных объектов. Большинство классов, центральных в концептуальной структуре, выражено с использованием виртуального наследования, чтобы помочь расширяемости. При виртуальном наследовании унаследованные объекты можно передать по кругу, как их базовый класс, но когда вызвана функция-член, ее версия определена в призванном унаследованном объекте. Гарантируя, что исходная программа Greenstone использует базовый класс повсюду, кроме точки объекта конструкции, это означает, что различное выполнение - использование возможно на основе радикально различных технологий - может быть легко размещено на месте. Например, предположим, что базовый класс по имени BaseCalc обеспечивает основную арифметику: сложение, вычитание, умножение и деление. Если все его функции объявлены виртуальными, а аргументы и возвратные типы все объявлены как строки, мы можем легко осуществить наследование версии объекта. Один из них, названный FixedPrecisionCalc, мог бы использовать библиотечные функции С, чтобы совершать конвертацию между строками и целыми числами и обратно, осуществляя вычисления, используя стандартные арифметические операторы: +, -, *, и /. Другой, названный InfinitePrecisionCalc, мог бы обращаться к строковым аргументам и символам одновременно, осуществляя арифметические операции с бесконечной точностью. Написанная главная программа использует BaseCalc повсюду, параметры ее работы могут регулироваться переключением между установленной точностью и бесконечной точностью, редактируя только одну строку: пункт, где создан объект - калькулятор. Объект Search
Figure 31
Search базовый класс API
Рисунок 31 демонстрирует листинг базового классф API для объекта Search (см. рисунок 24). Это определяет две виртуальные функции-члены: search() и docTargetDocument(). Из рисунка видно, что =0, который следует за описанием аргумента, это функция риге, означающая, что класс, который наследует объект, должен осуществить обе функции (иначе компилятор выдаст сообщение). Класс также включает два защищенных поля данных: collectdir и cache. Объект Search иллюстрируется примерами для специфической коллекции, а поле collectdir используется для хранения системы файлов (и что еще более важно - их файлы индекса) в месте нахождения коллекции. Поле сасйесохраняет результат запроса. Это используется, чтобы ускорить обработку последующих запросов, которые будут дублировать этот запрос (и его параметры). В то время, как идентичные вопросы могут казаться маловероятными, фактически они происходят регулярно. Протокол Greenstone является простым. Чтобы сгенерировать страницу результатов, подобную изображенной на рисунке 25, но для документов с 11 по 20 того же самого запроса, поиск роизводится снова, на сей раз оговорив заранее, возврат документов 11-20. Кэширование делает это эффективным, тот факт, что поиск был уже выполнен, обнаруженные результаты могут быть извлечены прямо из кэша. Оба поля данных применимы к каждому унаследованному объекту, который осуществляет механизм поиска. Это - то, почему они появляются в азовом классе, и объявлены защищенной секцией класса так, чтобы унаследованные классы могли получить доступ к ним непосредственно. Поиск с MGGreenstone использует MG (сокращенно от Managing Gigabytes, CM.Witten et al., 1999) для индексации и восстановления документов, исходная программа включена в директорию GSDLHOME/packages. MG использует методы сжатия, чтобы максимально использовать место на диске, не ставя под угрозу скорость выполнения. Для коллекции документов на английском языке сжатый текст и полный текст индексируются вместе и оычно занимают одну треть места, занимаемого несжатым текстом. Поиск и исправление часто происходят быстрее, чем подобное действие на несжатой версии, потому что производится меньше дисковых операций.
Figure 32
API для прямого доступа к MG (сокращенный вариант)
MG обычно используется в интерактивном режиме, печатая команды в командной строке. Один из способов активации mgsearchclass заключается в использовании библиотеки С systemQ, вызываемой с объектом, запуском соответствующей команды MG. Более эффективный подход, однако, состоит в том, чтобы открыть программу MG непосредственно, используя функцию вызова. Однако, для этого требуется более глубокое знание программы MG. Большая часть трудностей может быть скрыта за новым API , который становится точкой соприкосновения для объекта mgsearchclass. Это - роль colserver/mgq. с, чей API показан на рисунке 32. Способ передачи параметров к MG - через mgq_ask (), который берет варианты текста в формате, идентичном используемому в командной строке: mgq_ask( ".set casefold off ");
Это также используется при вызове запроса. К результатам обращаются через mgq_results, который использует указатель на функцию, как ее четвертый параметр. Это обеспечивает гибкий способ преобразования информации, возвращенной в структуру данных MG по требованию mgsearchclass. Вызов mgqjiumdocs О, mgqjiumterms () и mgq_docsretrieved () также возвращает информацию, но на сей раз с более жесткими условиями. Последние два оказывают поддержку при восстановлении. Обьект Source
Figure 33
Source базовый класс API
Роль Source (см. рисунок 24) - доступ к метаданным и тексту документа. Его базовый класс представлен на рисунке 33. Функция-член наносит на карту к каждой задаче: getjnetadata () и get_document () соответственно. Оба объявлены virtual, так что версию, обеспечивающуюся выполнением базового класса, вызывают во время работы. Одна унаследованная версия этого объекта использует GDBM, чтобы запустить get_metadata 0 и MG, чтобы запустить get_document (): мы детализируем эту версию ниже. Другая функция-член представлена на рисунке 33 - configure^, init () и translate_OID Q. Первые два касаются процесса инициализации, описанного в Разделе 3.9. Оставшаяся, translate_OID (), работает с синтаксисом для того, чтобы выразить идентификаторы документа. На рисунке 26 мы видели, как номер страницы мог быть добавлен в конец к идентификатору документа, чтобы вызвать только эту страницу. Это было возможно, потому что страницы были идентифицированы как "разделы" при формировании коллекции. Добавление в конец " . 1" к OID вызывает первый раздел соответствующего документа. Разделы могут быть вложенными и доступны для обращения благодаря определенному номеру, разделенному периодами. Так же, как иерархические номера разделов, синтаксис идентификатора документа поддерживает форму относительного доступа. Для текущего раздела документа возможно получить доступ к first child, добавляя в конец /с, к last child, добавляя в конец .1с, к parent, добавляя в конец .рг, к next sibling, добавляя в конец .ns и ^previous sibling, добавляя в конец .ps. Функция translate_OID () использует параметры OIDin и OIDout, чтобы хранить исходный вариант и результат преобразования. Требуется два дополнительных параметра - err и logout. Они сообщают любой статус ошибки, который может возникнуть во время перевода, и решают, когда и куда послать регистрационную информацию. Параметры являются близкими союзниками протокола, мы к этому вернемся в Разделе 3.7. Исправление баз данных с gdbm Программа GDBM - менеджер базы данных GNU (www.gnu.org). Она осуществляет простую структуру записей пар ключ/данные и совместима с DBM и NDBM. Операции включают хранение, исправление и удаление записей по ключу, а также пресечение всех незаказанных ключей.
Figure 34
GDBM база данных для коллекции Gutenberg (фрагмент)
Рисунок 34 представляет фрагмент информационной базы данных коллекции, которая создана при формировании коллекции Gutenberg. Фрагмент был создан, используя утилиту db2txt системы Greenstone, которая конвертирует GDBM двойной формат базы данных в текстовую форму. Рисунок 34 содержит три записи, отделенные горизонтальными линиями. Первый - вход документа, другие два - часть иерархии, созданной классификатором AZList для заголовков коллекции. Первая строка каждой записи - ее ключ. Запись документа хранит заглавие книги, автора и любые другие метаданные, созданные (или извлеченные) во время формирования коллекции. Запись такжебывает для внутреннего пользования и хранит информацию о том, где находятся файлы, связанные с этим документом (<archivedir>), и номер документа, используемый внутри MG (<docnum>). Поле <contains> хранит список элементов, отделенных точками с запятой. Это точки, связывающие записи в базе данных. Для записи документа <contains> используется, чтобы указать на вложенные разделы. Последующие ключи записи сформированы, связывая текущий ключ с одним из дочерних элементов (отделенных периодом). Вторая запись на рисунке 34 - главный узел для иерархии классификации Titles A—Z. Его дети доступны через поле <contains>, включая CL 1.1, CL1.2, CL1.3 и так далее, соответствующие индивидуальным страницам для символов А, В, С и т.д. Имеется только 24 ребенка: классификатор AZList слил Q-R и Y-Z вхождения, потому что они охватили только несколько заглавий. Дети в поле <contains> третьей записи, CL 1.1,- это конечные документы. Возможны и более сложные структуры - поле <contains> может включать смесь документов в качестве CL узлов. Ключи, выраженные относительно текущего, отличают от абсолютных ключей, потому что они начинаются с кавычек ("). Использование MG и GDBM для реализации объекта Source
Figure 35
API для sourceclass базовой версии MG и GDBM (сокращенный вариант)
Объект, который соединяет MG и GDBM, чтобы реализовать выполнение sourceclass -это mggdbmsourceclass. Рисунок 35 демонстрирует его API. Две новых функции-члена set_gdbmptr () и set_mgsearchptr () хранят указатели на соответствующие им объекты, так, чтобы при выполнении getjnetadata О и get_document () иметь доступ к соответствующим инструментальным средствам для завершения работы. Объект Filter
Figure 36
API for the Filter base class
API базового класса для объекта Filter (см. рисунок 24) показан на Рисунке 36. Он начинается с защищенных полей данных gsdlhome, collection и collectdir. Они обычно происходят в классах, которые должны обратиться к определенным коллекцией файлам.
mggdbsourceclass - другой класс, который включает эти три поля данных. Функции-члены conflgure() и init() (ранее упоминавшиеся в sourceclass) используются процессом инициализации. Сам объект находится недалеко от соответствующей части фильтра протокола; в особенности getjilteroptions О и fllter() соответствуют один другому.
Figure 37
Как хранится опция filter
К центральным опциям фильтра относятся два класса, показанные на рисунке 37. Сохраненные внутри FilterOption_t - это пате опции, ее type и действительно ли это - repeatable. Интерпретация validValues зависит от типа опции. Для Булева типа первым значением переменой является^г/5е,а вторым - true. Для целочисленного типа первое значение - минимальный номер, второй - максимальный. Для перечисляемого типа все значения перечислены. Для строкового типа значение игнорируется. Для более простых ситуаций используется OptionValue_t, в качестве записи как text_t name опции и ее value. Запрос и ответ объекта проходят как параметры juuLJilterclass. Созданью из них два класса используют ассоциативные массивы, чтобы сохранить набор типов опций, требуемых для InfoFilterOptionsResponseJ. Для детального исследования вопроса смотрите GSDLHOME/src/recpt/comtypes.h. Объекты наследования Filter
Figure 38
Иерархия наследования Filter
Два уровня наследования используются для фильтров, как показано на рисунке 38. Сначала было сделано различие между фильтрами Query и Browse, а затем первому из них определено выполнение, основанное на MG. Для корректной работы mgqueryfilterclass требуется доступ к MG через mgsearchclass и к GDBM через gdbmclass. browsefllterclass нуждается в доступе только к GDBM. Указатели на эти объекты сохранены как защищенные поля данных в пределах соответствующих классов. Программа сервера коллекцииРассмотрим файлы заголовка в GSDLHOME/SRC/COLSERVR с описанием каждого из них. Имя файла вообще повторяет имя объекта, определенное в его пределах.
3.7 Протокол
Table 18
Список запросов протокола
Таблица 18 представляет список запросов функции к протоколу и резюме для каждого вхождения. Примеры в Разделе 3.3 охватили большинство из них. Функции, не упомянутые предварительно - has_collection (), ping (), get_protocol_name () и get_filteroptions (). Первые две обеспечивают ответы да\нет на вопросы "коллекция существует на этом сервере? " и "это выполняется? ". Цель двух других состоит в том, чтобы поддержать множественные протоколы в пределах архитектуры, которая распределена по различным компьютерам, а не только нулевой протокол, базирующийся на отдельно выполняемой программе, описанной здесь. Одна из них различает, какой протокол используется. Другая позволяет регистратору опрашивать сервер коллекции, чтобы найти, какие варианты поддерживаются, и непосредственно динамически выбирать конфигурации, чтобы использовать все преимущества услуг, предлагаемых специфическим сервером.
Figure 39
Нулевой протокол API (сокращенный вариант)
Рисунок 39 показывает API для нулевого протокола. Комментарии и некоторые подробности низкого уровня были опущены (см. исходный файл recpt/nullproto.h для получения подробностей). Этот протокол наследовался от базового класса recptproto. Виртуальное наследование используется так, чтобы больше, чем один тип протокола, даже не задуманного, все же мог легко поддерживаться остальной частью исходной программы. За исключением get_protocol_name (), который не использует параметры и возвращает имя протокола как строку текста Unicode, все функции протокола включают параметр ошибки и выходной поток, как последние два аргумента. Параметр ошибки делает запись любых ошибок, которые происходят в ходе выполнения запроса протокола, а выходной поток используется для регистрации цели. Функции относятся к типу void - они не возвращают информацию как их заключительная инструкция, но вместо этого возвращают данные через определяемые параметры типа уже введенного параметра ошибки. В некоторых языках программирования такие подпрограммы были бы определены как процедуры, а не функции, но C++ не делает никакого синтаксического различия. Большинство функций использует имя коллекции как параметр. Три функции-члены, get_filteroptions (), filterQ, и get_document (), следуют за обеспечением параметра Request и получением результатов в параметре Response. 3.8 РегистраторЗаключительный уровень концептуальной модели - регистратор. Как только CGI-аргументы будут проанализированы, основная деятельность запускает на выполнение Action, поддерживаемую объектами Format и Macro Language. Они описаны ниже. Хотя они представлены как объекты в концептуальной структуре, объекты Format и Macro Language - не совсем являются объектами с точки зрения C++. В действительности, Format - коллекция структур данных с набором функций, которые оперируют ими, а объект Macro Language сформирован вокруг display class, определен в lib/display.h, с потоковой поддержкой конвертации от lib/gsdlunicode.h. Действия
Table 19
Actions в системе Greenstone
Greenstone поддерживает одиннадцать действий, сведенных в Таблице 19.
Figure 40
Использование cgiargsinfoclass из pageaction.cpp
CGI-аргументы являются необходимыми действиями формально объявленными в функции конструктора, использующими cgiarginfo (определенный в recpt/cgiargs.h). Рисунок 40 показывает выборку из pageaction функции конструктора, которая определяет размер и свойства CGI-аргументов а и р. Для каждого CGI-аргумента конструктор должен определить его краткое имя (строки 2 и 10), которое непосредственно является именем CGI-переменной; полное имя (строки 3 и 11), которое используется, чтобы обеспечить более значимое описание действия; представляет ли это единственное или множественное символьное значение (строки 4 и 12); возможное значение по умолчанию (строки 5 и 13); что случается, когда задано больше чем одно значение по умолчанию (строки 6 и 14) (так как значения по умолчанию могут также быть установлены в файлах конфигурации); действительно ли значение сохраняется в конце этого действия (строки 7 и 15). Так как это встроено в программу, web-страницы, которые детализируют информацию, могут быть сгенерированы автоматически. Statusaction производит эту информацию. Вы можете увидеть это, введя URL страницы администрирования Greenstone. Двенадцать унаследованных действий созданы в main(), функция верхнего уровня для выполняемой программы library, определение которой дается в recpt/librarymain.cpp, там же, где был создан объект регистратора (определенный в recpt/receptionist.cpp). ответственность за все действия передают регистратору, который обрабатывает их, обслуживая как поле данных ассоциативного массива базового класса Action, индексированного названием действия.
Figure 41
Action базовый класс API
Рисунок 41 показывает API для базового класса Action. При выполнении действия, receptionist (регистратор) вызывает несколько функций, начиная с check_cgiargs (). Большинство помогают проверить, установить, и определить значения и макросы; в то время как dojzction () фактически генерирует страницу вывода. В частности, унаследованный объект не имеет никакого определения для специфической функции-члена, это проходит через определение базового класса, которое осуществляет заданное по умолчанию поведение. Объяснения функций-членов следующие.
В начале определения класса argsinfo - это защищенное поле данных (используемое в выборке программы, см.рисунок 40), которое хранит информацию CGI-аргумента, указанную в унаследованной функции конструктора Action. Другое поле данных, gsdlhome, создает запись GSDLHOME для удобного доступа[1]. Объект также включает conflgure() и init () с целью инициализации. Форматирование
Figure 42
Основные структуры данных в Format
Хотя на рисунке 24 форматирование представлено как отдельный объект, в действительности оно составляет коллекцию структур данных и функций. Они собраны вместе под файлом заголовка recpt/formattools.h. Основные структуры данных показаны на рисунке 42.
Figure 43
Структуры данных, сформированные для выборки оператора /ormutf
Этот процесс лучше всего объяснить на примере. Когда оператор задания формата format CL1Vlist
"[link][Title]{If}{[Creator], by [Creator]}[/link]} " читается из файла конфигурации коллекции, он анализируется функциями в formattools.cpp и формирует связанные структуры данных, показанные на рисунке 43. Значение для gsdlhome исходит из gsdlsite.cfg, расположенного в том же самом каталоге, что и CGI-выполняемая library, тогда как GSDLHOME устанавливается запуском скрипта setup, который обращается к другому файлу, так как технически это возможно для двух различных значений. Хотя это и возможно, но все же не желательно, и все вышесказанное написано в предположении, что файлы те же самые. Когда оператор задания формата должен быть оценен действием, структура данных пересекается. Маршрут, предпринятый в comIf и comOr узлах, зависит от метаданных, которые возвращают запрос протоколу. Одно осложнение состоит в том, что, когда метаданные найдены, они могут включать последующие макросы и формат синтаксиса. При необходимости это обрабатывается переключеним назад и вперед между синтаксическим анализом и оценкой, как необходимо. МакроязыкMacro Language, представленный на рисунке 24, так же, как Format не является функцией одного класса C++. В этом случае - это основной класс, но выполнение макроязыка также вызывает функции поддержки и классы. И снова в объяснении используем пример. Сначала мы даем некоторые типовые макроопределения, которые иллюстрируют макростаршинство, затем, при помощи диаграммы, мы описываем основные структуры данных, сформированные, чтобы поддержать эту деятельность. Наконец, мы представляем и описываем открытые функции-члены displayclass, макрообъекта верхнего уровня.
Figure 44
Иллюстрация макро старшинства
В типичной инсталляции Greenstone, макростаршинство обычно: с (для коллекции) имеет приоритет над v (для графического или текстового интерфейса), который имеет приоритет над / (для языка). Это достигается строкой macroprecedence c,v,l
в основном файле конфигурации main.cfg. Макроинструкции на рисунке 44 определяют типовой макрос для _header_ в пакете запроса для различных параметров настройки с, v и /. Если CGI -аргумент задан, когда вызванное действие включает c=dls, v=l и 1=еп, макрокоманда Jieader _ [v=l] была бы выбрана для отображения. Она была бы выбрана раньше of _content _ [1=еп], потому что v имеет более высокий приоритет, чем /. А макрокоманда _content_[l=fr, v=l, c=dls] не была бы выбрана вообще, потому что параметр / для страницы задан совсем другой.
Figure 45
Структуры данных, представляющие макрос, заданный по умолчанию
Рисунок 45 показывает основную структуру данных, сформированную при чтении макрофайлов, указанных в etc/main.cfg. По существу, это -ассоциативный массив ассоциативных массивов ассоциативных массивов. Высший уровень (показанный слева) - это индексы, которые упаковывают макрокоманду, второй уровень индексирует макроимя. Конечный уровень индексирует любые параметры, которые были определены, сохраняя каждый как тип mvalue, который создает запись, наряду с макрозначением, файла, из которого оно исходило. Например, текст, определенный для Jieader _ [1=еп] на рисунке 44 можно увидеть сохраненным ниже двух записей mvalue на рисунке 45.
Figure 46
Displayclass API (сокращенный вариант)
Центральный объект, который поддерживает макроязык - displayclass, определенный в lib/display, h. Его открытые функции-члены показаны на рисунке 46. Класс читает указанные макрофайлы, используя loaddefaultmacros (), сохраняя в защищенном разделе класса (не показан) тип структуры данных, показанной на рисунке 45. Для макроса также допустимо быть установленным системой поддержки выполнения, используя setmacro () (в последнем примере Раздела 3.3, pageaction устанавливает _homeextra_ как динамически сгенерированную таблицу доступных коллекций, используя эту функцию). Это поддерживается набором ассоциативных массивов, подобных используемым для представления макрофайлов (они не идентичны, потому что в первом случае не требуется уровень "параметра"). В displayclass чтение макроса из файла упоминается как default macros. Локальный макрос, указанный через setmacro (), упоминается как current macros и удаляется из памяти, как только страница была сгенерирована. Когда страница должна быть воспроизведена, сначала вызывается openpage О, чтобы сообщить текущие параметры настройки параметров страницы (1=еп и так далее). Следом, текст и макрос потоково проходят через класс, обычно с actionclass, используя строку программы: cout << text_t2ascii << display << "_amacro_ "
<< "_anothermacro_ ";
Результат - макрос, расширенный согласно настройкам параметров страницы. Если требуется, эти параметры настройки могут быть отчасти изменены, используя setpageparams(). Остающиеся открытые функции-члены обеспечивают поддержку более низкого уровня. Программа регистратораОсновные объекты регистратора были описаны. Ниже мы детализируем классы поддержки, которые постоянно находятся в GSDLHOME/SRC/RECPT. Кроме того, там где главным является эффективность, когда определения встроены, подробности выполнения содержатся в пределах копии файла заголовка .срр. Файлы поддержки часто включают слово tool как часть имени файла, как в OIDtools.h nformattools.h. Второй набор лексически определенных файлов включает префикс z3950. Файлы обеспечивают удаленный доступ к интерактивным базам данных и каталогам, которые делают их содержимое общедоступным, используя протокол Z39.50 . Другая большая группа файлов поддержки включает термин browserclass. Это файлы, связанные через виртуальную иерархию наследования. Как группа, они поддерживают абстрактное понятие просмотра: последовательная генерация страницы документа, разделенного содержанием или метаданными. Просматривающие действия включают просмотр документов, упорядоченных в алфавитном порядке в соответствии с заголовком или хронологически по времени; развитие через заголовки, возвращенные запросом, - десять входов одновременно; доступ к отдельным страницам книги, используя механизм "go to page" (перейти к странице) . Каждое действие просмотра наследовано от базового класса browserclass:
Действия обращаются к объектам browserclass через browsetools.h.
3.9 ИнициализацияИнициализация в Greenstone - сложная операция, которая обрабатывает файлы конфигурации и присваивает полям данных значения по умолчанию . В дополнение к наследованию и функциям конструктора, основные объекты определяют функции init () и conflgure(), чтобы помочь стандартизовать задачу. Даже в этом случае порядок выполнения может быть затруднен. Настоящий раздел описывает как все это происходит. Greenstone использует несколько файлов конфигурации для различных целей, но все они используют один и тот же синтаксис. Если строка не начинается с хэш-символа (#) или состоит полностью из пустого пространства, первое слово определяет ключевое слово, а остающиеся слова представляют установку частности для того ключевого слова. Строки файлов конфигурации передают, по одному, для conflgure() два параметра: ключевое слово и массив остающихся слов. Основанная на ключевом слове, специфическая версия выбора configure() решает, представляет ли информация интерес, и если это так то сохраняет ее. Например, collectserver (который показан на рисунке 24), обрабатывает операторы задания формата в файле конфигурации коллекции. Когда формат ключевого слова передают configure(), оператор if запускает сохраненную в объекте копию второго аргумента функции. После обработки ключевого слова и прежде, чем функция закончит выполнение, некоторые версии conflgure() передают данные функции configure() в других объектах. Объект Receptionist (регистратор) вызывает выбор configureQ для Actions , Protocols и Browsers. Объект NullProtocol вызывает conflgure() для каждого объекта Collection; Collection вызывает Filters и Sources. В C++ поля данных обычно инициализируются функцией конструктора объекта. Однако, в Greenstone некоторая инициализация зависит от чтения значений из файлов конфигурации, так что необходим второй раунд инициализации. Это - цель init () функции-члена, в некоторых случаях она ведет к последующему вызову conflgure().
Figure 47
Инициализация Greenstone, с использованием, нуль-протокола
На рисуноке 47 показаны диагностические инструкции, сгенерированные Greenstone, увеличенные так, чтобы выделить процесс инициализации. Программа начинается с функции main() в recpt/librarymain.cpp. Она создает объект Receptionist и объект NullProtocol, затем просматривает gsdlsite.cfg (расположенный в том же самом каталоге, что и выполняемая программа library) для gsdlhome и сохраняет его значение в переменной. Далее mainQ добавляет объект NullProtocol к Receptionist (Регистратору), который сохраняет массив протоколов базового класса в защищенном поле данных, а затем устанавливает несколько конвертеров. main() создает все Actions и Browsers, используемые в выполняемой программе, и добавляет их к Регистратору. В завершение функция вызывает cgiwrapper () в cgiwrapper.cpp, который непосредственно включает существенную объектную инициализацию. Существуют три этапа cgiwrapper(): конфигурация, инициализация и генерация страницы. Сначала производятся аппаратные запросы conflgure(). Затем читается gsdlsite.cfg и вызывается configure() для каждой строки. То же самое производится для etc/main.cfg. Второй этап cgiwrapper () делает запросы к init (). Регистратор делает только один запрос к своей функции init (), но действием вызова запускает функции init () в различных объектах, сохраненных в его пределах. Сначала производится аппаратный запрос conflgure(), чтобы установить collectdir, после чего читаются макрофайлы. Для каждого действия вызывают его init () функцию. То же самое происходит для каждого протокола, сохраненного в регистраторе, но в описываемой системе сохранен только один протокол - NullProtocol. Запрос init () для этого объекта вызывает дальнейшую конфигурацию: для каждой коллекции в NullProtocol читаются и обрабатываются определенные коллекцией build.cfg и collect.cfg, с запросом configure() для каждой строки. На заключительном этапе cgiwrapper () должен проанализировать CGI -аргументы и затем вызвать соответствующее действие. Оба этих запроса производятся при поддержке объекта Receptionist. Причина для разделения конфигурации, инициализации и программы генерации страницы состоит в том, что Greenstone оптимизирован для работы в качестве сервера (используя Fast-cgi, или протокол Corba, или Windows Local Library). В этом режиме работы конфигурация и программа инициализации запускаются однажды, программа остается в памяти и генерирует множество web-страниц в ответ на запросы от клиентов, не требуя переустановки. [1] начение для gsdlhome исходит из gsdlsite.cfg, расположенного в том же самом каталоге, что и CGI-выполняемая library, тогда как GSDLHOME устанавливается запуском скрипта setup, который обращается к другому файлу, так как технически это возможно для двух различных значений. Хотя это и возможно, но все же не желательно, и все вышесказанное написано в предположении, что файлы те же самые. |
Copyright © 2002 2003 2004 2005 2006 2007 by the New Zealand Digital Library Project at the University of Waikato, New Zealand.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled “GNU Free Documentation License.”