ERAV: Entity Relation Attribute Value:
|
00001 // ERAV.cpp (C) 2010 adolfo@di-mare.com 00002 00003 #include "ERAV.h" 00004 00005 //* DELETE */ extern void dump_tuples( const std::list<ERAV::tuple>& L ); 00006 //* DELETE */ #include <iostream> 00007 00008 /// all : head docs; 00009 void ERAV::parser::all() { 00010 head(); 00011 docs(); 00012 } 00013 00014 /// head : "<?" "xml" args "?>" | /* empty */; 00015 void ERAV::parser::head() { 00016 if ( *m_lookahead==token::LTQ ) { // "<?" 00017 match( token::LTQ ); 00018 {{ pushContext(); }} // args() needs it 00019 {{ if ( m_lookahead->lexeme() != "xml") error( "Invalid XML header line" ); }} 00020 match( token::ID ); // ID == "xml" 00021 args(); 00022 match( token::QGT ); 00023 {{ popContext(); }} 00024 } 00025 else { /* empty */ } 00026 } 00027 00028 /// docs : doc docs | /* empty */; 00029 void ERAV::parser::docs() { 00030 if ( *m_lookahead=='<' ) { 00031 doc(); 00032 docs(); 00033 } 00034 else { /* empty */ } 00035 } 00036 00037 /// doc : '<' ID args nest; 00038 void ERAV::parser::doc() { 00039 std::string ID_val; 00040 if ( *m_lookahead=='<' ) { 00041 match( '<' ); 00042 {{ ID_val = m_lookahead->lexeme(); }} 00043 {{ pushContext(); }} {{ ;;;;;;;; }} 00044 match( token::ID ); 00045 args(); 00046 nest(ID_val); 00047 } 00048 else { error( "Missing '<' in XML document" ); } 00049 } 00050 00051 /// nest : "/>" | '>' docs "</" ID "/>"; 00052 void ERAV::parser::nest(const std::string& lexeme) { 00053 std::string ID_val; 00054 if ( *m_lookahead==token::SGT ) { 00055 match( token::SGT ); // "/>" 00056 {{ emit(); }} 00057 } 00058 else if ( *m_lookahead=='>' ) { 00059 match( '>' ); 00060 {{ emit(); }} 00061 docs(); 00062 match( token::LTS ); // "</" 00063 {{ m_tag = m_lookahead->getTag(); }} 00064 {{ popContext(); }} {{ ;;;;;;;; }} 00065 {{ ID_val = m_lookahead->lexeme(); }} 00066 match( token::ID ); 00067 {{ if ( ID_val != lexeme ) { error( "Non matching XML tag" ); } }} 00068 match( '>' ); 00069 } 00070 else { error( "Not well formed XML document" ); } 00071 } 00072 00073 /// args : arg args | /* empty */; 00074 void ERAV::parser::args() { 00075 if ( *m_lookahead==token::ID ) { 00076 arg(); 00077 args(); 00078 } 00079 else { /* empty */ } 00080 } 00081 00082 /// arg : ID '=' STRING; 00083 void ERAV::parser::arg() { 00084 {{ setAttrib(); }} 00085 match( token::ID ); 00086 if ( *m_lookahead=='=' ) { 00087 match( '=' ); 00088 {{ setAttribVal(); }} 00089 match( token::STRING ); 00090 } 00091 else { error( "Missing '=' in XML argument" ); } 00092 } 00093 00094 void ERAV::parser::emit() { 00095 if ( (m_context==0) && (m_tag!=tag::descr) ) { 00096 return; 00097 } 00098 ERAV::tuple TUPLE; 00099 switch ( m_tag ) { 00100 case tag::attrib: 00101 TUPLE.ID_ENT = m_id; 00102 if ( m_context==0 ) { 00103 error( "<attrib> tag with no <record> context" ); 00104 } 00105 else { 00106 TUPLE.REL.ID = m_context->REL_ID; 00107 TUPLE.REL.SUB = ( m_context->prev == 0 ? 0 : m_context->prev->REL_ID ); 00108 } 00109 TUPLE.REL.TYPE = m_rel_type; 00110 TUPLE.ATTRIB = m_attrib_ref; 00111 TUPLE.VAL.BLOB = this->m_str_val; 00112 if (m_L!=0) { m_L->push_back( TUPLE ); } 00113 break; 00114 case tag::descr: { 00115 ERAV::WORD * p = ( m_inWord ? m_WORD : m_STRING ); 00116 if ( p!=0 ) { 00117 p->update( m_id_word , m_lang , m_str_val ); 00118 } 00119 } 00120 break; 00121 case tag::head: 00122 case tag::erav: 00123 case tag::entity: 00124 // DO NOTHING 00125 break; 00126 case tag::record: 00127 TUPLE.ID_ENT = m_id; 00128 TUPLE.REL.ID = m_context->REL_ID; 00129 TUPLE.REL.SUB = ( m_context->prev == 0 ? 0 : m_context->prev->REL_ID ); 00130 TUPLE.REL.TYPE = attrib::eREL_TYPE; 00131 TUPLE.ATTRIB = m_attrib_ref; 00132 TUPLE.VAL.BLOB.clear(); 00133 if (m_L!=0) { m_L->push_back( TUPLE ); } 00134 break; 00135 case tag::word: 00136 case tag::string: // do nothing 00137 break; 00138 default: error( "Non matching XML tag" ); 00139 } 00140 } 00141 00142 void ERAV::parser::pushContext() { 00143 m_tag = m_lookahead->getTag(); 00144 if ( ! ( (tag::first<=m_tag) && (m_tag<=tag::last) ) ) { 00145 error( "Incorrect XML tag" ); 00146 } 00147 00148 if ( m_tag == tag::entity ) { 00149 m_nest_id = 0; 00150 } 00151 else if ( m_tag==tag::record || m_tag==tag::head ) { 00152 { context* p; // get new context 00153 if ( m_context_old == 0 ) { 00154 p = new context(); 00155 } 00156 else { 00157 p = m_context_old; 00158 m_context_old = m_context_old->prev; 00159 } 00160 p->prev = m_context; 00161 m_context = p; 00162 } 00163 m_context->REL_ID = m_nest_id; 00164 if ( m_tag==tag::record ) { m_nest_id++; } // avoid <head> ++ 00165 } 00166 else if ( m_tag==tag::word || m_tag==tag::string ) { 00167 m_inWord = (m_tag==tag::word); 00168 } 00169 } 00170 00171 void ERAV::parser::popContext() { 00172 if ( m_tag==tag::record || m_tag==tag::head ) { 00173 context * toRelease = m_context; 00174 m_context = m_context->prev; 00175 00176 toRelease->prev = m_context_old; 00177 m_context_old = toRelease; 00178 // Only <record> && <head> tags pushContext() <==> 00179 // Only <record> && <head> tags popContext() <==> 00180 } 00181 } 00182 00183 void ERAV::parser::setAttrib() { 00184 if ( m_tag==tag::head ) { 00185 return; // ignore all attribs for <?xml header ?> 00186 } 00187 m_attrib = m_lookahead->getAttrib(); 00188 bool correct = ( attrib::FIRST <= m_attrib ); 00189 correct = correct && ( m_attrib <= attrib::LAST ); 00190 if ( ! correct ) { 00191 error( "Incorrect XML attribute" ); 00192 } 00193 } 00194 00195 void ERAV::parser::setAttribVal() { 00196 if ( m_tag==tag::head ) { 00197 return; // ignore all attribs for <?xml header ?> 00198 } 00199 int int_val = atoi( m_lookahead->lexeme().c_str() ); // ansi atoi() 00200 bool isEntity, correct; // avoid cross initialization over case label 00201 switch ( m_attrib ) { 00202 case attrib::ATTRIB: 00203 if ( m_tag==tag::record || m_tag==tag::attrib ) { 00204 m_attrib_ref = int_val; 00205 } 00206 else { error( "Incorrect attribute: ATTRIB only for <record>&&<attrib> tags" ); } 00207 break; 00208 case attrib::DESCR: 00209 m_str_val = m_lookahead->lexeme(); 00210 break; 00211 case attrib::ID_ENT: 00212 if ( m_tag==tag::entity ) { 00213 m_id = int_val; 00214 } 00215 else { error( "Incorrect attribute: ID_ENT only for <entity> tag" ); } 00216 break; 00217 case attrib::REL_ID: // ignore value: 00218 case attrib::REL_SUB: // - use record nesting to determine these values 00219 break; 00220 case attrib::REL_TYPE: 00221 m_rel_type = tolower( m_lookahead->lexeme()[0] ); 00222 isEntity = m_tag==tag::entity; 00223 isEntity = isEntity || m_tag==tag::record; 00224 if ( (m_rel_type == attrib::eREL_TYPE) ) { 00225 if ( ! isEntity ) { 00226 assert( (m_rel_type == attrib::eREL_TYPE) && (! isEntity) ); 00227 error( "Incorrect attribute REL_TYPE: not <entity> or <record> tag" ); 00228 } 00229 } 00230 else if ( isEntity ) { 00231 assert( (m_rel_type != attrib::eREL_TYPE) && ( isEntity ) ); 00232 error( "Incorrect attribute REL_TYPE in <entity> or <record>" ); 00233 } 00234 break; 00235 case attrib::VAL: 00236 m_str_val = m_lookahead->lexeme(); 00237 break; 00238 case attrib::ID_WORD: 00239 if ( m_tag==tag::word ) { 00240 m_id_word = int_val; 00241 } 00242 else { 00243 error( "Incorrect attribute ID_WORD: not for <word>" ); 00244 } 00245 break; 00246 case attrib::ID_STRING: 00247 if ( m_tag==tag::string ) { 00248 m_id_word = int_val; 00249 } 00250 else { 00251 error( "Incorrect attribute ID_STRING: not for <string>" ); 00252 } 00253 break; 00254 case attrib::LANG: 00255 correct = ( m_tag==tag::string ); 00256 correct = correct || ( m_tag!=tag::word ); 00257 if ( correct ) { 00258 m_lang[0] = tolower( m_lookahead->lexeme()[0] ); 00259 m_lang[1] = tolower( m_lookahead->lexeme()[1] ); 00260 m_lang[2] = 0; // EOS 00261 } 00262 else { 00263 error( "Incorrect LANG attribute: not in <word> or <string>" ); 00264 } 00265 break; 00266 default: 00267 error( "Incorrect XML attribute" ); 00268 } 00269 } 00270 00271 void ERAV::parser::parse() { 00272 // m_L->clear(); 00273 if ( m_XML==0 ) { 00274 return; 00275 } 00276 else { 00277 m_cursor = m_yytext = m_XML; 00278 int token = yylex( &m_cursor, &m_yytext, &m_yyleng, &m_yyline ); 00279 m_lookahead->set(token, m_yytext, m_yyleng, m_yyline); 00280 all(); 00281 if ( !(*m_lookahead==token::ZERO) ) { 00282 error( "Non XML document: trailer invalid" ); // didn´t process whole input 00283 } 00284 } 00285 } 00286 00287 ERAV::parser::~parser() { 00288 if ( deleteXML ) { delete [] m_XML; } 00289 if ( m_context!=0 ) { delete m_context; } 00290 if ( m_context_old!=0 ) { delete m_context_old; } 00291 } 00292 00293 void ERAV::parser::real_set( 00294 const char* XML, std::list<ERAV::tuple> & L, 00295 ERAV::WORD & W, ERAV::WORD & S, 00296 bool makeCopy 00297 ) { 00298 m_lookahead = &token1; 00299 m_yyline = 1; 00300 m_L = &L; m_WORD = &W; m_STRING = &S; // parser's output appended to these 00301 00302 if ( deleteXML ) { 00303 delete [] XML; 00304 deleteXML = false; 00305 } 00306 00307 if ( makeCopy ) { 00308 size_t sz = strlen(XML)+1; 00309 try { 00310 m_XML = new char[sz]; 00311 } 00312 catch ( ... ) { 00313 m_XML = 0; 00314 error( "Not enough memory to copy XML string" ); 00315 } 00316 memcpy( const_cast<char*>(m_XML), XML, sz ); 00317 deleteXML = true; 00318 } 00319 else { 00320 m_XML = XML; 00321 } 00322 } 00323 00324 void ERAV::parser::match( int tkn ) { 00325 if ( *m_lookahead==tkn ) { 00326 int token_number = yylex( &m_cursor, &m_yytext, &m_yyleng, &m_yyline ); 00327 m_lookahead = ( m_lookahead==&token1 ? &token2 : &token1 ); // flip 00328 m_lookahead->set(token_number, m_yytext, m_yyleng, m_yyline); 00329 } 00330 else { 00331 error( "Unexpected token" ); 00332 } 00333 } 00334 00335 #if 0 00336 #include <sstream> // std::basic_ostringstream<> 00337 #ifdef Spanish_dox 00338 /// Retorna una hilera \c std::string contruida desde el valor de \c val. 00339 /// - \c toString() with standard C++ 00340 #endif 00341 #ifdef English_dox 00342 /// Returns a \c std::string constructed form value \c val. 00343 /// - \c toString() with standard C++ 00344 #endif 00345 template <class T> 00346 std::string toString( const T & val ) { 00347 // typedef basic_ostringstream<char> ostringstream; 00348 std::basic_ostringstream<char> temp; // ostringstream temp; 00349 temp << val; 00350 return temp.str( ); 00351 } 00352 #endif 00353 00354 /** Reverses in place all bytes in \c ptr. 00355 - Mnemonic: memrev() <==> memory-reverse. 00356 - Returns \c "ptr". 00357 */ 00358 void* memrev( void *ptr , size_t num ) { 00359 char *l = (char*)(ptr); // left 00360 char *r = (char*)(ptr)+num-1; // right 00361 num /= 2; 00362 for ( size_t i=0; i<num; i++, l++, r-- ) { 00363 char tmp=*r; *r=*l; *l=tmp; 00364 } 00365 return ptr; 00366 } 00367 00368 /** Reverses in place all characters in \c str. 00369 Changes all the characters in \c "str" to reverse order, 00370 except for the terminating null. 00371 - Mnemonic: strrev() <==> string-reverse. 00372 - Returns \c "str". 00373 */ 00374 inline char* strrev(char *str) { 00375 size_t len = strlen(str); 00376 return (char*) memrev(str,len); 00377 } 00378 00379 /** Convert integer \c int to string \c str(non-standard function). 00380 Converts an integer value to a null-terminated string 00381 using the specified \c base and stores the result in the 00382 array given by \c str parameter. 00383 00384 If \c base is 10 and \c value is negative, the resulting 00385 string is preceded with a minus sign (-). With any 00386 other base, value is always considered unsigned. 00387 00388 \c str should be an array long enough to contain any 00389 possible value: <code> (sizeof(int)*8+1) </code> 00390 for radix=2, i.e. 17 bytes in 16-bits platforms and 00391 33 in 32-bits platforms. 00392 */ 00393 char* itoa( int value, char *str, int base ) { 00394 char *r = str; // r -> result 00395 if ( base<2 || base>(10+('z'-'a'+1)) ) { // base>36 00396 *str = 0; 00397 return str; 00398 } 00399 unsigned val = value; // bitwise copy 00400 bool negBase10 = (value<0) && (base==10); 00401 if ( negBase10 ) { val = -value; } 00402 if ( val == 0 ) { 00403 *r = '0'; ++r; 00404 } 00405 else do { 00406 unsigned digit = (val % (unsigned)(base)); 00407 val /= (unsigned)(base); 00408 *r = ( (digit>=10) ? ((digit-10)+'a') : ('0'+digit) ); 00409 ++r; 00410 } while ( val !=0 ); 00411 if ( negBase10 ) { *r = '-'; r++;} 00412 *r = 0; // EOS 00413 00414 // strrev( str ); 00415 r--; // r -> right 00416 char *l = str; // l -> left 00417 while ( l<r ) { 00418 char tmp=*r; *r=*l; *l=tmp; 00419 l++; r--; 00420 } 00421 return str; 00422 00423 const int its_36 = 10+('z'-'a'+1); 00424 struct ITS_36 { // compile time verify that it´s 36 00425 char its[ its_36==36 ? +1 : -1 ]; 00426 }; 00427 } 00428 00429 /// <code> return itoa(i,my_itoa::static_buffer,10)</code>. 00430 inline char* my_itoa( int i ) { 00431 static char itoa_buffer[ sizeof(int)*8+1 ]; 00432 return itoa(i,itoa_buffer,10); 00433 } 00434 00435 void ERAV::parser::error( const char* msg ) { 00436 std::string err = "*** ERROR *** "; 00437 if ( msg !=0 ) if ( *msg != 0 ) { 00438 err += "[ "; 00439 err += msg; 00440 err += " ]\n *** "; 00441 } 00442 err += "line("; 00443 err += my_itoa(m_yyline); 00444 err += ") "; 00445 m_lookahead = ( m_lookahead==&token1 ? &token2 : &token1 ); // flip 00446 err += m_lookahead->lexeme() + ' '; 00447 m_lookahead = ( m_lookahead==&token1 ? &token2 : &token1 ); // flop 00448 const char *y=m_yytext; 00449 for (int i=0; i<m_yyleng; ++i,++y) { err += (*y); } 00450 err += '\n'; 00451 { 00452 static char error_mem[128]; 00453 size_t ln = err.length()+1; 00454 ln = ( sizeof(error_mem) <= ln ? sizeof(error_mem) : ln ); 00455 ln--; 00456 err.copy(error_mem, ln); 00457 error_mem[ ln ] = 0; 00458 throw ERAV::exception( error_mem ); 00459 } 00460 } 00461 00462 /// cursor++ && (line++ <==> '\n') 00463 #define nextChar(cursor,line) { if ('\n'==*(*cursor)) { (*line)++; } (*cursor)++; } 00464 00465 // scanner: scans from cursor and sets next token in yytext 00466 int ERAV::yylex( char const** cursor, char const** yytext, int* yyleng, int* yyline ) { 00467 start_here: (*yytext) = (*cursor); (*yyleng) = 1; 00468 if ( isspace( *(*cursor) ) ) { // skip whitespace 00469 while ( isspace( *(*cursor) ) ) { nextChar(cursor,yyline); } 00470 #undef USE_goto 00471 #define USE_goto 00472 #ifdef USE_goto 00473 goto start_here; // avoid recursion 00474 #else 00475 return yylex( cursor, yytext, yyleng, yyline ); 00476 #endif 00477 } 00478 else if ( *(*cursor)=='<' ) { 00479 if ( (*cursor)[1]=='/') { // "</" LTS 00480 (*yyleng) = 2; (*cursor) += 2; 00481 return ERAV::token::LTS; 00482 } 00483 else if ( (*cursor)[1]=='?') { // "<?" LTQ 00484 (*yyleng) = 2; (*cursor) += 2; 00485 return ERAV::token::LTQ; 00486 } 00487 if ( 0==memcmp( (*cursor),"<!--",4 ) ) { // skip commments 00488 (*cursor) +=4; (*yyleng) = 4; // 4 == strlen("<!--"); 00489 for (;;) { 00490 while (*(*cursor) != '-') { nextChar(cursor,yyline); } // next '-' 00491 if ( 0==memcmp( (*cursor),"-->",3 ) ) { 00492 (*cursor) += 3; // 3 == strlen("-->") 00493 #ifdef USE_goto 00494 goto start_here; // avoid recursion 00495 #else 00496 return yylex( cursor, yytext, yyleng, yyline ); 00497 #endif 00498 } 00499 else { 00500 nextChar(cursor,yyline); // keep trying 00501 } 00502 } 00503 } 00504 else { 00505 nextChar(cursor,yyline); 00506 return **yytext; 00507 } 00508 } 00509 else if ( *(*cursor)=='\'' ) { // single quoted string 00510 nextChar(cursor,yyline); *yyleng = 0; // count 1 less 00511 while ( *(*cursor)!='\'' ) { nextChar(cursor,yyline); (*yyleng)++; } 00512 (*yytext)++; nextChar(cursor,yyline); // skip quotes 00513 return ERAV::token::STRING; 00514 } 00515 else if ( *(*cursor)=='\"' ) { // double quoted string 00516 nextChar(cursor,yyline); *yyleng = 0; // count 1 less 00517 while ( *(*cursor)!='\"' ) { nextChar(cursor,yyline); (*yyleng)++; } 00518 (*yytext)++; nextChar(cursor,yyline); // skip quotes 00519 return ERAV::token::STRING; 00520 } 00521 else if ( isalpha(*(*cursor)) || *(*cursor)=='_' ) { 00522 *yyleng = 0; // count 1 less 00523 while ( 00524 isalpha(*(*cursor)) || isdigit(*(*cursor)) || 00525 *(*cursor)=='_' || *(*cursor)==':' ) 00526 { 00527 (*yyleng)++; nextChar(cursor,yyline); 00528 } 00529 return ERAV::token::ID; 00530 } 00531 else if ( *(*cursor)=='/' ) { 00532 if ( (*cursor)[1]=='>' ) { // "/>" SGT 00533 (*yyleng) = 2; (*cursor) += 2; 00534 return ERAV::token::SGT; 00535 } 00536 else { 00537 nextChar(cursor,yyline); 00538 return **yytext; 00539 } 00540 } 00541 else if ( *(*cursor)=='?' ) { 00542 if ( (*cursor)[1]=='>' ) { 00543 (*yyleng) = 2; (*cursor) += 2; 00544 return ERAV::token::QGT; // "?>" 00545 } 00546 else { 00547 nextChar(cursor,yyline); 00548 return **yytext; 00549 } 00550 } 00551 #if 0 00552 else if ( *(*cursor)=='=' ) { 00553 nextChar(cursor,yyline); 00554 return **yytext; 00555 } 00556 else if ( *(*cursor)==0 ) { 00557 nextChar(cursor,yyline); 00558 return **yytext; 00559 } 00560 #endif 00561 else { 00562 nextChar(cursor,yyline); 00563 return **yytext; 00564 } 00565 00566 // make sure that token numbers never collide with ASCII 00567 struct LTQ_is_bigger_than_255 { 00568 int check[ (ERAV::token::LTQ > 255) ? +1 : -1 ]; 00569 }; 00570 } 00571 00572 namespace ERAV { 00573 00574 /// (l<r)==>(-1) (l==r)==>((0)) (l>r)==>(+1). 00575 int tplcmp( const ERAV::tuple& l, const ERAV::tuple& r ); 00576 /// (l<r) ???. 00577 bool isLess( const ERAV::tuple& l, const ERAV::tuple& r ); 00578 00579 #ifdef USE_isLess 00580 00581 // Old implementation: I prefer tplcmp() 00582 bool isLess( const ERAV::tuple& l, const ERAV::tuple& r ) { 00583 if ( l.ID_ENT < r.ID_ENT ) { // compare [ID_ENT] 00584 return true; 00585 } 00586 bool eq = (l.ID_ENT == r.ID_ENT); 00587 00588 if ( eq && (l.REL.ID < r.REL.ID) ) { // compare [REL.ID] 00589 return true; 00590 } 00591 eq = eq && (l.REL.ID == r.REL.ID); 00592 00593 if ( eq && (l.REL.SUB < r.REL.SUB) ) { // compare [REL.SUB] 00594 return true; 00595 } 00596 eq = eq && (l.REL.SUB == r.REL.SUB); 00597 00598 char l_rel_type = tolower(l.REL.TYPE); // special case REL.TYPE == eREL_TYPE 00599 char r_rel_type = tolower(r.REL.TYPE); 00600 00601 if ( eq && (l_rel_type != r_rel_type) ) { 00602 if ( r_rel_type==ERAV::attrib::eREL_TYPE ) { // eREL_TYPE is always bigger 00603 return false; 00604 } 00605 else if ( l_rel_type==ERAV::attrib::eREL_TYPE ) { 00606 return true; 00607 } 00608 else { 00609 return ( l_rel_type < r_rel_type ); 00610 } 00611 } 00612 return false; // ( eq && (l_rel_type == r_rel_type) ) 00613 } 00614 00615 #else // USE_isLess ==> NOT defined 00616 00617 /* NOTE: 00618 [] REL.TYPE == attrib::eREL_TYPE has precedence over the other TYPE's to 00619 have each record name appear before its attributes inlist<ERAV::tuple>. 00620 This ensures that <entity> and <record> tags come before <attrib> tags 00621 in list<ERAV::tuple>. 00622 [] The implementations for functions tplcmp() && isLess() are inside the ERAV 00623 namespace to avoid naming conflicts. 00624 [] When ordering a list of tuples using the ERAV::tuple_compare() funtor 00625 all tuples for each entity are contiguos. Furthermore, as attrib::eREL_TYPE 00626 REL_TYPE takes precedence, the first record of a new record entity 00627 comes first in the std::list<ERAV::tuple>. 00628 [] Lexicographic Comparison for an \c "ERAV_Tuple". 00629 - Comparison criteria: 00630 <code> { ID_ENT ~ REL.ID ~ REL.SUB ~ REL.TYPE }</code> 00631 - REL.TYPE==attrib::eREL_TYPE has precedence and its always 00632 smaller than the rest. 00633 - This is the sort criteria used by list::sort() for sorting 00634 list<ERAV::tuple>. 00635 */ 00636 int tplcmp( const ERAV::tuple& l, const ERAV::tuple& r ) { 00637 enum { lt=-1, eq=0, gt=+1 }; 00638 if ( l.ID_ENT < r.ID_ENT ) { // compare [ID_ENT] 00639 return lt; 00640 } 00641 else if ( l.ID_ENT > r.ID_ENT ) { 00642 return gt; 00643 } 00644 assert( l.ID_ENT == r.ID_ENT ); 00645 00646 if ( l.REL.ID < r.REL.ID ) { // compare [REL.ID] 00647 return lt; 00648 } 00649 else if ( l.REL.ID > r.REL.ID ) { 00650 return gt; 00651 } 00652 assert( l.REL.ID == r.REL.ID ); 00653 00654 if ( l.REL.SUB < r.REL.SUB ) { // compare [REL.SUB] 00655 return lt; 00656 } 00657 else if ( l.REL.SUB > r.REL.SUB ) { 00658 return gt; 00659 } 00660 assert( l.REL.SUB == r.REL.SUB ); 00661 00662 char l_rel_type = tolower(l.REL.TYPE); // special case REL.TYPE == eREL_TYPE 00663 char r_rel_type = tolower(r.REL.TYPE); 00664 { 00665 if ( l_rel_type==ERAV::attrib::eREL_TYPE ) { 00666 if ( r_rel_type==ERAV::attrib::eREL_TYPE ) { 00667 return eq; 00668 } 00669 else { 00670 return lt; 00671 } 00672 } 00673 else if ( r_rel_type==ERAV::attrib::eREL_TYPE ) { 00674 return gt; 00675 } 00676 else if ( l_rel_type < r_rel_type ) { 00677 return lt; 00678 } 00679 else if ( l_rel_type > r_rel_type ) { 00680 return gt; 00681 } 00682 assert( l_rel_type == r_rel_type ); 00683 return eq; 00684 } 00685 } 00686 00687 #endif 00688 00689 } // namespace ERAV 00690 00691 00692 #define USE_algorithm 00693 #undef USE_algorithm 00694 00695 #ifdef USE_algorithm 00696 #include <algorithm> 00697 /* NOTE: 00698 I didn´t want to include the <algorithm> header file to slim down the program. 00699 If you don´t care, just define macro USE_algorithm and the STL find_if() 00700 algorithm will be used instead of my straightforward loop implementation. 00701 */ 00702 00703 template<class InputIterator, class Predicate> 00704 InputIterator find_if( InputIterator first, InputIterator last, Predicate pred ) { 00705 for ( ; first!=last ; first++ ) { 00706 if ( pred(*first) ) { 00707 break; 00708 } 00709 } 00710 return first; 00711 } 00712 00713 struct DESCR_cmp { 00714 typedef ERAV::WORD::DESCR::const_iterator iter; 00715 DESCR_cmp( std::string value ) : m_value(value) { } 00716 bool operator()( const std::pair<std::string,std::string>& l ) { 00717 return ( l.first == m_value ); 00718 } 00719 private: 00720 std::string m_value; 00721 }; 00722 #endif 00723 00724 #ifdef USE_algorithm 00725 inline 00726 #endif 00727 ERAV::WORD::DESCR::iterator ERAV::WORD::lFind( 00728 DESCR& L, 00729 const std::string& lang 00730 ) { 00731 #ifdef USE_algorithm 00732 // lFind() should be inline 00733 return std::find_if( L.begin(), L.end(), DESCR_cmp(lang) ); 00734 #else 00735 DESCR::iterator jt = L.begin(); 00736 while ( jt!=L.end() ) { 00737 if ( jt->first == lang ) { 00738 break; 00739 } 00740 ++jt; 00741 } 00742 return jt; 00743 #endif 00744 } 00745 00746 std::string ERAV::WORD::lookUp( int n , const std::string& lang ) const { 00747 MAP::const_iterator it; // search with ID_WORD 00748 it = m_WORD.find(n); 00749 if ( it == m_WORD.end() ) { 00750 return std::string(); 00751 } 00752 else { // seach with LANG ( 'es' 'en' '**' etc ) 00753 DESCR::const_iterator jt; 00754 jt = lFind( it->second, lang ); 00755 if ( jt != it->second.end() ) { 00756 return jt->second; 00757 } 00758 jt = lFind( it->second, "**" ); 00759 if ( jt != it->second.end() ) { 00760 return jt->second; 00761 } 00762 return std::string(); 00763 } 00764 } 00765 00766 bool ERAV::WORD::update( int n , const std::string& lang , const std::string& descr ) { 00767 MAP::iterator it; // search with ID_WORD 00768 it = m_WORD.find(n); 00769 if ( it == m_WORD.end() ) { 00770 std::pair<MAP::iterator,bool> ins; 00771 ins = m_WORD.insert( MAP::value_type(n,DESCR()) ); 00772 if ( ins.second ) { 00773 ins.first->second.push_back( DESCR::value_type(lang,descr) ); 00774 } 00775 return ins.second; 00776 } 00777 else { 00778 DESCR::iterator jt; 00779 jt = lFind( it->second, lang ); 00780 if ( jt == it->second.end() ) { 00781 it->second.push_back( DESCR::value_type(lang,descr) ); 00782 } 00783 else { 00784 jt->second = descr; // write over 00785 } 00786 return true; 00787 } 00788 } 00789 00790 void ERAV::WORD::erase( int n ) { 00791 MAP::iterator it = m_WORD.find(n); // search with ID_WORD 00792 if ( it != m_WORD.end() ) { 00793 m_WORD.erase( it ); 00794 } 00795 } 00796 00797 void ERAV::WORD::toList( std::list<ERAV::WORD_tuple>& L ) const { 00798 WORD_tuple tuple; 00799 ERAV::WORD::MAP::const_iterator it; 00800 ERAV::WORD::DESCR::const_iterator jt; 00801 for ( it=m_WORD.begin(); it!=m_WORD.end() ; ++it ) { 00802 for ( jt=it->second.begin(); jt!=it->second.end() ; ++jt ) { 00803 tuple.ID_WORD = it->first; 00804 tuple.LANG[0] = jt->first[0]; 00805 tuple.LANG[1] = jt->first[1]; 00806 tuple.DESCR = jt->second; 00807 L.push_back( tuple ); 00808 } 00809 } 00810 } 00811 00812 // Exclude from Doxygen Documentation 00813 // http://www.doxygen.org/commands.html#cmdcond 00814 /// @cond 00815 // { 00816 00817 // toupper( lexeme ) == str ??? 00818 // - Requires str == toupper( str ) 00819 bool upCMP( const std::string& lexeme, const char* str ) { 00820 std::string::const_iterator it, end; 00821 it = lexeme.begin(); 00822 end = lexeme.end(); 00823 while ( (*str!=0) && (it!=end) ) { 00824 if ( toupper(*it) != *str ) { 00825 return false; 00826 } 00827 ++str; ++it; 00828 } 00829 return (it==end) && (*str==0); 00830 // for_each( LEXEME.begin(), LEXEME.end(), toupper ); 00831 } 00832 00833 // keywords // toupper( keywords ) 00834 const char sErav [] ="erav"; const char sERAV [] ="ERAV"; 00835 const char sID_ENT [] = "ID_ENT"; 00836 const char sREL_ID [] = "REL_ID"; 00837 const char sREL_SUB [] = "REL_SUB"; 00838 const char sREL_TYPE [] = "REL_TYPE"; 00839 const char sAttrib [] = "attrib"; const char sATTRIB [] = "ATTRIB"; 00840 const char sVAL [] = "VAL"; 00841 const char sID_WORD [] = "ID_WORD"; 00842 const char sID_STRING[] = "ID_STRING"; 00843 const char sLANG [] = "LANG"; 00844 const char sDescr [] = "descr"; const char sDESCR [] = "DESCR"; 00845 const char sEntity [] = "entity"; const char sENTITY [] = "ENTITY"; 00846 const char sRecord [] = "record"; const char sRECORD [] = "RECORD"; 00847 const char sWord [] = "word"; const char sWORD [] = "WORD"; 00848 const char sString [] = "string"; const char sSTRING [] = "STRING"; 00849 const char sXml [] = "xml"; const char sXML [] = "XML"; 00850 00851 void ERAV::token::setTagAttrib() { 00852 // all comparisons are made with the uppercase version of each string 00853 if ( upCMP( m_lexeme , sATTRIB ) ) { m_tag_attrib = attrib::ATTRIB; } 00854 else if ( upCMP( m_lexeme , sVAL ) ) { m_tag_attrib = attrib::VAL; } 00855 else if ( upCMP( m_lexeme , sREL_TYPE ) ) { m_tag_attrib = attrib::REL_TYPE; } 00856 else if ( upCMP( m_lexeme , sRECORD ) ) { m_tag_attrib = tag::record; } 00857 00858 else if ( upCMP( m_lexeme , sLANG ) ) { m_tag_attrib = attrib::LANG; } 00859 else if ( upCMP( m_lexeme , sDESCR ) ) { m_tag_attrib = attrib::DESCR; } 00860 00861 else if ( upCMP( m_lexeme , sWORD ) ) { m_tag_attrib = tag::word; } 00862 else if ( upCMP( m_lexeme , sSTRING ) ) { m_tag_attrib = tag::string; } 00863 00864 else if ( upCMP( m_lexeme , sENTITY ) ) { m_tag_attrib = tag::entity; } 00865 else if ( upCMP( m_lexeme , sID_ENT ) ) { m_tag_attrib = attrib::ID_ENT; } 00866 00867 else if ( upCMP( m_lexeme , sID_WORD ) ) { m_tag_attrib = attrib::ID_WORD; } 00868 else if ( upCMP( m_lexeme , sID_STRING ) ) { m_tag_attrib = attrib::ID_STRING; } 00869 00870 else if ( upCMP( m_lexeme , sREL_ID ) ) { m_tag_attrib = attrib::REL_ID; } 00871 else if ( upCMP( m_lexeme , sREL_SUB ) ) { m_tag_attrib = attrib::REL_SUB; } 00872 else if ( upCMP( m_lexeme , sERAV ) ) { m_tag_attrib = tag::erav; } 00873 else if ( upCMP( m_lexeme , sXML ) ) { m_tag_attrib = tag::head; } 00874 else { m_tag_attrib = attrib::NONE; } 00875 // the more used tags are tested for first 00876 00877 /* The SAME code values are used for these record tag and attrib codes: 00878 - attrib::NONE == tag::none 00879 - attrib::DESCR == tag::desc 00880 - attrib::ATTRIB == tag::attrib 00881 This helps because a token::setTagAttrib() method can be used to set both 00882 attrib and record tag codes. The following code insures at compile time 00883 that these equalities hold. 00884 */ 00885 // Compile time ensure attrib::NONE == tag::none, etc. 00886 struct NONE_DESCR_ATTRIB { 00887 int vec_NONE[ 0==(attrib::NONE-tag::none) ? +1 : -1 ]; 00888 int vec_DESCR[ 0==(attrib::DESCR-tag::descr) ? +1 : -1 ]; 00889 int vec_ATTRIB[ 0==(attrib::ATTRIB-tag::attrib) ? +1 : -1 ]; 00890 int LAST_gt_last[0<( attrib::LAST-tag::last ) ? +1 : -1 ]; 00891 }; 00892 #define isEqual(a,b) ( 0 ==(a)-(b) ? +1 : -1 ) 00893 #define myAbs(a) ( (a)>0 ? (a) : -(a) ) 00894 #define isBigger(a,b) ( 0 <myAbs(a)-myAbs(b) ? +1 : -1 ) 00895 struct none_descr_attrib { 00896 int vec_NONE[ isEqual( attrib::NONE , tag::none ) ]; 00897 int vec_DESCR[ isEqual( attrib::DESCR , tag::descr ) ]; 00898 int vec_ATTRIB[ isEqual( attrib::ATTRIB , tag::attrib ) ]; 00899 int LAST_gt_last[ isBigger( attrib::LAST , tag::last ) ]; 00900 int eREL_TYPE_islower[ isBigger( 'z'-'a' , attrib::eREL_TYPE-'a' ) ]; 00901 }; 00902 #undef isBigger 00903 #undef myAbs 00904 #undef isEqual // @cond @endcond tells Doxygen not to document this 00905 } 00906 00907 /// Tree->node [ erav2xml() ]. 00908 class node { 00909 friend void erav2xml(std::list<ERAV::tuple>& L, std::string& XML ); 00910 friend void recursive_store( std::string& XML, node * root , int indent, 00911 std::list<ERAV::tuple>::const_iterator stop); 00912 std::list<ERAV::tuple>::const_iterator it; ///< Tree->data 00913 std::list< node* > L; ///< Tree->children 00914 node() : it(), L() { } ///< Constructor 00915 ~node(); ///< Destructor 00916 }; 00917 00918 node::~node() { 00919 if ( ! L.empty() ) { 00920 std::list< node* >::iterator jt; 00921 for ( jt=L.begin(); jt!=L.end(); ++jt ) { 00922 delete (*jt); 00923 } 00924 L.clear(); 00925 } 00926 } 00927 00928 void recursive_store( std::string& XML, node *root , int indent , 00929 std::list<ERAV::tuple>::const_iterator stop 00930 ) { 00931 using std::string; 00932 std::list<ERAV::tuple>::const_iterator jt = root->it; 00933 //* DELETE */ ERAV::tuple view = *jt; 00934 char rel_type = tolower(jt->REL.TYPE); 00935 if ( rel_type!=ERAV::attrib::eREL_TYPE ) { 00936 return; // error( "first tuple must be eREL_TYPE <record>" ) 00937 } 00938 //* DELETE */ int rel_id = jt->REL.ID; 00939 00940 std::string STR; 00941 { // <record> 00942 for (int i=0; i<indent; ++i) { STR += '\t'; } 00943 STR += string() + '<' + sRecord + ' '; 00944 #if 0 00945 STR += string() + sREL_ID + "=\"" + my_itoa( jt->REL.ID ) + "\" "; 00946 #endif 00947 STR += string() + sREL_TYPE + "=\"" + ERAV::attrib::eREL_TYPE + "\" "; 00948 STR += string() + sATTRIB + "=\"" + my_itoa( jt->ATTRIB ) + "\" >\n"; 00949 XML += STR; STR.clear(); 00950 ++jt; 00951 //* DELETE */ if (jt!=stop) { view = *jt; } // can´t view the last tuple 00952 } 00953 bool done = (jt==stop); // root->[ it, std::list<node*> ] 00954 while ( ! done ) { 00955 rel_type = tolower(jt->REL.TYPE); 00956 if ( rel_type==ERAV::attrib::eREL_TYPE ) // next <record> reached 00957 #if 1 00958 { 00959 done = true; 00960 } 00961 #else 00962 { 00963 if ( rel_id == jt->REL.ID ) { 00964 ++jt; // skip duplicate record 00965 done = (jt==stop); 00966 } 00967 else { 00968 done = true; 00969 } 00970 } 00971 #endif 00972 else { // <attrib> 00973 for (int i=0; i<indent; ++i) { STR += '\t'; } 00974 STR += string() + "\t<" + sAttrib + ' '; 00975 STR += string() + sREL_TYPE + "=\"" + rel_type + "\" "; 00976 STR += string() + sATTRIB + "=\"" + my_itoa( jt->ATTRIB ) + "\" "; 00977 STR += string() + sVAL + "=\"" + jt->VAL.BLOB + "\" />\n"; 00978 XML += STR; STR.clear(); 00979 ++jt; 00980 //* DELETE */ if (jt!=stop) { view = *jt; } // can´t view the last tuple 00981 done = (jt==stop); 00982 } 00983 } 00984 { // recursive store of every child 00985 std::list< node* >::const_iterator it, end; 00986 end = root->L.end(); 00987 it = root->L.begin(); 00988 for ( ; it!=end; ++it ) { 00989 recursive_store( XML, *it, indent+1 , stop ); 00990 } 00991 } 00992 00993 // </record> 00994 for (int i=0; i<indent; ++i) { STR += '\t'; } 00995 STR += string() + "</" + sRecord + ">\n"; 00996 //* DELETE */ std::cout << STR; 00997 XML += STR; 00998 // STR.clear(); // not necessary 00999 } 01000 01001 // } 01002 /// @endcond 01003 01004 // Stores tuples from L as an XML document in string XML 01005 01006 // L<tuple> ==> XML ((append)) 01007 void erav2xml( std::list<ERAV::tuple>& L, std::string& XML ) { 01008 using std::string; 01009 L.sort( ); 01010 L.unique( ); // remove duplicates 01011 //* DELETE */ dump_tuples( L ); 01012 01013 std::list<ERAV::tuple>::const_iterator itB; 01014 std::list<ERAV::tuple>::const_iterator it,endE; // itB ~ it BIG loop 01015 typedef std::map< int, node* > M_type; 01016 std::pair<M_type::iterator,bool> ret; // for M_type.insert() 01017 M_type::iterator mt; 01018 M_type M; // M[id_red]==>root(node*) 01019 01020 for ( itB=L.begin(); itB != L.end(); itB=endE ) { 01021 01022 node *root = 0; 01023 // make nodes && find end of current <entity> && build M[#]->list<node*> 01024 it=itB; bool done = (it==L.end()); 01025 while ( ! done ) { 01026 //* DELETE */ ERAV::tuple view = *it; 01027 node *p; 01028 if ( it->REL.TYPE==ERAV::attrib::eREL_TYPE ) { // creates the nodes for the tree 01029 mt = M.find(it->REL.ID); 01030 if ( mt==M.end() ) { 01031 p = new node(); 01032 p->it = it; 01033 ret = M.insert( M_type::value_type(it->REL.ID, p) ); 01034 //* DELETE */ std::cout << it->REL.ID << ' '; 01035 01036 // sets root's field 01037 if ( it->REL.ID == it->REL.SUB ) { 01038 // only the root node has these 2 fields equal 01039 if ( root == 0 ) { root = p; } 01040 } 01041 } 01042 else { /* IGNORE: duplicate <record> tuple */ } 01043 // The numbering used for REL.ID && REL.SUB is arbitrary; 01044 // this implies that: 01045 // - The first record is NOT always the root record. 01046 // - Parent nodes don´t necessarily come before child nodes. 01047 } 01048 01049 ++it; 01050 endE = it; // marks past last tupple for this <entity> 01051 if ( it==L.end() ) { break; } 01052 done = ( it->ID_ENT != itB->ID_ENT ); 01053 } 01054 01055 //* DELETE */ std::cout << std::endl; 01056 // create the list of children for each node 01057 for ( mt=M.begin(); mt!=M.end(); ++mt ) { 01058 int rel_id = mt->second->it->REL.ID; assert( M.find(rel_id) != M.end() ); 01059 int rel_sub = mt->second->it->REL.SUB; assert( M.find(rel_sub) != M.end() ); 01060 if ( rel_id != rel_sub ) { // avoid root cycle 01061 M[rel_sub]->L.push_back( M[rel_id] ); 01062 //* DELETE */ std::cout << "M["<<rel_sub<<"]->L.push_back( M["<<rel_id <<"] )" << std::endl; 01063 } 01064 } 01065 //* DELETE */ std::cout << "M.size() == " << M.size() << std::endl; 01066 M.clear(); // cleanup asap 01067 01068 // store 01069 { // <entity> 01070 XML += string() + "\t<" +sEntity+'\t'+sID_ENT + "=\""; 01071 XML += string() + my_itoa( itB->ID_ENT ) + "\">\n"; 01072 { // <record> && <attrib> 01073 recursive_store( XML, root , 2 , L.end() ); 01074 } 01075 XML += string() + "\t</" + sEntity + ">\n"; 01076 } 01077 01078 { 01079 delete root; // cleanup tree 01080 } 01081 } // for() ~ itB 01082 } 01083 01084 void erav2xml_enclose( std::string& XML ) { 01085 using std::string; 01086 XML = string() 01087 + "<?" + sXml + " version=\"1.0\" encoding=\"Windows-1252\"?>\n" 01088 + "<" + sErav + ">\n" 01089 + XML; 01090 XML += string()+"</"+sErav+">\n"; 01091 } 01092 01093 void erav2xml( const std::list<ERAV::WORD_tuple>& L, ERAV::WORD& W ) { 01094 typedef std::list<ERAV::WORD_tuple>::const_iterator iter; 01095 iter it, end = L.end(); 01096 for ( it=L.begin(); it!=end ; ++it ) { 01097 W.update( it->ID_WORD , std::string(it->LANG,ERAV::LANG_LEN), it->DESCR ); 01098 } 01099 } 01100 01101 void erav2xml( const std::list<ERAV::WORD_tuple>& L, std::string& XML ) { 01102 ERAV::WORD W; 01103 erav2xml( L,W ); 01104 erav2xml( W,XML ); 01105 } 01106 01107 void erav2xml( const ERAV::WORD& W , std::string& XML, bool isWORD ) { 01108 using std::string; 01109 const char* sTag = ( isWORD ? sWord : sString ); 01110 const char* sID = ( isWORD ? sID_WORD : sID_STRING ); 01111 ERAV::WORD::MAP::const_iterator it, wend=W.m_WORD.end(); 01112 ERAV::WORD::DESCR::const_iterator jt, lend; 01113 for ( it=W.m_WORD.begin(); it!= wend; ++it ) { 01114 XML += string() + "\t<" + sTag + '\t' + sID + "=\""; 01115 XML += string() + my_itoa(it->first) + "\" >\n"; 01116 lend = it->second.end(); 01117 for ( jt=it->second.begin(); jt!= lend; ++jt ) { 01118 XML += string() + "\t\t<" + sDescr + ' '; 01119 XML += string() + sLANG + "=\"" + jt->first + "\" "; 01120 XML += string() + sDESCR + "=\"" + jt->second + "\" />\n"; 01121 } 01122 XML += string() + "\t</" + sTag +">\n"; 01123 } 01124 } 01125 01126 // Load L from FROM skipping duplicates. 01127 // - Skips duplicate ID_ENT records already in L 01128 // - Duplicates remain in FROM 01129 // - Does not copy: uses std::list<>::splice() 01130 void ERAV::merge_into( std::list<ERAV::tuple>& L, std::list<ERAV::tuple>& FROM ) { 01131 if ( &L == &FROM ) { return; } // avoid auto-copy 01132 L.sort(); L.unique(); 01133 FROM.sort(); FROM.unique(); 01134 std::list<ERAV::tuple>::iterator itL = L.begin(); 01135 std::list<ERAV::tuple>::iterator itF = FROM.begin(); 01136 std::list<ERAV::tuple>::iterator stop; 01137 01138 while ( itL!=L.end() && itF!=FROM.end() ) { 01139 if ( itL->ID_ENT < itF->ID_ENT ) { // advance in L 01140 for ( stop=itL ; stop->ID_ENT==itL->ID_ENT ; ++stop ) { 01141 if ( stop==L.end() ) { break; } 01142 } 01143 itL = stop; 01144 } 01145 else if ( itL->ID_ENT > itF->ID_ENT ) { // splice chunk into L 01146 for ( stop=itF ; stop->ID_ENT==itF->ID_ENT ; ++stop ) { 01147 if ( stop==FROM.end() ) { break; } 01148 } 01149 L.splice( itL, FROM, itF, stop ); 01150 itF = stop; 01151 } 01152 else { // skip duplicate in FROM 01153 assert( itL->ID_ENT == itF->ID_ENT ); 01154 for ( stop=itF ; stop->ID_ENT==itF->ID_ENT ; ++stop ) { 01155 if ( stop==FROM.end() ) { break; } 01156 } 01157 itF = stop; 01158 } 01159 } 01160 01161 if (itL != L.end() ) { 01162 return; 01163 } 01164 if (itF != FROM.end() ) { 01165 L.splice( L.end(), FROM, itF, FROM.end() ); 01166 return; 01167 } 01168 } 01169 01170 #ifdef Spanish_dox 01171 /// Construye \c S una hilera XML a partir de los valores \c ERAV almacenados en una copia \c L. 01172 /// - Usa el diccionario \c W de palabras \c ERAV_WORD para interpretar los valores de la lista \c L. 01173 #endif 01174 #ifdef English_dox 01175 /// Builds \c S an XML string from the \c ERAV values stored in a copy of \c L. 01176 /// - Uses diccionary \c W of \c ERAV_WORD to interpret values from list \c L. 01177 #endif 01178 /** \dontinclude test_ERAV.cpp 01179 \skipline test::erav2xml() 01180 \until }} 01181 \see test_ERAV::test_erav2xml() 01182 */ 01183 /// \fn void erav2xml( std::list<ERAV_tuple> L , std::string& XML , const ERAV_WORD& W ); 01184 01185 /***************\ 01186 |* *| 01187 |* DOXYGEN *| 01188 |* *| 01189 \***************/ 01190 01191 #ifdef Spanish_dox 01192 /** \file ERAV.h 01193 \brief Biblioteca para manipular Relaciones Jerárquicas Anidadas. 01194 \author Adolfo Di Mare <adolfo@di-mare.com> 01195 \date 2010 01196 - Why English names??? ==> http://www.di-mare.com/adolfo/binder/c01.htm#sc04 01197 */ 01198 #endif 01199 #ifdef English_dox 01200 /** \file ERAV.h 01201 \brief Library to handle Nested Hierarchical Relations. 01202 \author Adolfo Di Mare <adolfo@di-mare.com> 01203 \date 2010 01204 - Why English names??? ==> http://www.di-mare.com/adolfo/binder/c01.htm#sc04 01205 */ 01206 #endif 01207 01208 #ifdef Spanish_dox 01209 /** \file ERAV.cpp 01210 \brief Implementaciones para \c ERAV.h. 01211 \author Adolfo Di Mare <adolfo@di-mare.com> 01212 \date 2010 01213 - Why English names??? ==> http://www.di-mare.com/adolfo/binder/c01.htm#sc04 01214 */ 01215 #endif 01216 #ifdef English_dox 01217 /** \file ERAV.cpp 01218 \brief Implementation for \c ERAV.h. 01219 \author Adolfo Di Mare <adolfo@di-mare.com> 01220 \date 2010 01221 - Why English names??? ==> http://www.di-mare.com/adolfo/binder/c01.htm#sc04 01222 */ 01223 #endif 01224 01225 #ifdef Spanish_dox 01226 /// Estos son los campos de la relación \c ERAV en la base de datos. 01227 #endif 01228 #ifdef English_dox 01229 /// These are the fields for the \c ERAV relation in the data base. 01230 #endif 01231 /// \class class ERAV_tuple; 01232 01233 #ifdef Spanish_dox 01234 /// Diccionario que contiene los tuples de la tabla \c WORD del \c ERAV. 01235 #endif 01236 #ifdef English_dox 01237 /// Diccionary that contians the tuples from the \c WORD in the \c ERAV. 01238 #endif 01239 /// \class class WORD; 01240 01241 #ifdef Spanish_dox 01242 /// Valores almacenados en relación \c WORD de la base de datos \c ERAV. 01243 #endif 01244 #ifdef English_dox 01245 /// Values stored in the \c WORD table of the \c ERAV. 01246 #endif 01247 /// \class class WORD_tuple; 01248 01249 01250 #ifdef Spanish_dox 01251 /// Utilizando los tuples de \c "L" llena \c "XML" en formato de documento XML. 01252 #endif 01253 #ifdef English_dox 01254 /// Uses tuples in \c L" to fill \c "XML" in XML document format. 01255 #endif 01256 /// \fn erav2xml( std::list<ERAV::tuple>& L, std::string& XML ); 01257 01258 01259 01260 /* IMPLEMENTATION notes 01261 ==================== 01262 [] parser::m_lookahead is a pointer to preserve the previous token to be 01263 printed on error. It looks nice to see the last token and the next 01264 one on error(). 01265 [] parser::m_lookahead cannot be a reference because it would not be 01266 posible to change it after initialization. 01267 [] parser::deleteXML is used to ensure deletion of the copy of the string 01268 made when it was detected that method string::c_str() was used, because 01269 it returns a pointer into a global static area which would hinder using 01270 c_str() in this implementation. Programmer should know this, but its 01271 better to be safe than sorry. 01272 [] Two implementations of the WORD::lookup() method are needed, but they 01273 use the same algorithm. A [[ lFind( *const_cast<DESCR*>(&L), lang ) ]] 01274 trick is used to have them share the same implementation. 01275 [] parser::m_lang[4] has 4 bytes to help in alignment Ughhh??? 01276 [] Macro [[[ #define ID_val const std::string & lexeme ]]] was used to put 01277 the XML grammar in the declaration of the methods that recognize each 01278 non-terminal. It's a harmless quirk. 01279 [] Instead of using template function toString() the non-ansii itoa() 01280 function was used. This avoids including all the <sstream> parafernalia 01281 in this module. I further use my_itoa() that uses a global buffer. 01282 [] Upper and lower case string constants sString && sSTRING, etc where 01283 used to avoid converting them to uppercase in function upCMP(). 01284 [] Both parser( const parser& ); && void operator= ( const parser& ); 01285 (copy constructor && copy operator) are declared for the parser class, 01286 but none is implemented. It never makes sense to copy a parser object. 01287 [] As 2 fields parser::m_id && parser::m_id_word exist, its is posible to 01288 mix [ <word> && <string> ] XML values within <record> values. Perhaps 01289 that should not be permited, but it more fun to intermix those values 01290 in the XML document (and it's easier to program). 01291 [] Even when REL_ID && REL_SUB fields are included in the XML document, 01292 the hierarchical relationshiep between record values is determined by 01293 their relative nesting in the XML document. Field parser::m_nest_id 01294 holds the next REL_ID that gets used, ignoring the values that where 01295 written the REL_ID && REL_SUB fields of the XML document. 01296 [] Field parser::m_context_old is used to hold deleted context objects to 01297 reuse them later. This is easy to do and speeds up execution a little 01298 bit. 01299 */ 01300 01301 01302 // EOF: ERAV.cpp