WORM 0.2
A C++ DAL/ORM code generation framework

src/orm/wormclassgenerator.cpp

Go to the documentation of this file.
00001 /*
00002     WORM - a DAL/ORM code generation framework
00003     Copyright (C) 2011  Erik Winn <sidewalksoftware@gmail.com>
00004 
00005     This program is free software: you can redistribute it and/or modify
00006     it under the terms of the GNU General Public License as published by
00007     the Free Software Foundation, either version 3 of the License, or
00008     (at your option) any later version.
00009 
00010     This program is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013     GNU General Public License for more details.
00014 
00015     You should have received a copy of the GNU General Public License
00016     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017 */
00018 
00019 //class_definition.tpl is currently the shortest ..
00020 #define MIN_TPL_FILENAME_SIZE 20  
00021 
00022 #include "wormclassgenerator.h"
00023 #include <ctemplate/template.h>
00024 #include <dirent.h>
00025 #include <errno.h>
00026 #include <fstream>
00027 
00028 using namespace ctemplate;
00029 
00030 namespace WSql {
00035     static const ::ctemplate::StaticTemplateString kcd_INCLUDES = STS_INIT_WITH_HASH(kcd_INCLUDES, "INCLUDES", 7699670683738647257LLU);
00036     static const ::ctemplate::StaticTemplateString kcd_INCLUDE = STS_INIT_WITH_HASH(kcd_INCLUDE, "INCLUDE", 434435386609578605LLU);
00037     static const ::ctemplate::StaticTemplateString kcd_FORWARD_DECLARATIONS = STS_INIT_WITH_HASH(kcd_FORWARD_DECLARATIONS, "FORWARD_DECLARATIONS", 15273852411010322531LLU);
00038     static const ::ctemplate::StaticTemplateString kcd_REFERENCED_CLASSNAME = STS_INIT_WITH_HASH(kcd_REFERENCED_CLASSNAME, "REFERENCED_CLASSNAME", 5376938313052943395LLU);
00039     static const ::ctemplate::StaticTemplateString kcd_CLASS_NAME = STS_INIT_WITH_HASH(kcd_CLASS_NAME, "CLASS_NAME", 13981977283673860485LLU);
00040     static const ::ctemplate::StaticTemplateString kcd_BELONGS_TO = STS_INIT_WITH_HASH(kcd_BELONGS_TO, "BELONGS_TO", 15166195752158467517LLU);
00041     static const ::ctemplate::StaticTemplateString kcd_HAS_MANY = STS_INIT_WITH_HASH(kcd_HAS_MANY, "HAS_MANY", 12417993436827992317LLU);
00042     static const ::ctemplate::StaticTemplateString kcd_FOREIGNKEY_CLASSNAME = STS_INIT_WITH_HASH(kcd_FOREIGNKEY_CLASSNAME, "FOREIGNKEY_CLASSNAME", 14113744978891695861LLU);
00043     static const ::ctemplate::StaticTemplateString kcd_FOREIGNKEY_CLASS_PLURAL = STS_INIT_WITH_HASH(kcd_FOREIGNKEY_CLASS_PLURAL, "FOREIGNKEY_CLASS_PLURAL", 13464148753922874173LLU);
00044     static const ::ctemplate::StaticTemplateString kcd_COLUMNS = STS_INIT_WITH_HASH(kcd_COLUMNS, "COLUMNS", 15302874640052016969LLU);
00045     static const ::ctemplate::StaticTemplateString kcd_UNSUPPORTED = STS_INIT_WITH_HASH(kcd_UNSUPPORTED, "UNSUPPORTED", 8112833089436120679LLU);
00046     static const ::ctemplate::StaticTemplateString kcd_UNSIGNED = STS_INIT_WITH_HASH(kcd_UNSIGNED, "UNSIGNED", 10867561526856517727LLU);
00047     static const ::ctemplate::StaticTemplateString kcd_DATATYPE = STS_INIT_WITH_HASH(kcd_DATATYPE, "DATATYPE", 6518949878326190781LLU);
00048     static const ::ctemplate::StaticTemplateString kcd_VARIABLE_GETTOR = STS_INIT_WITH_HASH(kcd_VARIABLE_GETTOR, "VARIABLE_GETTOR", 4376112485907229951LLU);
00049     static const ::ctemplate::StaticTemplateString kcd_VARIABLE_NAME = STS_INIT_WITH_HASH(kcd_VARIABLE_NAME, "VARIABLE_NAME", 5051229879184672055LLU);
00050     static const ::ctemplate::StaticTemplateString kcd_VARIABLE_SETTOR = STS_INIT_WITH_HASH(kcd_VARIABLE_SETTOR, "VARIABLE_SETTOR", 18309610407346123363LLU);
00051     static const ::ctemplate::StaticTemplateString kcd_COLUMN_NAME = STS_INIT_WITH_HASH(kcd_COLUMN_NAME, "COLUMN_NAME", 16524890828269290931LLU);
00052     static const ::ctemplate::StaticTemplateString kcd_REFERENCED_TABLENAME = STS_INIT_WITH_HASH(kcd_REFERENCED_TABLENAME, "REFERENCED_TABLENAME", 14486319327059551333LLU);
00053     static const ::ctemplate::StaticTemplateString kcd_TABLE_NAME = STS_INIT_WITH_HASH(kcd_TABLE_NAME, "TABLE_NAME", 3760310134096538793LLU);
00088 WormClassGenerator::WormClassGenerator(WSqlDatabase& db):_db(db)
00089 {
00090 }
00101 bool WormClassGenerator::init()
00102 {
00103     //load available templates
00104     DIR *dir;
00105     struct dirent *ent;
00106         
00107     dir = opendir (_templateDirectory.c_str());
00108     if (dir != NULL) 
00109     {
00110         WormCodeTemplate tpl;
00111         while ((ent = readdir (dir)) != NULL) 
00112         {
00113             if(ent->d_type != DT_REG)
00114                 continue;
00115             std::string entry = ent->d_name;
00116             size_t sz = entry.size();
00117             if(sz < MIN_TPL_FILENAME_SIZE )
00118                 continue;
00119             //only accept .tpl files
00120             std::string ext = entry.substr(sz - 4);
00121             if( ext.compare(".tpl"))
00122                 continue;
00123             tpl.setUri(entry);
00124             _templates.push_back(tpl);
00125             
00126             /* maybe use, pass a string to ctemplate:            std::string tmp;
00127             fs.open(entry.c_str());
00128             while(fs)
00129                 std::getline(fs,tmp);
00130             tpl.setContent(tmp);
00131             */
00132         }
00133         closedir (dir);
00134     } else {
00135         std::string msg = "Cannot open directory: " + _templateDirectory;
00136         if(errno == EACCES )
00137             msg.append(" - Access denied.");
00138         _db.addError(WSqlError(msg, errno,WSql::WSqlError::SYSTEM, WSql::WSqlError::FATAL));
00139         return false;
00140     }
00141     _db.initMetaData();
00142     return true;
00143 }
00155 void WormClassGenerator::run()
00156 {
00157     if(_tablesToGenerate.empty())
00158     {
00159         std::cerr << "No tables specified - assuming all tables .." << std::endl;
00160         _tablesToGenerate = _db.tableNames();
00161     }
00162     
00163     std::string outbuffer;
00164     std::string outfilename;
00165     std::vector<WormCodeTemplate>::iterator tpl_it;
00166     std::vector<std::string>::const_iterator tbl_it = _tablesToGenerate.begin();
00167     //each table translates to a class object ..
00168     for(; tbl_it != _tablesToGenerate.end();++tbl_it)
00169     {
00170         WSqlTable table = _db.tableMetaData(*tbl_it);
00171         //foreach template, expand for this table
00172         for(tpl_it = _templates.begin(); tpl_it != _templates.end();++ tpl_it)
00173         {
00174             const std::string tplfilename = _templateDirectory + tpl_it->uri();
00175             outbuffer = expand(tplfilename, table);
00176             outfilename = createOutFileName(tpl_it->type(), table);
00177             if(!writeFile(outbuffer, outfilename))
00178                 std::cerr << "Warning: failed to write File " << outfilename << std::endl;
00179         }
00180     }
00181 }
00182 
00194 std::string WormClassGenerator::expand( const std::string& filename, const WSqlTable& table)
00195 {
00196 //    std::cerr << "=============   Processing table " << table.name() << std::endl;
00197     std::string strToReturn;
00198     bool has_string=false;
00199     std::string type_declaration;
00200     std::string variable_name;
00201     std::string variable_settor;
00202     std::string variable_gettor;
00203     std::vector<std::string> forward_declarations;
00204     
00205     TemplateDictionary *topdict = new TemplateDictionary(filename);
00206     TemplateDictionary *forwarddecls_dict;
00207     TemplateDictionary *belongsto_dict;
00208     TemplateDictionary *hasmany_dict;
00209     TemplateDictionary *coldict;
00210     topdict->SetValue(kcd_CLASS_NAME, table.className());
00211     topdict->SetValue(kcd_TABLE_NAME, table.name());
00212     
00213     const std::vector<WSqlColumn>& columns = table.columns();
00214     std::vector<WSqlColumn>::const_iterator col_it = columns.begin();
00215     
00216     if(table.hasForeignKeys())
00217     {
00218         std::vector< WSqlForeignKey >fks = table.foreignKeys();
00219         std::vector< WSqlForeignKey >::const_iterator fks_it = fks.begin();
00220         for(;fks_it != fks.end();++fks_it)
00221         {
00222             //fks_it->dump();
00223             std::vector<std::string>::const_iterator it = std::find(forward_declarations.begin(), forward_declarations.end(), fks_it->referencedClassName());           
00224             if (it== forward_declarations.end())
00225             {
00226                 forwarddecls_dict = topdict->AddSectionDictionary(kcd_FORWARD_DECLARATIONS);
00227                 forwarddecls_dict->SetValue(kcd_REFERENCED_CLASSNAME, fks_it->referencedClassName());
00228                 forward_declarations.push_back(fks_it->referencedClassName());
00229             }            
00230             belongsto_dict = topdict->AddSectionDictionary(kcd_BELONGS_TO);
00231             belongsto_dict->SetValue(kcd_REFERENCED_CLASSNAME, fks_it->referencedClassName()); 
00232             belongsto_dict->SetValue(kcd_REFERENCED_TABLENAME, fks_it->referencedTableName());
00233         }       
00234     }    
00235     if(table.hasReferencedKeys())
00236     {
00237         std::vector< WSqlReferencedKey >rks = table.referencedKeys();
00238         std::vector< WSqlReferencedKey >::const_iterator rks_it = rks.begin();
00239         for(;rks_it != rks.end();++rks_it)
00240         {
00241             //rks_it->dump();
00242             std::vector<std::string>::const_iterator it = std::find(forward_declarations.begin(), forward_declarations.end(), rks_it->referingClassName());           
00243             if (it == forward_declarations.end())
00244             {
00245                 forwarddecls_dict = topdict->AddSectionDictionary(kcd_FORWARD_DECLARATIONS);
00246                 forwarddecls_dict->SetValue(kcd_REFERENCED_CLASSNAME, rks_it->referingClassName());
00247                 forward_declarations.push_back(rks_it->referingClassName());
00248             }            
00249             hasmany_dict = topdict->AddSectionDictionary(kcd_HAS_MANY);
00250             hasmany_dict->SetValue(kcd_FOREIGNKEY_CLASSNAME, rks_it->referingClassName());
00251             hasmany_dict->SetValue(kcd_FOREIGNKEY_CLASS_PLURAL, rks_it->referingClassNamePlural());
00252         }
00253     }
00254     for(;col_it != columns.end();++col_it)
00255     {
00256         coldict = topdict->AddSectionDictionary(kcd_COLUMNS);
00257         type_declaration = col_it->typeDeclaration();
00258         variable_name = col_it->variableName();
00259         std::string tmp = variable_name;
00260         tmp[0] = toupper(tmp[0]);
00261         variable_settor = "set" + tmp;
00262         variable_gettor = "get" + tmp;
00263         if(type_declaration.compare("std::string") == 0)
00264             has_string=true;
00265         if(! col_it->typeIsSupported())
00266             coldict->ShowSection(kcd_UNSUPPORTED);
00267         coldict->SetValue(kcd_COLUMN_NAME, col_it->columnName());
00268         coldict->SetValue(kcd_DATATYPE, type_declaration);
00269         coldict->SetValue(kcd_VARIABLE_NAME, variable_name );        
00270         coldict->SetValue(kcd_VARIABLE_SETTOR, variable_settor);
00271         coldict->SetValue(kcd_VARIABLE_GETTOR, variable_gettor);
00272         
00273     //FIXME always unsigned .. deep strangeness ..        
00274 /*      if(col_it->isUnsigned())
00275         {
00276             coldict->ShowSection(kcd_UNSIGNED);
00277             std::cerr << "in table " << tbl.className() << " " << col_it->columnName() << "is unsigned .." << std::endl
00278             << "        type: " << WSqlDataType::toString(col_it->type()) << std::endl; 
00279         }    */
00280     }
00281     
00283     if(has_string)
00284         topdict->SetValueAndShowSection("INCLUDE","#include <string>", kcd_INCLUDES);
00285     
00286     ExpandTemplate(filename, DO_NOT_STRIP, topdict, &strToReturn);
00287     delete topdict;
00288     return strToReturn;
00289 }
00290 
00301 std::string WormClassGenerator::createOutFileName(const WormCodeTemplate::Type type, const WSqlTable& table)
00302 {
00303     std::string strToReturn;
00304     switch(type){
00305         case WormCodeTemplate::ClassDeclarationBase: 
00306             strToReturn = table.className() +  "Base.h";
00307             break;
00308         case WormCodeTemplate::ClassDefinitionBase:
00309             strToReturn = table.className() +  "Base.cpp";
00310             break;
00311         case WormCodeTemplate::ClassDeclaration: 
00312             strToReturn = table.className() +  ".h";
00313             break;
00314         case WormCodeTemplate::ClassDefinition: 
00315             strToReturn = table.className() +  ".cpp";
00316             break;
00317         default:
00318             std::cerr << "WormClassGenerator: WARNING: template type unsupported!";
00319             ;
00320     }
00321     return strToReturn;
00322 }
00323 
00328 bool WormClassGenerator::writeFile( const std::string content, const std::string filename )
00329 {
00330     std::ofstream fs;
00331     std::string target = _outputDirectory + "/" + filename; 
00332     fs.open(target.c_str());
00333     if(!fs)
00334         return false;
00335     fs << content;
00336     fs.close();
00337     return true;
00338 }
00339 
00345 void WormClassGenerator::setOutputDirectory( const std::string dir )
00346 {
00347     _outputDirectory = dir;
00348     if('/' != _outputDirectory[ _outputDirectory.size()-1])
00349         _outputDirectory.append("/");
00350 }
00356 void WormClassGenerator::setTemplateDirectory( const std::string dir )
00357 {
00358     _templateDirectory = dir;
00359     if('/' != _templateDirectory[ _templateDirectory.size()-1])
00360         _templateDirectory.append("/");
00361 }
00362 
00363 
00364 } //namespace WSql
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Defines