COOPY » Guide  version 0.6.5
/home/paulfitz/cvs/coopy_scm/coopy/src/libcoopy_full/PolyBook.cpp
Go to the documentation of this file.
00001 #include <coopy/PolyBook.h>
00002 #include <coopy/ShortTextBook.h>
00003 #include <coopy/CsvTextBook.h>
00004 #include <coopy/CsvFile.h>
00005 #include <coopy/FormatSniffer.h>
00006 #include <coopy/Dbg.h>
00007 #include <coopy/TextBookFactory.h>
00008 #include <coopy/ShortTextBookFactory.h>
00009 #include <coopy/FormatDesc.h>
00010 
00011 #include <stdlib.h>
00012 #include <stdio.h>
00013 
00014 #include <map>
00015 #include <fstream>
00016 
00017 using namespace std;
00018 using namespace coopy::store;
00019 using namespace coopy::format;
00020 
00021 extern TextBook *readHelper(const char *fname,
00022                             const char *ext,
00023                             const char *data);
00024 
00025 extern bool readHelperJson(coopy::store::Property& config,
00026                            const char *filename);
00027 
00028 extern void getFactories(vector<TextBookFactory *>& lst);
00029 extern void getFactoriesList(vector<FormatDesc>& descs);
00030 
00031 class Factories {
00032 public:
00033   vector<TextBookFactory *> all;
00034 
00035   Factories() {
00036     all.push_back(new ShortTextBookFactory);
00037     all.push_back(CsvTextBookFactory::makeFactory());
00038     all.push_back(CsvTextBookFactory::makeCompactFactory());
00039     getFactories(all);
00040   }
00041 
00042   ~Factories() {
00043     for (int i=0; i<(int)all.size(); i++) {
00044       if (all[i]!=NULL) {
00045         delete all[i];
00046       }
00047     }
00048   }
00049 
00050   TextBook *open(AttachConfig& config, AttachReport& report) {
00051     string key = config.options.get("type").asString();
00052     for (int i=0; i<(int)all.size(); i++) {
00053       if (all[i]==NULL) {
00054         fprintf(stderr,"Failed to allocate a factory\n");
00055         return NULL;
00056       }
00057       if (all[i]->getName()==key) {
00058         dbg_printf("Factories::open operating on %s (prev book: |%ld|%s)\n", key.c_str(),
00059                    (long int)config.prevBook,
00060                    (config.prevBook!=NULL)?config.prevBook->desc().c_str():"");
00061         TextBook *result = all[i]->open(config,report);
00062         dbg_printf("success [%s] msg [%s]\n", report.success?"ok":"fail",
00063                    report.msg.c_str());
00064         if (!report.success) {
00065           fprintf(stderr,"* %s%s%s\n", 
00066                   key.c_str(),
00067                   (report.msg!="")?": ":" operation failed.",
00068                   report.msg.c_str());
00069         }
00070         return result;
00071       }
00072     }
00073     fprintf(stderr,"* Book type not known (%s)\n", key.c_str());
00074     return NULL;
00075   }
00076 };
00077 
00078 class Namer {
00079 public:
00080   map<string,int> existing;
00081   string name(string input) {
00082     string sane = "";
00083     for (size_t i=0; i<input.length(); i++) {
00084       char ch = input[i];
00085       if (ch=='"') {
00086         sane += '"';
00087       }
00088       sane += ch;
00089     }
00090     if (existing.find(sane)!=existing.end()) {
00091       char buf[256];
00092       int at = 0;
00093       do {
00094         sprintf(buf,"%d",at);
00095         at++;
00096       } while (existing.find(sane+buf)==existing.end());
00097       sane += buf;
00098     }
00099     existing[sane] = 1;
00100     return sane;
00101   }
00102 };
00103 
00104 class Valuer {
00105 public:
00106   string name(string input) {
00107     string sane = "";
00108     for (size_t i=0; i<input.length(); i++) {
00109       char ch = input[i];
00110       if (ch=='\'') {
00111         sane += '\'';
00112       }
00113       sane += ch;
00114     }
00115     return sane;
00116   }
00117 };
00118 
00119 bool PolyBook::expand(Property& config) {
00120   string filename = config.get("file").asString();
00121   string name = filename;
00122   string ext = config.get("ext").asString();
00123   if (ext=="") {
00124     size_t eid = name.rfind(".");
00125     if (eid!=string::npos) {
00126       ext = name.substr(eid);
00127     }
00128   } else {
00129     if (ext[0]!='.') {
00130       ext = string(".") + ext;
00131     }
00132   }
00133   for (size_t i=0; i<ext.length(); i++) {
00134     ext[i] = tolower(ext[i]);
00135   }
00136   //dbg_printf("Attach: extension is %s\n", ext.c_str());
00137 
00138   if (filename.substr(0,4)!="dbi:") {
00139     if (ext == ".json") {
00140       dbg_printf("Asked to attach, with json configuration\n");
00141       if (!readHelperJson(config,filename.c_str())) {
00142         return false;
00143       }
00144       filename = config.get("file",PolyValue::makeString("-")).asString();
00145       ext = "";
00146       config.put("ext","");
00147     } else {
00148       config.put("ext",ext);
00149     }
00150   }
00151 
00152   if (filename.substr(0,4)=="dbi:") {
00153     string s = filename + ":";
00154     int first = 0;
00155     vector<string> words;
00156     char prev = '*';
00157     bool last = false;
00158     for (int i=0; i<(int)s.length(); i++) {
00159       char ch = s[i];
00160       if (ch==':'&&(i==s.length()-1||!last)) {
00161         if (prev==ch) {
00162           last = true;
00163         }
00164         string word = s.substr(first,i-first);
00165         size_t div = word.find('=');
00166         if (div==string::npos) {
00167           words.push_back(word);
00168           //dbg_printf("dbi: part %s\n", word.c_str());
00169         } else {
00170           string key = word.substr(0,div);
00171           string val = word.substr(div+1,word.length());
00172           //dbg_printf("dbi: %s->%s\n", key.c_str(), val.c_str());
00173           if (key=="port") {
00174             config.put(key.c_str(),atoi(val.c_str()));
00175           } else {
00176             config.put(key.c_str(),val);
00177           }
00178         }
00179         first = i+1;
00180       }
00181       prev = ch;
00182     }
00183     if (words.size()>1) {
00184       config.put("type",words[1]);
00185     }
00186     if (words.size()>2) {
00187       config.put("database",words[2]);
00188     }
00189     if (words.size()>3) {
00190       config.put("host",words[3]);
00191     }
00192     if (words.size()>4) {
00193       config.put("port",atoi(words[4].c_str()));
00194     }
00195   }
00196 
00197   string key = config.get("type",PolyValue::makeString("")).asString();
00198 
00199   if (key=="") {
00200     if (ext==".csv"||ext==".ssv"||ext==".tsv"||ext==".txt") {
00201       key = "csv";
00202     }
00203     if (ext==".csvs") {
00204       key = "csvs";
00205     }
00206     if (ext==".xls"||ext==".xlsx"||ext==".gnumeric"||ext==".ods") {
00207       key = "gnumeric";
00208     }
00209     if (ext==".socialcalc"||ext==".sc") {
00210       key = "socialcalc";
00211     }
00212     if (ext==".jsonbook") {
00213       key = "jsonbook";
00214     }
00215     if (ext==".mdb") {
00216       key = "access";
00217     }
00218     if (ext==".sqlite") {
00219       key = "sqlite";
00220     }
00221     if (ext==".sqlitext") {
00222       key = "sqlitext";
00223     }
00224     if (ext==".book") {
00225       key = "book";
00226     }
00227   }
00228   if (key==""&&filename=="-") {
00229     key = "csvs";
00230   }
00231 
00232   if (key=="") {
00233     //printf("SNIFFING %s\n",filename.c_str());
00234     FormatSniffer sniffer;
00235     sniffer.open(filename.c_str(),false);
00236     Format f = sniffer.getFormat();
00237     switch (f.id) {
00238     case FORMAT_BOOK_SQLITE:
00239       key = "sqlite";
00240       break;
00241     case FORMAT_BOOK_CSVS:
00242       key = "csvs";
00243       break;
00244     }
00245   }
00246 
00247   dbg_printf("FILE: type [%s] file [%s] settings: %s\n", key.c_str(), filename.c_str(), config.toString().c_str());
00248 
00249   if (config.get("file").asString()=="-"&&config.check("only_if_exists")) {
00250     config.put("skip",true);
00251   }
00252 
00253   if (key=="") {
00254     fprintf(stderr,"* Extension %s not known, maybe use a .json config file?\n",
00255             ext.c_str());
00256     return false;
00257   }
00258   config.put("type",key);
00259   config.put("expanded_filename",filename.c_str());
00260   config.put("expanded_ext",ext.c_str());
00261 
00262   return true;
00263 }
00264 
00265 bool PolyBook::attach(Property& config, PolyBook *base) {
00266   dbg_printf("PolyBook::attach %s\n", config.toString().c_str());
00267   if (config.check("should_clear")) {
00268     if (config.get("should_clear").asBoolean()) {
00269       clear();
00270     }
00271   }
00272 
00273   bool ok = expand(config);
00274   if (!ok) return false;
00275 
00276   string filename = config.get("expanded_filename").asString();
00277   string ext = config.get("expanded_ext").asString();
00278   
00279   Factories f;
00280   AttachConfig ac;
00281   AttachReport ar;
00282   ac.fname = config.get("file").asString();
00283   if (ac.fname=="") ac.fname = filename;
00284   ac.ext = ext;
00285   ac.data = "";
00286   ac.options = config;
00287   ac.canCreate = config.get("can_create").asBoolean();
00288   ac.canOverwrite = true;
00289   ac.shouldRead = config.get("should_read").asBoolean();
00290   ac.shouldWrite = config.get("should_write").asBoolean();
00291   if (config.check("skip")) {
00292     ac.shouldRead = false;
00293     ac.shouldWrite = false;
00294   }
00295   ac.prevBook = book;
00296   ac.baseBook = base;
00297   ac.prevOptions = options;
00298   TextBook *nextBook = f.open(ac,ar);
00299   if (nextBook!=NULL) {
00300     if (book!=NULL) {
00301       if (nextBook!=book) {
00302         clear();
00303       }
00304     }
00305   }
00306   if (nextBook!=NULL && nextBook!=book) {
00307     nextBook->addReference();
00308   }
00309   book = nextBook;
00310   options = config;
00311   return book!=NULL;
00312 }
00313 
00314 bool PolyBook::flush() {
00315   if (!inplace()) {
00316     options.put("can_create",true);
00317     options.put("should_read",false);
00318     options.put("should_write",true);
00319     return attach(options);
00320   }
00321   return true;
00322 }
00323 
00324 
00325 #define STRVAL PolyValue::makeString
00326 
00327 std::vector<FormatDesc> PolyBook::getFormatList() {
00328 
00329   vector<FormatDesc> descs;
00330 
00331   FormatDesc csv("CSV: plain-text delimiter-separated family of formats");
00332   csv.addExtension(".csv","Comma-separated values");
00333   csv.addExtension(".tsv","Tab-separated values");
00334   csv.addExtension(".ssv","Semicolon-separated values");
00335   csv.addOption("type",STRVAL("csv"),"CSV family",true);
00336   csv.addOption("file",STRVAL("fname.dsv"),"File name",true);
00337   csv.addOption("delimiter",STRVAL("|"),"Delimiter character",true);
00338   descs.push_back(csv);
00339 
00340   getFactoriesList(descs);
00341   
00342   return descs;
00343 }
00344 
00345 void PolyBook::showFormats() {
00346   vector<FormatDesc> descs = getFormatList();
00347   
00348   printf("Supported formats\n");
00349   printf("-----------------\n\n");
00350   for (int i=0; i<(int)descs.size(); i++) {
00351     descs[i].show();
00352   }
00353 
00354   printf("Tips:\n");
00355   printf("*  Most spreadsheet/database formats support a 'table' option to restrict\n");
00356   printf("   input/output to a named table or tables\n");
00357   printf("*  json options and dbi options are interchangeable\n");
00358 }
00359 
00360 
00361 bool PolyBook::copyFile(const char *src, const char *dest) {
00362   PolyBook bsrc;
00363   if (!bsrc.read(src)) {
00364     fprintf(stderr,"Failed to read %s\n", src);
00365     return false;
00366   }
00367   if (!bsrc.write(dest)) {
00368     fprintf(stderr,"Failed to write %s\n", dest);
00369     return false;
00370   }
00371   return true;
00372 }
00373 
00374 
00375 bool PolyBook::copy(const TextBook& alt, const Property& options) {
00376   if (!book)
00377     book = new CsvTextBook(true);
00378   if (book)
00379     return book->copy(alt,options);
00380   return false;
00381 }
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines