Одним из стандартных сервисов CASL является сервис XML API.
Используя этот сервис прикладной программист может использовать
функциональность разбора и обработки XML в своих приложениях.
Что конкретно входит в это XML API:
Что такое потоковый (или управляемый событиями) парсер: это модуль, который читает элементы XML и генерирует события (т. е. вызывает соответствующие функции клиента парсера) по мере чтения XML потока.
Исходя из этого мы можем определить соответствующие структуры данных.
Очень высокоуровневое представление дает следующая диаграмма классов: png
Точное описание API привадится в справочнике по API, этот документ играет дополнительную роль.
standalone атрибута.
Xxx существует соответствующий виртуальный метод
receiveXxx, которы должен быть переопределен пользователем.
например:
receiveXMLDecl(WStringChunk& versionInfo,
WStringChunk& encName,
bool standalone,
XMLContext& ctx);
void receiveStartOfDOCTYPE(WStringChunk& name,
WStringChunk& externalId,
XMLContext& ctx);
void setStartOfElement(WStringChunk& name, XMLAttributes& attributes,
XMLContext& ctx);
receiveEndOfElement(WStringChunk& name, XMLContext& ctx) -
генерируется при чтении закрывающего тега элемента.
Полное описание находится в справочнике по API XMLParseListener
send<Xxx>, с такими-же параметрами.
XMLNotificator::setListener(XMLListener* listener, bool listenerOwnity);Т. е. нотификатор посылеет сообщения слушателю. В самом простом случае, слушатель это обычно класс пользователя (или комбинация классов в нашей системе обработки, о чем речь пойдет ниже), нотификатор - поставляемый нами парсер XML.
listenerOwnity указывает передачу ответственности за удаление listener-а.
Т. е. если listenerOwnity==true то указатель на listener
удаляется в деструкторе XMLNotifier, иначе - клиент должен удалить
этот указатель где-то у себя.
Метод process позволяет 'запустить' источник, инициировав процесс
разбора XML источника.
std::istream).
1.
Наконец XMLTransformer - собственно из-за чего все это и затевалось.
Как видно из UML диаграммы, XMLTransformer преобразовывает входной
поток XML событий в выходной.
Вход XMLTransformer задается с помощью метода
setXMLSource(XMLSource* src,bool srcOwnity),
Для каждого XML события
Xxx определен виртульный метод processXxx, с обычным набором
параметров.
Каков алгоритм XML процессинга: для каждого события вызывается
соответствующая функция трансформатора. Если она возворащает true,
то событие передается приемнику, иначе - пропускается.
Клиент перегружает в своем классе, наследуемом от XMLTramsformer,
соответствующие функции обработки processXxx. В этих функциях
обработки он может использовать методы нотификатора sendXXX, для
передачи сообщений дальше (к конечному приемнику).
Так как XMLTransformer реализует интерфейсы XMLSource и
XMLDostination, то мы можем выстраивать цепочки преобразователей,
как на следующей диаграмме взаимодействия:
Для выстраивания таких цепочек предназначены методы chainBefore и
chainAfter
К примеру, напишем XML трансформатор, который преобразовывает инструкции
преобразования типа <?include-file fname ?> в содержимое файла
fname
/*
* this program illustrate how to build own processing instructions handlers.
* Level 0:
* handle XML process instruction <?include fname >
* change during processing to content of file (as CharData) or to
* processing instruction <?error-opening-file fname>
*/
using namespace CASL;
using namespace std;
/**
* So, we derive our transformer from XML transformer.
**/
class PIIncludeHandler: public XMLTransformer
{
public:
/**
* and overload method 'processPI'
**/
bool processPI(WStringChunk& piName, WStringChunk& piBody, XMLContext& ctx);
};
/**
*@param piName - name of processing instruction. (must be 'include')
*@param piBody - body of processing instruction. in our case: name of
* file, may-be surrounded by extra whitespaces.
*@return true, if we does not handle this instruction, and what to pass it
* to next handlers in chain, otherwise
* (i. e. if this is our instruction) - false.
**/
bool PIIncludeHandler::processPI(WStringChunk& piName,
WStringChunk& piBody,
XMLContext& ctx)
{
cerr << "processPI" << endl;
std::string sPIName = CharConventer::toEncoding(piName,"ASCII").to_string();
cerr << "piName=" << sPIName << endl;
if (piName.equalASCII("include")) { // we must use ASCII name.
string fname = Utils::toASCII(piBody);
// delete extra spaces.
int wspos=fname.find_first_not_of(" \t\r\n");
fname=fname.substr(wspos, fname.find_last_not_of(" \t\r\n")-wspos+1);
std::ifstream input(fname.c_str()); // try to open file.
if (!input) {
// create new PI 'error-opening-file'
WStringChunk newPI=Utils::fromASCII("error-opening-file");
// send one.
sendPI(newPI,piBody,ctx);
}else{
// copy all as sequence of CharData items.
while(!input.eof()) {
string buff;
input >> buff;
if (!input.eof()) buff+="\n";
WStringChunk content = CharConventer::toUtf16(buff,"utf8");
sendCharData(content,ctx);
}
input.close();
}
// As we handle this instruction, does not pass it up.
return false;
} else {
// this is not our instruction - so simple return true.
return true;
}
}
Этот пример находится в директории demo/XML/PIInclude исходного
дистрибутива CASL.
При разборе примера советуем обратить внимание на использование утилит и классов преобразования символов: соответствующее описание API: Utils и CharConventer
Хорошо, мы создали преобразователь, теперь как его применить ?
CASL предоставляет нам статические методы класса XMLService
API
Вот фрагмент кода, запускающий наш преобразователь:
01 try {
02 ifstream input(inputFileName.c_str());
03 if (!input) {
04 errors() << "Can't open file " << inputFileName <<
05 " for reading!" << endl;
06 return;
07 }
08 ofstream output(outputFileName.c_str());
09 if (!output) {
10 errors() << "Can't open file " << outputFileName <<
11 " for writing!" << endl;
12 return;
13 }
14 // do transformation:
15 // we can cath errors in hight-level block
16 // create transformer.
17 auto_ptr<XMLTransformer> tr(XMLService::createXMLTransformer(input,output));
18
19 // and chain our transformer into.
20 PIInclude0Handler myTransformer;
21 tr->chainAfter(myTransformer,false);
22
23 // now create context and run all.
24 XMLContext ctx;
25 tr->process(ctx);
26
27 // That's all
28 input.close();
29 output.close();
30 }catch(const XMLException& ex){
31 errors() << ex.what() << endl;
32 }
Обратите внимание на строчки с 14 по 25 : мы создаем XMLTransformer, связанный
со входным потоком input и выходным потоком output.
После этого добавляем в цепочку наш трансформер, создаем XMLContext
и запускаем процесс разбора.
Теперь обратим внимание на класс XMLContext.
API
Экземпляры
этого класса используются для передачи информации между различными стадиями
XML процессинга.
Сам XMLContext это последвовательность слотов. Что такое XMLContext::Slot - абстрактный класс, полностью определяемый пользователем.
class XMLContext : public Scriptable
{
public:
class Slot: public Scriptable
{
public:
Slot();
virtual ~Slot();
virtual ValueProxy doCommand(const char* command, Value* params);
......
};
.......
};
std::istream).1