* Преобразование базы данных в XML-формат электронной библиотеки.

Для решения задачи преобразования базы данных "Местообитания Восточной
Фенноскандии" из формата Microsoft Access в XML-формат электронной
библиотеки КарНЦ РАН была разработана программная система DbReader.

Программа DbReader является универсальным средством для представления
информации из баз данных в текстовых форматах. Программа обрабатывает
заданные шаблоны текстовых файлов и заменяет найденные в них
инструкции на результаты их выполнения. Различные инструкции позволяют
формировать SQL-запросы, выполнять их и обрабатывать полученные
данные.


* Формат инструкций:

    <%<список_функций>{:|\}<данные>%>
  где 

<список_функций> - список имен функций, разделенных пробелами.
    Может быть пустым.
<данные> - данные, передаваемые функции.


* Формат данных:

    [<список_аргументов>][<текст>]
  где

<список_аргументов> - список аргументов функции, разделенных пробелами.
    Может быть пустым.
<текст> - текст, передаваемый функции без разбиения на аргументы.
    Может быть пустым. Количество аргументов, после которого следует
    текст, зависит от функции.

Разделитель между списком функций и данными определяет, как должны
обрабатываться данные функции:
: - рекурсивная обработка,
\ - передать без обработки.

Если в списке функций задано две и более функции, они выполняются,
начиная с последней, так что каждая функция получает в качестве данных
результат выполнения следующей функции.

Каждая функция имеет код возврата - целое число. Код возврата
инструкции - код возврата первой в списке функции. Код возврата,
получаемый при обработке текста - сумма кодов возврата обработанных
инструкций.

Вместо скобок '<', '>' можно использовать скобки '[', ']'.


* Реализованные функции:

Функция:      set
Аргументы:    <имя>
Текст:        <значение>
Действие:     устанавливает значение переменной <имя>
Результат:    <имя>
Код возврата: 1

Функция:      get
Аргументы:    <имя>
Результат:    значение переменной <имя>. 
Код возврата: 1, если значение переменной было установлено, 0 - иначе.

Функция:      prepare
Аргументы:    <имя>
Текст:        <запрос>
Действие:     Подготавливает SQL-запрос <запрос> для выполнения,
              записывает подготовленный запрос в переменную <имя>.
Результат:    <имя>
Код возврата: 1

Функция:      query
Аргументы:    <имя_запроса> <шаблон> <парам1> ... <парамN>
Действие:     Выполняет запрос с параметрами, подготовленный с помощью
              функции prepare. Значения параметров подставляются в
              запрос вместо символа '?'. Значения полей ответа будут
              записаны, как переменные шаблона <шаблон>. При обработки
              шаблона <шаблон> также определяется переменная NUMBER,
              содержащая номер текущей строки ответа. Переменные,
              определенные в вызывающем шаблоне, доступны из шаблона
              <шаблон> по имени SUPER.<имя_переменной>.

Результат:    результат обработки шаблона <шаблон> для каждой строки
              ответа.
Код возврата: количество полученных строк ответа.

Функция:      optional
Текст:        <данные>
Результат:    <данные>, если при обработке данных был получен
              код возврата отличный от 0, иначе - пустая строка.
Код возврата: 1, если результат - пустая строка, 0 - иначе.

Функция:      image
Аргументы:    <исх_файл> <кон_файл> <формат> [<макс_ширина> [<макс_высота>]]
Действие:     Загружает изображение из файла <исх_файл> (путь определяется
              относительно конфигурационного параметра "resource_base") и
              преобразует его в указанный формат, сохраняя результат в
              <кон_файл>. Если заданы максимальная высота и ширина, большие
              изображения будут уменьшены. 
Результат:    <кон_файл> при успешном выполнении, пустая строка - иначе.
Код возврата: 1 при успешном выполнении, 0 - иначе.

Функция:      copy
Аргументы:    <исх_файл> <кон_файл>
Действие:     Копирует файл <исх_файл> в файл <кон_файл>
	      (путь <исх_файл> определяется относительно
              конфигурационного параметра "resource_base").
Результат:    <кон_файл> при успешном выполнении, пустая строка - иначе.
Код возврата: 1 при успешном выполнении, 0 - иначе.

Функция:      write
Аргументы:    <имя_файла>
Текст:        <данные>
Действие:     Записывает <данные> в файл <исх_файл>.
Результат:    <кон_файл> при успешном выполнении, пустая строка - иначе.
Код возврата: 1 при успешном выполнении, 0 - иначе.

Функция:      read
Текст:        <имя_файла>
Действие:     Читает файл <имя_файла>.
Результат:    прочитанные данные при успешном выполнении, пустая
              строка - иначе.
Код возврата: 1 при успешном выполнении, 0 - иначе.

Функция:      include
Текст:        <имя_файла>
Действие:     включает шаблон из файла <имя_файла>.
Результат:    результат обработки шаблона.
Код возврата: код возврата, полученный при обработке шаблона.

Функция:      !
Текст:        <данные>
Действие:     нет.
Результат:    нет.
Код возврата: код возврата, полученный при обработке текста данных.

Функция:      replace
Аргументы:    <стр1> <стр2>
Текст:        <данные>
Результат:    данные, с замененными вхождениями подстроки <стр1>
	      на <стр2>.
Код возврата: код возврата, полученный при обработке текста данных.

Функция:      xml_escape
Текст:        <данные>
Результат:    текст <данные>, в котором  символы
              '&', '<', '>', '`', '\' заменены на соответствующие
	      сущности XML.
Код возврата: код возврата, полученный при обработке текста данных.

Функция:      xml_cdata
Текст:        <данные>
Результат:    данные в виде блока XML CDATA.
Код возврата: код возврата, полученный при обработке текста данных.

Система DbReader расширяема: возможно добавление в систему новых
функций, реализованных в виде классов на языке Java.


* Применение системы DbReader для преобразования базы данных
  "Местообитания Восточной Фенноскандии" из формата Microsoft Access в
  XML-формат электронной библиотеки КарНЦ РАН

Для преобразования используются две таблицы базы данных "Местообитания
Восточной Фенноскандии" (перечислены только используемые поля):

biotopelist (code, rangcode, rusname, engname, descript, geobotdescr,
             contributors, descrdate)

  code - счетчик, ключевое поле;
  rangcode - поле в формате ABBCCDDEE, где А, BB, CC, DD, ЕЕ - коды
             класса, подклассов и растения;
  rusname, engname, descript, geobotdescr - текстовые поля;
  descrdate - дата.

photos (biotope, photo, authphoto)

  biotope - число, указывает на code в biotopelist;
  photo - имя файла фотографии;
  authphoto - текст.

Требуется сгенерировать xml-файл для каждой записи в biotopelist, файл
со списком классов, файл со списком растений, полноразмерные и
уменьшенные версии каждой фотографии.

Для генерации xml-файлов с описаниями биотопов использовался следующий
шаблон DbReader (файл biotope.template):

----------------------------------------------------------------------
<?xml version="1.0" encoding="ISO-8859-5"?>
<!DOCTYPE описание_вида SYSTEM "brief.dtd">
<!-- rangcode: <%xml_escape get\rangcode%> -->
<описание_вида show="0">
  <рисунки display="0">
    <%optional:<карта_распространения номер="1"
    file="<%xml_escape image:maps/<%get\rangcode%>.gif maps/<%get\code%>.png png%>"/>%>
<%query:photo_sql [%\<%include\photo.template%>%] <%get\code%>%>
  </рисунки>
  <название><%xml_escape get\rusname%></название>
  <класс><%xml_escape get\class0%></класс>
  <подкласс1><%xml_escape get\class1%></подкласс1>
  <подкласс2><%xml_escape get\class2%></подкласс2>
  <подкласс3><%xml_escape get\class3%></подкласс3>
  <краткая_характеристика rows="3"><%xml_cdata get\descript%></краткая_характеристика>
  <пример_описания rows="3"><%xml_cdata get\geobotdescr%></пример_описания>
  <авторы_описания><%xml_escape get\contributors%></авторы_описания>
