WORM 0.2
A C++ DAL/ORM code generation framework
|
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