// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/> // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. #ifndef UTILS_H_ #define UTILS_H_ // DtName must be the 1st one enum TDataCol { DtName, DtTitle, DtRMFamily, DtGroup, DtEcosystem, DtLevelZone, DtStatQuality, DtProp, DtCreature, DtCreaTitle, DtCraftSlotName, DtCraftCivSpec, DtColor, DtAverageEnergy, DtMaxLevel, DtCustomizedProperties, DtNbCols }; const char *DataColStr [DtNbCols] = { "Code", "Name", "Family", "Group", "Ecosystem", "LevelZone", "Stat Quality", "Properties", "Creature sheets", "Creatures", "Item parts", "Craft civ spec", "Color", "Average energy", "Max level", "Customized" }; /* * Multi-indexed array. * NC is the number of columns. */ template <uint32 NC> class CSortableData { public: /// A row is made of fields, usually 1 per column but there may be more than one (each one is a key) struct TSortableItem { std::vector<std::string> Fields [NC]; /// void push( uint32 column, const std::string& f, bool allowDuplicates=false ) { if ( (allowDuplicates) || (find( Fields[column].begin(), Fields[column].end(), f ) == Fields[column].end()) ) Fields[column].push_back( f ); } /// void reset( uint32 column ) { Fields[ column ].clear(); } /** * Display the item as a row of a HTML table. * If (key!=previousKey) and (name==previousName), the row will not be displayed entirely to save space * * \param keyColumn If not ~0, column used for sorting => this column displays only the field matching the key * \param key The key used for sorting (see keyColumn) * \param previousKey Previous key * \param nameColumn If not ~0, column used for the unique name (column must have exaclty one element) * \param previousName Previous name */ std::string toHTMLRow( uint32 keyColumn=~0, const string& key=string(), const string& previousKey=string(), uint32 nameColumn=~0, const string& previousName=string() ) const { std::string s = "<tr>"; bool lightMode = (nameColumn == ~0) ? false : ((key != previousKey) && (Fields[nameColumn][0] == previousName)); for ( uint32 c=0; c!=NC; ++c ) { s += "<td>"; if ( c == keyColumn ) s += key; // key should be a substr of toString( c ) else { if ( lightMode ) s += "\""; else s += columnToString( c ); } s += "</td>"; } s += "</tr>\n"; return s; } /// std::string toCSVLine( char columnSeparator=',', string internalSeparator=" - ", uint32 keyColumn=~0, const string& key=string(), const string& previousKey=string(), uint32 nameColumn=~0, const string& previousName=string() ) const { std::string s; bool lightMode = (nameColumn == ~0) ? false : ((key != previousKey) && (Fields[nameColumn][0] == previousName)); for ( uint32 c=0; c!=NC; ++c ) { if ( c == keyColumn ) s += key; // key should be a substr of columnToString( c ) else { if ( lightMode ) s += "\""; else s += columnToString( c, internalSeparator ); } s += columnSeparator; } s += "\n"; return s; } /// std::string columnToString( uint32 column, const std::string& internalSeparator=", " ) const { std::string s; std::vector<std::string>::const_iterator ivs; for ( ivs=Fields[column].begin(); ivs!=Fields[column].end(); ++ivs ) { if ( ivs!=Fields[column].begin() ) s += internalSeparator; s += (*ivs); } return s; } }; typedef std::multimap< std::string, uint32 > CLookup; // key to index (not pt because reallocation invalidates pointers) typedef std::vector< TSortableItem > CItems; /// Init void init( bool enabled ) { _Enabled = enabled; } /// Add a row void addItem( const TSortableItem& item ) { if ( ! _Enabled ) return; _Items.push_back( item ); for ( uint32 c=0; c!=NC; ++c ) { for ( std::vector<std::string>::const_iterator ik=item.Fields[c].begin(); ik!=item.Fields[c].end(); ++ik ) { _Indices[c].insert( make_pair( *ik, (uint32)_Items.size()-1 ) ); } } } /** * Update a row (found by the first column, which must have exactly one element). * Returns true if it existed before, false if it's being created. * If it existed before: * - Does not remove elements that already exist and are not in the new item * - Adds the new elements found in the new item at the specified columns, and updates lookup map */ bool updateItemAppend( const TSortableItem& item, uint32 column ) { if ( ! _Enabled ) return true; // quiet uint32 nameColumn = 0; CLookup::iterator ilk = _Indices[nameColumn].find( item.Fields[nameColumn][0] ); if ( ilk != _Indices[nameColumn].end() ) { uint32& index = (*ilk).second; // Update map for the specified column // and update item column for ( std::vector<std::string>::const_iterator ivs=item.Fields[column].begin(); ivs!=item.Fields[column].end(); ++ivs ) { ilk = _Indices[column].find( *ivs ); if ( (ilk == _Indices[column].end()) || ( getRow( (*ilk).second ).Fields[nameColumn][0] != item.Fields[nameColumn][0]) ) { _Indices[column].insert( make_pair( *ivs, index ) ); _Items[index].Fields[column].push_back( *ivs ); } } return true; } else { addItem( item ); return false; } } /** * Update a row (found by the first column, which must have exactly one element). * Returns true if it existed before, false if it's being created. * If it existed before: * - Does not update lookup maps or item for columns that were already present. * - Adds entries in lookup maps and updates item for new columns (fields that were empty). */ /*bool updateItemAppend( const TSortableItem& item ) { if ( ! _Enabled ) return true; // quiet CLookup::iterator ilk = _Indices[0].find( item.Fields[0][0] ); if ( ilk != _Indices[0].end() ) { uint32& index = (*ilk).second; for ( uint32 c=1; c!=NC; ++c ) { // Update maps for previously empty columns if ( _Items[index].Fields[c].empty() ) { for ( std::vector<std::string>::iterator ivs=item.Fields[c].begin(); ivs!=item.Fields[c].end(); ++ivs ) _Indices[c].insert( make_pair( *ivs, index ) ); } // Update item column _Items[index].Fields[c] = item.Fields[c]; } return true; } else { addItem( item ); return false; } }*/ /// Find or browse by key CLookup& lookup( uint32 column ) { return _Indices[column]; } /// Browse by adding order CItems& items() { return _Items; } /// Get a row by index TSortableItem& getRow( uint32 index ) { return _Items[index]; } private: CLookup _Indices [NC]; CItems _Items; bool _Enabled; }; typedef CSortableData<DtNbCols> CRMData; typedef CRMData::TSortableItem TRMItem; /** * */ class CProducedDocHtml { public: /// CProducedDocHtml() : _File(NULL), _Enabled(false) {} /// void open( const std::string& filename, const std::string& title, bool enableFlag ) { _Enabled = enableFlag; if ( ! _Enabled ) return; _File = fopen( filename.c_str(), "wt" ); if(!_File) { throw Exception("Could not open html: %s", filename.c_str()); } fprintf( _File, ("<html><head>\n<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n<title>" + title + "</title>\n</head><body>\n").c_str() ); } /// void write( const std::string& htmlCode ) { if ( ! _Enabled ) return; fprintf( _File, htmlCode.c_str() ); } /// void writeln( const std::string& htmlCode ) { write( htmlCode + "\n" ); } /// void writebln( const std::string& htmlCode ) { write( htmlCode + "<br>\n" ); } /// void writepln( const std::string& htmlCode ) { write( "<p>" + htmlCode + "</p>\n" ); } /// void save() { if ( ! _Enabled ) return; fprintf( _File, "</body></html>\n" ); fclose( _File ); } private: FILE *_File; bool _Enabled; }; #endif