COOPY » Guide
version 0.6.5
|
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