data/VCardMergePlan.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "VCardMergePlan.h" | ||
| 2 | |||
| 3 | #include "VCardCodec.h" | ||
| 4 | |||
| 5 | #include <QDateTime> | ||
| 6 | #include <QSet> | ||
| 7 | |||
| 8 | namespace VCardMergePlan { | ||
| 9 | |||
| 10 | namespace { | ||
| 11 | |||
| 12 | const QSet<QString> kMultiValued{QStringLiteral("EMAIL"), QStringLiteral("TEL"), QStringLiteral("ADR"), | ||
| 13 | QStringLiteral("URL"), QStringLiteral("IMPP"), QStringLiteral("RELATED"), | ||
| 14 | QStringLiteral("MEMBER"), QStringLiteral("X-ADDRESSBOOKSERVER-MEMBER"), | ||
| 15 | QStringLiteral("X-SOCIALPROFILE"), QStringLiteral("KEY"), QStringLiteral("LANG"), | ||
| 16 | QStringLiteral("LABEL"), | ||
| 17 | // Sprint 103 S103.1.7: people details merge as sets too. | ||
| 18 | QStringLiteral("X-ABRELATEDNAMES"), QStringLiteral("X-ABDATE"), | ||
| 19 | QStringLiteral("SOCIALPROFILE"), QStringLiteral("INTEREST"), | ||
| 20 | QStringLiteral("X-MAILJD-INTEREST"), QStringLiteral("X-MAILJD-LANG"), | ||
| 21 | QStringLiteral("HOBBY"), QStringLiteral("EXPERTISE"), QStringLiteral("X-MAILJD-MAIL")}; | ||
| 22 | const QSet<QString> kFromTheirs{QStringLiteral("VERSION"), QStringLiteral("UID"), QStringLiteral("PRODID"), | ||
| 23 | QStringLiteral("REV")}; | ||
| 24 | |||
| 25 | // Label lines of a property group (X-ABLabel, X-ABADR, …). X-ABDATE and | ||
| 26 | // X-ABRELATEDNAMES are values of their own (Sprint 103), not companions. | ||
| 27 | 9845 | bool isCompanion(const ICal::Property &property) { | |
| 28 |
3/3✓ Branch 2 → 3 taken 9845 times.
✓ Branch 5 → 6 taken 9356 times.
✓ Branch 5 → 7 taken 489 times.
|
9845 | if (VCardCodec::groupOf(property).isEmpty()) |
| 29 | 9356 | return false; | |
| 30 |
1/1✓ Branch 7 → 8 taken 489 times.
|
489 | const QString name = VCardCodec::baseName(property); |
| 31 |
5/5✓ Branch 9 → 10 taken 489 times.
✓ Branch 10 → 11 taken 272 times.
✓ Branch 10 → 18 taken 217 times.
✓ Branch 13 → 14 taken 245 times.
✓ Branch 13 → 18 taken 27 times.
|
734 | return name.startsWith(QLatin1String("X-AB")) && name != QLatin1String("X-ABDATE") && |
| 32 |
1/2✓ Branch 16 → 17 taken 245 times.
✗ Branch 16 → 18 not taken.
|
245 | name != QLatin1String("X-ABRELATEDNAMES"); |
| 33 | 489 | } | |
| 34 | |||
| 35 | // A property with the label lines of its group. | ||
| 36 | struct Unit { | ||
| 37 | QString name; | ||
| 38 | QString key; // multi-valued: name + value + types; single: name | ||
| 39 | QString text; // comparable rendering (group names stripped) | ||
| 40 | QList<ICal::Property> lines; | ||
| 41 | }; | ||
| 42 | |||
| 43 | 8467 | QString withoutGroup(const ICal::Property &property) { | |
| 44 |
1/1✓ Branch 2 → 3 taken 8467 times.
|
8467 | const QString line = property.contentLine(); |
| 45 |
1/1✓ Branch 3 → 4 taken 8467 times.
|
8467 | const QString group = VCardCodec::groupOf(property); |
| 46 |
3/3✓ Branch 5 → 6 taken 8245 times.
✓ Branch 5 → 7 taken 222 times.
✓ Branch 8 → 9 taken 222 times.
|
16934 | return group.isEmpty() ? line : line.mid(group.size() + 1); |
| 47 | 8467 | } | |
| 48 | |||
| 49 | 977 | QString normalizedValue(const QString &name, const ICal::Property &property) { | |
| 50 |
2/2✓ Branch 2 → 3 taken 977 times.
✓ Branch 3 → 4 taken 977 times.
|
977 | QString value = ICal::unescapeText(property.value).trimmed(); |
| 51 |
2/2✓ Branch 7 → 8 taken 481 times.
✓ Branch 7 → 10 taken 496 times.
|
977 | if (name == QLatin1String("EMAIL")) |
| 52 |
1/1✓ Branch 8 → 9 taken 481 times.
|
481 | return value.toLower(); |
| 53 |
2/2✓ Branch 12 → 13 taken 203 times.
✓ Branch 12 → 31 taken 293 times.
|
496 | if (name == QLatin1String("TEL")) { |
| 54 | 203 | QString digits; | |
| 55 |
4/4✓ Branch 13 → 14 taken 203 times.
✓ Branch 14 → 15 taken 203 times.
✓ Branch 27 → 16 taken 2333 times.
✓ Branch 27 → 28 taken 203 times.
|
2536 | for (const QChar c : value) |
| 56 |
6/6✓ Branch 17 → 18 taken 603 times.
✓ Branch 17 → 22 taken 1730 times.
✓ Branch 21 → 22 taken 201 times.
✓ Branch 21 → 23 taken 402 times.
✓ Branch 24 → 25 taken 1931 times.
✓ Branch 24 → 26 taken 402 times.
|
2333 | if (c.isDigit() || c == QLatin1Char('+')) |
| 57 |
1/1✓ Branch 25 → 26 taken 1931 times.
|
1931 | digits += c; |
| 58 | 203 | return digits; | |
| 59 | 203 | } | |
| 60 | 293 | return value; | |
| 61 | 977 | } | |
| 62 | |||
| 63 | 977 | QString typeKey(const ICal::Property &property) { | |
| 64 | 977 | QStringList types; | |
| 65 |
2/2✓ Branch 28 → 4 taken 813 times.
✓ Branch 28 → 29 taken 977 times.
|
1790 | for (const ICal::Parameter ¶meter : property.parameters) |
| 66 |
2/2✓ Branch 7 → 8 taken 763 times.
✓ Branch 7 → 26 taken 50 times.
|
813 | if (parameter.name == QLatin1String("TYPE")) |
| 67 |
5/5✓ Branch 11 → 12 taken 763 times.
✓ Branch 12 → 13 taken 763 times.
✓ Branch 13 → 14 taken 763 times.
✓ Branch 23 → 15 taken 985 times.
✓ Branch 23 → 24 taken 763 times.
|
1748 | for (const QString &type : parameter.value.split(QLatin1Char(','), Qt::SkipEmptyParts)) |
| 68 |
3/3✓ Branch 16 → 17 taken 985 times.
✓ Branch 17 → 18 taken 985 times.
✓ Branch 18 → 19 taken 985 times.
|
1748 | types << type.trimmed().toLower(); |
| 69 |
1/1✓ Branch 32 → 33 taken 977 times.
|
977 | types.removeAll(QStringLiteral("pref")); |
| 70 |
1/1✓ Branch 35 → 36 taken 977 times.
|
977 | types.sort(); |
| 71 |
1/1✓ Branch 38 → 39 taken 977 times.
|
1954 | return types.join(QLatin1Char(',')); |
| 72 | 977 | } | |
| 73 | |||
| 74 | 1143 | QList<Unit> unitsOf(const ICal::Component &card) { | |
| 75 | 1143 | QList<Unit> units; | |
| 76 |
2/2✓ Branch 73 → 4 taken 8467 times.
✓ Branch 73 → 74 taken 1143 times.
|
9610 | for (const ICal::Property &property : card.properties) { |
| 77 |
3/3✓ Branch 5 → 6 taken 8467 times.
✓ Branch 6 → 7 taken 111 times.
✓ Branch 6 → 8 taken 8356 times.
|
8467 | if (isCompanion(property)) |
| 78 | 111 | continue; | |
| 79 | 8356 | Unit unit; | |
| 80 |
1/1✓ Branch 8 → 9 taken 8356 times.
|
8356 | unit.name = VCardCodec::baseName(property); |
| 81 |
1/1✓ Branch 11 → 12 taken 8356 times.
|
8356 | unit.lines.append(property); |
| 82 |
1/1✓ Branch 12 → 13 taken 8356 times.
|
8356 | unit.text = withoutGroup(property); |
| 83 |
1/1✓ Branch 15 → 16 taken 8356 times.
|
8356 | const QString group = VCardCodec::groupOf(property); |
| 84 |
2/2✓ Branch 17 → 18 taken 111 times.
✓ Branch 17 → 44 taken 8245 times.
|
8356 | if (!group.isEmpty()) |
| 85 |
2/2✓ Branch 42 → 20 taken 1329 times.
✓ Branch 42 → 43 taken 111 times.
|
1440 | for (const ICal::Property &companion : card.properties) |
| 86 |
10/12✓ Branch 21 → 22 taken 1329 times.
✓ Branch 22 → 23 taken 133 times.
✓ Branch 22 → 27 taken 1196 times.
✓ Branch 23 → 24 taken 133 times.
✓ Branch 25 → 26 taken 111 times.
✓ Branch 25 → 27 taken 22 times.
✓ Branch 28 → 29 taken 133 times.
✓ Branch 28 → 30 taken 1196 times.
✓ Branch 30 → 31 taken 111 times.
✓ Branch 30 → 40 taken 1218 times.
✗ Branch 78 → 79 not taken.
✗ Branch 78 → 80 not taken.
|
1329 | if (isCompanion(companion) && VCardCodec::groupOf(companion) == group) { |
| 87 |
1/1✓ Branch 31 → 32 taken 111 times.
|
111 | unit.lines.append(companion); |
| 88 |
3/3✓ Branch 32 → 33 taken 111 times.
✓ Branch 35 → 36 taken 111 times.
✓ Branch 36 → 37 taken 111 times.
|
111 | unit.text += QLatin1Char('\n') + withoutGroup(companion); |
| 89 | } | ||
| 90 | 8356 | unit.key = kMultiValued.contains(unit.name) | |
| 91 |
18/28✓ Branch 45 → 46 taken 977 times.
✓ Branch 45 → 54 taken 7379 times.
✓ Branch 46 → 47 taken 977 times.
✓ Branch 48 → 49 taken 977 times.
✓ Branch 50 → 51 taken 977 times.
✓ Branch 51 → 52 taken 977 times.
✓ Branch 52 → 53 taken 977 times.
✓ Branch 53 → 55 taken 977 times.
✓ Branch 57 → 58 taken 977 times.
✓ Branch 57 → 59 taken 7379 times.
✓ Branch 59 → 60 taken 977 times.
✓ Branch 59 → 61 taken 7379 times.
✓ Branch 61 → 62 taken 977 times.
✓ Branch 61 → 63 taken 7379 times.
✓ Branch 63 → 64 taken 977 times.
✓ Branch 63 → 65 taken 7379 times.
✓ Branch 65 → 66 taken 977 times.
✓ Branch 65 → 67 taken 7379 times.
✗ Branch 91 → 92 not taken.
✗ Branch 91 → 93 not taken.
✗ Branch 95 → 96 not taken.
✗ Branch 95 → 97 not taken.
✗ Branch 99 → 100 not taken.
✗ Branch 99 → 101 not taken.
✗ Branch 104 → 105 not taken.
✗ Branch 104 → 106 not taken.
✗ Branch 109 → 110 not taken.
✗ Branch 109 → 111 not taken.
|
16712 | ? unit.name + QChar(0x1f) + normalizedValue(unit.name, property) + QChar(0x1f) + typeKey(property) |
| 92 | 8356 | : unit.name; | |
| 93 |
1/1✓ Branch 67 → 68 taken 8356 times.
|
8356 | units.append(unit); |
| 94 | 8356 | } | |
| 95 | 1143 | return units; | |
| 96 | ✗ | } | |
| 97 | |||
| 98 | 2567 | QList<Unit> unitsNamed(const QList<Unit> &units, const QString &name) { | |
| 99 | 2567 | QList<Unit> result; | |
| 100 |
2/2✓ Branch 10 → 4 taken 19498 times.
✓ Branch 10 → 11 taken 2567 times.
|
22065 | for (const Unit &unit : units) |
| 101 |
2/2✓ Branch 6 → 7 taken 2270 times.
✓ Branch 6 → 8 taken 17228 times.
|
19498 | if (unit.name == name) |
| 102 |
1/1✓ Branch 7 → 8 taken 2270 times.
|
2270 | result << unit; |
| 103 | 2567 | return result; | |
| 104 | ✗ | } | |
| 105 | |||
| 106 | // Rendering of all single-valued units of `name` (several FN lines compare as one). | ||
| 107 | 2049 | QString singleText(const QList<Unit> &units, const QString &name) { | |
| 108 | 2049 | QStringList texts; | |
| 109 |
2/2✓ Branch 10 → 4 taken 15698 times.
✓ Branch 10 → 11 taken 2049 times.
|
17747 | for (const Unit &unit : units) |
| 110 |
2/2✓ Branch 6 → 7 taken 1894 times.
✓ Branch 6 → 8 taken 13804 times.
|
15698 | if (unit.name == name) |
| 111 |
1/1✓ Branch 7 → 8 taken 1894 times.
|
1894 | texts << unit.text; |
| 112 |
1/1✓ Branch 12 → 13 taken 2049 times.
|
4098 | return texts.join(QChar(0x1e)); |
| 113 | 2049 | } | |
| 114 | |||
| 115 | // The last unit of `key` (the one VCardMerge compared in place). | ||
| 116 | 965 | const Unit *lastWithKey(const QList<Unit> &units, const QString &key) { | |
| 117 | 965 | const Unit *found = nullptr; | |
| 118 |
2/2✓ Branch 10 → 4 taken 8953 times.
✓ Branch 10 → 11 taken 965 times.
|
9918 | for (const Unit &unit : units) |
| 119 |
2/2✓ Branch 6 → 7 taken 633 times.
✓ Branch 6 → 8 taken 8320 times.
|
8953 | if (unit.key == key) |
| 120 | 633 | found = &unit; | |
| 121 | 965 | return found; | |
| 122 | } | ||
| 123 | |||
| 124 | 1143 | QStringList categoriesOf(const ICal::Component &card) { | |
| 125 | 1143 | QStringList result; | |
| 126 |
2/2✓ Branch 34 → 4 taken 8467 times.
✓ Branch 34 → 35 taken 1143 times.
|
9610 | for (const ICal::Property &property : card.properties) |
| 127 |
3/3✓ Branch 6 → 7 taken 8467 times.
✓ Branch 9 → 10 taken 156 times.
✓ Branch 9 → 32 taken 8311 times.
|
8467 | if (VCardCodec::baseName(property) == QLatin1String("CATEGORIES")) |
| 128 |
5/5✓ Branch 10 → 11 taken 156 times.
✓ Branch 11 → 12 taken 156 times.
✓ Branch 12 → 13 taken 156 times.
✓ Branch 29 → 14 taken 224 times.
✓ Branch 29 → 30 taken 156 times.
|
380 | for (const QString &value : ICal::splitValues(property.value)) |
| 129 |
3/4✓ Branch 15 → 16 taken 224 times.
✓ Branch 16 → 17 taken 224 times.
✓ Branch 20 → 21 taken 224 times.
✗ Branch 20 → 27 not taken.
|
224 | if (!result.contains(ICal::unescapeText(value).trimmed(), Qt::CaseInsensitive)) |
| 130 |
3/3✓ Branch 21 → 22 taken 224 times.
✓ Branch 22 → 23 taken 224 times.
✓ Branch 23 → 24 taken 224 times.
|
380 | result << ICal::unescapeText(value).trimmed(); |
| 131 | 1143 | return result; | |
| 132 | ✗ | } | |
| 133 | |||
| 134 | 5 | QString freeGroup(const ICal::Component &card) { | |
| 135 | 5 | int highest = 0; | |
| 136 |
2/2✓ Branch 19 → 4 taken 47 times.
✓ Branch 19 → 20 taken 5 times.
|
52 | for (const ICal::Property &property : card.properties) { |
| 137 |
2/2✓ Branch 5 → 6 taken 47 times.
✓ Branch 6 → 7 taken 47 times.
|
47 | const QString group = VCardCodec::groupOf(property).toUpper(); |
| 138 |
3/3✓ Branch 9 → 10 taken 47 times.
✓ Branch 10 → 11 taken 10 times.
✓ Branch 10 → 16 taken 37 times.
|
47 | if (group.startsWith(QLatin1String("ITEM"))) |
| 139 |
2/2✓ Branch 11 → 12 taken 10 times.
✓ Branch 12 → 13 taken 10 times.
|
10 | highest = qMax(highest, group.mid(4).toInt()); |
| 140 | 47 | } | |
| 141 |
1/1✓ Branch 24 → 25 taken 5 times.
|
10 | return QStringLiteral("item%1").arg(highest + 1); |
| 142 | } | ||
| 143 | |||
| 144 | 13 | void removeUnit(ICal::Component *card, const Unit &unit) { | |
| 145 | 13 | const ICal::Property &main = unit.lines.first(); | |
| 146 |
1/1✓ Branch 3 → 4 taken 13 times.
|
13 | const QString group = VCardCodec::groupOf(main); |
| 147 |
1/2✓ Branch 20 → 5 taken 87 times.
✗ Branch 20 → 21 not taken.
|
87 | for (int i = 0; i < card->properties.size(); ++i) { |
| 148 |
4/4✓ Branch 5 → 6 taken 87 times.
✓ Branch 7 → 8 taken 87 times.
✓ Branch 11 → 12 taken 74 times.
✓ Branch 11 → 13 taken 13 times.
|
87 | if (card->properties.at(i).contentLine() != main.contentLine()) |
| 149 | 74 | continue; | |
| 150 |
1/1✓ Branch 13 → 14 taken 13 times.
|
13 | card->properties.removeAt(i); |
| 151 |
2/2✓ Branch 15 → 16 taken 1 time.
✓ Branch 15 → 17 taken 12 times.
|
13 | if (!group.isEmpty()) |
| 152 |
1/1✓ Branch 16 → 17 taken 1 time.
|
1 | card->properties.removeIf([&group](const ICal::Property &p) { |
| 153 |
7/10✓ Branch 2 → 3 taken 8 times.
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 8 taken 7 times.
✓ Branch 4 → 5 taken 1 time.
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 8 not taken.
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 7 times.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 16 not taken.
|
8 | return isCompanion(p) && VCardCodec::groupOf(p) == group; |
| 154 | }); | ||
| 155 | 13 | return; | |
| 156 | } | ||
| 157 | 13 | } | |
| 158 | |||
| 159 | 29 | void appendUnit(ICal::Component *card, const Unit &unit) { | |
| 160 |
1/1✓ Branch 3 → 4 taken 29 times.
|
29 | const QString oldGroup = VCardCodec::groupOf(unit.lines.first()); |
| 161 |
3/3✓ Branch 5 → 6 taken 24 times.
✓ Branch 5 → 7 taken 5 times.
✓ Branch 7 → 8 taken 5 times.
|
29 | const QString group = oldGroup.isEmpty() ? QString() : freeGroup(*card); |
| 162 |
2/2✓ Branch 28 → 10 taken 34 times.
✓ Branch 28 → 29 taken 29 times.
|
63 | for (ICal::Property line : unit.lines) { |
| 163 |
2/2✓ Branch 13 → 14 taken 10 times.
✓ Branch 13 → 24 taken 24 times.
|
34 | if (!group.isEmpty()) { |
| 164 |
3/3✓ Branch 14 → 15 taken 10 times.
✓ Branch 17 → 18 taken 10 times.
✓ Branch 18 → 19 taken 10 times.
|
10 | line.name = group + QLatin1Char('.') + VCardCodec::baseName(line); |
| 165 | 10 | line.originalLine.clear(); | |
| 166 | } | ||
| 167 |
1/1✓ Branch 24 → 25 taken 34 times.
|
34 | card->properties.append(line); |
| 168 | 34 | } | |
| 169 | 29 | } | |
| 170 | |||
| 171 | // Replaces every unit of `name` in `card` with `replacement` (at the first | ||
| 172 | // old position, else at the end). | ||
| 173 | 50 | void replaceSingle(ICal::Component *card, const QString &name, const QList<Unit> &replacement) { | |
| 174 | 50 | int position = -1; | |
| 175 |
2/2✓ Branch 26 → 4 taken 362 times.
✓ Branch 26 → 27 taken 50 times.
|
412 | for (int i = card->properties.size() - 1; i >= 0; --i) { |
| 176 |
8/12✓ Branch 5 → 6 taken 362 times.
✓ Branch 7 → 8 taken 41 times.
✓ Branch 7 → 11 taken 321 times.
✓ Branch 9 → 10 taken 41 times.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 41 times.
✓ Branch 13 → 14 taken 362 times.
✗ Branch 13 → 15 not taken.
✓ Branch 15 → 16 taken 321 times.
✓ Branch 15 → 17 taken 41 times.
✗ Branch 49 → 50 not taken.
✗ Branch 49 → 51 not taken.
|
362 | if (VCardCodec::baseName(card->properties.at(i)) != name || isCompanion(card->properties.at(i))) |
| 177 | 321 | continue; | |
| 178 |
1/1✓ Branch 18 → 19 taken 41 times.
|
41 | const QString group = VCardCodec::groupOf(card->properties.at(i)); |
| 179 |
1/1✓ Branch 19 → 20 taken 41 times.
|
41 | card->properties.removeAt(i); |
| 180 | 41 | position = i; | |
| 181 |
1/2✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 41 times.
|
41 | if (!group.isEmpty()) |
| 182 | ✗ | card->properties.removeIf([&group](const ICal::Property &p) { | |
| 183 | ✗ | return isCompanion(p) && VCardCodec::groupOf(p) == group; | |
| 184 | }); | ||
| 185 | 41 | } | |
| 186 |
5/6✓ Branch 27 → 28 taken 41 times.
✓ Branch 27 → 30 taken 9 times.
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 41 times.
✓ Branch 32 → 33 taken 9 times.
✓ Branch 32 → 35 taken 41 times.
|
50 | if (position < 0 || position > card->properties.size()) |
| 187 | 9 | position = int(card->properties.size()); | |
| 188 |
2/2✓ Branch 47 → 37 taken 44 times.
✓ Branch 47 → 48 taken 50 times.
|
94 | for (const Unit &unit : replacement) |
| 189 |
2/2✓ Branch 44 → 40 taken 44 times.
✓ Branch 44 → 45 taken 44 times.
|
88 | for (const ICal::Property &line : unit.lines) |
| 190 |
1/1✓ Branch 41 → 42 taken 44 times.
|
44 | card->properties.insert(position++, line); |
| 191 | 50 | } | |
| 192 | |||
| 193 | 8 | void writeCategories(ICal::Component *card, const QStringList &categories) { | |
| 194 | 8 | int position = -1; | |
| 195 |
2/2✓ Branch 13 → 4 taken 63 times.
✓ Branch 13 → 14 taken 8 times.
|
71 | for (int i = card->properties.size() - 1; i >= 0; --i) |
| 196 |
3/3✓ Branch 6 → 7 taken 63 times.
✓ Branch 9 → 10 taken 8 times.
✓ Branch 9 → 12 taken 55 times.
|
63 | if (VCardCodec::baseName(card->properties.at(i)) == QLatin1String("CATEGORIES")) { |
| 197 |
1/1✓ Branch 10 → 11 taken 8 times.
|
8 | card->properties.removeAt(i); |
| 198 | 8 | position = i; | |
| 199 | } | ||
| 200 |
1/2✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 8 times.
|
8 | if (categories.isEmpty()) |
| 201 | ✗ | return; | |
| 202 | 8 | QStringList escaped; | |
| 203 |
2/2✓ Branch 25 → 19 taken 16 times.
✓ Branch 25 → 26 taken 8 times.
|
24 | for (const QString &category : categories) |
| 204 |
2/2✓ Branch 20 → 21 taken 16 times.
✓ Branch 21 → 22 taken 16 times.
|
16 | escaped << ICal::escapeText(category); |
| 205 | 8 | ICal::Property line; | |
| 206 | 8 | line.name = QStringLiteral("CATEGORIES"); | |
| 207 |
1/1✓ Branch 34 → 35 taken 8 times.
|
8 | line.value = escaped.join(QLatin1Char(',')); |
| 208 |
2/3✗ Branch 37 → 38 not taken.
✓ Branch 37 → 40 taken 8 times.
✓ Branch 41 → 42 taken 8 times.
|
8 | card->properties.insert(position < 0 ? int(card->properties.size()) : position, line); |
| 209 | 8 | } | |
| 210 | |||
| 211 | struct Cards { | ||
| 212 | ICal::Component base; | ||
| 213 | ICal::Component local; | ||
| 214 | ICal::Component server; | ||
| 215 | QString error; | ||
| 216 | }; | ||
| 217 | |||
| 218 | 396 | Cards parseCards(const QString &base, const QString &local, const QString &server) { | |
| 219 | 396 | Cards cards; | |
| 220 | 396 | int skippedBase = 0, skippedLocal = 0, skippedServer = 0; | |
| 221 |
1/1✓ Branch 2 → 3 taken 396 times.
|
396 | if (!VCardCodec::parseTree(base, &cards.base, &cards.error, &skippedBase) || |
| 222 |
6/7✓ Branch 3 → 4 taken 396 times.
✗ Branch 3 → 8 not taken.
✓ Branch 4 → 5 taken 396 times.
✓ Branch 5 → 6 taken 394 times.
✓ Branch 5 → 8 taken 2 times.
✓ Branch 10 → 11 taken 5 times.
✓ Branch 10 → 21 taken 391 times.
|
790 | !VCardCodec::parseTree(local, &cards.local, &cards.error, &skippedLocal) || |
| 223 |
3/3✓ Branch 6 → 7 taken 394 times.
✓ Branch 7 → 8 taken 3 times.
✓ Branch 7 → 9 taken 391 times.
|
394 | !VCardCodec::parseTree(server, &cards.server, &cards.error, &skippedServer)) { |
| 224 |
1/2✗ Branch 12 → 13 not taken.
✓ Branch 12 → 20 taken 5 times.
|
5 | if (cards.error.isEmpty()) |
| 225 | ✗ | cards.error = QStringLiteral("Unreadable card"); | |
| 226 | 5 | return cards; | |
| 227 | } | ||
| 228 | // S106-152: a version whose parse dropped lines would be PUT back | ||
| 229 | // without them; it is a conflict the user resolves. | ||
| 230 |
2/2✓ Branch 21 → 22 taken 10 times.
✓ Branch 21 → 29 taken 381 times.
|
391 | if (skippedBase + skippedLocal + skippedServer > 0) |
| 231 | 10 | cards.error = QStringLiteral("A version has lines MailJD cannot read; it is not merged"); | |
| 232 | 391 | return cards; | |
| 233 | ✗ | } | |
| 234 | |||
| 235 | 683 | QString singleId(const QString &name) { return name; } | |
| 236 |
1/1✓ Branch 5 → 6 taken 753 times.
|
1506 | QString setId(const QString &key) { return QStringLiteral("SET:") + key; } |
| 237 |
2/2✓ Branch 2 → 3 taken 160 times.
✓ Branch 6 → 7 taken 160 times.
|
320 | QString categoryId(const QString &value) { return QStringLiteral("CATEGORIES:") + value.toLower(); } |
| 238 | |||
| 239 |
2/2✓ Branch 2 → 3 taken 569 times.
✓ Branch 2 → 4 taken 307 times.
|
876 | Merge::Value unitValue(const Unit *unit) { return unit ? Merge::Value{true, unit->text} : Merge::Value{}; } |
| 240 | |||
| 241 | 234 | QString plainNote(const QList<Unit> &units) { | |
| 242 |
1/1✓ Branch 5 → 6 taken 234 times.
|
234 | const QList<Unit> notes = unitsNamed(units, QStringLiteral("NOTE")); |
| 243 |
3/3✓ Branch 9 → 10 taken 68 times.
✓ Branch 9 → 11 taken 166 times.
✓ Branch 13 → 14 taken 166 times.
|
468 | return notes.isEmpty() ? QString() : ICal::unescapeText(notes.first().lines.first().value); |
| 244 | 234 | } | |
| 245 | |||
| 246 | 15 | Merge::Plan rawPlan(const QString &base, const QString &local, const QString &server, const QString &error) { | |
| 247 | 15 | Merge::Plan plan; | |
| 248 | 15 | plan.domain = QStringLiteral("vcard"); | |
| 249 | 15 | plan.comparable = false; | |
| 250 | 15 | plan.error = error; | |
| 251 | 15 | plan.baseText = base; | |
| 252 | 15 | plan.localText = local; | |
| 253 | 15 | plan.serverText = server; | |
| 254 | 15 | Merge::Item raw; | |
| 255 | 15 | raw.id = kRawItem; | |
| 256 | 15 | raw.name = QStringLiteral("card"); | |
| 257 | 15 | raw.kind = Merge::Kind::Text; | |
| 258 | 15 | raw.base = {true, base}; | |
| 259 | 15 | raw.local = {true, local}; | |
| 260 | 15 | raw.server = {true, server}; | |
| 261 |
2/3✓ Branch 28 → 29 taken 15 times.
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 15 times.
|
15 | raw.state = Merge::stateOf(raw.base, raw.local, raw.server) == Merge::State::Same ? Merge::State::Same |
| 262 | : Merge::State::Conflict; | ||
| 263 | 15 | raw.basePlain = base; | |
| 264 | 15 | raw.localPlain = local; | |
| 265 | 15 | raw.serverPlain = server; | |
| 266 |
1/1✓ Branch 35 → 36 taken 15 times.
|
15 | raw.hunks = TextMerge::diff3(base, local, server); |
| 267 |
1/1✓ Branch 38 → 39 taken 15 times.
|
15 | plan.items.append(raw); |
| 268 |
1/1✓ Branch 39 → 40 taken 15 times.
|
15 | Merge::applyDefaults(&plan); |
| 269 | 15 | return plan; | |
| 270 | 60 | } | |
| 271 | |||
| 272 | // The NOTE unit of the user's text, based on the local (else server) line. | ||
| 273 | 3 | QList<Unit> noteUnits(const QString &text, const QList<Unit> &local, const QList<Unit> &server) { | |
| 274 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 3 times.
|
3 | if (text.isEmpty()) |
| 275 | ✗ | return {}; | |
| 276 |
1/1✓ Branch 8 → 9 taken 3 times.
|
3 | QList<Unit> source = unitsNamed(local, QStringLiteral("NOTE")); |
| 277 |
1/2✗ Branch 12 → 13 not taken.
✓ Branch 12 → 22 taken 3 times.
|
3 | if (source.isEmpty()) |
| 278 | ✗ | source = unitsNamed(server, QStringLiteral("NOTE")); | |
| 279 | 3 | Unit unit; | |
| 280 | 3 | unit.name = QStringLiteral("NOTE"); | |
| 281 | 3 | ICal::Property property; | |
| 282 |
1/2✓ Branch 29 → 30 taken 3 times.
✗ Branch 29 → 34 not taken.
|
3 | if (!source.isEmpty()) { |
| 283 |
1/1✓ Branch 30 → 31 taken 3 times.
|
3 | unit.lines = source.first().lines; |
| 284 |
1/1✓ Branch 32 → 33 taken 3 times.
|
3 | property = unit.lines.first(); |
| 285 | } else { | ||
| 286 | ✗ | property.name = QStringLiteral("NOTE"); | |
| 287 | ✗ | unit.lines.append(property); | |
| 288 | } | ||
| 289 |
2/2✓ Branch 41 → 42 taken 3 times.
✓ Branch 42 → 43 taken 3 times.
|
3 | property.setValue(ICal::escapeText(text)); |
| 290 |
1/1✓ Branch 44 → 45 taken 3 times.
|
3 | unit.lines[0] = property; |
| 291 |
2/4✓ Branch 49 → 50 taken 3 times.
✓ Branch 49 → 51 taken 3 times.
✗ Branch 73 → 74 not taken.
✗ Branch 73 → 75 not taken.
|
6 | return {unit}; |
| 292 | 6 | } | |
| 293 | |||
| 294 | } // namespace | ||
| 295 | |||
| 296 | 250 | Merge::Plan analyze(const QString &baseText, const QString &localText, const QString &serverText) { | |
| 297 |
1/1✓ Branch 2 → 3 taken 250 times.
|
250 | const Cards cards = parseCards(baseText, localText, serverText); |
| 298 |
2/2✓ Branch 4 → 5 taken 15 times.
✓ Branch 4 → 7 taken 235 times.
|
250 | if (!cards.error.isEmpty()) |
| 299 |
1/1✓ Branch 5 → 6 taken 15 times.
|
15 | return rawPlan(baseText, localText, serverText, cards.error); |
| 300 | 235 | Merge::Plan plan; | |
| 301 | 235 | plan.domain = QStringLiteral("vcard"); | |
| 302 | 235 | plan.baseText = baseText; | |
| 303 | 235 | plan.localText = localText; | |
| 304 | 235 | plan.serverText = serverText; | |
| 305 |
1/1✓ Branch 16 → 17 taken 235 times.
|
235 | const QList<Unit> b = unitsOf(cards.base); |
| 306 |
1/1✓ Branch 17 → 18 taken 235 times.
|
235 | const QList<Unit> o = unitsOf(cards.local); |
| 307 |
1/1✓ Branch 18 → 19 taken 235 times.
|
235 | const QList<Unit> t = unitsOf(cards.server); |
| 308 | |||
| 309 | 235 | QStringList singles; | |
| 310 |
2/2✓ Branch 41 → 21 taken 705 times.
✓ Branch 41 → 42 taken 235 times.
|
940 | for (const QList<Unit> *units : {&b, &o, &t}) |
| 311 |
2/2✓ Branch 39 → 23 taken 5075 times.
✓ Branch 39 → 40 taken 705 times.
|
5780 | for (const Unit &unit : *units) |
| 312 |
2/2✓ Branch 27 → 28 taken 1981 times.
✓ Branch 27 → 34 taken 2525 times.
|
9581 | if (!kMultiValued.contains(unit.name) && !kFromTheirs.contains(unit.name) && |
| 313 |
8/8✓ Branch 25 → 26 taken 4506 times.
✓ Branch 25 → 34 taken 569 times.
✓ Branch 30 → 31 taken 1894 times.
✓ Branch 30 → 34 taken 87 times.
✓ Branch 32 → 33 taken 683 times.
✓ Branch 32 → 34 taken 1211 times.
✓ Branch 35 → 36 taken 683 times.
✓ Branch 35 → 37 taken 4392 times.
|
9581 | unit.name != QLatin1String("CATEGORIES") && !singles.contains(unit.name)) |
| 314 |
1/1✓ Branch 36 → 37 taken 683 times.
|
683 | singles << unit.name; |
| 315 |
4/4✓ Branch 42 → 43 taken 235 times.
✓ Branch 43 → 44 taken 235 times.
✓ Branch 146 → 45 taken 683 times.
✓ Branch 146 → 147 taken 235 times.
|
918 | for (const QString &name : singles) { |
| 316 |
1/1✓ Branch 46 → 47 taken 683 times.
|
683 | const QString baseValue = singleText(b, name); |
| 317 |
1/1✓ Branch 47 → 48 taken 683 times.
|
683 | const QString localValue = singleText(o, name); |
| 318 |
1/1✓ Branch 48 → 49 taken 683 times.
|
683 | const QString serverValue = singleText(t, name); |
| 319 | 683 | Merge::Item item; | |
| 320 | 683 | item.id = singleId(name); | |
| 321 | 683 | item.name = name; | |
| 322 |
6/6✓ Branch 55 → 56 taken 448 times.
✓ Branch 55 → 59 taken 235 times.
✓ Branch 58 → 59 taken 235 times.
✓ Branch 58 → 60 taken 213 times.
✓ Branch 61 → 62 taken 470 times.
✓ Branch 61 → 69 taken 213 times.
|
683 | if (name == QLatin1String("N") || name == QLatin1String("FN")) |
| 323 | 470 | item.group = QStringLiteral("name"); | |
| 324 |
1/1✓ Branch 69 → 70 taken 683 times.
|
1366 | item.base = {!unitsNamed(b, name).isEmpty(), baseValue}; |
| 325 |
1/1✓ Branch 75 → 76 taken 683 times.
|
1366 | item.local = {!unitsNamed(o, name).isEmpty(), localValue}; |
| 326 |
1/1✓ Branch 81 → 82 taken 683 times.
|
1366 | item.server = {!unitsNamed(t, name).isEmpty(), serverValue}; |
| 327 | // VCardMerge: local == base or local == server → the server holds the | ||
| 328 | // result; server == base → local; otherwise a conflict. | ||
| 329 |
6/6✓ Branch 88 → 89 taken 486 times.
✓ Branch 88 → 92 taken 197 times.
✓ Branch 90 → 91 taken 418 times.
✓ Branch 90 → 92 taken 68 times.
✓ Branch 93 → 94 taken 418 times.
✓ Branch 93 → 95 taken 265 times.
|
683 | if (localValue == baseValue && serverValue == baseValue) |
| 330 | 418 | item.state = Merge::State::Same; | |
| 331 |
2/2✓ Branch 96 → 97 taken 26 times.
✓ Branch 96 → 98 taken 239 times.
|
265 | else if (localValue == serverValue) |
| 332 | 26 | item.state = Merge::State::BothSame; | |
| 333 |
2/2✓ Branch 99 → 100 taken 68 times.
✓ Branch 99 → 101 taken 171 times.
|
239 | else if (localValue == baseValue) |
| 334 | 68 | item.state = Merge::State::ServerOnly; | |
| 335 |
2/2✓ Branch 102 → 103 taken 49 times.
✓ Branch 102 → 104 taken 122 times.
|
171 | else if (serverValue == baseValue) |
| 336 | 49 | item.state = Merge::State::LocalOnly; | |
| 337 | else | ||
| 338 | 122 | item.state = Merge::State::Conflict; | |
| 339 |
11/18✓ Branch 107 → 108 taken 78 times.
✓ Branch 107 → 118 taken 605 times.
✓ Branch 108 → 109 taken 78 times.
✓ Branch 110 → 111 taken 78 times.
✗ Branch 110 → 118 not taken.
✓ Branch 111 → 112 taken 78 times.
✓ Branch 113 → 114 taken 78 times.
✗ Branch 113 → 118 not taken.
✓ Branch 116 → 117 taken 78 times.
✗ Branch 116 → 118 not taken.
✓ Branch 123 → 124 taken 78 times.
✓ Branch 123 → 125 taken 605 times.
✓ Branch 125 → 126 taken 78 times.
✓ Branch 125 → 139 taken 605 times.
✗ Branch 295 → 296 not taken.
✗ Branch 295 → 297 not taken.
✗ Branch 299 → 300 not taken.
✗ Branch 299 → 301 not taken.
|
1444 | if (name == QLatin1String("NOTE") && unitsNamed(b, name).size() <= 1 && unitsNamed(o, name).size() <= 1 && |
| 340 |
5/7✓ Branch 114 → 115 taken 78 times.
✓ Branch 119 → 120 taken 78 times.
✓ Branch 119 → 121 taken 605 times.
✓ Branch 121 → 122 taken 78 times.
✓ Branch 121 → 123 taken 605 times.
✗ Branch 291 → 292 not taken.
✗ Branch 291 → 293 not taken.
|
761 | unitsNamed(t, name).size() <= 1) { |
| 341 | 78 | item.kind = Merge::Kind::Text; | |
| 342 |
1/1✓ Branch 126 → 127 taken 78 times.
|
78 | item.basePlain = plainNote(b); |
| 343 |
1/1✓ Branch 129 → 130 taken 78 times.
|
78 | item.localPlain = plainNote(o); |
| 344 |
1/1✓ Branch 132 → 133 taken 78 times.
|
78 | item.serverPlain = plainNote(t); |
| 345 |
1/1✓ Branch 135 → 136 taken 78 times.
|
78 | item.hunks = TextMerge::diff3(item.basePlain, item.localPlain, item.serverPlain); |
| 346 | } | ||
| 347 |
1/1✓ Branch 139 → 140 taken 683 times.
|
683 | plan.items.append(item); |
| 348 | 683 | } | |
| 349 | |||
| 350 | 235 | QStringList keys; | |
| 351 |
2/2✓ Branch 164 → 149 taken 705 times.
✓ Branch 164 → 165 taken 235 times.
|
940 | for (const QList<Unit> *units : {&t, &o, &b}) |
| 352 |
2/2✓ Branch 162 → 151 taken 5075 times.
✓ Branch 162 → 163 taken 705 times.
|
5780 | for (const Unit &unit : *units) |
| 353 |
6/6✓ Branch 153 → 154 taken 569 times.
✓ Branch 153 → 157 taken 4506 times.
✓ Branch 155 → 156 taken 292 times.
✓ Branch 155 → 157 taken 277 times.
✓ Branch 158 → 159 taken 292 times.
✓ Branch 158 → 160 taken 4783 times.
|
5075 | if (kMultiValued.contains(unit.name) && !keys.contains(unit.key)) |
| 354 |
1/1✓ Branch 159 → 160 taken 292 times.
|
292 | keys << unit.key; |
| 355 |
4/4✓ Branch 165 → 166 taken 235 times.
✓ Branch 166 → 167 taken 235 times.
✓ Branch 226 → 168 taken 292 times.
✓ Branch 226 → 227 taken 235 times.
|
527 | for (const QString &key : keys) { |
| 356 |
1/1✓ Branch 169 → 170 taken 292 times.
|
292 | const Unit *baseUnit = lastWithKey(b, key); |
| 357 |
1/1✓ Branch 170 → 171 taken 292 times.
|
292 | const Unit *localUnit = lastWithKey(o, key); |
| 358 |
1/1✓ Branch 171 → 172 taken 292 times.
|
292 | const Unit *serverUnit = lastWithKey(t, key); |
| 359 | 292 | Merge::Item item; | |
| 360 |
1/1✓ Branch 172 → 173 taken 292 times.
|
292 | item.id = setId(key); |
| 361 |
4/4✓ Branch 175 → 176 taken 92 times.
✓ Branch 175 → 180 taken 200 times.
✓ Branch 176 → 177 taken 67 times.
✓ Branch 176 → 178 taken 25 times.
|
292 | item.name = (localUnit ? localUnit : serverUnit ? serverUnit : baseUnit)->name; |
| 362 | 292 | item.kind = Merge::Kind::SetValue; | |
| 363 | 292 | item.base = unitValue(baseUnit); | |
| 364 | 292 | item.local = unitValue(localUnit); | |
| 365 | 292 | item.server = unitValue(serverUnit); | |
| 366 | // What VCardMerge does with this key: remove it (removed locally), add | ||
| 367 | // or replace the local version, or keep the server's card as it is. | ||
| 368 |
6/6✓ Branch 191 → 192 taken 174 times.
✓ Branch 191 → 195 taken 118 times.
✓ Branch 192 → 193 taken 137 times.
✓ Branch 192 → 195 taken 37 times.
✓ Branch 193 → 194 taken 9 times.
✓ Branch 193 → 195 taken 128 times.
|
292 | const bool removedLocally = baseUnit && serverUnit && !localUnit; |
| 369 |
5/6✓ Branch 196 → 197 taken 200 times.
✓ Branch 196 → 200 taken 92 times.
✓ Branch 197 → 198 taken 60 times.
✓ Branch 197 → 200 taken 140 times.
✓ Branch 198 → 199 taken 60 times.
✗ Branch 198 → 200 not taken.
|
292 | const bool addedLocally = localUnit && !baseUnit && !serverUnit; |
| 370 |
9/10✓ Branch 201 → 202 taken 200 times.
✓ Branch 201 → 209 taken 92 times.
✓ Branch 202 → 203 taken 140 times.
✓ Branch 202 → 209 taken 60 times.
✓ Branch 203 → 204 taken 128 times.
✓ Branch 203 → 209 taken 12 times.
✓ Branch 205 → 206 taken 9 times.
✓ Branch 205 → 209 taken 119 times.
✓ Branch 207 → 208 taken 9 times.
✗ Branch 207 → 209 not taken.
|
301 | const bool changedLocally = localUnit && baseUnit && serverUnit && localUnit->text != baseUnit->text && |
| 371 | 9 | serverUnit->text == baseUnit->text; | |
| 372 | 358 | const auto sameValue = [](const Merge::Value &x, const Merge::Value &y) { | |
| 373 |
5/6✓ Branch 2 → 3 taken 263 times.
✓ Branch 2 → 7 taken 95 times.
✓ Branch 3 → 4 taken 238 times.
✓ Branch 3 → 6 taken 25 times.
✓ Branch 5 → 6 taken 238 times.
✗ Branch 5 → 7 not taken.
|
358 | return x.present == y.present && (!x.present || x.text == y.text); |
| 374 | }; | ||
| 375 |
6/6✓ Branch 210 → 211 taken 283 times.
✓ Branch 210 → 213 taken 9 times.
✓ Branch 211 → 212 taken 223 times.
✓ Branch 211 → 213 taken 60 times.
✓ Branch 212 → 213 taken 9 times.
✓ Branch 212 → 214 taken 214 times.
|
292 | if (removedLocally || addedLocally || changedLocally) |
| 376 | 78 | item.state = Merge::State::LocalOnly; | |
| 377 |
2/2✓ Branch 215 → 216 taken 144 times.
✓ Branch 215 → 221 taken 70 times.
|
214 | else if (sameValue(item.local, item.server)) |
| 378 |
2/2✓ Branch 217 → 218 taken 119 times.
✓ Branch 217 → 219 taken 25 times.
|
144 | item.state = sameValue(item.local, item.base) ? Merge::State::Same : Merge::State::BothSame; |
| 379 | else | ||
| 380 | 70 | item.state = Merge::State::ServerOnly; | |
| 381 |
1/1✓ Branch 222 → 223 taken 292 times.
|
292 | plan.items.append(item); |
| 382 | 292 | } | |
| 383 | |||
| 384 |
1/1✓ Branch 227 → 228 taken 235 times.
|
235 | const QStringList baseCategories = categoriesOf(cards.base); |
| 385 |
1/1✓ Branch 228 → 229 taken 235 times.
|
235 | const QStringList localCategories = categoriesOf(cards.local); |
| 386 |
1/1✓ Branch 229 → 230 taken 235 times.
|
235 | const QStringList serverCategories = categoriesOf(cards.server); |
| 387 | 235 | QStringList seen; | |
| 388 |
2/2✓ Branch 267 → 232 taken 705 times.
✓ Branch 267 → 268 taken 235 times.
|
940 | for (const QStringList *list : {&serverCategories, &localCategories, &baseCategories}) |
| 389 |
2/2✓ Branch 265 → 234 taken 123 times.
✓ Branch 265 → 266 taken 705 times.
|
828 | for (const QString &value : *list) { |
| 390 |
2/2✓ Branch 236 → 237 taken 64 times.
✓ Branch 236 → 238 taken 59 times.
|
123 | if (seen.contains(value, Qt::CaseInsensitive)) |
| 391 | 64 | continue; | |
| 392 |
1/1✓ Branch 238 → 239 taken 59 times.
|
59 | seen << value; |
| 393 | 177 | const auto valueIn = [&value](const QStringList &values) { | |
| 394 | 177 | return Merge::Value{values.contains(value, Qt::CaseInsensitive), value}; | |
| 395 | 59 | }; | |
| 396 | 59 | Merge::Item item; | |
| 397 |
1/1✓ Branch 239 → 240 taken 59 times.
|
59 | item.id = categoryId(value); |
| 398 | 59 | item.name = QStringLiteral("CATEGORIES"); | |
| 399 | 59 | item.kind = Merge::Kind::SetValue; | |
| 400 | 59 | item.base = valueIn(baseCategories); | |
| 401 | 59 | item.local = valueIn(localCategories); | |
| 402 | 59 | item.server = valueIn(serverCategories); | |
| 403 |
1/1✓ Branch 257 → 258 taken 59 times.
|
59 | item.state = Merge::stateOf(item.base, item.local, item.server); |
| 404 |
1/2✗ Branch 258 → 259 not taken.
✓ Branch 258 → 260 taken 59 times.
|
59 | if (item.state == Merge::State::Conflict) // presence only: cannot differ on both |
| 405 | ✗ | item.state = Merge::State::LocalOnly; | |
| 406 |
1/1✓ Branch 260 → 261 taken 59 times.
|
59 | plan.items.append(item); |
| 407 | 59 | } | |
| 408 |
1/1✓ Branch 268 → 269 taken 235 times.
|
235 | Merge::applyDefaults(&plan); |
| 409 | 235 | return plan; | |
| 410 | 2299 | } | |
| 411 | |||
| 412 | 156 | Applied apply(const Merge::Plan &plan, const QString &now) { | |
| 413 | 156 | Applied applied; | |
| 414 |
2/2✓ Branch 16 → 4 taken 685 times.
✓ Branch 16 → 17 taken 150 times.
|
835 | for (const Merge::Item &item : plan.items) |
| 415 |
3/3✓ Branch 5 → 6 taken 685 times.
✓ Branch 6 → 7 taken 6 times.
✓ Branch 6 → 14 taken 679 times.
|
685 | if (Merge::itemOpen(plan, item)) { |
| 416 | 6 | applied.error = QStringLiteral("Open conflicts remain"); | |
| 417 | 6 | return applied; | |
| 418 | } | ||
| 419 |
2/2✓ Branch 17 → 18 taken 4 times.
✓ Branch 17 → 26 taken 146 times.
|
150 | if (!plan.comparable) { |
| 420 |
1/1✓ Branch 18 → 19 taken 4 times.
|
4 | const Merge::Item *raw = plan.item(kRawItem); |
| 421 |
1/2✗ Branch 19 → 20 not taken.
✓ Branch 19 → 22 taken 4 times.
|
4 | if (!raw) { |
| 422 | ✗ | applied.error = plan.error; | |
| 423 | ✗ | return applied; | |
| 424 | } | ||
| 425 | 4 | applied.ok = true; | |
| 426 |
1/1✓ Branch 22 → 23 taken 4 times.
|
4 | applied.text = Merge::resultText(*raw); |
| 427 | 4 | return applied; | |
| 428 | } | ||
| 429 |
1/1✓ Branch 26 → 27 taken 146 times.
|
146 | const Cards cards = parseCards(plan.baseText, plan.localText, plan.serverText); |
| 430 |
1/2✗ Branch 28 → 29 not taken.
✓ Branch 28 → 31 taken 146 times.
|
146 | if (!cards.error.isEmpty()) { |
| 431 | ✗ | applied.error = cards.error; | |
| 432 | ✗ | return applied; | |
| 433 | } | ||
| 434 |
1/1✓ Branch 31 → 32 taken 146 times.
|
146 | const QList<Unit> b = unitsOf(cards.base); |
| 435 |
1/1✓ Branch 32 → 33 taken 146 times.
|
146 | const QList<Unit> o = unitsOf(cards.local); |
| 436 |
1/1✓ Branch 33 → 34 taken 146 times.
|
146 | const QList<Unit> t = unitsOf(cards.server); |
| 437 | 146 | ICal::Component merged = cards.server; | |
| 438 | |||
| 439 |
2/2✓ Branch 64 → 37 taken 669 times.
✓ Branch 64 → 65 taken 146 times.
|
815 | for (const Merge::Item &item : plan.items) { |
| 440 |
2/2✓ Branch 38 → 39 taken 254 times.
✓ Branch 38 → 40 taken 415 times.
|
669 | if (item.kind == Merge::Kind::SetValue) |
| 441 | 254 | continue; | |
| 442 |
3/4✓ Branch 40 → 41 taken 255 times.
✗ Branch 40 → 48 not taken.
✓ Branch 40 → 55 taken 3 times.
✓ Branch 40 → 61 taken 157 times.
|
415 | switch (item.choice) { |
| 443 | 255 | case Merge::Choice::Local: | |
| 444 |
2/2✓ Branch 42 → 43 taken 47 times.
✓ Branch 42 → 47 taken 208 times.
|
255 | if (item.local.text != item.server.text) |
| 445 |
2/2✓ Branch 43 → 44 taken 47 times.
✓ Branch 44 → 45 taken 47 times.
|
47 | replaceSingle(&merged, item.name, unitsNamed(o, item.name)); |
| 446 | 255 | break; | |
| 447 | ✗ | case Merge::Choice::Base: | |
| 448 | ✗ | if (item.base.text != item.server.text) | |
| 449 | ✗ | replaceSingle(&merged, item.name, unitsNamed(b, item.name)); | |
| 450 | ✗ | break; | |
| 451 | 3 | case Merge::Choice::Edited: | |
| 452 |
3/3✓ Branch 55 → 56 taken 3 times.
✓ Branch 56 → 57 taken 3 times.
✓ Branch 57 → 58 taken 3 times.
|
3 | replaceSingle(&merged, item.name, noteUnits(Merge::resultText(item), o, t)); |
| 453 | 3 | break; | |
| 454 | 157 | default: | |
| 455 | 157 | break; | |
| 456 | } | ||
| 457 | } | ||
| 458 | |||
| 459 | // The chosen presence of a set key: the side's value or none. | ||
| 460 |
2/2✓ Branch 2 → 3 taken 461 times.
✓ Branch 3 → 4 taken 461 times.
|
461 | const auto chosen = [&plan](const QString &key) -> const Merge::Item * { return plan.item(setId(key)); }; |
| 461 | 145 | const auto wantsAbsent = [](const Merge::Item *item) { | |
| 462 |
4/4✓ Branch 2 → 3 taken 73 times.
✓ Branch 2 → 4 taken 72 times.
✓ Branch 3 → 4 taken 64 times.
✓ Branch 3 → 6 taken 9 times.
|
281 | return (item->choice == Merge::Choice::Local && !item->local.present) || |
| 463 |
1/4✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 136 times.
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 7 not taken.
|
281 | (item->choice == Merge::Choice::Base && !item->base.present); |
| 464 | }; | ||
| 465 |
2/2✓ Branch 80 → 67 taken 1124 times.
✓ Branch 80 → 81 taken 146 times.
|
1270 | for (const Unit &unit : t) |
| 466 |
2/2✓ Branch 69 → 70 taken 145 times.
✓ Branch 69 → 78 taken 979 times.
|
1124 | if (kMultiValued.contains(unit.name)) |
| 467 |
6/7✓ Branch 70 → 71 taken 145 times.
✓ Branch 71 → 72 taken 145 times.
✗ Branch 71 → 75 not taken.
✓ Branch 73 → 74 taken 9 times.
✓ Branch 73 → 75 taken 136 times.
✓ Branch 76 → 77 taken 9 times.
✓ Branch 76 → 78 taken 136 times.
|
145 | if (const Merge::Item *item = chosen(unit.key); item && wantsAbsent(item)) |
| 468 |
1/1✓ Branch 77 → 78 taken 9 times.
|
9 | removeUnit(&merged, unit); |
| 469 | 292 | QSet<QString> serverKeys; | |
| 470 |
2/2✓ Branch 88 → 84 taken 1124 times.
✓ Branch 88 → 89 taken 146 times.
|
1270 | for (const Unit &unit : t) |
| 471 |
1/1✓ Branch 85 → 86 taken 1124 times.
|
1124 | serverKeys.insert(unit.key); |
| 472 |
2/2✓ Branch 105 → 91 taken 1104 times.
✓ Branch 105 → 106 taken 146 times.
|
1250 | for (const Unit &unit : o) |
| 473 |
6/6✓ Branch 93 → 94 taken 146 times.
✓ Branch 93 → 97 taken 958 times.
✓ Branch 95 → 96 taken 53 times.
✓ Branch 95 → 97 taken 93 times.
✓ Branch 98 → 99 taken 53 times.
✓ Branch 98 → 103 taken 1051 times.
|
1104 | if (kMultiValued.contains(unit.name) && !serverKeys.contains(unit.key)) |
| 474 |
4/5✓ Branch 99 → 100 taken 53 times.
✓ Branch 100 → 101 taken 53 times.
✗ Branch 100 → 103 not taken.
✓ Branch 101 → 102 taken 25 times.
✓ Branch 101 → 103 taken 28 times.
|
53 | if (const Merge::Item *item = chosen(unit.key); item && item->choice == Merge::Choice::Local) |
| 475 |
1/1✓ Branch 102 → 103 taken 25 times.
|
25 | appendUnit(&merged, unit); |
| 476 | // A local value whose server version differs replaces it (Sprint 103 | ||
| 477 | // S103.1.7: a pinned ADR;GEO=, a changed relation label). | ||
| 478 |
2/2✓ Branch 128 → 108 taken 1104 times.
✓ Branch 128 → 129 taken 146 times.
|
1250 | for (const Unit &unit : o) { |
| 479 |
3/3✓ Branch 110 → 111 taken 146 times.
✓ Branch 110 → 113 taken 958 times.
✓ Branch 111 → 112 taken 146 times.
|
1104 | const Merge::Item *item = kMultiValued.contains(unit.name) ? chosen(unit.key) : nullptr; |
| 480 |
4/4✓ Branch 114 → 115 taken 146 times.
✓ Branch 114 → 116 taken 958 times.
✓ Branch 115 → 116 taken 57 times.
✓ Branch 115 → 117 taken 89 times.
|
1104 | if (!item || item->choice != Merge::Choice::Local) |
| 481 | 1015 | continue; | |
| 482 |
1/1✓ Branch 117 → 118 taken 89 times.
|
89 | const Unit *serverUnit = lastWithKey(t, unit.key); |
| 483 |
6/6✓ Branch 118 → 119 taken 64 times.
✓ Branch 118 → 122 taken 25 times.
✓ Branch 120 → 121 taken 4 times.
✓ Branch 120 → 122 taken 60 times.
✓ Branch 123 → 124 taken 4 times.
✓ Branch 123 → 126 taken 85 times.
|
89 | if (serverUnit && serverUnit->text != unit.text) { |
| 484 |
1/1✓ Branch 124 → 125 taken 4 times.
|
4 | removeUnit(&merged, *serverUnit); |
| 485 |
1/1✓ Branch 125 → 126 taken 4 times.
|
4 | appendUnit(&merged, unit); |
| 486 | } | ||
| 487 | } | ||
| 488 |
2/2✓ Branch 158 → 131 taken 1053 times.
✓ Branch 158 → 159 taken 146 times.
|
1199 | for (const Unit &unit : b) { |
| 489 |
3/3✓ Branch 133 → 134 taken 117 times.
✓ Branch 133 → 136 taken 936 times.
✓ Branch 134 → 135 taken 117 times.
|
1053 | const Merge::Item *item = kMultiValued.contains(unit.name) ? chosen(unit.key) : nullptr; |
| 490 |
4/9✓ Branch 137 → 138 taken 117 times.
✓ Branch 137 → 141 taken 936 times.
✗ Branch 138 → 139 not taken.
✓ Branch 138 → 141 taken 117 times.
✗ Branch 139 → 140 not taken.
✗ Branch 140 → 141 not taken.
✗ Branch 140 → 142 not taken.
✓ Branch 143 → 144 taken 1053 times.
✗ Branch 143 → 145 not taken.
|
1053 | if (!item || item->choice != Merge::Choice::Base || lastWithKey(b, unit.key) != &unit) |
| 491 | 1053 | continue; | |
| 492 | ✗ | const Unit *serverUnit = lastWithKey(t, unit.key); | |
| 493 | ✗ | if (serverUnit && serverUnit->text == unit.text) | |
| 494 | ✗ | continue; | |
| 495 | ✗ | if (serverUnit) | |
| 496 | ✗ | removeUnit(&merged, *serverUnit); | |
| 497 | ✗ | appendUnit(&merged, unit); | |
| 498 | } | ||
| 499 | |||
| 500 |
1/1✓ Branch 159 → 160 taken 146 times.
|
292 | const QStringList baseCategories = categoriesOf(cards.base); |
| 501 |
1/1✓ Branch 160 → 161 taken 146 times.
|
292 | const QStringList localCategories = categoriesOf(cards.local); |
| 502 |
1/1✓ Branch 161 → 162 taken 146 times.
|
292 | const QStringList serverCategories = categoriesOf(cards.server); |
| 503 | 101 | const auto included = [&](const QString &value) { | |
| 504 |
2/2✓ Branch 2 → 3 taken 101 times.
✓ Branch 3 → 4 taken 101 times.
|
101 | const Merge::Item *item = plan.item(categoryId(value)); |
| 505 |
1/2✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 101 times.
|
101 | if (!item) |
| 506 | ✗ | return false; | |
| 507 |
2/2✓ Branch 7 → 8 taken 56 times.
✓ Branch 7 → 12 taken 45 times.
|
157 | const QStringList &side = item->choice == Merge::Choice::Server ? serverCategories |
| 508 |
1/2✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 56 times.
|
56 | : item->choice == Merge::Choice::Base ? baseCategories |
| 509 | : localCategories; | ||
| 510 | 101 | return side.contains(value, Qt::CaseInsensitive); | |
| 511 | 146 | }; | |
| 512 | ✗ | QStringList categories; | |
| 513 |
2/2✓ Branch 179 → 164 taken 438 times.
✓ Branch 179 → 180 taken 146 times.
|
584 | for (const QStringList *list : {&serverCategories, &localCategories, &baseCategories}) |
| 514 |
2/2✓ Branch 177 → 166 taken 101 times.
✓ Branch 177 → 178 taken 438 times.
|
539 | for (const QString &value : *list) |
| 515 |
7/7✓ Branch 167 → 168 taken 101 times.
✓ Branch 168 → 169 taken 89 times.
✓ Branch 168 → 172 taken 12 times.
✓ Branch 170 → 171 taken 39 times.
✓ Branch 170 → 172 taken 50 times.
✓ Branch 173 → 174 taken 39 times.
✓ Branch 173 → 175 taken 62 times.
|
101 | if (included(value) && !categories.contains(value, Qt::CaseInsensitive)) |
| 516 |
1/1✓ Branch 174 → 175 taken 39 times.
|
39 | categories << value; |
| 517 |
3/3✓ Branch 180 → 181 taken 146 times.
✓ Branch 181 → 182 taken 8 times.
✓ Branch 181 → 183 taken 138 times.
|
146 | if (categories != serverCategories) |
| 518 |
1/1✓ Branch 182 → 183 taken 8 times.
|
8 | writeCategories(&merged, categories); |
| 519 | |||
| 520 |
4/4✓ Branch 183 → 184 taken 146 times.
✓ Branch 184 → 185 taken 146 times.
✓ Branch 188 → 189 taken 89 times.
✓ Branch 188 → 211 taken 57 times.
|
146 | if (ICal::serialize(merged) != ICal::serialize(cards.server)) |
| 521 |
1/1✓ Branch 200 → 201 taken 89 times.
|
89 | merged.setPropertyValue(QStringLiteral("REV"), |
| 522 |
10/16✓ Branch 190 → 191 taken 81 times.
✓ Branch 190 → 196 taken 8 times.
✓ Branch 191 → 192 taken 81 times.
✓ Branch 195 → 197 taken 81 times.
✓ Branch 204 → 205 taken 81 times.
✓ Branch 204 → 206 taken 8 times.
✓ Branch 206 → 207 taken 81 times.
✓ Branch 206 → 208 taken 8 times.
✓ Branch 208 → 209 taken 81 times.
✓ Branch 208 → 210 taken 8 times.
✗ Branch 261 → 262 not taken.
✗ Branch 261 → 263 not taken.
✗ Branch 265 → 266 not taken.
✗ Branch 265 → 267 not taken.
✗ Branch 269 → 270 not taken.
✗ Branch 269 → 271 not taken.
|
259 | now.isEmpty() ? QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyyMMdd'T'HHmmss'Z'")) |
| 523 | : now); | ||
| 524 | 146 | applied.ok = true; | |
| 525 |
1/1✓ Branch 211 → 212 taken 146 times.
|
146 | applied.text = ICal::serialize(merged); |
| 526 | 146 | return applied; | |
| 527 | 146 | } | |
| 528 | |||
| 529 | } // namespace VCardMergePlan | ||
| 530 |