1 #ifndef OSMIUM_OSM_CHANGESET_HPP
2 #define OSMIUM_OSM_CHANGESET_HPP
3 
4 /*
5 
6 This file is part of Osmium (https://osmcode.org/libosmium).
7 
8 Copyright 2013-2021 Jochen Topf <jochen@topf.org> and others (see README).
9 
10 Boost Software License - Version 1.0 - August 17th, 2003
11 
12 Permission is hereby granted, free of charge, to any person or organization
13 obtaining a copy of the software and accompanying documentation covered by
14 this license (the "Software") to use, reproduce, display, distribute,
15 execute, and transmit the Software, and to prepare derivative works of the
16 Software, and to permit third-parties to whom the Software is furnished to
17 do so, all subject to the following:
18 
19 The copyright notices in the Software and this entire statement, including
20 the above license grant, this restriction and the following disclaimer,
21 must be included in all copies of the Software, in whole or in part, and
22 all derivative works of the Software, unless such copies or derivative
23 works are solely in the form of machine-executable object code generated by
24 a source language processor.
25 
26 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
29 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
30 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
31 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32 DEALINGS IN THE SOFTWARE.
33 
34 */
35 
36 #include <osmium/memory/collection.hpp>
37 #include <osmium/memory/item.hpp>
38 #include <osmium/osm/box.hpp>
39 #include <osmium/osm/entity.hpp>
40 #include <osmium/osm/item_type.hpp>
41 #include <osmium/osm/tag.hpp>
42 #include <osmium/osm/timestamp.hpp>
43 #include <osmium/osm/types.hpp>
44 #include <osmium/osm/types_from_string.hpp>
45 
46 #include <cstdint>
47 #include <cstring>
48 #include <iterator>
49 
50 namespace osmium {
51 
52     namespace builder {
53         class ChangesetDiscussionBuilder;
54         class ChangesetBuilder;
55     } // namespace builder
56 
57     class Changeset;
58 
59     class ChangesetComment : public osmium::memory::detail::ItemHelper {
60 
61         friend class osmium::builder::ChangesetDiscussionBuilder;
62 
63         osmium::Timestamp m_date;
64         osmium::user_id_type m_uid = 0;
65         changeset_comment_size_type m_text_size;
66         string_size_type m_user_size;
67 
endpos()68         unsigned char* endpos() {
69             return data() + osmium::memory::padded_length(sizeof(ChangesetComment) + m_user_size + m_text_size);
70         }
71 
endpos() const72         const unsigned char* endpos() const {
73             return data() + osmium::memory::padded_length(sizeof(ChangesetComment) + m_user_size + m_text_size);
74         }
75 
76         template <typename TMember>
77         friend class osmium::memory::CollectionIterator;
78 
next()79         unsigned char* next() {
80             return endpos();
81         }
82 
next() const83         const unsigned char* next() const {
84             return endpos();
85         }
86 
set_user_size(string_size_type size)87         void set_user_size(string_size_type size) noexcept {
88             m_user_size = size;
89         }
90 
set_text_size(changeset_comment_size_type size)91         void set_text_size(changeset_comment_size_type size) noexcept {
92             m_text_size = size;
93         }
94 
95     public:
96 
97         static constexpr item_type collection_type = item_type::changeset_discussion;
98 
ChangesetComment(osmium::Timestamp date,osmium::user_id_type uid)99         ChangesetComment(osmium::Timestamp date, osmium::user_id_type uid) noexcept :
100             m_date(date),
101             m_uid(uid),
102             m_text_size(0),
103             m_user_size(0) {
104         }
105 
106         ChangesetComment(const ChangesetComment&) = delete;
107         ChangesetComment& operator=(const ChangesetComment&) = delete;
108 
109         ChangesetComment(ChangesetComment&&) = delete;
110         ChangesetComment& operator=(ChangesetComment&&) = delete;
111 
112         ~ChangesetComment() noexcept = default;
113 
date() const114         osmium::Timestamp date() const noexcept {
115             return m_date;
116         }
117 
uid() const118         osmium::user_id_type uid() const noexcept {
119             return m_uid;
120         }
121 
user() const122         const char* user() const noexcept {
123             return reinterpret_cast<const char*>(data() + sizeof(ChangesetComment));
124         }
125 
text() const126         const char* text() const noexcept {
127             return reinterpret_cast<const char*>(data() + sizeof(ChangesetComment) + m_user_size);
128         }
129 
130     }; // class ChangesetComment
131 
132     class ChangesetDiscussion : public osmium::memory::Collection<ChangesetComment, osmium::item_type::changeset_discussion> {
133 
134     public:
135 
136         ChangesetDiscussion() noexcept = default;
137 
138     }; // class ChangesetDiscussion
139 
140     static_assert(sizeof(ChangesetDiscussion) % osmium::memory::align_bytes == 0, "Class osmium::ChangesetDiscussion has wrong size to be aligned properly!");
141 
142     /**
143      * \brief An OSM Changeset, a group of changes made by a single user over
144      *        a short period of time.
145      *
146      * You can not create Changeset objects directly. Use the ChangesetBuilder
147      * class to create Changesets in a Buffer.
148      */
149     class Changeset : public osmium::OSMEntity {
150 
151         friend class osmium::builder::ChangesetBuilder;
152 
153         osmium::Box       m_bounds;
154         osmium::Timestamp m_created_at;
155         osmium::Timestamp m_closed_at;
156         changeset_id_type m_id = 0;
157         num_changes_type  m_num_changes = 0;
158         num_comments_type m_num_comments = 0;
159         user_id_type      m_uid = 0;
160         string_size_type  m_user_size = 0;
161         int16_t           m_padding1 = 0;
162         int32_t           m_padding2 = 0;
163 
Changeset()164         Changeset() :
165             OSMEntity(sizeof(Changeset), osmium::item_type::changeset) {
166         }
167 
set_user_size(string_size_type size)168         void set_user_size(string_size_type size) noexcept {
169             m_user_size = size;
170         }
171 
user_size() const172         string_size_type user_size() const noexcept {
173             return m_user_size;
174         }
175 
subitems_position()176         unsigned char* subitems_position() {
177             return data() + osmium::memory::padded_length(sizeof(Changeset) + m_user_size);
178         }
179 
subitems_position() const180         const unsigned char* subitems_position() const {
181             return data() + osmium::memory::padded_length(sizeof(Changeset) + m_user_size);
182         }
183 
184     public:
185 
186         static constexpr osmium::item_type itemtype = osmium::item_type::changeset;
187 
is_compatible_to(osmium::item_type t)188         constexpr static bool is_compatible_to(osmium::item_type t) noexcept {
189             return t == itemtype;
190         }
191 
192         // Dummy to avoid warning because of unused private fields. Do not use.
do_not_use() const193         int32_t do_not_use() const noexcept {
194             return m_padding1 + m_padding2;
195         }
196 
197         /// Get ID of this changeset
id() const198         changeset_id_type id() const noexcept {
199             return m_id;
200         }
201 
202         /**
203          * Set ID of this changeset
204          *
205          * @param id The id.
206          * @returns Reference to changeset to make calls chainable.
207          */
set_id(changeset_id_type id)208         Changeset& set_id(changeset_id_type id) noexcept {
209             m_id = id;
210             return *this;
211         }
212 
213         /**
214          * Set ID of this changeset.
215          *
216          * @param id The id.
217          * @returns Reference to object to make calls chainable.
218          */
set_id(const char * id)219         Changeset& set_id(const char* id) {
220             return set_id(osmium::string_to_changeset_id(id));
221         }
222 
223         /// Get user id.
uid() const224         user_id_type uid() const noexcept {
225             return m_uid;
226         }
227 
228         /**
229          * Set user id.
230          *
231          * @param uid The user id.
232          * @returns Reference to changeset to make calls chainable.
233          */
set_uid(user_id_type uid)234         Changeset& set_uid(user_id_type uid) noexcept {
235             m_uid = uid;
236             return *this;
237         }
238 
239         /**
240          * Set user id to given uid or to 0 (anonymous user) if the given
241          * uid is smaller than 0.
242          *
243          * @param uid The user id.
244          * @returns Reference to changeset to make calls chainable.
245          */
set_uid_from_signed(signed_user_id_type uid)246         Changeset& set_uid_from_signed(signed_user_id_type uid) noexcept {
247             m_uid = uid < 0 ? 0 : static_cast<user_id_type>(uid);
248             return *this;
249         }
250 
251         /**
252          * Set user id to given uid or to 0 (anonymous user) if the given
253          * uid is smaller than 0.
254          *
255          * @returns Reference to changeset to make calls chainable.
256          */
set_uid(const char * uid)257         Changeset& set_uid(const char* uid) {
258             m_uid = string_to_uid(uid);
259             return *this;
260         }
261 
262         /// Is this user anonymous?
user_is_anonymous() const263         bool user_is_anonymous() const noexcept {
264             return m_uid == 0;
265         }
266 
267         /// Get timestamp when this changeset was created.
created_at() const268         osmium::Timestamp created_at() const noexcept {
269             return m_created_at;
270         }
271 
272         /**
273          * Get timestamp when this changeset was closed.
274          *
275          * @returns Timestamp. Will return the empty Timestamp when the
276          *          changeset is not yet closed.
277          */
closed_at() const278         osmium::Timestamp closed_at() const noexcept {
279             return m_closed_at;
280         }
281 
282         /// Is this changeset open?
open() const283         bool open() const noexcept {
284             return m_closed_at == osmium::Timestamp();
285         }
286 
287         /// Is this changeset closed?
closed() const288         bool closed() const noexcept {
289             return !open();
290         }
291 
292         /**
293          * Set the timestamp when this changeset was created.
294          *
295          * @param timestamp Timestamp
296          * @returns Reference to changeset to make calls chainable.
297          */
set_created_at(const osmium::Timestamp & timestamp)298         Changeset& set_created_at(const osmium::Timestamp& timestamp) {
299             m_created_at = timestamp;
300             return *this;
301         }
302 
303         /**
304          * Set the timestamp when this changeset was closed.
305          *
306          * @param timestamp Timestamp
307          * @returns Reference to changeset to make calls chainable.
308          */
set_closed_at(const osmium::Timestamp & timestamp)309         Changeset& set_closed_at(const osmium::Timestamp& timestamp) {
310             m_closed_at = timestamp;
311             return *this;
312         }
313 
314         /// Get the number of changes in this changeset
num_changes() const315         num_changes_type num_changes() const noexcept {
316             return m_num_changes;
317         }
318 
319         /// Set the number of changes in this changeset
set_num_changes(num_changes_type num_changes)320         Changeset& set_num_changes(num_changes_type num_changes) noexcept {
321             m_num_changes = num_changes;
322             return *this;
323         }
324 
325         /// Set the number of changes in this changeset
set_num_changes(const char * num_changes)326         Changeset& set_num_changes(const char* num_changes) {
327             return set_num_changes(osmium::string_to_num_changes(num_changes));
328         }
329 
330         /// Get the number of comments in this changeset
num_comments() const331         num_comments_type num_comments() const noexcept {
332             return m_num_comments;
333         }
334 
335         /// Set the number of comments in this changeset
set_num_comments(num_comments_type num_comments)336         Changeset& set_num_comments(num_comments_type num_comments) noexcept {
337             m_num_comments = num_comments;
338             return *this;
339         }
340 
341         /// Set the number of comments in this changeset
set_num_comments(const char * num_comments)342         Changeset& set_num_comments(const char* num_comments) {
343             return set_num_comments(osmium::string_to_num_comments(num_comments));
344         }
345 
346         /**
347          * Get the bounding box of this changeset.
348          *
349          * @returns Bounding box. Can be empty.
350          */
bounds()351         osmium::Box& bounds() noexcept {
352             return m_bounds;
353         }
354 
355         /**
356          * Get the bounding box of this changeset.
357          *
358          * @returns Bounding box. Can be empty.
359          */
bounds() const360         const osmium::Box& bounds() const noexcept {
361             return m_bounds;
362         }
363 
364         /// Get user name.
user() const365         const char* user() const {
366             return reinterpret_cast<const char*>(data() + sizeof(Changeset));
367         }
368 
369         /// Clear user name.
clear_user()370         void clear_user() noexcept {
371             std::memset(data() + sizeof(Changeset), 0, user_size());
372         }
373 
374         /// Get the list of tags.
tags() const375         const TagList& tags() const {
376             return osmium::detail::subitem_of_type<const TagList>(cbegin(), cend());
377         }
378 
379         /**
380          * Set named attribute.
381          *
382          * @param attr Name of the attribute (must be one of "id", "version",
383          *             "changeset", "timestamp", "uid", "visible")
384          * @param value Value of the attribute
385          */
set_attribute(const char * attr,const char * value)386         void set_attribute(const char* attr, const char* value) {
387             if (!std::strcmp(attr, "id")) {
388                 set_id(value);
389             } else if (!std::strcmp(attr, "num_changes")) {
390                 set_num_changes(value);
391             } else if (!std::strcmp(attr, "comments_count")) {
392                 set_num_comments(value);
393             } else if (!std::strcmp(attr, "created_at")) {
394                 set_created_at(osmium::Timestamp(value));
395             } else if (!std::strcmp(attr, "closed_at")) {
396                 set_closed_at(osmium::Timestamp(value));
397             } else if (!std::strcmp(attr, "uid")) {
398                 set_uid(value);
399             }
400         }
401 
402         using iterator       = osmium::memory::CollectionIterator<Item>;
403         using const_iterator = osmium::memory::CollectionIterator<const Item>;
404 
begin()405         iterator begin() {
406             return iterator(subitems_position());
407         }
408 
end()409         iterator end() {
410             return iterator(data() + padded_size());
411         }
412 
cbegin() const413         const_iterator cbegin() const {
414             return const_iterator(subitems_position());
415         }
416 
cend() const417         const_iterator cend() const {
418             return const_iterator(data() + padded_size());
419         }
420 
begin() const421         const_iterator begin() const {
422             return cbegin();
423         }
424 
end() const425         const_iterator end() const {
426             return cend();
427         }
428 
discussion()429         ChangesetDiscussion& discussion() {
430             return osmium::detail::subitem_of_type<ChangesetDiscussion>(begin(), end());
431         }
432 
discussion() const433         const ChangesetDiscussion& discussion() const {
434             return osmium::detail::subitem_of_type<const ChangesetDiscussion>(cbegin(), cend());
435         }
436 
437     }; // class Changeset
438 
439     static_assert(sizeof(Changeset) % osmium::memory::align_bytes == 0, "Class osmium::Changeset has wrong size to be aligned properly!");
440 
441     /**
442      * Changesets are equal if their IDs are equal.
443      */
operator ==(const Changeset & lhs,const Changeset & rhs)444     inline bool operator==(const Changeset& lhs, const Changeset& rhs) {
445         return lhs.id() == rhs.id();
446     }
447 
operator !=(const Changeset & lhs,const Changeset & rhs)448     inline bool operator!=(const Changeset& lhs, const Changeset& rhs) {
449         return !(lhs == rhs);
450     }
451 
452     /**
453      * Changesets can be ordered by id.
454      */
operator <(const Changeset & lhs,const Changeset & rhs)455     inline bool operator<(const Changeset& lhs, const Changeset& rhs) {
456         return lhs.id() < rhs.id();
457     }
458 
operator >(const Changeset & lhs,const Changeset & rhs)459     inline bool operator>(const Changeset& lhs, const Changeset& rhs) {
460         return rhs < lhs;
461     }
462 
operator <=(const Changeset & lhs,const Changeset & rhs)463     inline bool operator<=(const Changeset& lhs, const Changeset& rhs) {
464         return !(rhs < lhs);
465     }
466 
operator >=(const Changeset & lhs,const Changeset & rhs)467     inline bool operator>=(const Changeset& lhs, const Changeset& rhs) {
468         return !(lhs < rhs);
469     }
470 
471 } // namespace osmium
472 
473 #endif // OSMIUM_OSM_CHANGESET_HPP
474