Warning, file /office/tellico/src/utils/mergeconflictresolver.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /*************************************************************************** 0002 Copyright (C) 2021 Robby Stephenson <robby@periapsis.org> 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or * 0008 * modify it under the terms of the GNU General Public License as * 0009 * published by the Free Software Foundation; either version 2 of * 0010 * the License or (at your option) version 3 or any later version * 0011 * accepted by the membership of KDE e.V. (or its successor approved * 0012 * by the membership of KDE e.V.), which shall act as a proxy * 0013 * defined in Section 14 of version 3 of the license. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0022 * * 0023 ***************************************************************************/ 0024 0025 #include "mergeconflictresolver.h" 0026 #include "../collectionfactory.h" 0027 #include "../tellico_debug.h" 0028 0029 using namespace Tellico; 0030 0031 bool Merge::mergeEntry(Data::EntryPtr e1, Data::EntryPtr e2, Merge::ConflictResolver* resolver_) { 0032 if(!e1 || !e2) { 0033 myDebug() << "bad entry pointer"; 0034 return false; 0035 } 0036 bool ret = true; 0037 foreach(Data::FieldPtr field, e1->collection()->fields()) { 0038 if(e2->field(field).isEmpty()) { 0039 continue; 0040 } 0041 // never try to merge entry id, creation date or mod date. Those are unique to each entry 0042 if(field->name() == QLatin1String("id") || 0043 field->name() == QLatin1String("cdate") || 0044 field->name() == QLatin1String("mdate")) { 0045 continue; 0046 } 0047 // myLog() << "reading field: " << field->name(); 0048 if(e1->field(field) == e2->field(field)) { 0049 continue; 0050 } else if(e1->field(field).isEmpty()) { 0051 // myLog() << e1->title() << ": updating field(" << field->name() << ") to " << e2->field(field); 0052 e1->setField(field, e2->field(field)); 0053 ret = true; 0054 } else if(field->type() == Data::Field::Table) { 0055 // if field F is a table-type field (album tracks, files, etc.), merge rows (keep their position) 0056 // if e1's F val in [row i, column j] empty, replace with e2's val at same position 0057 // if different (non-empty) vals at same position, CONFLICT! 0058 QStringList vals1 = FieldFormat::splitTable(e1->field(field)); 0059 QStringList vals2 = FieldFormat::splitTable(e2->field(field)); 0060 while(vals1.count() < vals2.count()) { 0061 vals1 += QString(); 0062 } 0063 for(int i = 0; i < vals2.count(); ++i) { 0064 if(vals2[i].isEmpty()) { 0065 continue; 0066 } 0067 if(vals1[i].isEmpty()) { 0068 vals1[i] = vals2[i]; 0069 ret = true; 0070 } else { 0071 QStringList parts1 = FieldFormat::splitRow(vals1[i]); 0072 QStringList parts2 = FieldFormat::splitRow(vals2[i]); 0073 bool changedPart = false; 0074 while(parts1.count() < parts2.count()) { 0075 parts1 += QString(); 0076 } 0077 for(int j = 0; j < parts2.count(); ++j) { 0078 if(parts2[j].isEmpty()) { 0079 continue; 0080 } 0081 if(parts1[j].isEmpty()) { 0082 parts1[j] = parts2[j]; 0083 changedPart = true; 0084 } else if(resolver_ && parts1[j] != parts2[j]) { 0085 const int resolverResponse = resolver_->resolve(e1, e2, field, parts1[j], parts2[j]); 0086 if(resolverResponse == Merge::ConflictResolver::CancelMerge) { 0087 return false; // cancel all the merge right now 0088 } else if(resolverResponse == Merge::ConflictResolver::KeepSecond) { 0089 parts1[j] = parts2[j]; 0090 changedPart = true; 0091 } 0092 } 0093 } 0094 if(changedPart) { 0095 vals1[i] = parts1.join(FieldFormat::columnDelimiterString()); 0096 ret = true; 0097 } 0098 } 0099 } 0100 if(ret) { 0101 e1->setField(field, vals1.join(FieldFormat::rowDelimiterString())); 0102 } 0103 // remove the merging due to user comments 0104 // maybe in the future have a more intelligent way 0105 #if 0 0106 } else if(field->hasFlag(Data::Field::AllowMultiple)) { 0107 // if field F allows multiple values and not a Table (see above case), 0108 // e1's F values = (e1's F values) U (e2's F values) (union) 0109 // replace e1's field with union of e1's and e2's values for this field 0110 QStringList items1 = e1->fields(field, false); 0111 QStringList items2 = e2->fields(field, false); 0112 foreach(const QString& item2, items2) { 0113 // possible to have one value formatted and the other one not... 0114 if(!items1.contains(item2) && !items1.contains(Field::format(item2, field->formatType()))) { 0115 items1.append(item2); 0116 } 0117 } 0118 // not sure if I think it should be sorted or not 0119 // items1.sort(); 0120 e1->setField(field, items1.join(FieldFormat::delimiterString())); 0121 ret = true; 0122 #endif 0123 } else if(resolver_) { 0124 const int resolverResponse = resolver_->resolve(e1, e2, field); 0125 if(resolverResponse == Merge::ConflictResolver::CancelMerge) { 0126 return false; // cancel all the merge right now 0127 } else if(resolverResponse == Merge::ConflictResolver::KeepSecond) { 0128 e1->setField(field, e2->field(field)); 0129 } 0130 } else { 0131 // myDebug() << "Keeping value of" << field->name() << "for" << e1->field(QStringLiteral("title")); 0132 } 0133 } 0134 return ret; 0135 } 0136 0137 QPair<Tellico::Data::FieldList, Tellico::Data::FieldList> Merge::mergeFields(Data::CollPtr coll_, 0138 Data::FieldList fields_, 0139 Data::EntryList entries_) { 0140 Data::FieldList modified, created; 0141 foreach(Data::FieldPtr field, fields_) { 0142 // don't add a field if it's a default field and not in the current collection 0143 if(coll_->hasField(field->name()) || CollectionFactory::isDefaultField(coll_->type(), field->name())) { 0144 // special case for choice fields, since we might want to add a value 0145 if(field->type() == Data::Field::Choice && coll_->hasField(field->name())) { 0146 // a2 are the existing fields in the collection, keep them in the same order 0147 QStringList a1 = coll_->fieldByName(field->name())->allowed(); 0148 foreach(const QString& newAllowedValue, field->allowed()) { 0149 if(!a1.contains(newAllowedValue)) { 0150 // could be slow for large merges, but we do only want to add new value 0151 // IF that value is actually used by an entry 0152 foreach(Data::EntryPtr entry, entries_) { 0153 if(entry->field(field) == newAllowedValue) { 0154 a1 += newAllowedValue; 0155 break; 0156 } 0157 } 0158 } 0159 } 0160 if(a1.count() != coll_->fieldByName(field->name())->allowed().count()) { 0161 Data::FieldPtr f(new Data::Field(*coll_->fieldByName(field->name()))); 0162 f->setAllowed(a1); 0163 modified.append(f); 0164 } 0165 } 0166 continue; 0167 } 0168 // add field if any values are not empty 0169 foreach(Data::EntryPtr entry, entries_) { 0170 if(!entry->field(field).isEmpty()) { 0171 created.append(Data::FieldPtr(new Data::Field(*field))); 0172 break; 0173 } 0174 } 0175 } 0176 return qMakePair(modified, created); 0177 }