вторник, 13 июля 2010 г.

Экватор - Первые итоги

Экватор - Первые итоги


Итак, цель проекта - написать генератор парсеров для Strigi. Т.е. на вход подается описание формата метаданных и описание того как соотносятся теги и свойства онтологий, а на выходе получался бы парсер который бы извлекал теги и создавал бы триплексы. С чего я начала:


1. С чтения документации форматов метаданных (т.е. только ту часть которая описывает метаданные). Пока осилила только png, exif(jpg),  id3(mp3), немного Vorbis comment (flac) и xmp. После этого увлекательного чтения нужно было придумать свой язык для описания метаданных. Вот это как раз было самым сложным этапом работы и прежде чем прейди к какому-то жизнеспособному решению я сделала штуки 3 неудачных попытки. Язык я назвала Tag language или сокращенно TL–оригинально :) И ключевым элементом в нем стал MetadataKey. На примере png (файл pngtag.txt):


  • /* Ключевое слово Binary означает старт + обозначает способ задания MetadataKey по смещения и размеру (планируется что дальше я расширю язык для извлечения тегов по ключевому слову, тогда для такого формата описание будет начинаться с ключевого слова Text). PngChunk – имя нашего описания, а так же префикс для будущего класса.*/
    1 Binary PngChunk;
    2
    /* ByteOrder и BitOrder порядок байтов и битов. Если эти константы не заданы, то по умолчанию они будут равны LSB.*/
    3 ByteOrder=MSB;
    4 BitOrder=MSB;
    5
    /*StartMetadataKey – это самый первый тег который определяет что те самые метаданные. offset для этого ключа задается всегда от начала файла. */
    6 StartMetadataKey IHDR(offset=12, size=4);
    /*Проверка значения ключа. Для StartMetadataKey –обязательна и может проверять значение строки (как указано ниже) или hex.*/
    7 checkKey(IHDR)="IHDR";
    8
    /*MetadataKey –область в которой хранятся нужные нам данные. Задается тремя параметрами:
    другой MetadataKey или ключевой слово start, означающее начало файла. Этот параметр указывает точку относительно которой будет считаться смещение. В случае если указан MetadataKey – это конец ключа
    offset - смещение
    size – размер ключа*/
    9 MetadataKey width(IHDR, offset=0, size=4);
    /*Создаем тег с именем Widht с целочисленным значением, которое храниться в MetadataKey*/
    10 SetTag Width(getNumber(width));
    11
    12 MetadataKey height(width, offset=0, size=4);
    13 SetTag Height(getNumber(height));
    14
    15 MetadataKey ColorType(height, offset=0, size=1);
    16 SetTag Color_type(getNumber(ColorType));
    17
    18 MetadataKey Compression(ColorType, offset=0, size=1);
    19 SetTag Compression_method(getNumber(Compression));
    20
    21 MetadataKey Filter(Compression, offset=0, size=1);
    22 SetTag Filter_method(getNumber(Filter));
    23
    24 MetadataKey Interlace(Filter, offset=0, size=1);
  • 25 SetTag Interlace_method(getNumber(Interlace));



Еще есть функции getNumberByLink(key, offset, size) или getNumberByLink(offset, size) – берет целое число по смещению и размеру. Если key не задан, то смещение считается от начала файла.
getStringByLink(key, offset, size), getStringByLink( offset, size), getStringByLink(key, offset), getStringByLink( offset) – берет строку по смещению. Если key не задан, то считает смещение от начала файла. Если size не задан, то берет все пока не встретит знак конца строки или не дойдет до конца файла.
getValueByLink(key, offset, size), getValueByLink(offset, size) –берет значение без приведения к типу. (пока нигде не используется). Все эти функции короткая запись MetadataKey + getValue

Функция shift(key,n) – сдвигает ключ key на n позиций.
getBit(key,n) – берет n-ый бит в ключе key.
getByte(key, n) – берет n-ый байт в ключе key.
Примеры использования в файлах id3tag.txt и exif.txt

Так же есть цикл (пока только while) и условия(if - else).

Язык для описания соответствия между тегами и онтологией я назвала Tiny Format Language – TFL. Опять же для png (файл png_triplex.txt):


  • /* Format ключевое слово, после которого идет префикс для имени класса - парсера*/
    1 Format Png;
    2
    /* Metadata – ключевое слово, за которым следует список описаний метаданных. У png только один «вид» метаданных, а у jpg например может быть xmp или exif */
    3 Metadata: PngChunk;
    4
    5
    /*Эта проверка означает, что если мы нашли StartMetadataKey и он прошел проверку, то мы сопоставляем тери и свойства в онтологиях.*/
    6 if(PngChunk)
    7 {
    /* Имя тега в этом описании должно соответствовать имени тега заданным при помощи функции setTag в файле pngtag.txt */
    8 With = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#width";
    9 Height = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height";
    10 Color_type= "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorDepth";
    11 Compression_method = "http://freedesktop.org/standards/xesam/1.0/core#compressionAlgorithm";
    12 Interlace_method = "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#interlaceMode";
    13
  • 14 }




2. Далее для этих двух языков нужно было написать трансляторы. Каждый транслятор состоит из сканера, парсера и генератора кода. Для создания сканера и парсера я воспользовалась генератором coco/r. Вообще то сначала я планировала работать с antlr, уж очень у них удобная среда разработки и тестирования грамматики AntlrWork. Но отношения с этим инструментом не сложились, вернее не сложились отношения с его документацией, и я решила реализовывать транслятор при помощи coco.


В результате работы TL транслятора я получаю класс extractor, а в результате работы TFL транслятора класс парсер для strigi.

Вот что получается для png:


  • /*файлы PngChunkextractor.h и PngChunkextractor.cpp результат работы TL транслятора*/
    1 #ifndef PngChunkExtractor_H_
    2 #define PngChunkExtractor_H_
    3 #include "MetadataKey.h"
    4 #include "Tag.h"
    5 #include "MetadataFunc.h"
    6 #include
    7 #include
    8
    9
    10 class PngChunkExtractor {
    11 public:
    12 PngChunkExtractor(std::ifstream _is) {is=_is;}
    13 std::vector toExtract();
    14 bool toCheck();
    15 private:
    16 std::ifstream is;
    17 };
    18 #endif

    /* файлы Pngendanalyzer.h и Pngendanalyzer.cpp – результат работы TFL - транслятора */
    1 #include "Pngendanalyzer.h"
    2 #include
    3
    4
    5 void PngEndAnalyzerFactory::registerFields(FieldRegister& reg) {
    6 RF["PngChunk_Color_typeField"]=reg.registerField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorDepth");
    7 addField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorDepth");
    8 RF["PngChunk_Compression_methodField"]=reg.registerField("http://freedesktop.org/standards/xesam/1.0/core#compressionAlgorithm");
    9 addField("http://freedesktop.org/standards/xesam/1.0/core#compressionAlgorithm");
    10 RF["PngChunk_HeightField"]=reg.registerField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height");
    11 addField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height");
    12 RF["PngChunk_Interlace_methodField"]=reg.registerField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#interlaceMode");
    13 addField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#interlaceMode");
    14 RF["PngChunk_WithField"]=reg.registerField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#width");
    15 addField("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#width");
    16 }
    17
    18
    19 signed char PngEndAnalyzer::analyze(AnalysisResult& as, InputStream* in) {
    20 int flag=-1;
    21 PngChunkExtractor PngChunk(InputStream* in);
    22 if(PngChunk.toCheck()){
    23 std::vector tag_v=PngChunk.toExtract();
    24 flag=0;
    25 for(int i=0; igetField(name), t.getNumber()); break;
    34 case 2: as.addValue(factory->getField(name), t.getString()); break;
    35 }
    36 name.clear();
    37 }
    38 return flag;
  • 39 }




Что дальше:

1. Подправить классы для работы с потоками Strigi (кажется почти доделала)
2. Добавить в TL возможность брать значения типа double
3. Добавить в TFL возможность создавать триплексы
4. Дописать вывод ошибок декларирования переменных, работу с кодировкой и прочие нюансы, которые я упустила.
5. Доделать пример с exif, png, id3, которые бы иллюстрировали все возможности языка.


Ссылка на исходники:
http://gitorious.org/strigi/strigi-grammar/trees/master/Translator

4 комментария:

  1. Как предполагается работать со строками, полученными через getStringByLink, в _разных_ кодировках?

    ОтветитьУдалить
  2. 1. Расширить грамматику TL для явной спецификации локали, так как я сделала для BitOrder и ByteOrder. В некоторых форматах метаданных есть явное указание в какой кодировке строки.
    2. Использовать системную локаль.

    ОтветитьУдалить
  3. Пока не очень понятно, куда тут можно "прикрутить" автоматическое распознавание кодировки (для ID3-тегов).

    Хотел ещё написать про файлы, где кодировка указана в самом файле (например, mbox), но TL применим только для двоичных файлов.

    ОтветитьУдалить
  4. По стандарту id3 может содержать текст в кодировке 8859-1 (encoding =0) или Unicode (encoding =1). Могу только ввести возможность задания кодировки в TL вручную, но никак не автоматическое распознавание.

    ОтветитьУдалить