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