Wednesday, July 29, 2009

Добавление поддержки CSS в GtkHtml

В общем идея:
  • сейчас в gtkhtml - используется некое подобие SAX парсера, на основе которого сложно создать что-то подобное поддержке CSS. Конечно можно, так как все таки дерево элементов существует, но долго и есть другой вариант;
  • нужно заменить парсер на использование libxml;
  • заменить рендеринг на использующий XmlTree
  • использовать рендеринг в два прохода:
    • первичный рендеринг только html без css c созданием запросов на css
    • вторичный при доступности css применять его к дереву и вызывать перерисовку;
  • в теории мы получим немного более медленный рендеринг - запускающийся только после получения полного дерева, сейчас редеринг идет в параллель с парсингом.
Сейчас реализовал только замену парсера(мой fork на github) и тестовые приложения для проверки применения css. Для парсинга используется libcroco. За основу тестовых приложений взят код - позволяющий применить css к xml c использованием пространств имен.

Tuesday, July 28, 2009

Толстенный клиент

Пока не забыл, или помощь другим понять как все работает.

Описание архитектуры толстого клиента к разработке которого я был причастен. Данное произведение можно разбить на две части: интерфейс пользователя и и система связи с сервером и хранения разнообразных бизнес сущнотей. Все писалось и переписывалось несколько раз в связи с изменениями требований, и как следствие система немного не идеально выглядит, но вполне нормально работает (выполняет возложенную на нее роль). Описание содержит, то состояние архитектуры клиента, каким он был, когда я отвечал за его разработку, и может не соответствовать текущему состоянию.

Основные характеристики:
  • полностью написан на C#;
  • есть возможность конструировать и редактировать отображения внутри приложения;
  • протокол взаимодействия полностью основан на передача XML через HTTP;
  • может при запуске после регистрации полностью обновить справочники и загрузить новую версию самого себя и предложить переустановить;
  • есть возможность удаленного подключения к компоненту связи с сервером и просмотра рабочего стола пользователя(удаленное управления и debug), позволяет разобраться с проблемами клиента, если он в локальной сети, работает только с разрешения пользователя(пункт меню в трее);
  • полностью динамическая генерация визуальных форм на основе xml.

Подсистема отображения

  1. Общается с подсистемой связи с сервером и хранения словарей через протокол описанный в За и Против URI-based interface.
  2. общие принципы описаны здесь.

Система разбита не несколько частей:
  1. логика работы с динамическим интерфейсом, позволяет сконструировать компоненты на основе их описания в xml (сериализованного в объекты, но более простые чем полное описание компонента Windows Forms c некоторыми дополнительными свойствами);
  2. класс простейшей формы - Конструктор принимает ссылку на уже загруженные описания всех форм (словарь визуальных элементов) и ссылку на компонент связи с сервером (тот который имеет интерфейс на основе URI), содержит свойство текущая отображенная форма, позволяет узнать и изменить текущую отображенную форму. Также содержит внутренние виртуальные функции вызываемые при смене отображения, возврата делегата для события и получения списка для компонента с определенным тегом. Общий принцип работы этого компонента: при получении события о смене формы, получить описание новой формы, представляет собой список мета-компонент и их позиций с описанием расположения на форме. Все компоненты которые отсутствуют на новой форме делаются скрытыми. Мета-компоненты (содержат простые компоненты: кнопочки, поля для ввода) отсутствующие на форме создаются - через вызов виртуальной функции которая должна вернуть этот компонент (в базовом классе она ищет описание компонента в словаре визуальных элементов), в этой функции можно добавлять нестандартные компоненты, которые невозможно описать, как простейший элемент или реализовать привязку начальных обработчиков к элементам. При создании нового элемента ему привязывается обработчик полученный из виртуальной функции, которую должны все потомки перегрузить, чтобы обрабатывать специфическую для тега(идентификатора элемента) логику. (К тегу привязывает делегат вызываемый по событию смены содержимого элемента). Сбрасывается привязанный контент в элементе - вызывается виртуальная функция, которая возвращает список по умолчанию для элемента, который устанавливается (все элементы имеют свойство список по содержимого). Затем вызывается виртуальная функция для сообщения всем потомкам этого класса о том, что сменился вид отображения.
  3. Наследники предыдущего - реализуют вся логику реакции на смены отображения и содержимого. При смене содержимого элемента вызывается делегат в котором выполняется специфическая для данного элемента(тега) логика, например перегрузить содержимое элемента, так как связь с базой осуществляется через URI, то формируется запрос содержащий для простой логики тег изменившегося элемента и зависимые от него с значениями выбранными пользователем и передается на обработку которая возвращает список который сразу можно присвоить элементу. Для некоторых сложных элементов с циклическими зависимостями в классах есть булевские переменные состояния которые блокируют циклический вызов обработчиков. Основная сложность для простых элементов - для специфичного тега всегда одна логика(один обработчик) - и как следствие все одинаковые элементы всегда работают одинаково, и нужно помнить, что для специфичной логики лучше создавать новый тег. (Баги в этой логике - на новой форме с элементами у которых совпал тег с прежним значением(например: марка) будет вести себя по старой логике и сбрасывать значение модели - и можно долго искать причину сброса модели. В общем нужно четко следить за соответствием предполагаемой сущности и тегом элемента к которому привязываешься. И в логике обработке события быть готовым, что какого-то элемента не окажется.) Для упрощения отображения сущностей, каждый объект сущности имеет функции устанавливающие значение элементов сущности в зависимости от тега, можно пройтись по списку отображенных тегов на форме и получить значение компонента из сущности по тегу компонента, в этих функциях можно также преобразовывать значения элементов и тегов(если значение не текстовое или не четко преобразуется в элемент-значение). Для компонентов логику которых сложно отобразить через событие смены содержимого - список (например: таблица) - можно при смене отображения добавлять нужные обработчики и удалять лишние. (Эти элементы добавляются через перегрузку виртуальной функции поиска мета-компонента). При удалении элемента(случается при превышении количества скрытых элементов) - отвязываются все обработчики(опять таки через виртуальные функции - можно при желании перегрузить). Также остался атавизм - описание мета-компонента через UserControl, для таких форм существует возможность указать еще и отображение внутри элемента - ранее использовалось для сложных компонентов с различным расположением элементов в зависимости от формы, сейчас почти негде не используется, но возможность добавления таких мета-компонентов есть. Основной их недостаток их невозможно отредактировать внутри программы, нужно перекомпилироать исходники;
  4. Форма - содержит одного из наследников выше указанного класса. Должна содержать 2 основных обработчика - привязка делегата для выполнения команды выполнить в основной программе описанной через URI и функцию вызова команды в элементе отображения выполнить в починенном (перегружаемая функция в предыдущем классе);
  5. Программа - должна создать(загрузить) описание форм и связь с сервером и создать контролер окон. В нем вызвать команду создать одно окно с формой по умолчанию полученное из компонента связи сервером. Также есть простейшее меню в system tray - для простейших команд, можно передать различные команды компонентам: скрыть все окна, отключиться от сервера, разрешить удаленное управление программой и обновить справочники(внутреннюю базу);
  6. Контролер окон - позволяет создать окно выполнив команду выполнить в основной программе передав в окно через вызовов выполнить в починенном команду, закрыть все окна с определенным отображением(реализовано через вызовы в починенных окнах - закройся, если ты...), закрыть окно - обычно подчинеенное окно просит систему об этом.
Логика работы обработки взаимосвязей, похожа на Декоратор, на данный момент в основном завязана на использование WindowsForms, так как иногда обращается к элементам и событиям не через интерфейс, а непосредственно.

Связь с сервером:
  • Содержит описание всех сущностей;
  • отвечает за сериализацию и передачу объектов на сервер;
  • позволяет патчить XML(нужно при частичном обновлении справочников, позволяет избежать копирования всех сущностей перед наложением изменений, чтобы откатиться назад);
  • также выполняет всю бизнес логику общения с сервером;
  • может выполнять запросы синхронно и асинхронно(сейчас используется первый метод). При асинхронном методе возвращается номер сообщения по которому можно узнать результат и можно добавлять делегаты для реакции на завершение обработки;
  • реализует процедуру регистрации на сервере;
  • реализует процедуру выборки сущности из словаря (хранилища сущностей) по нечетким правилам(подобие SELECT) и кэширование результатов.
Сущности хранятся в иерархиях(выше обозначены как словари, реализую клиентскую копию базы данных), Общая структура обработки реализована через перекрытие виртуальных функций с добавление обработки новых типов запросов(Подобие шаблона Цепочка обязанностей).

Вот в общем-то и вся логика:-)

Wednesday, July 8, 2009

Концерты c The Gardner Museum

Концерты классической музыки с сайта The Gardner Museum. Вся музыка выложена под лицензией Creative Commons Music Sharing License(Разрешено свободное распространение с сохранением авторства).

Мелкие заметки:
  • Простенькая команде получения всех файлов где встречается /revision/, пропустив каталог .svn:
    grep "/revision/" -r . | grep -v -e "\.svn" | sed 's/:/\t/g'| awk '{ print $1 }' |sort -u

Friday, July 3, 2009

Поддержка entity в XML Parser(PHP)

По-умолчанию xml parser не поддерживает символические имена для спец символов(entity), при парсинге XML в котором встречаются такие символы - они отбрасываются. Для того чтобы этого избежать нужно заменить все символические имена на цифровые коды, которые он нормально обрабатывает.

Примерчик:
...

/* replace some named entity
amp|#38
quot|#34
gt|#62
nbsp|#160
lt|#60
iexcl|#161
cent|#162
pound|#163
copy|#169
*/
function replace_entity($document)
{
$search = array (
"'&'",
"'"'",
"'>'",
"' '",
"'<'",
"'¡'",
"'¢'",
"'£'",
"'©'"
);
$replace = array (
"&",
""",
">",
" ",
"<",
"¡",
"¢",
"£",
"©"
);
return preg_replace($search, $replace, $document);
}

function parse($data, $logs)
{
$this->logfile= $logs;
$this->parser = xml_parser_create('UTF-8');
xml_set_object($this->parser, $this);
xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
xml_set_element_handler($this->parser, 'tag_open', 'tag_close');
xml_set_character_data_handler($this->parser, 'cdata');
$this->log_all($data."\n");
if (!xml_parse($this->parser, $this->replace_entity($data)))
{
$this->error_code = xml_get_error_code($this->parser);
$this->error_string = xml_error_string($this->error_code);
$this->current_line = xml_get_current_line_number($this->parser);
$this->current_column = xml_get_current_column_number($this->parser);
}
xml_parser_free($this->parser);
}
...


P. S. Полгода - год назад это было так, сейчас может быть уже поддерживает, но решение вполне универсально, так как помогает добавить поддержку новых спецсимволов, если это потребуется.