</описание_вида>
----------------------------------------------------------------------

Соответствующий SQL-запрос, при выполнении которого устанавливаются
значения переменных шаблона rangcode, rusname, class0, class1, class2,
class3, descript, geobotdescr и contributors (файл biotope.sql):

----------------------------------------------------------------------
SELECT t1.*,

(SELECT t2.rusname FROM biotopelist AS t2
WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
AND Right(t2.rangcode, 8)  = '00000000'
AND Right(t1.rangcode, 8) <> '00000000') AS class0,

(SELECT t2.rusname FROM biotopelist AS t2
WHERE Left(t2.rangcode, 3) = Left(t1.rangcode, 3)
AND Right(t2.rangcode, 6)  = '000000'
AND Right(t1.rangcode, 6) <> '000000') AS class1,

(SELECT t2.rusname FROM biotopelist AS t2
WHERE Left(t2.rangcode, 5) = Left(t1.rangcode, 5)
AND Right(t2.rangcode, 4)  = '0000'
AND Right(t1.rangcode, 4) <> '0000') AS class2,

(SELECT t2.rusname FROM biotopelist AS t2
WHERE Left(t2.rangcode, 7) = Left(t1.rangcode, 7)
AND Right(t2.rangcode, 2)  = '00'
AND Right(t1.rangcode, 2) <> '00') AS class3

FROM biotopelist AS t1
WHERE rusname
----------------------------------------------------------------------

Шаблон biotope.template содержит инструкцию <%include\photo.template%>,
включающую шаблон описания фотографии (файл photo.template):

----------------------------------------------------------------------
    <фотография num="<%xml_escape get\NUMBER%>"
       <%optional:автор="<%xml_escape get\authphoto%>" %>text="" 
       file="<%xml_escape image:biotopephotos/<%get\photo%> images/<%get\SUPER.code%>_<%get\NUMBER%>.jpg jpg 300 300%>" 
        big="<%xml_escape copy:biotopephotos/<%get\photo%> images/big/<%get\SUPER.code%>_<%get\NUMBER%>.jpg%>"/>
----------------------------------------------------------------------

Соответствующий SQL-запрос для получения описания фотографий
(photo.sql):

----------------------------------------------------------------------
SELECT * from photos WHERE biotope = ?
----------------------------------------------------------------------

Шаблон, генерирующий список классов (файл classes.template):

----------------------------------------------------------------------
<?xml version="1.0" encoding="ISO-8859-5"?>
<классификация><%query:class_sql [%\
  <!-- rangcode: <%xml_escape get\rangcode%> -->
  <класс id="<%get\NUMBER%>"><%xml_escape get\rusname%></класс>%]%>
</классификация>
----------------------------------------------------------------------

Соответствующий SQL-запрос (class.sql):

----------------------------------------------------------------------
SELECT t1.*
FROM biotopelist AS t1
WHERE rusname AND Right(t1.rangcode, 8)  = '00000000'
  AND EXISTS
    (SELECT NULL FROM biotopelist AS t2
     WHERE Left(t2.rangcode, 1) = Left(t1.rangcode, 1)
     AND Right(t2.rangcode, 8) <> '00000000')
----------------------------------------------------------------------

Шаблон, генерирующий список растений (файл plants.template):

----------------------------------------------------------------------
<?xml version="1.0" encoding="ISO-8859-5"?>
<растения><%query:plant_sql [%\
  <!-- rangcode: <%xml_escape get\rangcode%> -->
  <растение><%xml_escape get\rusname%></растение>%]%>
</растения>
----------------------------------------------------------------------

Соответствующий SQL-запрос (plant.sql):

