COOPY » Guide  version 0.6.5
/home/paulfitz/cvs/coopy_scm/coopy/src/libcoopy_core/BookCompare.cpp
Go to the documentation of this file.
00001 #include <coopy/BookCompare.h>
00002 #include <coopy/SheetCompare.h>
00003 #include <coopy/Dbg.h>
00004 #include <coopy/CsvWrite.h>
00005 #include <coopy/IndexSniffer.h>
00006 
00007 #include <set>
00008 #include <list>
00009 #include <algorithm>
00010 
00011 #include <stdio.h>
00012 
00013 using namespace std;
00014 using namespace coopy::store;
00015 using namespace coopy::cmp;
00016 
00017 int BookCompare::create(coopy::store::TextBook& local, 
00018                         Patcher& output, const CompareFlags& flags) {
00019   if (!flags.pool) return -1;
00020 
00021   vector<string> names = local.getNames();
00022   output.mergeStart();
00023   for (int i=0; i<(int)names.size(); i++) {
00024     string sheetName = names[i];
00025     output.setSheet(sheetName.c_str());
00026     PolySheet sheet = local.readSheet(sheetName);
00027     output.addPoolsFromFlags(sheet);
00028     NameSniffer sniffer(sheet,flags);
00029     NameChange nc;
00030     nc.mode = NAME_CHANGE_DECLARE;
00031     nc.constant = true;
00032     nc.final = false;
00033     for (int i=0; i<sheet.width(); i++) {
00034       nc.names.push_back(sniffer.suggestColumnName(i));
00035     }
00036     nc.loud = (sheet.height()==0);
00037     output.changeName(nc);
00038 
00039     bool addedAuto = output.addPoolsFromSchema(sheet,sniffer,sheetName);
00040 
00041     for (int y=0; y<sheet.height(); y++) {
00042       RowChange rc;
00043       rc.mode = ROW_CHANGE_INSERT;
00044       rc.names = nc.names;
00045       rc.allNames = nc.names;
00046       for (int x=0; x<sheet.width(); x++) {
00047         string name = sniffer.suggestColumnName(x);
00048         rc.val[name] = sheet.cellSummary(x,y);
00049       }
00050       output.changeRow(rc);
00051     }
00052   }
00053   output.mergeDone();
00054   output.mergeAllDone();
00055 
00056   return 0;
00057 }
00058 
00059 int BookCompare::compare(TextBook& pivot, TextBook& local, TextBook& remote, 
00060                          Patcher& output, const CompareFlags& flags) {
00061   // Merge currently based purely on names, no content comparison.
00062   // Hence a sheet rename cannot be guessed at yet.
00063 
00064   if (!remote.isValid()) {
00065     return create(local,output,flags);
00066   }
00067 
00068   bool pivot_is_local = false;
00069   bool pivot_is_remote = false;
00070   bool local_is_remote = false;
00071   if (&pivot.tail()==&local.tail()) {
00072     pivot_is_local = true;
00073   }
00074   if (&pivot.tail()==&remote.tail()) {
00075     pivot_is_remote = true;
00076   }
00077   if (&local.tail()==&remote.tail()) {
00078     local_is_remote = true;
00079   }
00080   //printf("%d %d %d\n", pivot_is_local, pivot_is_remote, local_is_remote);
00081 
00082   dbg_printf("BookCompare::compare begins\n");
00083 
00084   output.setFlags(flags);
00085   output.mergeStart();
00086 
00087   vector<string> pivot_names = pivot.getNames();
00088   vector<string> local_names = local.getNames();
00089   vector<string> remote_names = remote.getNames();
00090   set<string> pivot_name_set(pivot_names.begin(),pivot_names.end());
00091   set<string> local_name_set(local_names.begin(),local_names.end());
00092   set<string> remote_name_set(remote_names.begin(),remote_names.end());
00093   set<string> candidate_name_set;
00094   set<string> name_set;
00095   set_union(local_name_set.begin(),
00096             local_name_set.end(),
00097             remote_name_set.begin(),
00098             remote_name_set.end(),
00099             inserter(candidate_name_set, candidate_name_set.end()));
00100   for (set<string>::const_iterator it=candidate_name_set.begin();
00101        it!=candidate_name_set.end();
00102        it++) {
00103     string name = *it;
00104 
00105     if (flags.tables.size()>0) {
00106       if (flags.tables.find(name)==flags.tables.end()) continue;
00107     }
00108 
00109     bool in_pivot = (pivot_name_set.find(name)!=pivot_name_set.end());
00110     bool in_local = (local_name_set.find(name)!=local_name_set.end());
00111     bool in_remote = (remote_name_set.find(name)!=remote_name_set.end());
00112     if (!in_pivot) {
00113       // added in either local or remote
00114       name_set.insert(name);
00115     } else if (in_local&&in_remote) {
00116       // not removed
00117       name_set.insert(name);
00118     }
00119   }
00120 
00121   for (set<string>::const_iterator it=name_set.begin();
00122        it!=name_set.end();
00123        it++) {
00124     string name = *it;
00125     dbg_printf("\n\nBookCompare::compare - Working on \"%s\"\n", name.c_str());
00126     PolySheet pivot_sheet;
00127     PolySheet local_sheet;
00128     PolySheet remote_sheet;
00129     if (pivot_names.size()==1 ||
00130         local_names.size()==1 ||
00131         remote_names.size()==1) {
00132       pivot_sheet = pivot.readSheetByIndex(0);
00133       local_sheet = pivot_is_local?pivot_sheet:local.readSheetByIndex(0);
00134       remote_sheet = pivot_is_remote?pivot_sheet:(local_is_remote?local_sheet:remote.readSheetByIndex(0));
00135     } else {
00136       pivot_sheet = pivot.readSheet(name.c_str());
00137       local_sheet =  pivot_is_local?pivot_sheet:local.readSheet(name.c_str());
00138       remote_sheet = pivot_is_remote?pivot_sheet:(local_is_remote?local_sheet:remote.readSheet(name.c_str()));
00139     }
00140 
00141     PolySheet mapping;
00142     CompareFlags flags2 = flags;
00143     if (flags2.mapping_book) {
00144       mapping = flags2.mapping_book->readSheet(name.c_str());
00145       if (mapping.isValid()) {
00146         flags2.mapping = &mapping;
00147       }
00148     }
00149 
00150     if (pivot_sheet.isValid()&&
00151         local_sheet.isValid()&&
00152         remote_sheet.isValid()) {
00153       SheetCompare cmp;
00154       /*
00155         // setSheet call moved to SheetCompare
00156       if (local.namedSheets()&&remote.namedSheets()) {
00157         bool ok = output.setSheet(name.c_str());
00158         if (!ok) {
00159           fprintf(stderr,"Output format rejected sheet \"%s\"\n", name.c_str());
00160           return -1;
00161         }
00162       }
00163       */
00164       int r = cmp.compare(pivot_sheet,local_sheet,remote_sheet,output,
00165                           flags2,
00166                           (local.namedSheets()&&remote.namedSheets())?name.c_str():NULL);
00167       if (r!=0) return r;
00168     } else if (local_sheet.isValid()&&pivot_sheet.isValid()&&
00169                !remote_sheet.isValid()) {
00170       dbg_printf("sheet removed in remote\n");
00171       output.removeSheet(name.c_str());
00172     } else if (remote_sheet.isValid()&&pivot_sheet.isValid()&&
00173                !local_sheet.isValid()) {
00174       dbg_printf("sheet removed in local\n");
00175       output.removeSheet(name.c_str());
00176     } else if (remote_sheet.isValid()&&!pivot_sheet.isValid()&&
00177                !local_sheet.isValid()) {
00178       dbg_printf("sheet added in remote\n");
00179       output.addSheet(name.c_str(),remote_sheet);
00180     } else if (local_sheet.isValid()&&!pivot_sheet.isValid()&&
00181                !remote_sheet.isValid()) {
00182       dbg_printf("sheet added in local\n");
00183       output.addSheet(name.c_str(),local_sheet);
00184     } else {
00185       fprintf(stderr,"Could not find matching sheets - no big deal, but not ready for this yet\n");
00186       return -1;
00187     }
00188     dbg_printf("BookCompare::compare - Done with \"%s\"\n", name.c_str());
00189     output.mergeDone();
00190   }
00191 
00192   output.mergeAllDone();
00193 
00194   return 0;
00195 }
00196   
00197 void BookCompare::setVerbose(bool verbose) {
00198   _csv_verbose = verbose;
00199 }
00200 
00201 class ConflictCell {
00202 public:
00203   SheetCell local, remote, pivot;
00204   bool have_local;
00205   bool have_remote;
00206   bool have_pivot;
00207 
00208   ConflictCell() { reset(); }
00209 
00210   void reset() {
00211     local = remote = pivot = SheetCell();
00212     have_local = have_remote = have_pivot = false;
00213   }
00214 
00215   bool parse(const SheetCell& src);
00216 };
00217 
00218 bool ConflictCell::parse(const SheetCell& src) {
00219   reset();
00220 
00221   if (src.text.substr(0,3)!="(((") return false;
00222 
00223   // current format: ((( pivot ))) local /// remote
00224   // not worrying about null or quoting yet
00225 
00226   string::size_type pivot_start = 4;
00227   string::size_type pivot_end = src.text.find(" ))) ");
00228   if (pivot_end == string::npos) return false;
00229   string::size_type local_start = pivot_end + 5;
00230   string::size_type local_end = src.text.find(" /// ");
00231   if (local_end == string::npos) return false;
00232   string::size_type remote_start = local_end + 5;
00233   local = SheetCell(src.text.substr(local_start,local_end-local_start),false);
00234   remote = SheetCell(src.text.substr(remote_start,src.text.length()),false);
00235   pivot = SheetCell(src.text.substr(pivot_start,pivot_end-pivot_start),false);
00236   have_local = have_remote = have_pivot = true;
00237 
00238   return true;
00239 }
00240 
00241 
00242 int BookCompare::resolve(coopy::store::TextBook& pivot, 
00243                          coopy::store::TextBook& local, 
00244                          coopy::store::TextBook& remote, 
00245                          Patcher& output, const CompareFlags& flags) {
00246   string resolve = flags.resolve;
00247   bool use_remote = false;
00248   bool use_local = false;
00249   if (resolve=="theirs") {
00250     use_remote = true;
00251   } else if (resolve=="ours") {
00252     use_local = true;
00253   } else if (resolve=="neither") {
00254     // ok
00255   } else {
00256     if (resolve=="") {
00257       fprintf(stderr,
00258               "Please choose one of --theirs / --ours / --neither for conflict resolution.\n");
00259     } else
00260       fprintf(stderr,
00261               "Do not understand choice '%s' for conflict resolution.\n",
00262               resolve.c_str());
00263     return 1;
00264   }
00265 
00266   output.setFlags(flags);
00267   output.mergeStart();
00268 
00269   vector<string> local_names = local.getNames();
00270   for (int i=0; i<(int)local_names.size(); i++) {
00271     PolySheet sheet = local.readSheetByIndex(i);
00272     if (!sheet.isValid()) continue;
00273     sheet.mustHaveSchema();
00274     SheetSchema *ss = sheet.getSchema();
00275     if (!ss) continue;
00276     int idx = ss->getColumnIndexByName("_MERGE_");
00277     if (idx<0) continue;
00278     output.setSheet(local_names[i].c_str());
00279     NameChange nc0;
00280     nc0.mode = NAME_CHANGE_DECLARE;
00281     nc0.final = false;
00282     nc0.constant = false;
00283     NameChange nc1;
00284     nc1.mode = NAME_CHANGE_DECLARE;
00285     nc1.final = true;
00286     nc1.constant = false;
00287 
00288     OrderChange oc;
00289     oc.mode = ORDER_CHANGE_DELETE;
00290     for (int i=0; i<ss->getColumnCount(); i++) {
00291       string name = ss->getColumnInfo(i).getName();
00292       oc.indicesBefore.push_back(i);
00293       oc.namesBefore.push_back(name);
00294       nc0.names.push_back(name);
00295       if (name!="_MERGE_") {
00296         oc.indicesAfter.push_back(i);
00297         oc.namesAfter.push_back(name);
00298         nc1.names.push_back(name);
00299       } else {
00300         oc.subject = i;
00301       }
00302     }
00303 
00304     sheet.hideHeaders();
00305     int w = sheet.width();
00306     int h = sheet.height();
00307     vector<string> onames = nc0.names;
00308     vector<string> names = nc1.names;
00309 
00310     // logic dupe from Merger.cpp
00311     NameSniffer ns(sheet,flags);
00312     IndexSniffer localIndex(sheet,flags,ns);
00313     vector<int> index_flags = localIndex.suggestIndexes();
00314     RowChange::txt2bool indexes;
00315     bool atLeastOne = false;
00316     for (int i=0; i<(int)names.size(); i++) {
00317       string name = names[i];
00318       indexes[name] = (index_flags[i]>0);
00319       atLeastOne = atLeastOne||indexes[name];
00320     }
00321     if (!atLeastOne) {
00322       indexes.clear();
00323     }
00324     // end logic dup
00325 
00326     list<RowChange> rcs;
00327     for (int y=0; y<h; y++) {
00328       string state = sheet.cellString(idx,y);
00329       if (state!="CONFLICT") continue;
00330       RowChange rc;
00331       rc.mode = ROW_CHANGE_UPDATE;
00332       rc.indexes = indexes;
00333       rc.allNames = nc1.names;
00334       rc.names = nc1.names;
00335       for (int x=0; x<w; x++) {
00336         if (x==idx) continue;
00337         SheetCell v = sheet.cellSummary(x,y);
00338         string col = onames[x];
00339         rc.cond[col] = v;
00340         ConflictCell cc;
00341         if (cc.parse(v)) {
00342           if (use_local) {
00343             rc.val[col] = cc.local;
00344           } else if (use_remote) {
00345             rc.val[col] = cc.remote;
00346           } else {
00347             rc.val[col] = cc.pivot;
00348           }
00349         }
00350       }
00351       //output.changeRow(rc);
00352       rcs.push_back(rc);
00353     }
00354 
00355     output.changeName(nc0);
00356     output.changeColumn(oc);
00357     output.changeName(nc1);
00358     for (list<RowChange>::iterator it=rcs.begin(); it!=rcs.end(); it++) {
00359       output.changeRow(*it);
00360     }
00361 
00362     output.mergeDone();
00363   }
00364   output.mergeAllDone();
00365 
00366   return 0;
00367 }
00368 
00369 
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines