1 /******************************************************************************
2  *
3  * Project:  PROJ
4  * Purpose:  ISO19111:2019 implementation
5  * Author:   Even Rouault <even dot rouault at spatialys dot com>
6  *
7  ******************************************************************************
8  * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com>
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ****************************************************************************/
28 
29 #ifndef FROM_PROJ_CPP
30 #define FROM_PROJ_CPP
31 #endif
32 
33 #include "proj/util.hpp"
34 #include "proj/io.hpp"
35 
36 #include "proj/internal/internal.hpp"
37 
38 #include <map>
39 #include <memory>
40 #include <string>
41 
42 using namespace NS_PROJ::internal;
43 
44 #if 0
45 namespace dropbox{ namespace oxygen {
46 template<> nn<NS_PROJ::util::BaseObjectPtr>::~nn() = default;
47 template<> nn<NS_PROJ::util::BoxedValuePtr>::~nn() = default;
48 template<> nn<NS_PROJ::util::ArrayOfBaseObjectPtr>::~nn() = default;
49 template<> nn<NS_PROJ::util::LocalNamePtr>::~nn() = default;
50 template<> nn<NS_PROJ::util::GenericNamePtr>::~nn() = default;
51 template<> nn<NS_PROJ::util::NameSpacePtr>::~nn() = default;
52 }}
53 #endif
54 
55 NS_PROJ_START
56 namespace util {
57 
58 // ---------------------------------------------------------------------------
59 
60 //! @cond Doxygen_Suppress
61 struct BaseObject::Private {
62     // This is a manual implementation of std::enable_shared_from_this<> that
63     // avoids publicly deriving from it.
64     std::weak_ptr<BaseObject> self_{};
65 };
66 //! @endcond
67 
68 // ---------------------------------------------------------------------------
69 
BaseObject()70 BaseObject::BaseObject() : d(internal::make_unique<Private>()) {}
71 
72 // ---------------------------------------------------------------------------
73 
74 //! @cond Doxygen_Suppress
75 BaseObject::~BaseObject() = default;
76 
77 // ---------------------------------------------------------------------------
78 
79 BaseObjectNNPtr::~BaseObjectNNPtr() = default;
80 //! @endcond
81 
82 // ---------------------------------------------------------------------------
83 
84 /** Keep a reference to ourselves as an internal weak pointer. So that
85  * extractGeographicBaseObject() can later return a shared pointer on itself.
86  */
assignSelf(const BaseObjectNNPtr & self)87 void BaseObject::assignSelf(const BaseObjectNNPtr &self) {
88     assert(self.get() == this);
89     d->self_ = self.as_nullable();
90 }
91 
92 // ---------------------------------------------------------------------------
93 
94 //! @cond Doxygen_Suppress
shared_from_this() const95 BaseObjectNNPtr BaseObject::shared_from_this() const {
96     // This assertion checks that in all code paths where we create a
97     // shared pointer, we took care of assigning it to self_, by calling
98     // assignSelf();
99     return NN_CHECK_ASSERT(d->self_.lock());
100 }
101 //! @endcond
102 
103 // ---------------------------------------------------------------------------
104 
105 //! @cond Doxygen_Suppress
106 struct BoxedValue::Private {
107     BoxedValue::Type type_{BoxedValue::Type::INTEGER};
108     std::string stringValue_{};
109     int integerValue_{};
110     bool booleanValue_{};
111 
Privateutil::BoxedValue::Private112     explicit Private(const std::string &stringValueIn)
113         : type_(BoxedValue::Type::STRING), stringValue_(stringValueIn) {}
114 
Privateutil::BoxedValue::Private115     explicit Private(int integerValueIn)
116         : type_(BoxedValue::Type::INTEGER), integerValue_(integerValueIn) {}
117 
Privateutil::BoxedValue::Private118     explicit Private(bool booleanValueIn)
119         : type_(BoxedValue::Type::BOOLEAN), booleanValue_(booleanValueIn) {}
120 };
121 //! @endcond
122 
123 // ---------------------------------------------------------------------------
124 
BoxedValue()125 BoxedValue::BoxedValue() : d(internal::make_unique<Private>(std::string())) {}
126 
127 // ---------------------------------------------------------------------------
128 
129 /** \brief Constructs a BoxedValue from a string.
130  */
BoxedValue(const char * stringValueIn)131 BoxedValue::BoxedValue(const char *stringValueIn)
132     : d(internal::make_unique<Private>(
133           std::string(stringValueIn ? stringValueIn : ""))) {}
134 
135 // ---------------------------------------------------------------------------
136 
137 /** \brief Constructs a BoxedValue from a string.
138  */
BoxedValue(const std::string & stringValueIn)139 BoxedValue::BoxedValue(const std::string &stringValueIn)
140     : d(internal::make_unique<Private>(stringValueIn)) {}
141 
142 // ---------------------------------------------------------------------------
143 
144 /** \brief Constructs a BoxedValue from an integer.
145  */
BoxedValue(int integerValueIn)146 BoxedValue::BoxedValue(int integerValueIn)
147     : d(internal::make_unique<Private>(integerValueIn)) {}
148 
149 // ---------------------------------------------------------------------------
150 
151 /** \brief Constructs a BoxedValue from a boolean.
152  */
BoxedValue(bool booleanValueIn)153 BoxedValue::BoxedValue(bool booleanValueIn)
154     : d(internal::make_unique<Private>(booleanValueIn)) {}
155 
156 // ---------------------------------------------------------------------------
157 
158 //! @cond Doxygen_Suppress
BoxedValue(const BoxedValue & other)159 BoxedValue::BoxedValue(const BoxedValue &other)
160     : d(internal::make_unique<Private>(*other.d)) {}
161 
162 // ---------------------------------------------------------------------------
163 
164 BoxedValue::~BoxedValue() = default;
165 
166 // ---------------------------------------------------------------------------
167 
type() const168 const BoxedValue::Type &BoxedValue::type() const { return d->type_; }
169 
170 // ---------------------------------------------------------------------------
171 
stringValue() const172 const std::string &BoxedValue::stringValue() const { return d->stringValue_; }
173 
174 // ---------------------------------------------------------------------------
175 
integerValue() const176 int BoxedValue::integerValue() const { return d->integerValue_; }
177 
178 // ---------------------------------------------------------------------------
179 
booleanValue() const180 bool BoxedValue::booleanValue() const { return d->booleanValue_; }
181 //! @endcond
182 
183 // ---------------------------------------------------------------------------
184 
185 //! @cond Doxygen_Suppress
186 struct ArrayOfBaseObject::Private {
187     std::vector<BaseObjectNNPtr> values_{};
188 };
189 //! @endcond
190 // ---------------------------------------------------------------------------
191 
192 //! @cond Doxygen_Suppress
ArrayOfBaseObject()193 ArrayOfBaseObject::ArrayOfBaseObject() : d(internal::make_unique<Private>()) {}
194 
195 // ---------------------------------------------------------------------------
196 
197 ArrayOfBaseObject::~ArrayOfBaseObject() = default;
198 //! @endcond
199 
200 // ---------------------------------------------------------------------------
201 
202 /** \brief Adds an object to the array.
203  *
204  * @param obj the object to add.
205  */
add(const BaseObjectNNPtr & obj)206 void ArrayOfBaseObject::add(const BaseObjectNNPtr &obj) {
207     d->values_.emplace_back(obj);
208 }
209 
210 // ---------------------------------------------------------------------------
211 
212 //! @cond Doxygen_Suppress
begin() const213 std::vector<BaseObjectNNPtr>::const_iterator ArrayOfBaseObject::begin() const {
214     return d->values_.begin();
215 }
216 
217 // ---------------------------------------------------------------------------
218 
end() const219 std::vector<BaseObjectNNPtr>::const_iterator ArrayOfBaseObject::end() const {
220     return d->values_.end();
221 }
222 
223 // ---------------------------------------------------------------------------
224 
empty() const225 bool ArrayOfBaseObject::empty() const { return d->values_.empty(); }
226 //! @endcond
227 
228 // ---------------------------------------------------------------------------
229 
230 /** \brief Instantiate a ArrayOfBaseObject.
231  *
232  * @return a new ArrayOfBaseObject.
233  */
create()234 ArrayOfBaseObjectNNPtr ArrayOfBaseObject::create() {
235     return ArrayOfBaseObject::nn_make_shared<ArrayOfBaseObject>();
236 }
237 
238 // ---------------------------------------------------------------------------
239 
240 //! @cond Doxygen_Suppress
241 struct PropertyMap::Private {
242     std::list<std::pair<std::string, BaseObjectNNPtr>> list_{};
243 
244     // cppcheck-suppress functionStatic
setutil::PropertyMap::Private245     void set(const std::string &key, const BoxedValueNNPtr &val) {
246         for (auto &pair : list_) {
247             if (pair.first == key) {
248                 pair.second = val;
249                 return;
250             }
251         }
252         list_.emplace_back(key, val);
253     }
254 };
255 //! @endcond
256 
257 // ---------------------------------------------------------------------------
258 
PropertyMap()259 PropertyMap::PropertyMap() : d(internal::make_unique<Private>()) {}
260 
261 // ---------------------------------------------------------------------------
262 
263 //! @cond Doxygen_Suppress
PropertyMap(const PropertyMap & other)264 PropertyMap::PropertyMap(const PropertyMap &other)
265     : d(internal::make_unique<Private>(*(other.d))) {}
266 //! @endcond
267 
268 // ---------------------------------------------------------------------------
269 
270 //! @cond Doxygen_Suppress
271 PropertyMap::~PropertyMap() = default;
272 //! @endcond
273 
274 // ---------------------------------------------------------------------------
275 
276 //! @cond Doxygen_Suppress
get(const std::string & key) const277 const BaseObjectNNPtr *PropertyMap::get(const std::string &key) const {
278     for (const auto &pair : d->list_) {
279         if (pair.first == key) {
280             return &(pair.second);
281         }
282     }
283     return nullptr;
284 }
285 // ---------------------------------------------------------------------------
286 
287 //! @cond Doxygen_Suppress
unset(const std::string & key)288 void PropertyMap::unset(const std::string &key) {
289     for (auto iter = d->list_.begin(); iter != d->list_.end(); ++iter) {
290         if (iter->first == key) {
291             d->list_.erase(iter);
292             return;
293         }
294     }
295 }
296 //! @endcond
297 
298 // ---------------------------------------------------------------------------
299 
300 /** \brief Set a BaseObjectNNPtr as the value of a key. */
set(const std::string & key,const BaseObjectNNPtr & val)301 PropertyMap &PropertyMap::set(const std::string &key,
302                               const BaseObjectNNPtr &val) {
303     for (auto &pair : d->list_) {
304         if (pair.first == key) {
305             pair.second = val;
306             return *this;
307         }
308     }
309     d->list_.emplace_back(key, val);
310     return *this;
311 }
312 
313 // ---------------------------------------------------------------------------
314 
315 /** \brief Set a string as the value of a key. */
set(const std::string & key,const std::string & val)316 PropertyMap &PropertyMap::set(const std::string &key, const std::string &val) {
317     d->set(key, util::nn_make_shared<BoxedValue>(val));
318     return *this;
319 }
320 
321 // ---------------------------------------------------------------------------
322 
323 /** \brief Set a string as the value of a key. */
set(const std::string & key,const char * val)324 PropertyMap &PropertyMap::set(const std::string &key, const char *val) {
325     d->set(key, util::nn_make_shared<BoxedValue>(val));
326     return *this;
327 }
328 
329 // ---------------------------------------------------------------------------
330 
331 /** \brief Set a integer as the value of a key. */
set(const std::string & key,int val)332 PropertyMap &PropertyMap::set(const std::string &key, int val) {
333     d->set(key, util::nn_make_shared<BoxedValue>(val));
334     return *this;
335 }
336 
337 // ---------------------------------------------------------------------------
338 
339 /** \brief Set a boolean as the value of a key. */
set(const std::string & key,bool val)340 PropertyMap &PropertyMap::set(const std::string &key, bool val) {
341     d->set(key, util::nn_make_shared<BoxedValue>(val));
342     return *this;
343 }
344 
345 // ---------------------------------------------------------------------------
346 
347 /** \brief Set a vector of strings as the value of a key. */
set(const std::string & key,const std::vector<std::string> & arrayIn)348 PropertyMap &PropertyMap::set(const std::string &key,
349                               const std::vector<std::string> &arrayIn) {
350     ArrayOfBaseObjectNNPtr array = ArrayOfBaseObject::create();
351     for (const auto &str : arrayIn) {
352         array->add(util::nn_make_shared<BoxedValue>(str));
353     }
354     return set(key, array);
355 }
356 
357 // ---------------------------------------------------------------------------
358 
359 //! @cond Doxygen_Suppress
getStringValue(const std::string & key,std::string & outVal) const360 bool PropertyMap::getStringValue(
361     const std::string &key,
362     std::string &outVal) const // throw(InvalidValueTypeException)
363 {
364     for (const auto &pair : d->list_) {
365         if (pair.first == key) {
366             auto genVal = dynamic_cast<const BoxedValue *>(pair.second.get());
367             if (genVal && genVal->type() == BoxedValue::Type::STRING) {
368                 outVal = genVal->stringValue();
369                 return true;
370             }
371             throw InvalidValueTypeException("Invalid value type for " + key);
372         }
373     }
374     return false;
375 }
376 //! @endcond
377 
378 // ---------------------------------------------------------------------------
379 
380 //! @cond Doxygen_Suppress
getStringValue(const std::string & key,optional<std::string> & outVal) const381 bool PropertyMap::getStringValue(
382     const std::string &key,
383     optional<std::string> &outVal) const // throw(InvalidValueTypeException)
384 {
385     for (const auto &pair : d->list_) {
386         if (pair.first == key) {
387             auto genVal = dynamic_cast<const BoxedValue *>(pair.second.get());
388             if (genVal && genVal->type() == BoxedValue::Type::STRING) {
389                 outVal = genVal->stringValue();
390                 return true;
391             }
392             throw InvalidValueTypeException("Invalid value type for " + key);
393         }
394     }
395     return false;
396 }
397 //! @endcond
398 
399 // ---------------------------------------------------------------------------
400 
401 //! @cond Doxygen_Suppress
402 struct GenericName::Private {};
403 //! @endcond
404 
405 // ---------------------------------------------------------------------------
406 
GenericName()407 GenericName::GenericName() : d(internal::make_unique<Private>()) {}
408 
409 // ---------------------------------------------------------------------------
410 
GenericName(const GenericName & other)411 GenericName::GenericName(const GenericName &other)
412     : d(internal::make_unique<Private>(*other.d)) {}
413 
414 // ---------------------------------------------------------------------------
415 
416 //! @cond Doxygen_Suppress
417 GenericName::~GenericName() = default;
418 //! @endcond
419 
420 // ---------------------------------------------------------------------------
421 
422 //! @cond Doxygen_Suppress
423 struct NameSpace::Private {
424     GenericNamePtr name{};
425     bool isGlobal{};
426     std::string separator = std::string(":");
427     std::string separatorHead = std::string(":");
428 };
429 //! @endcond
430 
431 // ---------------------------------------------------------------------------
432 
NameSpace(const GenericNamePtr & nameIn)433 NameSpace::NameSpace(const GenericNamePtr &nameIn)
434     : d(internal::make_unique<Private>()) {
435     d->name = nameIn;
436 }
437 
438 // ---------------------------------------------------------------------------
439 
NameSpace(const NameSpace & other)440 NameSpace::NameSpace(const NameSpace &other)
441     : d(internal::make_unique<Private>(*other.d)) {}
442 
443 // ---------------------------------------------------------------------------
444 
445 //! @cond Doxygen_Suppress
446 NameSpace::~NameSpace() = default;
447 //! @endcond
448 
449 // ---------------------------------------------------------------------------
450 
451 /** \brief Returns whether this is a global namespace. */
isGlobal() const452 bool NameSpace::isGlobal() const { return d->isGlobal; }
453 
454 // ---------------------------------------------------------------------------
455 
getGlobalFromThis() const456 NameSpaceNNPtr NameSpace::getGlobalFromThis() const {
457     NameSpaceNNPtr ns(NameSpace::nn_make_shared<NameSpace>(*this));
458     ns->d->isGlobal = true;
459     ns->d->name = LocalName::make_shared<LocalName>("global");
460     return ns;
461 }
462 
463 // ---------------------------------------------------------------------------
464 
465 /** \brief Returns the name of this namespace. */
name() const466 const GenericNamePtr &NameSpace::name() const { return d->name; }
467 
468 // ---------------------------------------------------------------------------
469 
separator() const470 const std::string &NameSpace::separator() const { return d->separator; }
471 
472 // ---------------------------------------------------------------------------
473 
createGLOBAL()474 NameSpaceNNPtr NameSpace::createGLOBAL() {
475     NameSpaceNNPtr ns(NameSpace::nn_make_shared<NameSpace>(
476         LocalName::make_shared<LocalName>("global")));
477     ns->d->isGlobal = true;
478     return ns;
479 }
480 
481 // ---------------------------------------------------------------------------
482 
483 const NameSpaceNNPtr NameSpace::GLOBAL(NameSpace::createGLOBAL());
484 
485 // ---------------------------------------------------------------------------
486 
487 //! @cond Doxygen_Suppress
488 struct LocalName::Private {
489     NameSpacePtr scope{};
490     std::string name{};
491 };
492 //! @endcond
493 
494 // ---------------------------------------------------------------------------
495 
LocalName(const std::string & name)496 LocalName::LocalName(const std::string &name)
497     : d(internal::make_unique<Private>()) {
498     d->name = name;
499 }
500 
501 // ---------------------------------------------------------------------------
502 
LocalName(const NameSpacePtr & ns,const std::string & name)503 LocalName::LocalName(const NameSpacePtr &ns, const std::string &name)
504     : d(internal::make_unique<Private>()) {
505     d->scope = ns ? ns : static_cast<NameSpacePtr>(NameSpace::GLOBAL);
506     d->name = name;
507 }
508 
509 // ---------------------------------------------------------------------------
510 
LocalName(const LocalName & other)511 LocalName::LocalName(const LocalName &other)
512     : GenericName(other), d(internal::make_unique<Private>(*other.d)) {}
513 
514 // ---------------------------------------------------------------------------
515 
516 //! @cond Doxygen_Suppress
517 LocalName::~LocalName() = default;
518 //! @endcond
519 
520 // ---------------------------------------------------------------------------
521 
scope() const522 const NameSpacePtr LocalName::scope() const {
523     if (d->scope)
524         return d->scope;
525     return NameSpace::GLOBAL;
526 }
527 
528 // ---------------------------------------------------------------------------
529 
toFullyQualifiedName() const530 GenericNameNNPtr LocalName::toFullyQualifiedName() const {
531     if (scope()->isGlobal())
532         return LocalName::nn_make_shared<LocalName>(*this);
533 
534     return LocalName::nn_make_shared<LocalName>(
535         d->scope->getGlobalFromThis(),
536         d->scope->name()->toFullyQualifiedName()->toString() +
537             d->scope->separator() + d->name);
538 }
539 
540 // ---------------------------------------------------------------------------
541 
toString() const542 std::string LocalName::toString() const { return d->name; }
543 
544 // ---------------------------------------------------------------------------
545 
546 /** \brief Instantiate a NameSpace.
547  *
548  * @param name name of the namespace.
549  * @param properties Properties. Allowed keys are "separator" and
550  * "separator.head".
551  * @return a new NameFactory.
552  */
createNameSpace(const GenericNameNNPtr & name,const PropertyMap & properties)553 NameSpaceNNPtr NameFactory::createNameSpace(const GenericNameNNPtr &name,
554                                             const PropertyMap &properties) {
555     NameSpaceNNPtr ns(NameSpace::nn_make_shared<NameSpace>(name));
556     properties.getStringValue("separator", ns->d->separator);
557     properties.getStringValue("separator.head", ns->d->separatorHead);
558 
559     return ns;
560 }
561 
562 // ---------------------------------------------------------------------------
563 
564 /** \brief Instantiate a LocalName.
565  *
566  * @param scope scope.
567  * @param name string of the local name.
568  * @return a new LocalName.
569  */
createLocalName(const NameSpacePtr & scope,const std::string & name)570 LocalNameNNPtr NameFactory::createLocalName(const NameSpacePtr &scope,
571                                             const std::string &name) {
572     return LocalName::nn_make_shared<LocalName>(scope, name);
573 }
574 
575 // ---------------------------------------------------------------------------
576 
577 /** \brief Instantiate a GenericName.
578  *
579  * @param scope scope.
580  * @param parsedNames the components of the name.
581  * @return a new GenericName.
582  */
583 GenericNameNNPtr
createGenericName(const NameSpacePtr & scope,const std::vector<std::string> & parsedNames)584 NameFactory::createGenericName(const NameSpacePtr &scope,
585                                const std::vector<std::string> &parsedNames) {
586     std::string name;
587     const std::string separator(scope ? scope->separator()
588                                       : NameSpace::GLOBAL->separator());
589     bool first = true;
590     for (const auto &str : parsedNames) {
591         if (!first)
592             name += separator;
593         first = false;
594         name += str;
595     }
596     return LocalName::nn_make_shared<LocalName>(scope, name);
597 }
598 
599 // ---------------------------------------------------------------------------
600 
601 //! @cond Doxygen_Suppress
602 CodeList::~CodeList() = default;
603 //! @endcond
604 
605 // ---------------------------------------------------------------------------
606 
operator =(const CodeList & other)607 CodeList &CodeList::operator=(const CodeList &other) {
608     name_ = other.name_;
609     return *this;
610 }
611 
612 // ---------------------------------------------------------------------------
613 
614 //! @cond Doxygen_Suppress
Exception(const char * message)615 Exception::Exception(const char *message) : msg_(message) {}
616 
617 // ---------------------------------------------------------------------------
618 
Exception(const std::string & message)619 Exception::Exception(const std::string &message) : msg_(message) {}
620 
621 // ---------------------------------------------------------------------------
622 
623 Exception::Exception(const Exception &) = default;
624 
625 // ---------------------------------------------------------------------------
626 
627 Exception::~Exception() = default;
628 //! @endcond
629 
630 // ---------------------------------------------------------------------------
631 
632 /** Return the exception text. */
what() const633 const char *Exception::what() const noexcept { return msg_.c_str(); }
634 
635 // ---------------------------------------------------------------------------
636 
637 //! @cond Doxygen_Suppress
InvalidValueTypeException(const char * message)638 InvalidValueTypeException::InvalidValueTypeException(const char *message)
639     : Exception(message) {}
640 
641 // ---------------------------------------------------------------------------
642 
InvalidValueTypeException(const std::string & message)643 InvalidValueTypeException::InvalidValueTypeException(const std::string &message)
644     : Exception(message) {}
645 
646 // ---------------------------------------------------------------------------
647 
648 InvalidValueTypeException::~InvalidValueTypeException() = default;
649 
650 // ---------------------------------------------------------------------------
651 
652 InvalidValueTypeException::InvalidValueTypeException(
653     const InvalidValueTypeException &) = default;
654 //! @endcond
655 
656 // ---------------------------------------------------------------------------
657 
658 //! @cond Doxygen_Suppress
UnsupportedOperationException(const char * message)659 UnsupportedOperationException::UnsupportedOperationException(
660     const char *message)
661     : Exception(message) {}
662 
663 // ---------------------------------------------------------------------------
664 
UnsupportedOperationException(const std::string & message)665 UnsupportedOperationException::UnsupportedOperationException(
666     const std::string &message)
667     : Exception(message) {}
668 
669 // ---------------------------------------------------------------------------
670 
671 UnsupportedOperationException::~UnsupportedOperationException() = default;
672 
673 // ---------------------------------------------------------------------------
674 
675 UnsupportedOperationException::UnsupportedOperationException(
676     const UnsupportedOperationException &) = default;
677 //! @endcond
678 
679 // ---------------------------------------------------------------------------
680 
681 //! @cond Doxygen_Suppress
682 IComparable::~IComparable() = default;
683 //! @endcond
684 
685 // ---------------------------------------------------------------------------
686 
687 /** \brief Returns whether an object is equivalent to another one.
688  * @param other other object to compare to
689  * @param criterion comparison criterion.
690  * @param dbContext Database context, or nullptr.
691  * @return true if objects are equivalent.
692  */
isEquivalentTo(const IComparable * other,Criterion criterion,const io::DatabaseContextPtr & dbContext) const693 bool IComparable::isEquivalentTo(
694     const IComparable *other, Criterion criterion,
695     const io::DatabaseContextPtr &dbContext) const {
696     return _isEquivalentTo(other, criterion, dbContext);
697 }
698 
699 // ---------------------------------------------------------------------------
700 
701 } // namespace util
702 NS_PROJ_END
703