----------------------------------------------------------------------
SELECT t1.*
FROM biotopelist AS t1
WHERE rusname AND Right(t1.rangcode, 2) <> '00'
----------------------------------------------------------------------

Система DbReader начинает работу с обработки шаблона index.template,
который содержит вызовы перечисленных выше SQL-запросов и включения
шаблонов:

----------------------------------------------------------------------
<%!:
  <%prepare:biotope_sql <%read\biotope.sql%>%>
  <%prepare:photo_sql <%read\photo.sql%>%>
  <%prepare:class_sql <%read\class.sql%>%>
  <%prepare:plant_sql <%read\plant.sql%>%>

  <%query:biotope_sql [%\<%write:<%get\code%>.xml <%include\biotope.template%>%> %]%>

  <%write:classes.xml <%include\classes.template%>%>
  <%write:plants.xml <%include\plants.template%>%>
%>
----------------------------------------------------------------------

Пример сгенерированного XML-файла:

----------------------------------------------------------------------
<?xml version="1.0" encoding="ISO-8859-5"?>
<!DOCTYPE описание_вида SYSTEM "brief.dtd">
<!-- rangcode: E04020302 -->
<описание_вида show="0">
  <рисунки display="0">    
    <фотография num="1" автор="Крышень А.М." text="" 
                file="images/3_1.jpg" 
                big="images/big/3_1.jpg"/>
    <фотография num="2" автор="Крышень А.М." text="" 
                file="images/3_2.jpg" 
                big="images/big/3_2.jpg"/>
  </рисунки>
  <название>Betula sp. - Avenella flexuosa - Polytrichum commune</название>
  <класс>E. Суходольные местообитания</класс>
  <подкласс1>Автоморфные влажные</подкласс1>
  <подкласс2></подкласс2>
  <подкласс3></подкласс3>
  <краткая_характеристика rows="3"><![CDATA[Развиваются  на луговиковых вырубках лесов черничного типа.  Через 5 - 7 лет  после рубки порослевая береза выходит из  яруса травянистых растений, ее влияние сказывается, в первую очередь, на влажности почвы.]]></краткая_характеристика>
  <пример_описания rows="3"></пример_описания>
  <авторы_описания></авторы_описания>
</описание_вида>
----------------------------------------------------------------------


* Структура программы DbReader

Программа DbReader написана на языке программирования Java и состоит
из следующих основных классов:

ru.karrc.dbreader.DbReader: cодержит точку входа программы. Загружает
конфигурацию, устанавливает соединение с источником данных с помощью
библиотеки JDBC, запускает обработку основного шаблона.

ru.karrc.dbreader.TemplateParser: осуществляет разбор шаблона,
обработку инструкций и вызов функций.

ru.karrc.dbreader.Function: абстрактный класс. Реализации всех функций
DbReader наследуют этот класс.

ru.karrc.dbreader.FunctionDataParser: обработчик данных
функции. Посволяет получить список аргументов и текст, передаваемый
функции. При необходимости вызывает рекурсивную обработку данных с
помощью TemplateParser.

ru.karrc.dbreader.Functions: содержит реализации функций в виде
анонимных классов, наследующих Function.

ru.karrc.dbreader.TemplateParser: исключение, выбрасываемое в случае
возникновения ошибки при обработке шаблона.

Кроме того, реализовано несколько впомогательных классов, которые
используются в реализациях функций системы.


* Метрики реализованной программной системы.

Количество файлов исходного кода: 8,
Количество Java-классов (включая анонимные): 27,
Общее количество строк исходного текста: 1311,
Количество строк кода (исключая пустые строки и комментарии): 812,
Количество строк комментариев: 180.


* Заключение

Автором была разработана программная система DbReader, которая
является расширяемым и универсальным средством для представления
информации из баз данных в текстовых форматах. С помощью системы
DbReader база данных "Местообитания Восточной Фенноскандии" была
переведена в XML-формат электронной библиотеки КарНЦ РАН.