• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

xlnt-community / xlnt / 796b5d63-faf9-48e9-946d-5f649cf3d172

05 Mar 2025 05:11AM UTC coverage: 82.27% (+0.4%) from 81.87%
796b5d63-faf9-48e9-946d-5f649cf3d172

push

circleci

web-flow
Fix workbook comparisons, cleanup of included headers (#59)

This PR does the following:
1. Fixes issue https://github.com/xlnt-community/xlnt/issues/58. Please
read the issue for detailed infos.
2. Changes / removes a few definitions of `XLNT_API(_INTERNAL)` which
were defined in the wrong places (e.g. `.cpp` file, or in the header
although the function was implemented in the header too).
3. Cleans up many unnecessary or missing headers which I found along the
way.
4. Added `operator!=` to many classes to ease such comparisons and
improve programming experience.

103 of 143 new or added lines in 20 files covered. (72.03%)

3 existing lines in 2 files now uncovered.

11554 of 14044 relevant lines covered (82.27%)

1176465.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.97
./source/packaging/manifest.cpp
1
// Copyright (c) 2014-2022 Thomas Fussell
2
// Copyright (c) 2010-2015 openpyxl
3
// Copyright (c) 2024-2025 xlnt-community
4
//
5
// Permission is hereby granted, free of charge, to any person obtaining a copy
6
// of this software and associated documentation files (the "Software"), to deal
7
// in the Software without restriction, including without limitation the rights
8
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
// copies of the Software, and to permit persons to whom the Software is
10
// furnished to do so, subject to the following conditions:
11
//
12
// The above copyright notice and this permission notice shall be included in
13
// all copies or substantial portions of the Software.
14
//
15
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
// THE SOFTWARE
22
//
23
// @license: http://www.opensource.org/licenses/mit-license.php
24
// @author: see AUTHORS file
25

26
#include <algorithm>
27
#include <unordered_set>
28

29
#include <xlnt/packaging/manifest.hpp>
30
#include <xlnt/utils/exceptions.hpp>
31
#include <detail/serialization/parsers.hpp>
32

33
namespace xlnt {
34

35
void manifest::clear()
×
36
{
37
    default_content_types_.clear();
×
38
    override_content_types_.clear();
×
39
    relationships_.clear();
×
40
}
×
41

42
path manifest::canonicalize(const std::vector<xlnt::relationship> &rels) const
43,038✔
43
{
44
    xlnt::path relative;
43,038✔
45

46
    for (auto component : rels)
86,802✔
47
    {
48
        if (component == rels.back())
43,764✔
49
        {
50
            relative = relative.append(component.target().path());
43,038✔
51
        }
52
        else
53
        {
54
            relative = relative.append(component.target().path().parent());
726✔
55
        }
56
    }
43,764✔
57

58
    std::vector<std::string> absolute_parts;
43,038✔
59

60
    for (const auto &component : relative.split())
129,632✔
61
    {
62
        if (component == ".") continue;
86,594✔
63

64
        if (component == "..")
86,594✔
65
        {
66
            absolute_parts.pop_back();
42✔
67
            continue;
42✔
68
        }
69

70
        absolute_parts.push_back(component);
86,552✔
71
    }
43,038✔
72

73
    xlnt::path result;
43,038✔
74

75
    for (const auto &component : absolute_parts)
129,548✔
76
    {
77
        result = result.append(component);
86,510✔
78
    }
79

80
    return result;
43,038✔
81
}
43,038✔
82

83
bool manifest::has_relationship(const path &path, relationship_type type) const
50,174✔
84
{
85
    auto rels = relationships_.find(path);
50,174✔
86
    if (rels == relationships_.end())
50,174✔
87
    {
88
        return false;
907✔
89
    }
90
    return rels->second.end() != std::find_if(rels->second.begin(), rels->second.end(), [type](const std::pair<std::string, xlnt::relationship> &rel) { return rel.second.type() == type; });
114,513✔
91
}
92

93
bool manifest::has_relationship(const path &path, const std::string &rel_id) const
×
94
{
95
    auto rels = relationships_.find(path);
×
96
    if (rels == relationships_.end())
×
97
    {
98
        return false;
×
99
    }
100
    return rels->second.find(rel_id) != rels->second.end();
×
101
}
102

103
relationship manifest::relationship(const path &part, relationship_type type) const
43,751✔
104
{
105
    if (relationships_.find(part) == relationships_.end()) throw key_not_found();
43,751✔
106

107
    for (const auto &rel : relationships_.at(part))
172,153✔
108
    {
109
        if (rel.second.type() == type) return rel.second;
172,153✔
110
    }
111

112
    throw key_not_found();
×
113
}
114

115
std::vector<xlnt::relationship> manifest::relationships(const path &part, relationship_type type) const
318✔
116
{
117
    std::vector<xlnt::relationship> matches;
318✔
118

119
    if (has_relationship(part, type))
318✔
120
    {
121
        for (const auto &rel : relationships_.at(part))
872✔
122
        {
123
            if (rel.second.type() == type)
697✔
124
            {
125
                matches.push_back(rel.second);
275✔
126
            }
127
        }
128
    }
129

130
    return matches;
318✔
131
}
×
132

133
std::string manifest::content_type(const path &part) const
353✔
134
{
135
    auto absolute = part.resolve(path("/"));
353✔
136

137
    if (has_override_type(absolute))
353✔
138
    {
139
        return override_type(absolute);
273✔
140
    }
141

142
    if (has_default_type(part.extension()))
80✔
143
    {
144
        return default_type(part.extension());
80✔
145
    }
146

147
    throw key_not_found();
×
148
}
353✔
149

150
void manifest::register_override_type(const path &part, const std::string &content_type)
2,634✔
151
{
152
    override_content_types_[part] = content_type;
2,634✔
153
}
2,634✔
154

155
void manifest::unregister_override_type(const path &part)
10✔
156
{
157
    override_content_types_.erase(part);
10✔
158
}
10✔
159

160
std::vector<path> manifest::parts_with_overriden_types() const
88✔
161
{
162
    std::vector<path> overriden;
88✔
163

164
    for (const auto &part : override_content_types_)
721✔
165
    {
166
        overriden.push_back(part.first);
633✔
167
    }
168

169
    return overriden;
88✔
170
}
×
171

172
std::vector<relationship> manifest::relationships(const path &part) const
1,120✔
173
{
174
    if (relationships_.find(part) == relationships_.end())
1,120✔
175
    {
176
        return {};
642✔
177
    }
178

179
    std::vector<xlnt::relationship> relationships;
478✔
180

181
    for (const auto &rel : relationships_.at(part))
2,213✔
182
    {
183
        relationships.push_back(rel.second);
1,735✔
184
    }
185

186
    return relationships;
478✔
187
}
478✔
188

189
relationship manifest::relationship(const path &part, const std::string &rel_id) const
386✔
190
{
191
    if (relationships_.find(part) == relationships_.end())
386✔
192
    {
193
        throw key_not_found();
×
194
    }
195

196
    for (const auto &rel : relationships_.at(part))
1,435✔
197
    {
198
        if (rel.second.id() == rel_id)
1,435✔
199
        {
200
            return rel.second;
386✔
201
        }
202
    }
203

204
    throw key_not_found();
×
205
}
206

207
std::vector<path> manifest::parts() const
22✔
208
{
209
    std::unordered_set<path> parts;
22✔
210

211
    for (const auto &part_rels : relationships_)
74✔
212
    {
213
        parts.insert(part_rels.first);
52✔
214

215
        for (const auto &rel : part_rels.second)
254✔
216
        {
217
            if (rel.second.target_mode() == target_mode::internal)
202✔
218
            {
219
                parts.insert(rel.second.target().path());
186✔
220
            }
221
        }
222
    }
223

224
    return std::vector<path>(parts.begin(), parts.end());
44✔
225
}
22✔
226

227
std::string manifest::register_relationship(const uri &source,
2,059✔
228
    relationship_type type, const uri &target, target_mode mode)
229
{
230
    xlnt::relationship rel(next_relationship_id(source.path()), type, source, target, mode);
2,059✔
231
    return register_relationship(rel);
4,118✔
232
}
2,059✔
233

234
std::string manifest::register_relationship(const class relationship &rel)
3,567✔
235
{
236
    relationships_[rel.source().path()][rel.id()] = rel;
3,567✔
237
    return rel.id();
3,567✔
238
}
239

240
std::unordered_map<std::string, std::string> manifest::unregister_relationship(const uri &source, const std::string &rel_id)
261✔
241
{
242
    // This shouldn't happen, but just in case...
243
    if (rel_id.substr(0, 3) != "rId" || rel_id.size() < 4)
261✔
244
    {
245
        throw xlnt::invalid_parameter();
×
246
    }
247

248
    std::unordered_map<std::string, std::string> id_map;
261✔
249
    size_t rel_index = 0;
261✔
250
    detail::parse(rel_id.substr(3), rel_index);
261✔
251
    auto &part_rels = relationships_.at(source.path());
261✔
252

253
    for (auto i = rel_index; i <= part_rels.size() + 1; ++i)
757✔
254
    {
255
        auto old_id = "rId" + std::to_string(i);
496✔
256

257
        // Don't re-add the relationship to be deleted
258
        if (i > rel_index)
496✔
259
        {
260
            // Shift all relationships with IDs greater than the deleted one
261
            // down by one (e.g. rId7->rId6).
262
            auto new_id = "rId" + std::to_string(i - 1);
307✔
263
            const auto &old_rel = part_rels.at(old_id);
307✔
264
            register_relationship(xlnt::relationship(new_id, old_rel.type(),
307✔
265
                old_rel.source(), old_rel.target(), old_rel.target_mode()));
307✔
266
            id_map[old_id] = new_id;
307✔
267
        }
307✔
268

269
        part_rels.erase(old_id);
496✔
270
    }
496✔
271

272
    return id_map;
261✔
273
}
×
274

275
bool manifest::has_default_type(const std::string &extension) const
81✔
276
{
277
    return default_content_types_.find(extension) != default_content_types_.end();
81✔
278
}
279

280
std::vector<std::string> manifest::extensions_with_default_types() const
88✔
281
{
282
    std::vector<std::string> extensions;
88✔
283

284
    for (const auto &extension_type_pair : default_content_types_)
339✔
285
    {
286
        extensions.push_back(extension_type_pair.first);
251✔
287
    }
288

289
    return extensions;
88✔
290
}
×
291

292
std::string manifest::default_type(const std::string &extension) const
332✔
293
{
294
    if (default_content_types_.find(extension) == default_content_types_.end())
332✔
295
    {
296
        throw key_not_found();
1✔
297
    }
298

299
    return default_content_types_.at(extension);
331✔
300
}
301

302
void manifest::register_default_type(const std::string &extension, const std::string &content_type)
1,126✔
303
{
304
    default_content_types_[extension] = content_type;
1,126✔
305
}
1,126✔
306

307
void manifest::unregister_default_type(const std::string &extension)
×
308
{
309
    default_content_types_.erase(extension);
×
310
}
×
311

312
std::string manifest::next_relationship_id(const path &part) const
2,059✔
313
{
314
    if (relationships_.find(part) == relationships_.end()) return "rId1";
3,171✔
315

316
    std::size_t index = 1;
1,503✔
317
    const auto &part_rels = relationships_.at(part);
1,503✔
318

319
    while (part_rels.find("rId" + std::to_string(index)) != part_rels.end())
4,366✔
320
    {
321
        ++index;
2,863✔
322
    }
323

324
    return "rId" + std::to_string(index);
1,503✔
325
}
326

327
bool manifest::has_override_type(const xlnt::path &part) const
1,263✔
328
{
329
    return override_content_types_.find(part) != override_content_types_.end();
1,263✔
330
}
331

332
std::string manifest::override_type(const xlnt::path &part) const
906✔
333
{
334
    if (!has_override_type(part))
906✔
335
    {
336
        throw key_not_found();
×
337
    }
338

339
    return override_content_types_.at(part);
906✔
340
}
341

342
bool manifest::operator==(const manifest &other) const
14✔
343
{
344
    return default_content_types_ == other.default_content_types_
14✔
345
        && override_content_types_ == other.override_content_types_
14✔
346
        && relationships_ == other.relationships_;
28✔
347
}
348

NEW
349
bool manifest::operator!=(const manifest &other) const
×
350
{
NEW
351
    return !(*this == other);
×
352
}
353

354
} // namespace xlnt
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc