// ********************************************************************* // Возвращает следующий полный токен из входного потока, не перемещая // курсор чтения. // ********************************************************************* lem::UCString SentenceBroker::PickNextToken(void) { UFString buffer; UCString res; // Сначала пропустим пробелы while(true) { const wchar_t c = GetChar(); if( c==WEOF ) break; buffer.Add_Dirty(c); if( !lem::is_uspace(c) ) { res = c; break; } } // Теперь считываем символы до любого разделителя while(true) { const wchar_t c = GetChar(); if( c==WEOF ) break; buffer.Add_Dirty(c); if( tokenizer->IsTokenDelimiter(c) ) break; res += c; } // Возвращаем обратно все считанные символы. for( int i=buffer.length()-1; i>=0; --i ) UngetChar( buffer[i] ); return res; }
void Resource_List::LoadTxt( WideStream &txt ) { // Формат ресурсов: NNN LANG string // // Допускаются директивы: // $include filename language int last_good_id=0; const lem::UCString INCLUDE(L"$include"); const UFString cur_lang( lem::to_unicode( lem::UI::get_UI().GetCurrLang() ) ); const UFString english(L"en"); UCString nnn; std::string lang; UFString string; while( !txt.eof() ) { txt.skip_white(); nnn.clear(); lang.clear(); string.clear(); // read the number identifier (primary key) of the string record. while( !txt.eof() ) { const wchar_t u = txt.wget(); if( u==WEOF ) break; if( nnn.front()==L'#' ) { // Commentary string - read until EOL. while( !txt.eof() ) { const wchar_t u = txt.wget(); if( u == L'\n' || u==L'\r' || u==WEOF ) break; } break; } if( is_uspace(u) ) { txt.unget(u); break; } nnn += u; } if( nnn.empty() ) continue; if( nnn == INCLUDE ) { // Next fields are: 1) filename and 2) language UFString filename, language; filename.reserve(64); language.reserve(4); txt.skip_white(); while( !txt.eof() ) { const wchar_t u = txt.wget(); if( u==WEOF ) break; if( is_uspace(u) || u==L'\n' || u==L'\r' ) { txt.unget(u); break; } filename.Add_Dirty(u); } txt.skip_white(); while( !txt.eof() ) { const wchar_t u = txt.wget(); if( u==WEOF ) break; if( is_uspace(u) || u==L'\n' || u==L'\r' ) { txt.unget(u); break; } language.Add_Dirty(u); } language.calc_hash(); filename.calc_hash(); // Recursively load the file if( load_all_languages || language == cur_lang || language==english ) { // Если задан файл без указания пути, то путь берем из исходного файла lem::Path inc(filename); inc.RemoveLastLeaf(); if( inc.empty() ) { inc = txt.GetName(); inc.RemoveLastLeaf(); inc.ConcateLeaf(lem::Path(filename)); LoadTxt( inc ); } else { LoadTxt( lem::Path(filename) ); } } continue; } int iname = 0; if( lem::to_int( nnn.c_str(), &iname )==false || iname<=0 ) throw E_BaseException( lem::format_str( L"Incorrect format of string resource file: id is not a number [%ls]", nnn.c_str() ).c_str() ); last_good_id = iname; // метка языка txt.skip_white(); while( !txt.eof() ) { wchar_t u = txt.wget(); if( u==WEOF ) break; if( is_uspace(u) ) { txt.unget(u); break; } lang += static_cast<char>(u); } // Ведем список языков, для которых есть локализация ресурсов. // ищем такой язык в справочнике int ilang = FindLang(lang,NULL); int il=0; for( std::vector<std::string>::const_iterator j=langs.begin(); j!=langs.end(); j++, il++ ) if( *j == lang ) { ilang = il; break; } if( ilang==UNKNOWN && !lang.empty() ) { ilang = langs.size(); langs.push_back( lang ); } txt.skip_white(); bool apostrophed=true; // теперь сама строка ресурсов while( !txt.eof() ) { wchar_t u = txt.wget(); if( u==WEOF /*EOF fixed 17.09.2009 */ ) break; if( u==L'\r' || u=='\n' ) { break; } if( u==L'"' && string.empty() ) { // Начало строки в апострофах apostrophed = true; continue; } if( u==L'"' && !string.empty() ) { // Конец строки в апострофах // Если далее идет символ +, то будет продолжение на следующей строке. wchar_t next = txt.wget(); if( next==L'+' ) { // Это продолжение. txt.skip_white(); next = txt.wget(); if( next==WEOF || next==0 ) continue; LEM_CHECKIT_Z( next==L'"' ); continue; } break; } if( u==L'\\' ) { // спецсимволы wchar_t u2 = txt.wget(); if( u2==WEOF ) break; if( u2==L'\r' || u2=='\n' ) break; if( u2==L'r' || u2==L'n' ) u = L'\n'; else if( u2==L'"' ) u = L'"'; else u = u2; } string.Add_Dirty(u); } string.calc_hash(); if( !nnn.empty() && !string.empty() ) { // Сохраняем ресурс. // Ветка ресурса для этого nnn уже есть? string.trim(); string.strip_quotes(); Resource *r = NULL; std::map<int,Resource*>::iterator it = iname_2_res.find(iname); if( it!=iname_2_res.end() ) { r = it->second; } else { r = new Resource; r->name = iname; list.push_back(r); iname_2_res.insert( std::make_pair(iname,r) ); } r->list.push_back( make_pair( ilang, string ) ); } } return; }
// ****************************************************** // Загружаем узел вместе со всеми его вложенными узлами. // ****************************************************** Node* Parser::Load_Node( WideStream *reader ) const { if( reader->eof() ) return NULL; // Пропускаем пробелы. wchar_t c=0; while( !reader->eof() ) { c = reader->wget(); if( c==WEOF ) return NULL; if( c!=L' ' && c!=L'\t' && c!=L'\r' && c!=L'\n' ) break; } // Символ должен быть '<'. if( reader->eof() ) return NULL; if( c!=L'<' ) throw Invalid_Format(); // Считываем тэг. UFString tag; Read_Tag( reader, tag ); if( tag.empty() ) throw Invalid_Format(); Node *node = new Node; Break_Tag( tag, node->name, node->attrs, node->body ); // Тэги бывают пяти видов - единичный <abc/>, открывающий <abc>, // закрывающий </abc>, комментарий <!--...-->, блок <![CDATA[ ... ]> // Для HTML требуется также специальная обработка тэгов <script>...</script> // и одиночных типа <br> if( tag.back() == L'/' ) { // Единичный тэг. node->SetClosed(true); } else if( node->name==L"![CDATA[" ) { node->node_type = Node::CDataNode; node->SetClosed(true); } // Для HTML надо проверить одиночные теги типа <br> else if( doctype==HtmlDoc && IsHtmlClosed(tag) ) { // Единичный тэг. node->SetClosed(true); } else if( tag.front()==L'/' ) { // Закрывающий тэг. // В имени убираем символ '/' node->name = remove_char( node->name, L'/' ); node->SetClosing(true); } else if( tag.front()==L'!' ) { if( tag.length()>=3 && tag.eq_beg( L"!--" ) ) { // Комментарий <!-- ... --> node->SetClosed(true); node->node_type = Node::CommentNode; } /* else if( tag.eq_beg( L"CDATA[") ) { node->SetClosed(true); node->node_type = Node::CDataNode; } */ else { if( doctype==Parser::XmlDoc ) { delete node; throw Invalid_Format(); } else { node->SetClosed(true); node->node_type = Node::InvalidNode; } } } else if( doctype==Parser::HtmlDoc && node->name.eqi(L"script") ) { // Для пары тегов <script>...</script> правила особые - тело является // произвольным набором символов, читаемых как единое целое в тело узла. node->node_type = Node::ScriptNode; node->body.reserve(256); while( !reader->eof() ) { wchar_t c = reader->wget(); if( c==WEOF || reader->eof() ) break; if( c==L'<' ) { // вдруг это начало закрывающего тэга </script> lem::UFString tbuf; tbuf.reserve(32); tbuf = c; while( !reader->eof() ) { wchar_t c2 = reader->wget(); if( c2==WEOF || reader->eof() ) break; tbuf.Add_Dirty(c2); if( c2==L'>' ) break; } if( tbuf.eqi(L"</script>") ) { node->SetClosed(true); break; } else { node->body.Add_Dirty( tbuf ); } } else { node->body.Add_Dirty(c); } } node->body.calc_hash(); } else { // Открывающий тэг. // Дальше может идти либо список вложенных тэгов, либо строка - тело. wchar_t c=0; // Пропустим пробелы. while( !reader->eof() ) { c = reader->wget(); if( c!=L' ' && c!=L'\t' && c!=L'\n' && c!=L'\r' ) // Встретили непустой символ! break; } if( c==L'<' ) { reader->unget(c); // Теперь надо загрузить все вложенные тэги и построить дерево. while( !reader->eof() ) { Node *nested = Load_Node( reader ); if( !nested ) { lem_rub_off(node); throw Invalid_Format(); } if( nested->IsComment() ) { // Комментарии удаляем. lem_rub_off(nested); } else if( nested->IsCData() ) { // Блок <[CData[ ... ]]> добавляем к телу node->body += nested->body; lem_rub_off(nested); } else if( nested->GetClosing() ) { if( nested->GetName() != node->GetName() ) { lem_rub_off(node); lem_rub_off(nested); throw Invalid_Format(); } // Список вложенных узлов закончен. node->SetClosed(true); lem_rub_off(nested); break; } else if( !nested->GetClosed() ) { lem_rub_off(node); lem_rub_off(nested); throw Invalid_Format(); } if( nested!=NULL ) { node->GetNodes().push_back( nested ); if( doctype==Parser::HtmlDoc ) { node->visible_text += nested->visible_text; } nested = NULL; } // Идем до следующего тэга. // Пропустим пробелы. while( !reader->eof() ) { c = reader->wget(); if( c!=L' ' && c!=L'\t' && c!=L'\n' && c!=L'\r' ) { // Встретили непустой символ! reader->unget(c); break; } } if( reader->eof() ) break; if( c != L'<' ) { if( doctype!=Parser::HtmlDoc ) { lem_rub_off(node); // Тело тэга может быть либо список вложенных тэгов, либо строкой. // У нас тут получается смесь. throw Invalid_Format(); } else { reader->wget(); goto load_tag_body; } } } } else { load_tag_body: // Собираем тело тэга - до закрывающего. node->body.reserve(128); bool just_started=true; while( !reader->eof() ) { lem::Stream::pos_type tpos = reader->tellp(); if( !just_started ) c = reader->wget(); else just_started = false; if( c==L'<' ) { // Начало нового тэга if( reader->wget()==L'/' ) { // это закрывающий тэг, он должен закрывать текущий тэг. reader->seekp(tpos); break; } else { // Произвольный вложенный тэг (HTML) reader->seekp(tpos); Node *nested = Load_Node(reader); if( !nested || !nested->GetClosed() ) { throw Invalid_Format(); } else if( nested->IsComment() ) { lem_rub_off(nested); } else if( nested->IsCData() ) { node->body += nested->body; lem_rub_off(nested); } if( nested!=NULL ) { node->nodes.push_back(nested); if( doctype==Parser::HtmlDoc ) { node->visible_text += nested->visible_text; } } continue; } } if( c==L'&' ) { // Далее идет либо символическое имя символа, либо его числовой код. c = reader->wget(); if( c==L'#' ) { // Считываем hex код символа до ; UFString hex; hex.reserve(6); while( !reader->eof() ) { c = reader->wget(); if( c==L';' || c==WEOF ) break; hex.Add_Dirty(c); } c = lem::to_int( hex ); } else { FString char_name; char_name.reserve(8); char_name.Add_Dirty( char(c) ); while( !reader->eof() ) { c = reader->wget(); if( c==L';' || c==WEOF ) break; char_name.Add_Dirty( char(c) ); } c = lem::CodeConverter::Sgml_2_Char(char_name); } } node->body += c; if( doctype==Parser::HtmlDoc ) { node->visible_text += c; } } if( reader->wget()!=L'<' ) { lem_rub_off(node); throw Invalid_Format(); } // Сейчас должен быть закрывающий тэг! UFString close_tag; Read_Tag( reader, close_tag ); UFString name2, body2; Collect< pair<UFString,UFString> > attrs2; Break_Tag( close_tag, name2, attrs2, body2 ); if( doctype==Parser::HtmlDoc && !remove_char( name2,L'/').eqi(node->name) ) { lem_rub_off(node); throw Invalid_Format(); } else if( remove_char(name2,L'/') != node->name ) { lem_rub_off(node); throw Invalid_Format(); } node->SetClosed(true); } } if( doctype==Parser::HtmlDoc ) { // Некоторые типы тэгов в HTML фактически вводят в текст разделитель. if( IsTextDelimiterTag( node->GetName() ) ) { node->visible_text += L' '; } } return node; }
// ********************************************************** // Загружаем тэг - все символы между < >. // Открывающая угловая скобка уже считана! // ********************************************************** void Parser::Read_Tag( WideStream *reader, UFString &tag ) const { tag.clear(); int count=0; while( !reader->eof() ) { wchar_t c = reader->wget(); if( c==L'>' ) break; if( !count && c==L'!' ) { // Или комментарий <!-- ... --> // или блок кода <![CDATA[ ... ]]> // или начальный тэг <!DOCTYPE tag.Add_Dirty(c); count++; wchar_t c = reader->wget(); if( c==L'-' ) { // комментарий... tag.Add_Dirty(c); count++; while( !reader->eof() ) { wchar_t c = reader->wget(); if( c==WEOF ) break; if( count>3 && c==L'>' ) { // Комментарий заканчивается на --> if( tag.back()==L'-' && tag[tag.length()-2]==L'-' ) { goto tag_complete; } } tag.Add_Dirty(c); count++; } // Если комментарий не закончен нормально, то значит была ошибка throw Invalid_Format(); } else if( c==L'D' ) { // <!DOCTYPE ...> // загружаем как обычный тэг tag.Add_Dirty(c); count++; continue; } else if( c=='[' ) { // <![CDATA[ продолжаем загружать... tag.Add_Dirty(c); count++; continue; } else { // Неизвестный тэг, для HTML проигнорируем ошибку if( doctype==Parser::HtmlDoc ) { tag.Add_Dirty(c); count++; } else { throw Invalid_Format(); } } } else if( count==7 && c==L'[' && tag==L"![CDATA" ) { // блок кода после <![CDATA[ tag.Add_Dirty(c); count++; while( !reader->eof() ) { wchar_t c = reader->wget(); if( c==WEOF ) break; if( count>8 && c==L'>' ) { // Блок кода заканчивается на ]]> if( tag.back()==L']' && tag[tag.length()-2]==L']' ) { goto tag_complete; } } tag.Add_Dirty(c); count++; } if( doctype!=Parser::HtmlDoc ) throw Invalid_Format(); } if( c==WEOF || reader->eof() ) break; tag.Add_Dirty(c); count++; } tag_complete: tag.calc_hash(); return; }