ERAV: Entity Relation Attribute Value:
ERAV.cpp
Go to the documentation of this file.
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerator Friends Defines