COOPY » Guide  version 0.6.5
/home/paulfitz/cvs/coopy_scm/coopy/src/libcoopy_core/SheetPatcher.cpp
Go to the documentation of this file.
00001 #include <coopy/SheetPatcher.h>
00002 #include <coopy/NameSniffer.h>
00003 
00004 #include <map>
00005 #include <algorithm>
00006 
00007 #include <stdio.h>
00008 
00009 #define WANT_MAP2STRING
00010 #define WANT_VECTOR2STRING
00011 #include <coopy/Stringer.h>
00012 
00013 using namespace std;
00014 using namespace coopy::cmp;
00015 using namespace coopy::store;
00016 
00017 #define FULL_COLOR (65535)
00018 #define HALF_COLOR (65535/2)
00019 
00020 #define FULL_DEBUG
00021 
00022 static bool null_like(const SheetCell& a) {
00023   return a.escaped||a.text==""||a.text=="NULL";
00024 }
00025 
00026 static bool is_match(const SheetCell& a, const SheetCell& b) {
00027   return a==b;
00028   //if (a==b) return true;
00029   //if (!null_like(a)) return false;
00030   //return null_like(b);
00031 }
00032 
00033 void SheetPatcher::checkHeader() {
00034   if (checkedHeader) return;
00035   checkedHeader = true;
00036   if (flags.assume_header==false) return;
00037   PolySheet sheet = getSheet();
00038   if (!sheet.isValid()) return;
00039   int c = 0;
00040   if (activeCol.width() == sheet.width()) {
00041     for (c=0; c<sheet.width(); c++) {
00042       if (sheet.cellString(c,0) != activeCol.cellString(c,0)) {
00043         break;
00044       }
00045     }
00046   }
00047   if (c==sheet.width()) {
00048     if (!sheet.hasExternalColumnNames()) {
00049       dbg_printf("HEADER LINE NUDGE\n");
00050       //sheet.mustHaveSchema();
00051       rowCursor = 1;
00052     }
00053   }
00054 }
00055 
00056 int SheetPatcher::matchRow(const std::vector<int>& active_cond,
00057                            const std::vector<std::string>& active_name,
00058                            const std::vector<SheetCell>& cond,
00059                            int width, bool show) {
00060   PolySheet sheet = getSheet();
00061   if (!sheet.isValid()) return false;
00062   {
00063     int c;
00064     for (c=0; c<width; c++) {
00065       if (active_cond[c]) {
00066         if (cond[c].text != active_name[c]) {
00067           break;
00068         }
00069       }
00070     }
00071     if (c==width) {
00072       //printf("WORKING WITH: %s // %s\n", sheet.toString().c_str(),
00073       //vector2string(sheet.getNestedDescription()).c_str());
00074       //if (sheet.hasRowOffset()||sheet.hasExternalColumnNames()) {
00075       if (sheet.getSchema()&&!sheet.hasExternalColumnNames()) {
00076         dbg_printf("HEADER LINE MATCH\n");
00077         return -2;
00078       }
00079     }
00080   }
00081 
00082   int r = -1;
00083   int bct = 0;
00084   int rbest = -1;
00085   for (r=0; r<sheet.height(); r++) {
00086     int ct = 0;
00087     if (activeRow.cellString(0,r)!="---") {
00088       bool match = true;
00089       for (int c=0; c<width; c++) {
00090         if (active_cond[c]) {
00091           if (!is_match(sheet.cellSummary(c,r),cond[c])) {
00092             match = false;
00093             if (!show) {
00094               break;
00095             }
00096           } else {
00097             ct++;
00098             if (ct>bct) {
00099               bct = ct;
00100               rbest = r;
00101             }
00102           }
00103         }
00104       }
00105       if (match) {
00106         dbg_printf("Found row %d\n", r);
00107         return r;
00108       }
00109     }
00110   }
00111   if (!show) {
00112     dbg_printf("No match for update\n");
00113     matchRow(active_cond,active_name,cond,width,true);
00114   }
00115   if (show) {
00116     fprintf(stderr,"# No match for update");
00117     if (sheet.getSchema()) {
00118       fprintf(stderr," in %s",sheet.getSchema()->getSheetName().c_str()); 
00119     }
00120     if (rbest>=0) {
00121       fprintf(stderr," - closest was:\n"); 
00122       for (int c=0; c<width; c++) {
00123         if (active_cond[c]) {
00124           fprintf(stderr,"#   '%s' <-> '%s' %s\n",
00125                   sheet.cellSummary(c,rbest).text.c_str(),
00126                   cond[c].text.c_str(),
00127                   (sheet.cellSummary(c,rbest)!=cond[c])?"FAIL":"OK");
00128         }
00129       }
00130     } else {
00131       fprintf(stderr,"\n");
00132     }
00133   }
00134   return -1;
00135 }
00136 
00137 int SheetPatcher::matchCol(const std::string& mover) {
00138   string imover = mover;
00139   if (syn2name.find(mover)!=syn2name.end()) {
00140     imover = syn2name[mover];
00141   }
00142   for (int i=0; i<activeCol.width(); i++) {
00143     //printf("Checking %s against %s\n", activeCol.cellString(i,0).c_str(),
00144     //mover.c_str());
00145     if (activeCol.cellString(i,0)==imover) {
00146       if (statusCol.cellString(i,0)!="---") {
00147         //printf("Match at %d (%s)\n", i, mover.c_str());
00148         return i;
00149       }
00150     }
00151   }
00152   fprintf(stderr,"column not found: %s\n", mover.c_str());
00153   //PolySheet sheet = getSheet();
00154   exit(1);
00155   return -1;
00156 }
00157 
00158 int SheetPatcher::updateCols() {
00159   PolySheet sheet = getSheet();
00160   /*
00161   {
00162     printf("<activeCol>");
00163     for (int i=0; i<activeCol.width(); i++) {
00164       string name = activeCol.cellString(i,0);
00165       printf(" %s", name.c_str());
00166     }
00167     printf("\n");
00168     SheetSchema *ss = sheet.getSchema();
00169     if (ss!=NULL) {
00170       printf("<schema>   ");
00171       for (int i=0; i<ss->getColumnCount(); i++) {
00172         string name = ss->getColumnInfo(i).getName();
00173         printf(" %s", name.c_str());
00174       }
00175       printf("\n");
00176       for (int i=0; i<ss->getColumnCount(); i++) {
00177         string name = ss->getColumnInfo(i).getName();
00178         if (i<activeCol.width()) {
00179           string name2 = activeCol.cellString(i,0);
00180           if (name!=name2) {
00181             printf("    %s <---> %s\n", name.c_str(), name2.c_str());
00182             exit(1);
00183             break;
00184           }
00185         }
00186       }
00187     }
00188     printf("<sheet>    ");
00189     for (int i=0; i<sheet.width(); i++) {
00190       string name = sheet.cellString(i,0);
00191       printf(" %s", name.c_str());
00192     }
00193     printf("\n");
00194   }
00195   */
00196   if (activeCol.width()!=sheet.width()) {
00197     printf("* Please report bug: activeCol drift, %s %d\n",
00198            __FILE__,
00199            __LINE__);
00200     exit(1);
00201   }
00202   name2col.clear();
00203   for (int i=0; i<activeCol.width(); i++) {
00204     string name = activeCol.cellString(i,0);
00205     //printf("NAME %d %s\n", i, name.c_str());
00206     if (name!="---") {
00207       name2col[name] = i;
00208       col2name[i] = name;
00209       if (name2syn.find(name)!=name2syn.end()) {
00210         name2col[name2syn[name]] = i;
00211       }
00212     }
00213   }
00214   return 0;
00215 }
00216 
00217 bool SheetPatcher::renameColumn(int idx, const std::string& name,
00218                                 const std::string& oldName) {
00219   PolySheet sheet = getSheet();
00220   dbg_printf("Should rename column %d to %s\n", idx, name.c_str());
00221   bool ok = sheet.modifyColumn(ColumnRef(idx),ColumnInfo(name));
00222   activeCol.cellString(idx,0,name);
00223   statusCol.cellString(idx,0,
00224                        statusCol.cellString(idx,0) +
00225                        string("(") + oldName + ")");
00226   if (!ok) {
00227     fprintf(stderr,"Failed to rename column to %s\n", name.c_str());
00228   }
00229   updateCols();
00230   return true;
00231 }
00232 
00233 bool SheetPatcher::moveColumn(int idx, int idx2) {
00234   //printf("MOVE %d to %d\n", idx, idx2);
00235   PolySheet sheet = getSheet();
00236   ColumnRef from(idx);
00237   ColumnRef to(idx2);
00238   bool ok = sheet.moveColumn(from,to).isValid();
00239   activeCol.moveColumn(from,to);
00240   ColumnRef at = statusCol.moveColumn(from,to);
00241   if (descriptive) {
00242     /*
00243       int first = idx;
00244       int final = at.getIndex();
00245       int xfinal = final;
00246       int sgn = 1;
00247       string name = "";
00248       string ch = ">";
00249       if (final<first) {
00250       first = final;
00251       final = idx;
00252       sgn = -1;
00253       ch = "<";
00254       }
00255       for (int i=first; i<final; i++) {
00256       if (statusCol.cellString(i,0)!="---") {
00257       name += ch;
00258       }
00259       }
00260     */
00261     for (int k=0; k<(int)activeCol.width(); k++) {
00262       string name = activeCol.cellString(k,0);
00263       string code = statusCol.cellString(k,0);
00264       char ch = code[0];
00265       if (ch=='-'||ch=='+') continue;
00266       map<string,int>::iterator it = 
00267         original_index.find(name);
00268       if (it == original_index.end()) {
00269         continue;
00270       }
00271       int first = k;
00272       int last = it->second;
00273       ch = '<';
00274       string txt = "";
00275       int sgn = 1;
00276       if (last<first) {
00277         sgn = -1;
00278         ch = '>';
00279       }
00280       for (int i=first; i!=last; i += sgn) {
00281         txt += ch;
00282       }
00283       statusCol.cellString(k,0,txt);
00284     }
00285   }
00286   updateCols();
00287   return ok;
00288 }
00289 
00290 bool SheetPatcher::changeColumn(const OrderChange& change) {
00291   sheetUpdateNeeded = true;
00292   changeCount++;
00293   if (chain) chain->changeColumn(change);
00294 
00295   dbg_printf("\n======================\nChange column...\n");
00296 
00297   PolySheet sheet = getSheet();
00298   if (!sheet.isValid()) {
00299     fprintf(stderr,"No sheet available to patch\n");
00300     return false;
00301   }
00302 
00303 #ifdef FULL_DEBUG
00304   dbg_printf("STATE<<EOF\n%sEOF\n",sheet.toString().c_str());
00305 #endif
00306 
00307   switch (change.mode) {
00308   case ORDER_CHANGE_DELETE:
00309     //return sheet->deleteColumn(ColumnRef(change.subject));
00310     {
00311       string mover = change.namesBefore[change.identityToIndex(change.subject)];
00312       //printf("Deleting %s\n", mover.c_str());
00313       int idx = matchCol(mover);
00314       bool ok = true;
00315       if (!descriptive) {
00316         ColumnRef col(idx); //change.identityToIndex(change.subject));
00317         ok = sheet.deleteColumn(col);
00318         activeCol.deleteColumn(col);
00319         statusCol.deleteColumn(col);
00320         if (sheet.width()==0) {
00321           sheet.deleteData();
00322           rowCursor = -1;
00323         }
00324       } else {
00325         statusCol.cellString(idx,0,"---");
00326         if (descriptive) {
00327           Poly<Appearance> appear = sheet.getColAppearance(idx);
00328           if (appear.isValid()) {
00329             appear->begin();
00330             appear->setBackgroundRgb16(FULL_COLOR,
00331                                        HALF_COLOR,
00332                                        HALF_COLOR,
00333                                        AppearanceRange::full());
00334             appear->setStrikethrough(true,AppearanceRange::full());
00335             appear->end();
00336           }
00337         }
00338         bool gone = true;
00339         for (int i=0; i<(int)statusCol.width(); i++) {
00340           if (statusCol.cellString(i,0)=="") {
00341             gone = false;
00342             break;
00343           }
00344         }
00345         if (gone) {
00346           killNeutral = true;
00347         }
00348       }
00349       updateCols();
00350       return ok;
00351     }
00352     break;
00353   case ORDER_CHANGE_INSERT:
00354     {
00355       int toSheet = change.identityToIndexAfter(change.subject);
00356       string mover = change.namesAfter[toSheet];
00357       string before = "";
00358       int idx = -1;
00359       if (toSheet!=change.indicesAfter.size()-1) {
00360         before = change.namesAfter[toSheet+1];
00361         idx = matchCol(before);
00362       }
00363       ColumnInfo ci(mover);
00364       dbg_printf("Inserting column [%s]\n", mover.c_str());
00365       bool ok = sheet.insertColumn(ColumnRef(idx),ci).isValid();
00366       ColumnRef at = activeCol.insertColumn(ColumnRef(idx),ci);
00367       statusCol.insertColumn(ColumnRef(idx),ci);
00368       activeCol.cellString(at.getIndex(),0,mover);
00369       statusCol.cellString(at.getIndex(),0,"+++");
00370       if (descriptive) {
00371         Poly<Appearance> appear = sheet.getColAppearance(at.getIndex());
00372         if (appear.isValid()) {
00373           appear->begin();
00374           appear->setBackgroundRgb16(HALF_COLOR,
00375                                      FULL_COLOR,
00376                                      HALF_COLOR,
00377                                      AppearanceRange::full());
00378           appear->end();
00379         }
00380       }
00381       updateCols();
00382       return ok;
00383     }
00384     break;
00385   case ORDER_CHANGE_MOVE:
00386     //return sheet->moveColumn(ColumnRef(change.subject),
00387     //ColumnRef(change.object)
00388     //).isValid();
00389     {
00390       int toSheet = change.identityToIndexAfter(change.subject);
00391       string mover = change.namesAfter[toSheet];
00392       int idx = matchCol(mover);
00393       string before = "";
00394       int idx2 = -1;
00395       //printf("after %d for %d\n", toSheet, change.subject);
00396       if (toSheet!=change.indicesAfter.size()-1) {
00397         before = change.namesAfter[toSheet+1];
00398         //printf("Before %s\n", before.c_str());
00399         idx2 = matchCol(before);
00400       }
00401       //printf("Moving %s %d->%d\n", mover.c_str(), idx, idx2);
00402       bool ok = moveColumn(idx,idx2);
00403       return ok;
00404     }
00405     break;
00406   case ORDER_CHANGE_RENAME:
00407     {
00408       int fromSheet = change.identityToIndex(change.subject);
00409       int toSheet = change.identityToIndexAfter(change.subject);
00410       string target = change.namesBefore[fromSheet];
00411       string newName = change.namesAfter[toSheet];
00412       int idx = matchCol(target);
00413       return renameColumn(idx,newName,target);
00414     }
00415     break;
00416   default:
00417     fprintf(stderr,"* ERROR: Unknown column operation\n");
00418     break;
00419   }
00420   return false;
00421 }
00422 
00423 static string colorEncode(const SheetCell& c) {
00424   std::string str = c.text;
00425   if (!c.escaped) {
00426     int score = 0;
00427     for (score=0; score<(int)str.length(); score++) {
00428       if (str[score]!='_') {
00429         break;
00430       }
00431     }
00432     if (str.substr(score,str.length())=="NULL") {
00433       str = std::string("_") + str;
00434     }
00435     return str;
00436   }
00437   return "NULL";
00438 }
00439 
00440 bool SheetPatcher::markChanges(const RowChange& change, int r,int width,
00441                                std::vector<int>& active_val,
00442                                std::vector<SheetCell>& val,
00443                                std::vector<SheetCell>& cval,
00444                                std::vector<SheetCell>& pval) {
00445   PolySheet sheet = getSheet();
00446 
00447 
00448   // Check if there is at least one change that is not just an
00449   // addition
00450 
00451   bool have_mod = false;
00452   bool have_add = false;
00453   for (int c=0; c<width; c++) {
00454     if (active_val[c]) {
00455       string key = statusCol.cellString(c,0);
00456       if (key!="+++"&&key!="---") {
00457         have_mod = true;
00458       }
00459       if (key=="+++") {
00460         have_add = true;
00461       }
00462     }
00463   }
00464   string row_key = sheet.cellString(0,r);
00465 
00466   string separator = "";
00467   string conflict_separator = "";
00468   for (int c=0; c<width; c++) {
00469     if (active_val[c]) {
00470       string key = statusCol.cellString(c,0);
00471       bool this_mod = (key!="+++");
00472       if (descriptive) {
00473         if (have_mod) {
00474           if (separator=="") {
00475             separator = "->";
00476             bool more = true;
00477             while (more) {
00478               more = false;
00479               for (int i=0; i<width; i++) {
00480                 SheetCell prev = sheet.cellSummary(i,r);
00481                 if (prev.text.find(separator)!=string::npos) {
00482                   separator = string("-") + separator;
00483                   more = true;
00484                   break;
00485                 }
00486               }
00487             }
00488           }
00489         } else if (have_add) {
00490           separator = "+";
00491         }
00492       }
00493       string init = separator;
00494       if (change.conflicted) {
00495         if (conflict_separator=="") {
00496           conflict_separator = string("!");
00497           bool more = true;
00498           // this is actually far too conservative
00499           while (more) {
00500             more = false;
00501             for (int i=0; i<width; i++) {
00502               SheetCell prev = sheet.cellSummary(i,r);
00503               if (prev.text.find(conflict_separator)!=string::npos) {
00504                 conflict_separator = string("-") + conflict_separator;
00505                 more = true;
00506                 break;
00507               }
00508             }
00509           }
00510         }
00511         init = conflict_separator + separator;
00512       }
00513       if (init.length()>activeRow.cellString(0,r).length()) {
00514         activeRow.cellString(0,r,init);
00515       }
00516       if (change.conflicted) {
00517         if (descriptive) {
00518           Poly<Appearance> appear = sheet.getCellAppearance(0,r);
00519           if (appear.isValid()) {
00520             appear->begin();
00521             appear->setBackgroundRgb16(FULL_COLOR,
00522                                        0,
00523                                        0,
00524                                        AppearanceRange::full());
00525             appear->end();
00526           }
00527         }
00528       }
00529       if (descriptive) {
00530         SheetCell prev = sheet.cellSummary(c,r);
00531         string from = prev.toString();
00532         if (prev.escaped) from = "";
00533         //string to = val[c].toString();
00534         SheetCell to = val[c];
00535         bool conflicted = false;
00536         if (val[c]!=cval[c]) {
00537           to = cval[c];
00538           conflicted = true;
00539         }
00540         SheetStyle style;
00541         if (this_mod||conflicted) {
00542           if (conflicted&&!pval[c].escaped) {
00543             sheet.cellString(c,r,
00544                              colorEncode(pval[c]) + 
00545                              (conflicted?init:separator) + 
00546                              colorEncode(prev) + 
00547                              (conflicted?init:separator) + 
00548                              colorEncode(to));
00549           } else {
00550             sheet.cellString(c,r,colorEncode(prev) + 
00551                              (conflicted?init:separator) + 
00552                              colorEncode(to));
00553           }
00554         } else {
00555           /*
00556           if (row_key=="---"&&key=="+++") {
00557             // no point adding anything here
00558           } else if (row_key=="+++"&&key=="---") {
00559             // no point adding anything here
00560           } else {
00561           */
00562           sheet.cellSummary(c,r,to);
00563         }
00564         if (this_mod||conflicted) {
00565           Poly<Appearance> appear = sheet.getCellAppearance(c,r);
00566           if (appear.isValid()) {
00567             appear->begin();
00568             if (conflicted) {
00569               appear->setBackgroundRgb16(FULL_COLOR,
00570                                          0,
00571                                          0,
00572                                          AppearanceRange::full());
00573             } else {
00574               appear->setBackgroundRgb16(HALF_COLOR,
00575                                          HALF_COLOR,
00576                                          FULL_COLOR,
00577                                          AppearanceRange::full());
00578             }
00579             appear->setWeightBold(true,AppearanceRange::full());
00580             appear->end();
00581           }
00582         }
00583       } else {
00584         if (change.conflicted) {
00585           sheet.cellSummary(conflictColumn,r,SheetCell("CONFLICT",false));
00586           //printf("AT %d %d\n", conflictColumn,r);
00587 
00588           Poly<Appearance> appear = sheet.getCellAppearance(conflictColumn,r);
00589           if (appear.isValid()) {
00590             appear->begin();
00591             appear->setBackgroundRgb16(HALF_COLOR,
00592                                        HALF_COLOR,
00593                                        FULL_COLOR,
00594                                        AppearanceRange::full());
00595             appear->end();
00596           }
00597 
00598           SheetCell from = pval[c];
00599           SheetCell to = val[c];
00600           SheetCell alt = cval[c];
00601           if (to!=alt) {
00602             string ctxt = 
00603               "((( " + from.toString() + " ))) " +
00604               to.toString() + " /// " + alt.toString();
00605             sheet.cellSummary(c,r,SheetCell(ctxt,false));
00606             Poly<Appearance> appear = sheet.getCellAppearance(c,r);
00607             if (appear.isValid()) {
00608               appear->begin();
00609               appear->setBackgroundRgb16(FULL_COLOR,
00610                                          HALF_COLOR,
00611                                          HALF_COLOR,
00612                                          AppearanceRange::full());
00613               appear->end();
00614             }
00615           }
00616         } else {
00617           sheet.cellSummary(c,r,val[c]);
00618         }
00619       }
00620     }
00621   }
00622   return true;
00623 }
00624 
00625 
00626 class Invention {
00627 public:
00628   SheetCell origin;
00629   int offset;
00630   PoolColumnLink link;
00631 };
00632 
00633 bool SheetPatcher::handleConflicts() {
00634   if (readyForConflicts) return true;
00635   if (descriptive) return true;
00636   if (!merging) return false;
00637   for (int i=0; i<(int)column_names.size(); i++) {
00638     if (column_names[i]=="_MERGE_") {
00639       readyForConflicts = true;
00640       conflictColumn = i;
00641       return true;
00642     }
00643   }
00644   PolySheet sheet = getSheet();
00645   if (!sheet.isValid()) return false;
00646   sheet.mustHaveSchema();
00647 
00648   //printf("SCHEMA %s\n", sheet.getSchema()->toString().c_str());
00649   //printf("SCHEMA %s\n", active_sheet.getSchema()->toString().c_str());
00650   //printf("sheet before %s\n", sheet.toString().c_str());
00651   ColumnInfo con("_MERGE_");
00652   con.setType("text","sqlite");
00653   ColumnRef at = active_sheet.insertColumn(ColumnRef(-1),con);
00654   activeCol.insertColumn(ColumnRef(-1),con);
00655   statusCol.insertColumn(ColumnRef(-1),con);
00656   readyForConflicts = at.isValid();
00657   conflictColumn = at.getIndex();
00658 
00659   //printf("sheet after %s\n", sheet.toString().c_str());
00660   //printf("SCHEMA AFTER %s\n", active_sheet.getSchema()->toString().c_str());
00661 
00662   return readyForConflicts;
00663 }
00664 
00665 bool SheetPatcher::changeRow(const RowChange& change) {
00666   sheetUpdateNeeded = true;
00667 
00668   PolySheet sheet = getSheet();
00669   if (!sheet.isValid()) {
00670     fprintf(stderr,"No sheet available to patch.\n");
00671     return false;
00672   }
00673 
00674   if (!declaredNames) {
00675     if (chain) chain->declareNames(change.allNames,false);
00676     declareNames(change.allNames,false);
00677   }
00678 
00679   if (change.conflicted) {
00680     if (!handleConflicts()) {
00681       fprintf(stderr,"Cannot handle conflicts.\n");
00682       return false;
00683     }
00684   }
00685 
00686   if (activeRow.height()!=sheet.height() || activeRow.width()!=1) {
00687     activeRow.resize(1,sheet.height());
00688   }
00689 
00690   dbg_printf("\n======================\nRow cursor in: %d\n", rowCursor);
00691   if (coopy_is_verbose()) {
00692     RowChange c;
00693     c = change;
00694     c.show();
00695   }
00696 
00697   if (!change.sequential) rowCursor = -1;
00698   //map<string,int> dir;
00699   vector<int> active_cond;
00700   vector<string> active_name;
00701   int active_conds = 0;
00702   vector<SheetCell> cond;
00703   vector<int> active_val;
00704   vector<SheetCell> val;
00705   vector<SheetCell> cval;
00706   vector<SheetCell> pval;
00707   vector<string> allNames = change.allNames;
00708   int width = sheet.width(); //(int)change.allNames.size();
00709   /*
00710   if (width==0) {
00711     if (column_names.size()==0) {
00712       NameSniffer sniffer(sheet);
00713       column_names = sniffer.suggestNames();
00714     }
00715     allNames = column_names;
00716     width = (int)allNames.size();
00717   }
00718   */
00719   for (int i=0; i<width; i++) {
00720     //dir[allNames[i]] = i;
00721     active_cond.push_back(0);
00722     active_name.push_back("");
00723     cond.push_back(SheetCell());
00724     active_val.push_back(0);
00725     val.push_back(SheetCell());
00726     cval.push_back(SheetCell());
00727     pval.push_back(SheetCell());
00728   }
00729   for (RowChange::txt2cell::const_iterator it = change.cond.begin();
00730        it!=change.cond.end(); it++) {
00731     if (name2col.find(it->first)!=name2col.end()) {
00732       int idx = name2col[it->first]; //dir[it->first];
00733       //printf("  [cond] %d %s -> %s\n", idx, it->first.c_str(), it->second.toString().c_str());
00734       active_cond[idx] = 1;
00735       active_conds++;
00736       cond[idx] = it->second;
00737       active_name[idx] = it->first;
00738     }
00739   }
00740   for (RowChange::txt2cell::const_iterator it = change.val.begin();
00741        it!=change.val.end(); it++) {
00742     if (name2col.find(it->first)!=name2col.end()) {
00743       int idx = name2col[it->first]; //dir[it->first];
00744       //printf("  [val] %d %s -> %s\n", idx, it->first.c_str(), it->second.toString().c_str());
00745       active_val[idx] = 1;
00746       val[idx] = it->second;
00747       cval[idx] = it->second;
00748       pval[idx] = it->second;
00749     } else {
00750       if (std::find(change.names.begin(),change.names.end(),it->first)!=
00751           change.names.end()) {
00752         fprintf(stderr,"Unknown column %s\n", it->first.c_str());
00753       }
00754     }
00755   }
00756   for (RowChange::txt2cell::const_iterator it = change.conflictingVal.begin();
00757        it!=change.conflictingVal.end(); it++) {
00758     if (name2col.find(it->first)!=name2col.end()) {
00759       int idx = name2col[it->first];
00760       cval[idx] = it->second;
00761     }
00762   }
00763   for (RowChange::txt2cell::const_iterator it = change.conflictingParentVal.begin();
00764        it!=change.conflictingParentVal.end(); it++) {
00765     if (name2col.find(it->first)!=name2col.end()) {
00766       int idx = name2col[it->first];
00767       pval[idx] = it->second;
00768     }
00769   }
00770 
00771 
00772   bool result = false;
00773   bool defer = false;
00774   
00775   switch (change.mode) {
00776   case ROW_CHANGE_INSERT:
00777     {
00778       RowRef tail(rowCursor);
00779       Poly<SheetRow> inserter;
00780       if (sheet.isSequential()) {
00781         inserter = sheet.insertRowOrdered(tail);
00782       } else {
00783         inserter = sheet.insertRow();
00784       }
00785       vector<Invention> inventions;
00786       for (int c=0; c<width; c++) {
00787         if (active_val[c]) {
00788           PoolColumnLink link;
00789           if (flags.foreign_pool) {
00790             std::map<std::string,coopy::store::PoolColumnLink>::iterator it =
00791               name2pool.find(col2name[c]);
00792             if (it!=name2pool.end()) {
00793               link = it->second;
00794             }
00795           }
00796           if (link.isValid()&&!descriptive) {
00797             // Skip if inventor
00798             // But should try to recover id to place in pool
00799             
00800             if (!link.isInventor()) {
00801               bool found = false;
00802               PoolRecord v = link.getColumn().lookup(val[c],found);
00803               if (found) {
00804                 if (!v.linked) found = false;
00805               } else {
00806                 if (flags.pool) {
00807                   if (flags.pool->isScanned()) {
00808                     // if prescanned, then unmarked records can pass through
00809                     found = true;
00810                     v.cell = val[c];
00811                   }
00812                 }
00813               }
00814               if (!found) {
00815                 /*
00816                 fprintf(stderr, "Problem translating a local value to a remote value. Diagnostics:\n");
00817                 fprintf(stderr, "  * Table: %s\n", link.getTableName().c_str());
00818                 fprintf(stderr, "  * Column: %s\n", link.getColumnName().c_str());
00819                 fprintf(stderr, "  * Value: %s\n", val[c].toString().c_str());
00820                 fprintf(stderr, "This value is not (yet) present in the '%s' pool. Solutions:\n", link.getPoolName().c_str());
00821                 fprintf(stderr, "  * Use --table A --table B ... to control order in which tables are updated.\n");
00822                 fprintf(stderr, "    Try to do more fundamental inserts first, then actions that reference them\n");
00823                 fprintf(stderr, "  * Or rerun with --native flag if no translation is needed.\n");
00824                 //fprintf(stderr, "Exit, fatal error.\n");
00825                 //exit(1);
00826                 */
00827 
00828                 result = false;
00829                 defer = true;
00830                 inserter->undo();
00831                 break;
00832 
00833               } else {
00834                 inserter->setCell(c,v.cell);
00835               }
00836             } else {
00837               inserter->invent(c);
00838               Invention invent = { val[c], c, link };
00839               inventions.push_back(invent);
00840             }
00841           } else {
00842             inserter->setCell(c,val[c]);
00843           }
00844         }
00845       }
00846       if (defer) {
00847         break;
00848       }
00849       inserter->flush();
00850       if (inventions.size()>0) {
00851         int r = inserter->getRowAfterFlush().getIndex();
00852         for (int i=0; i<(int)inventions.size(); i++) {
00853           Invention& inv = inventions[i];
00854           SheetCell dest = sheet.cellSummary(inv.offset,r);
00855           inv.link.getColumn().put(inv.origin,dest);
00856           dbg_printf("Link %s -> %s\n", inv.origin.toString().c_str(),
00857                      dest.toString().c_str());
00858         }
00859       }
00860       if (sheet.isSequential()) {
00861         int r = inserter->getRowAfterFlush().getIndex();
00862         activeRow.insertRowOrdered(tail);
00863         activeRow.cellString(0,r,"+++");
00864         if (descriptive) {
00865           Poly<Appearance> appear = sheet.getRowAppearance(r);
00866           if (appear.isValid()) {
00867             appear->begin();
00868             appear->setBackgroundRgb16(HALF_COLOR,
00869                                        FULL_COLOR,
00870                                        HALF_COLOR,
00871                                        AppearanceRange::full());
00872             appear->setWeightBold(true,AppearanceRange::full());
00873             appear->end();
00874           }
00875         }
00876         r++;
00877         if (r>=sheet.height()) {
00878           r = -1;
00879         }
00880         rowCursor = r;
00881       }
00882     }
00883     break;
00884   case ROW_CHANGE_DELETE:
00885     {
00886       int r = matchRow(active_cond,active_name,cond,width);
00887       if (r<0) {
00888         result = false;
00889         break;
00890       }
00891       RowRef row(r);
00892       rowCursor = r;
00893       if (!descriptive) {
00894         sheet.deleteRow(row);
00895         activeRow.deleteRow(row);
00896       } else {
00897         Poly<Appearance> appear = sheet.getRowAppearance(r);
00898         if (appear.isValid()) {
00899           appear->begin();
00900           appear->setBackgroundRgb16(FULL_COLOR,
00901                                      HALF_COLOR,
00902                                      HALF_COLOR,
00903                                      AppearanceRange::full());
00904           //appear->setWeightBold(true,AppearanceRange::full());
00905           appear->setStrikethrough(true,AppearanceRange::full());
00906           appear->end();
00907         }
00908         activeRow.cellString(0,r,"---");
00909       }
00910       if (rowCursor>=sheet.height()) {
00911         rowCursor = -1;
00912       }
00913       result = true;
00914     }
00915     break;
00916   case ROW_CHANGE_CONTEXT:
00917     {
00918       if (active_conds>0) {
00919         int r = matchRow(active_cond,active_name,cond,width);
00920         if (r!=-2) {
00921           if (r<0) {
00922             result = false;
00923             break;
00924           }
00925           r++;
00926         } else {
00927           r = 0;
00928         }
00929         if (r>=sheet.height()) {
00930           r = -1;
00931         }
00932         RowRef row(r);
00933         rowCursor = r;
00934       } else {
00935         rowCursor = 0;
00936         checkHeader();
00937       }
00938       result = true;
00939     }
00940     break;
00941   case ROW_CHANGE_MOVE:
00942     {
00943       bool success = false;
00944       int r = matchRow(active_cond,active_name,cond,width);
00945       if (r<0) {
00946         result = false;
00947         break;
00948       }
00949       RowRef from(r);
00950       RowRef to(rowCursor);
00951       dbg_printf("Moving %d to %d in sheet of length %d\n", from.getIndex(), to.getIndex(), sheet.height());
00952       RowRef result = sheet.moveRow(from,to);
00953       if (result.getIndex() == -1) {
00954         fprintf(stderr,"Row move failed in sheet of type %s\n",
00955                 sheet.desc().c_str());
00956       } else {
00957         activeRow.moveRow(from,to);
00958       }
00959       r = result.getIndex();
00960       dbg_printf("Move result was %d\n", r);
00961       for (int y=0; y<sheet.height(); y++) {
00962         dbg_printf("%d %s / ", y, sheet.cellString(0,y).c_str());
00963       }
00964       dbg_printf("\n");
00965       markChanges(change,r,width,active_val,val,cval,pval);
00966       r++;
00967       if (r>=sheet.height()) {
00968         r = -1;
00969       }
00970       rowCursor = r;
00971       result = true;
00972     }
00973     break;
00974   case ROW_CHANGE_UPDATE:
00975     {
00976       bool success = false;
00977       int r = matchRow(active_cond,active_name,cond,width);
00978       if (r<0) {
00979         rowCursor = -1;
00980         result = false;
00981         break;
00982       }
00983       dbg_printf("Match for assignment\n");
00984       markChanges(change,r,width,active_val,val,cval,pval);
00985       r++;
00986       if (r>=sheet.height()) {
00987         r = -1;
00988       }
00989       rowCursor = r;
00990       dbg_printf("Cursor moved to %d\n", r);
00991       result = true;
00992     }
00993     break;
00994   default:
00995     fprintf(stderr,"* ERROR: unsupported row operation\n");
00996     result = false;
00997     break;
00998   }
00999 
01000   if (defer) {
01001     deferred_rows.push_back(RowUnit(sheetName,change));
01002     dbg_printf("DEFERRED a row\n");
01003   }
01004 
01005   if (result) {
01006     changeCount++;
01007     if (chain) chain->changeRow(change);
01008   }
01009 
01010   return result;
01011 }
01012 
01013 bool SheetPatcher::declareNames(const std::vector<std::string>& names, 
01014                                 bool final) {
01015   bool first = !declaredNames;
01016   declaredNames = true;
01017   PolySheet sheet = getSheet();
01018   if (!sheet.isValid()) return false;
01019   if (chain) chain->declareNames(names,final);
01020   if (config.trustNames==false) {
01021     if (!descriptive) {
01022       if ((int)names.size()!=sheet.width()) {
01023         //fprintf(stderr,"* ERROR: name mismatch\n");
01024         return false;
01025       }
01026     }
01027     if (first) {
01028       for (int i=0; i<(int)names.size(); i++) {
01029         if (names[i]!=activeCol.cellString(i,0)) {
01030           //if (names[i][0]=='[') {
01031           syn2name[names[i]] = activeCol.cellString(i,0);
01032           name2syn[activeCol.cellString(i,0)] = names[i];
01033           //}
01034         }
01035       }
01036       updateCols();
01037     } else {
01038       if (!descriptive) {
01039         for (int i=0; i<(int)names.size(); i++) {
01040           if (names[i]!=activeCol.cellString(i,0)) {
01041             if (names[i][0]!='[') {
01042               int idx = matchCol(names[i]);
01043               int idx1 = matchCol(activeCol.cellString(i,0));
01044               moveColumn(idx,idx1);
01045             }
01046           }
01047         }
01048         updateCols();
01049       }
01050     }
01051   } else {
01052     for (int i=0; i<(int)names.size(); i++) {
01053       printf("Checking %s\n", names[i].c_str());
01054     }
01055     fprintf(stderr,"Named columns not implemented yet\n");
01056     exit(1);
01057   }
01058   original_index.clear();
01059   for (int i=0; i<(int)names.size(); i++) {
01060     original_index[names[i]] = i;
01061   }
01062   return true;
01063 }
01064 
01065 bool SheetPatcher::setSheet(const char *name) {
01066   checkedHeader = false;
01067   updateSheet();
01068   sheetUpdateNeeded = true;
01069 
01070   if (chain) chain->setSheet(name);
01071 
01072   TextBook *book = getBook();
01073   if (book==NULL) return false;
01074 
01075   //reset
01076   config = ConfigChange();
01077   columns.clear();
01078   column_names.clear();
01079   rowCursor = -1;
01080   declaredNames = false;
01081 
01082   // load
01083   PolySheet psheet;
01084   attachSheet(psheet);
01085   psheet = book->readSheet(name);
01086   if (!psheet.isValid()) {
01087     if (!book->namedSheets()) {
01088       psheet = book->readSheetByIndex(0);
01089     }
01090   }
01091   if (!psheet.isValid()) {
01092     if (string(name)=="sheet") { // default name
01093       if (book->namedSheets()) {
01094         if (book->getNames().size() == 1) {
01095           psheet = book->readSheetByIndex(0);
01096         }
01097       }
01098     }
01099   }
01100   if (!psheet.isValid()) {
01101     fprintf(stderr,"Cannot find patch\n", name);
01102     return false;
01103   }
01104   dbg_printf("Moved to sheet %s\n", name);
01105   attachSheet(psheet);
01106   //sheet = &psheet;
01107 
01108   if (sheetName!=name) {
01109     sheetName = name;
01110     sheetChange = true;
01111   }
01112 
01113   setNames();
01114 
01115   addPoolsFromFlags(psheet);
01116 
01117   return true;
01118 }
01119 
01120 
01121 bool SheetPatcher::mergeStart() {
01122   sheetChange = true;
01123   sheetName = "";
01124   killNeutral = false;
01125   activeRow.resize(1,0);
01126   setNames();
01127   if (chain) chain->mergeStart();
01128   return true;
01129 }
01130 
01131 static string clean(const string& s) {
01132   return (s!="NULL")?s:"";
01133 }
01134 
01135 bool SheetPatcher::mergeAllDone() {
01136   dbg_printf("SheetPatcher::mergeAllDone\n");
01137   if (chain) chain->mergeAllDone();
01138   updateSheet();
01139   return true;
01140 }
01141 
01142 bool SheetPatcher::mergeDone() {
01143   int len = -1;
01144   int prev_len = -2;
01145   do {
01146     prev_len = len;
01147     len = deferred_rows.size();
01148     if (len>0) {
01149       dbg_printf("Dealing with deferred rows\n");
01150       std::list<RowUnit> cp = deferred_rows;
01151       deferred_rows.clear();
01152       for (std::list<RowUnit>::iterator it = cp.begin();
01153            it != cp.end(); it++) {
01154         RowUnit& unit = *it;
01155         if (unit.sheet_name!=sheetName) {
01156           setSheet(unit.sheet_name.c_str());
01157         }
01158         changeRow(unit.change);
01159       }
01160     }
01161   } while (len>0 && len!=prev_len);
01162   if (len>0) {
01163     fprintf(stderr,"Cyclic dependency, cannot apply deferred rows\n");
01164     exit(1);
01165   }
01166 
01167   if (chain) chain->mergeDone();
01168   return true;
01169 }
01170 
01171 
01172 bool SheetPatcher::setNames(bool forceSheetChange) {
01173   if (forceSheetChange) {
01174     sheetChange = true;
01175   }
01176   PolySheet sheet = getSheet();
01177   if (!sheet.isValid()) return false;
01178   if (sheetChange) {
01179     dbg_printf("SheetPatcher Working on sheet %ld\n",
01180                (long int)(&sheet.tail_const()));
01181     sheetChange = false;
01182     clearNames();
01183     activeCol.resize(0,1);
01184     statusCol.resize(0,1);
01185     sniffer = new NameSniffer(sheet,getFlags());
01186     COOPY_ASSERT(sniffer);
01187     sniffedSheet = &sheet;
01188     activeCol.resize(sheet.width(),1);
01189     statusCol.resize(sheet.width(),1);
01190     for (int i=0; i<sheet.width(); i++) {
01191       string name = sniffer->suggestColumnName(i);
01192       activeCol.cellString(i,0,name);
01193     }
01194     updateCols();
01195     updatePool();
01196   }
01197   return true;
01198 }
01199 
01200 
01201 void SheetPatcher::updatePool() {
01202   PolySheet sheet = getSheet();
01203   if (!sheet.isValid()) return;
01204   setNames();
01205   if (descriptive) return;
01206   if (!sniffer) return;
01207   if (sheetName=="") return;
01208   name2pool.clear();
01209   bool addedAuto = false;
01210   for (int i=0; i<sheet.width(); i++) {
01211     string name = sniffer->suggestColumnName(i);
01212     const CompareFlags& flags = getFlags();
01213     if (!flags.pool) continue;
01214     //printf("Looking up %s %s\n", sheetName.c_str(), name.c_str());
01215     PoolColumnLink pc = flags.pool->lookup(sheetName,name);
01216     if (!pc.isValid()) {
01217       ColumnType t = sniffer->suggestColumnType(i);
01218       if (t.autoIncrement&&!addedAuto) {
01219         //printf("Found autoinc'er\n");
01220         flags.pool->create(sheetName,sheetName,name,true);
01221         pc = flags.pool->lookup(sheetName,name);
01222         addedAuto = true;
01223       }
01224       if (t.foreignKey!="") {
01225         //printf("Found xref\n");
01226         flags.pool->create(t.foreignTable,sheetName,name,false);
01227         pc = flags.pool->lookup(sheetName,name);
01228       }
01229     }
01230     if (!pc.isValid()) continue;
01231     //printf("Found a pool at %s %s\n", sheetName.c_str(), name.c_str());
01232     name2pool[name] = pc;
01233     CompareFlags& flags2 = getMutableFlags();
01234     if (!flags2.foreign_pool_set) {
01235       flags2.foreign_pool = true;
01236       flags2.foreign_pool_set = true;
01237     }
01238   }
01239 }
01240 
01241 
01242 bool SheetPatcher::updateSheet() {
01243   if (!sheetUpdateNeeded) return false;
01244 
01245   if (descriptive) {
01246     PolySheet sheet = getSheet();
01247     if (!sheet.isValid()) return false;
01248     if (sheet.height()==0) return true;
01249     sheet.insertColumn(ColumnRef(0)); //,ColumnInfo("_ACTION_")); 
01250     dbg_printf("Added description column\n");
01251     //sheet.insertRow(RowRef(0));
01252     for (int i=0; i<sheet.height()&&i<activeRow.height(); i++) {
01253       dbg_printf("  row %d\n", i);
01254       string txt = activeRow.cellString(0,i);
01255       if (txt==""&&killNeutral) {
01256         txt = "---";
01257       }
01258       if (txt!="") {
01259         sheet.cellString(0,i,txt);
01260         Poly<Appearance> appear = sheet.getCellAppearance(0,i);
01261         if (appear.isValid()) {
01262           appear->begin();
01263           appear->setWeightBold(true,AppearanceRange::full());
01264           appear->setStrikethrough(false,AppearanceRange::full());
01265           appear->end();
01266         }
01267       }
01268     }
01269     COOPY_ASSERT(sniffer);
01270     int r = -1;
01271     r = sniffer->getHeaderHeight()-1;
01272     if (r>=0 && r<sheet.height()) {
01273       string key = clean(sheet.cellString(0,r));
01274       if (key == "" || key == "->") {
01275         for (int i=0; i<=r; i++) {
01276           sheet.cellString(0,i,string("@@")+clean(sheet.cellString(0,i)));
01277         }
01278       } else {
01279         r = -1;
01280       }
01281     }
01282     int yoff = 0;
01283     if (r<0) {
01284       sheet.insertRow(RowRef(0)); 
01285       sheet.cellString(0,yoff,"@@");
01286       r = 0;
01287     } else {
01288       yoff = r;
01289     }
01290     for (int i=1; i<sheet.width(); i++) {
01291       dbg_printf("Header clobber %s\n", sheet.cellString(i,yoff).c_str());
01292       sheet.cellString(i,yoff,activeCol.cellString(i-1,0));
01293     }
01294     bool colAction = false;
01295     for (int i=1; i<sheet.width(); i++) {
01296       if (statusCol.cellString(i-1,0)!="") {
01297         colAction = true;
01298         break;
01299       }
01300     }
01301     if (colAction) {
01302       sheet.insertRow(RowRef(0)); 
01303       sheet.cellString(0,0,"!");
01304       for (int i=1; i<sheet.width(); i++) {
01305         sheet.cellString(i,0,statusCol.cellString(i-1,0));
01306       }
01307       yoff++;
01308     }
01309     Poly<Appearance> appear = sheet.getRowAppearance(r+yoff);
01310     if (appear.isValid()) {
01311       appear->begin();
01312       appear->setWeightBold(true,AppearanceRange::full());
01313       appear->end();
01314     }
01315 
01316     int lines = getFlags().context_lines;
01317     if (lines==-2) lines = 2;
01318     if (lines>=0) {
01319       vector<int> show;
01320       show.resize(sheet.height(),0);
01321       for (int i=0; i<sheet.height(); i++) {
01322         bool present = clean(sheet.cellString(0,i))!="";
01323         if (present) {
01324           for (int j=-lines; j<=lines; j++) {
01325             int k = i+j;
01326             if (k>=0 && k<sheet.height()) {
01327               show[k] = 1;
01328               dbg_printf("* Marking %d...\n", k);
01329             }
01330           }
01331         }
01332       }
01333       int offset = 0;
01334       bool addedBreak = false;
01335       int start_delete = -1;
01336       int count_delete = 0;
01337       for (int i=0; i<(int)show.size(); i++) {
01338         dbg_printf("Checking %d (%d)...\n", i, show[i]);
01339         if (show[i]==0) {
01340           int k = i+offset;
01341           if (addedBreak) {
01342             dbg_printf("Deleting row %d\n", k);
01343             if (start_delete==-1) {
01344               start_delete = k;
01345               count_delete = 0;
01346             }
01347             count_delete++;
01348             //sheet.deleteRow(RowRef(k));
01349             offset--;
01350           } else {
01351             if (start_delete!=-1) {
01352               dbg_printf("Implementing deletion of %d rows starting at %d\n",
01353                          count_delete,
01354                          start_delete);
01355             
01356               sheet.deleteRows(RowRef(start_delete),
01357                                RowRef(start_delete+count_delete-1));
01358               start_delete = -1;
01359             }
01360             dbg_printf("Adding ... marks to %d\n", k);
01361             sheet.cellString(0,k,"...");
01362             for (int j=1; j<sheet.width(); j++) {
01363               sheet.cellString(j,k,"...");
01364             }
01365             addedBreak = true;
01366           }
01367         } else {
01368           addedBreak = false;
01369         }
01370       }
01371       if (start_delete!=-1) {
01372         dbg_printf("Implementing deletion of %d rows starting at %d\n",
01373                    count_delete,
01374                    start_delete);
01375         sheet.deleteRows(RowRef(start_delete),
01376                          RowRef(start_delete+count_delete-1));
01377         start_delete = -1;
01378       }
01379     }
01380   }
01381 
01382   if (forReview) {
01383     PolySheet sheet = getSheet();
01384     if (!sheet.isValid()) return false;
01385     sheet.insertColumn(ColumnRef(0)); 
01386     sheet.insertRow(RowRef(0)); 
01387     sheet.cellString(0,0,"Allow? (yes/no)"); 
01388   }
01389 
01390   sheetUpdateNeeded = false;
01391 
01392   return true;
01393 }
01394 
01395 /*
01396 coopy::store::PolySheet SheetPatcher::getSheet() {
01397   PolySheet sheet = Patcher::getSheet();
01398   if (!sheet.isValid()) return sheet;
01399   if (!active_sheet.isValid()) {
01400     active_sheet = sheet;
01401   }
01402   if (&sheet.tail()!=&active_sheet.tail()) {
01403     active_sheet = sheet;
01404   }
01405   if (!getFlags().assume_header) {
01406     active_sheet.forbidSchema();
01407   }
01408   return active_sheet;
01409 }
01410 */
01411 
01412 
01413 
01414 
01415 bool SheetPatcher::changeName(const NameChange& change) {
01416   if (change.strong) {
01417     PolySheet sheet = getSheet();
01418     setNames();
01419     map<string,int> includes;
01420     for (int i=0; i<(int)change.names.size(); i++) { 
01421       includes[change.names[i]] = 1;
01422     }
01423     int at = 0;
01424     int mods = 0;
01425     for (int i=0; i<activeCol.width(); i++) {
01426       string name = activeCol.cellString(i,0);
01427       bool included = includes.find(name) != includes.end();
01428       if (!included) {
01429         ColumnRef col(at);
01430         sheet.deleteColumn(at);
01431         mods++;
01432       } else {
01433         at++;
01434       }
01435     }
01436     if (mods) setNames(true);
01437   }
01438   if (chain) chain->changeName(change);
01439   bool ok = Patcher::changeName(change);
01440   if (!ok) return ok;
01441   return ok;
01442 }
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines