|
|
|||
Warning, file /include/boost/url/param.hpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 // 0002 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) 0003 // Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) 0004 // 0005 // Distributed under the Boost Software License, Version 1.0. (See accompanying 0006 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 0007 // 0008 // Official repository: https://github.com/boostorg/url 0009 // 0010 0011 #ifndef BOOST_URL_PARAM_HPP 0012 #define BOOST_URL_PARAM_HPP 0013 0014 #include <boost/url/detail/config.hpp> 0015 #include <boost/url/detail/optional_string.hpp> 0016 #include <boost/url/pct_string_view.hpp> 0017 #include <cstddef> 0018 #include <string> 0019 0020 namespace boost { 0021 namespace urls { 0022 0023 #ifndef BOOST_URL_DOCS 0024 struct param_pct_view; 0025 struct param_view; 0026 #endif 0027 0028 /** The type of @ref no_value 0029 */ 0030 struct no_value_t 0031 { 0032 }; 0033 0034 /** Constant indicating no value in a param 0035 */ 0036 constexpr no_value_t no_value{}; 0037 0038 //------------------------------------------------ 0039 0040 /** A query parameter 0041 0042 Objects of this type represent a single key 0043 and value pair in a query string where a key 0044 is always present and may be empty, while the 0045 presence of a value is indicated by 0046 @ref has_value equal to true. 0047 An empty value is distinct from no value. 0048 0049 Depending on where the object was obtained, 0050 the strings may or may not contain percent 0051 escapes. 0052 0053 For most usages, key comparisons are 0054 case-sensitive and duplicate keys in 0055 a query are possible. However, it is 0056 the authority that has final control 0057 over how the query is interpreted. 0058 0059 @par BNF 0060 @code 0061 query-params = query-param *( "&" query-param ) 0062 query-param = key [ "=" value ] 0063 key = *qpchar 0064 value = *( qpchar / "=" ) 0065 @endcode 0066 0067 @par Specification 0068 @li <a href="https://en.wikipedia.org/wiki/Query_string" 0069 >Query string (Wikipedia)</a> 0070 0071 @see 0072 @ref param_view, 0073 @ref param_pct_view. 0074 */ 0075 struct param 0076 { 0077 /** The key 0078 0079 For most usages, key comparisons are 0080 case-sensitive and duplicate keys in 0081 a query are possible. However, it is 0082 the authority that has final control 0083 over how the query is interpreted. 0084 */ 0085 std::string key; 0086 0087 /** The value 0088 0089 The presence of a value is indicated by 0090 @ref has_value equal to true. 0091 An empty value is distinct from no value. 0092 */ 0093 std::string value; 0094 0095 /** True if a value is present 0096 0097 The presence of a value is indicated by 0098 `has_value == true`. 0099 An empty value is distinct from no value. 0100 */ 0101 bool has_value = false; 0102 0103 /** Constructor 0104 0105 Default constructed query parameters 0106 have an empty key and no value. 0107 0108 @par Example 0109 @code 0110 param qp; 0111 @endcode 0112 0113 @par Postconditions 0114 @code 0115 this->key == "" && this->value == "" && this->has_value == false 0116 @endcode 0117 0118 @par Complexity 0119 Constant. 0120 0121 @par Exception Safety 0122 Throws nothing. 0123 */ 0124 param() = default; 0125 0126 /** Constructor 0127 0128 Upon construction, this acquires 0129 ownership of the members of other 0130 via move construction. The moved 0131 from object is as if default 0132 constructed. 0133 0134 @par Complexity 0135 Constant. 0136 0137 @par Exception Safety 0138 Throws nothing. 0139 0140 @param other The object to construct from. 0141 */ 0142 param(param&& other) noexcept 0143 : key(std::move(other.key)) 0144 , value(std::move(other.value)) 0145 , has_value(other.has_value) 0146 { 0147 #ifdef BOOST_URL_COW_STRINGS 0148 // for copy-on-write std::string 0149 other.key.clear(); 0150 other.value.clear(); 0151 #endif 0152 other.has_value = false; 0153 } 0154 0155 /** Constructor 0156 0157 Upon construction, this becomes a copy 0158 of `other`. 0159 0160 @par Postconditions 0161 @code 0162 this->key == other.key && this->value == other.value && this->has_value == other.has_value 0163 @endcode 0164 0165 @par Complexity 0166 Linear in `other.key.size() + other.value.size()`. 0167 0168 @par Exception Safety 0169 Calls to allocate may throw. 0170 0171 @param other The object to construct from. 0172 @return A reference to this object. 0173 */ 0174 param(param const& other) = default; 0175 0176 /** Assignment 0177 0178 Upon assignment, this acquires 0179 ownership of the members of other 0180 via move assignment. The moved 0181 from object is as if default 0182 constructed. 0183 0184 @par Complexity 0185 Constant. 0186 0187 @par Exception Safety 0188 Throws nothing. 0189 0190 0191 @param other The object to assign from. 0192 @return A reference to this object. 0193 */ 0194 param& 0195 operator=(param&& other) noexcept 0196 { 0197 key = std::move(other.key); 0198 value = std::move(other.value); 0199 has_value = other.has_value; 0200 #ifdef BOOST_URL_COW_STRINGS 0201 // for copy-on-write std::string 0202 other.key.clear(); 0203 other.value.clear(); 0204 #endif 0205 other.has_value = false; 0206 return *this; 0207 } 0208 0209 /** Assignment 0210 0211 Upon assignment, this becomes a copy 0212 of `other`. 0213 0214 @par Postconditions 0215 @code 0216 this->key == other.key && this->value == other.value && this->has_value == other.has_value 0217 @endcode 0218 0219 @par Complexity 0220 Linear in `other.key.size() + other.value.size()`. 0221 0222 @par Exception Safety 0223 Calls to allocate may throw. 0224 0225 0226 @param other The object to assign from. 0227 @return A reference to this object. 0228 */ 0229 param& operator=( 0230 param const& other) = default; 0231 0232 //-------------------------------------------- 0233 0234 /** Constructor 0235 0236 This constructs a parameter with a key 0237 and value. 0238 0239 No validation is performed on the strings. 0240 Ownership of the key and value is acquired 0241 by making copies. 0242 0243 @par Example 0244 @code 0245 param qp( "key", "value" ); 0246 @endcode 0247 0248 @code 0249 param qp( "key", optional<core::string_view>("value") ); 0250 @endcode 0251 0252 @code 0253 param qp( "key", boost::none ); 0254 @endcode 0255 0256 @code 0257 param qp( "key", nullptr ); 0258 @endcode 0259 0260 @code 0261 param qp( "key", no_value ); 0262 @endcode 0263 0264 @par Postconditions 0265 @code 0266 this->key == key && this->value == value && this->has_value == true 0267 @endcode 0268 0269 @par Complexity 0270 Linear in `key.size() + value.size()`. 0271 0272 @par Exception Safety 0273 Calls to allocate may throw. 0274 0275 @tparam OptionalString An optional string 0276 type, such as `core::string_view`, 0277 `std::nullptr`, @ref no_value_t, or 0278 `optional<core::string_view>`. 0279 0280 @param key The key to set. 0281 @param value The value to set. 0282 */ 0283 template <class OptionalString> 0284 param( 0285 core::string_view key, 0286 OptionalString const& value) 0287 : param(key, detail::get_optional_string(value)) 0288 { 0289 } 0290 0291 /** Assignment 0292 0293 The members of `other` are copied, 0294 re-using already existing string capacity. 0295 0296 @par Postconditions 0297 @code 0298 this->key == other.key && this->value == other.value && this->has_value == other.has_value 0299 @endcode 0300 0301 @par Complexity 0302 Linear in `other.key.size() + other.value.size()`. 0303 0304 @par Exception Safety 0305 Calls to allocate may throw. 0306 0307 @param other The parameter to copy. 0308 @return A reference to this object. 0309 */ 0310 param& 0311 operator=(param_view const& other); 0312 0313 /** Assignment 0314 0315 The members of `other` are copied, 0316 re-using already existing string capacity. 0317 0318 @par Postconditions 0319 @code 0320 this->key == other.key && this->value == other.value && this->has_value == other.has_value 0321 @endcode 0322 0323 @par Complexity 0324 Linear in `other.key.size() + other.value.size()`. 0325 0326 @par Exception Safety 0327 Calls to allocate may throw. 0328 0329 @param other The parameter to copy. 0330 @return A reference to this object. 0331 */ 0332 param& 0333 operator=(param_pct_view const& other); 0334 0335 /** Arrow support 0336 0337 This operator returns the address of the 0338 object so that it can be used in pointer 0339 contexts. 0340 0341 @return A pointer to the object. 0342 0343 */ 0344 param const* 0345 operator->() const noexcept 0346 { 0347 return this; 0348 } 0349 0350 /** Aggregate construction 0351 0352 @param key The key to set. 0353 @param value The value to set. 0354 @param has_value True if a value is present. 0355 */ 0356 param( 0357 core::string_view key, 0358 core::string_view value, 0359 bool has_value) noexcept 0360 : key(key) 0361 , value(has_value 0362 ? value 0363 : core::string_view()) 0364 , has_value(has_value) 0365 { 0366 } 0367 0368 private: 0369 param( 0370 core::string_view key, 0371 detail::optional_string const& value) 0372 : param(key, value.s, value.b) 0373 { 0374 } 0375 }; 0376 0377 //------------------------------------------------ 0378 0379 /** A view of a query parameter 0380 0381 Objects of this type represent a single key 0382 and value pair in a query string where a key 0383 is always present and may be empty, while the 0384 presence of a value is indicated by 0385 @ref has_value equal to true. 0386 An empty value is distinct from no value. 0387 0388 Depending on where the object was obtained, 0389 the strings may or may not contain percent 0390 escapes. 0391 0392 For most usages, key comparisons are 0393 case-sensitive and duplicate keys in 0394 a query are possible. However, it is 0395 the authority that has final control 0396 over how the query is interpreted. 0397 0398 <br> 0399 0400 Keys and values in this object reference 0401 external character buffers. 0402 Ownership of the buffers is not transferred; 0403 the caller is responsible for ensuring that 0404 the assigned buffers remain valid until 0405 they are no longer referenced. 0406 0407 @par BNF 0408 @code 0409 query-params = query-param *( "&" query-param ) 0410 query-param = key [ "=" value ] 0411 key = *qpchar 0412 value = *( qpchar / "=" ) 0413 @endcode 0414 0415 @par Specification 0416 @li <a href="https://en.wikipedia.org/wiki/Query_string" 0417 >Query string (Wikipedia)</a> 0418 0419 @see 0420 @ref param, 0421 @ref param_pct_view. 0422 */ 0423 struct param_view 0424 { 0425 /** The key 0426 0427 For most usages, key comparisons are 0428 case-sensitive and duplicate keys in 0429 a query are possible. However, it is 0430 the authority that has final control 0431 over how the query is interpreted. 0432 */ 0433 core::string_view key; 0434 0435 /** The value 0436 0437 The presence of a value is indicated by 0438 @ref has_value equal to true. 0439 An empty value is distinct from no value. 0440 */ 0441 core::string_view value; 0442 0443 /** True if a value is present 0444 0445 The presence of a value is indicated by 0446 `has_value == true`. 0447 An empty value is distinct from no value. 0448 */ 0449 bool has_value = false; 0450 0451 //-------------------------------------------- 0452 0453 /** Constructor 0454 0455 Default constructed query parameters 0456 have an empty key and no value. 0457 0458 @par Example 0459 @code 0460 param_view qp; 0461 @endcode 0462 0463 @par Postconditions 0464 @code 0465 this->key == "" && this->value == "" && this->has_value == false 0466 @endcode 0467 0468 @par Complexity 0469 Constant. 0470 0471 @par Exception Safety 0472 Throws nothing. 0473 */ 0474 param_view() = default; 0475 0476 /** Constructor 0477 0478 This constructs a parameter with a key 0479 and value. 0480 No validation is performed on the strings. 0481 The new key and value reference 0482 the same corresponding underlying 0483 character buffers. 0484 Ownership of the buffers is not transferred; 0485 the caller is responsible for ensuring that 0486 the assigned buffers remain valid until 0487 they are no longer referenced. 0488 0489 @par Example 0490 @code 0491 param_view qp( "key", "value" ); 0492 @endcode 0493 0494 @par Postconditions 0495 @code 0496 this->key.data() == key.data() && this->value.data() == value.data() && this->has_value == true 0497 @endcode 0498 0499 @par Complexity 0500 Constant. 0501 0502 @par Exception Safety 0503 Throws nothing. 0504 0505 @tparam OptionalString An optional string 0506 type, such as `core::string_view`, 0507 `std::nullptr`, @ref no_value_t, or 0508 `optional<core::string_view>`. 0509 0510 @param key The key to set. 0511 @param value The value to set. 0512 */ 0513 template <class OptionalString> 0514 param_view( 0515 core::string_view key, 0516 OptionalString const& value) noexcept 0517 : param_view(key, detail::get_optional_string(value)) 0518 { 0519 } 0520 0521 /** Constructor 0522 0523 This function constructs a param 0524 which references the character buffers 0525 representing the key and value in another 0526 container. 0527 Ownership of the buffers is not transferred; 0528 the caller is responsible for ensuring that 0529 the assigned buffers remain valid until 0530 they are no longer referenced. 0531 0532 @par Example 0533 @code 0534 param qp( "key", "value" ); 0535 param_view qpv( qp ); 0536 @endcode 0537 0538 @par Postconditions 0539 @code 0540 this->key == key && this->value == value && this->has_value == other.has_value 0541 @endcode 0542 0543 @par Complexity 0544 Constant. 0545 0546 @par Exception Safety 0547 Throws nothing. 0548 0549 @param other The param to reference 0550 */ 0551 param_view( 0552 param const& other) noexcept 0553 : param_view( 0554 other.key, 0555 other.value, 0556 other.has_value) 0557 { 0558 } 0559 0560 /** Conversion 0561 0562 This function performs a conversion from 0563 a reference-like query parameter to one 0564 retaining ownership of the strings by 0565 making a copy. 0566 No validation is performed on the strings. 0567 0568 @par Complexity 0569 Linear in `this->key.size() + this->value.size()`. 0570 0571 @par Exception Safety 0572 Calls to allocate may throw. 0573 0574 @return A new query parameter. 0575 */ 0576 explicit 0577 operator 0578 param() 0579 { 0580 return { key, value, has_value }; 0581 } 0582 0583 /** Arrow support 0584 0585 This operator returns the address of the 0586 object so that it can be used in pointer 0587 contexts. 0588 0589 @return A pointer to the object. 0590 */ 0591 param_view const* 0592 operator->() const noexcept 0593 { 0594 return this; 0595 } 0596 0597 /** Aggregate construction 0598 0599 @param key_ The key to set. 0600 @param value_ The value to set. 0601 @param has_value_ True if a value is present. 0602 */ 0603 param_view( 0604 core::string_view key_, 0605 core::string_view value_, 0606 bool has_value_) noexcept 0607 : key(key_) 0608 , value(has_value_ 0609 ? value_ 0610 : core::string_view()) 0611 , has_value(has_value_) 0612 { 0613 } 0614 0615 private: 0616 param_view( 0617 core::string_view key, 0618 detail::optional_string const& value) 0619 : param_view(key, value.s, value.b) 0620 { 0621 } 0622 }; 0623 0624 //------------------------------------------------ 0625 0626 /** A view of a percent-encoded query parameter 0627 0628 Objects of this type represent a single key 0629 and value pair in a query string where a key 0630 is always present and may be empty, while the 0631 presence of a value is indicated by 0632 @ref has_value equal to true. 0633 An empty value is distinct from no value. 0634 0635 The strings may have percent escapes, and 0636 offer an additional invariant: they never 0637 contain an invalid percent-encoding. 0638 0639 For most usages, key comparisons are 0640 case-sensitive and duplicate keys in 0641 a query are possible. However, it is 0642 the authority that has final control 0643 over how the query is interpreted. 0644 0645 <br> 0646 0647 Keys and values in this object reference 0648 external character buffers. 0649 Ownership of the buffers is not transferred; 0650 the caller is responsible for ensuring that 0651 the assigned buffers remain valid until 0652 they are no longer referenced. 0653 0654 @par BNF 0655 @code 0656 query-params = query-param *( "&" query-param ) 0657 query-param = key [ "=" value ] 0658 key = *qpchar 0659 value = *( qpchar / "=" ) 0660 @endcode 0661 0662 @par Specification 0663 @li <a href="https://en.wikipedia.org/wiki/Query_string" 0664 >Query string (Wikipedia)</a> 0665 0666 @see 0667 @ref param, 0668 @ref param_view. 0669 */ 0670 struct param_pct_view 0671 { 0672 /** The key 0673 0674 For most usages, key comparisons are 0675 case-sensitive and duplicate keys in 0676 a query are possible. However, it is 0677 the authority that has final control 0678 over how the query is interpreted. 0679 */ 0680 pct_string_view key; 0681 0682 /** The value 0683 0684 The presence of a value is indicated by 0685 @ref has_value equal to true. 0686 An empty value is distinct from no value. 0687 */ 0688 pct_string_view value; 0689 0690 /** True if a value is present 0691 0692 The presence of a value is indicated by 0693 `has_value == true`. 0694 An empty value is distinct from no value. 0695 */ 0696 bool has_value = false; 0697 0698 //-------------------------------------------- 0699 0700 /** Constructor 0701 0702 Default constructed query parameters 0703 have an empty key and no value. 0704 0705 @par Example 0706 @code 0707 param_pct_view qp; 0708 @endcode 0709 0710 @par Postconditions 0711 @code 0712 this->key == "" && this->value == "" && this->has_value == false 0713 @endcode 0714 0715 @par Complexity 0716 Constant. 0717 0718 @par Exception Safety 0719 Throws nothing. 0720 */ 0721 param_pct_view() = default; 0722 0723 /** Constructor 0724 0725 This constructs a parameter with a key 0726 and value, which may both contain percent 0727 escapes. 0728 The new key and value reference 0729 the same corresponding underlying 0730 character buffers. 0731 Ownership of the buffers is not transferred; 0732 the caller is responsible for ensuring that 0733 the assigned buffers remain valid until 0734 they are no longer referenced. 0735 0736 @par Example 0737 @code 0738 param_pct_view qp( "key", "value" ); 0739 @endcode 0740 0741 @par Postconditions 0742 @code 0743 this->key.data() == key.data() && this->value.data() == value.data() && this->has_value == true 0744 @endcode 0745 0746 @par Complexity 0747 Linear in `key.size() + value.size()`. 0748 0749 @par Exception Safety 0750 Exceptions thrown on invalid input. 0751 0752 @throw system_error 0753 `key` or `value` contains an invalid percent-encoding. 0754 0755 @param key The key to set. 0756 @param value The value to set. 0757 */ 0758 param_pct_view( 0759 pct_string_view key, 0760 pct_string_view value) noexcept 0761 : key(key) 0762 , value(value) 0763 , has_value(true) 0764 { 0765 } 0766 0767 /** Constructor 0768 0769 This constructs a parameter with a key 0770 and optional value, which may both 0771 contain percent escapes. 0772 0773 The new key and value reference 0774 the same corresponding underlying 0775 character buffers. 0776 0777 Ownership of the buffers is not transferred; 0778 the caller is responsible for ensuring that 0779 the assigned buffers remain valid until 0780 they are no longer referenced. 0781 0782 @par Example 0783 @code 0784 param_pct_view qp( "key", optional<core::string_view>("value") ); 0785 @endcode 0786 0787 @par Postconditions 0788 @code 0789 this->key.data() == key.data() && this->value->data() == value->data() && this->has_value == true 0790 @endcode 0791 0792 @par Complexity 0793 Linear in `key.size() + value->size()`. 0794 0795 @par Exception Safety 0796 Exceptions thrown on invalid input. 0797 0798 @throw system_error 0799 `key` or `value` contains an invalid percent-encoding. 0800 0801 @tparam OptionalString An optional 0802 `core::string_view` type, such as 0803 `boost::optional<core::string_view>` or 0804 `std::optional<core::string_view>`. 0805 0806 @param key The key to set. 0807 @param value The optional value to set. 0808 @return A param object 0809 */ 0810 template <class OptionalString> 0811 param_pct_view( 0812 pct_string_view key, 0813 OptionalString const& value) 0814 : param_pct_view(key, detail::get_optional_string(value)) 0815 { 0816 } 0817 0818 /** Construction 0819 0820 This converts a param which may 0821 contain unvalidated percent-escapes into 0822 a param whose key and value are 0823 guaranteed to contain strings with no 0824 invalid percent-escapes, otherwise 0825 an exception is thrown. 0826 0827 The new key and value reference 0828 the same corresponding underlying 0829 character buffers. 0830 Ownership of the buffers is not transferred; 0831 the caller is responsible for ensuring that 0832 the assigned buffers remain valid until 0833 they are no longer referenced. 0834 0835 @par Example 0836 @code 0837 param_pct_view qp( param_view( "key", "value" ) ); 0838 @endcode 0839 0840 @par Complexity 0841 Linear in `key.size() + value.size()`. 0842 0843 @par Exception Safety 0844 Exceptions thrown on invalid input. 0845 0846 @throw system_error 0847 `key` or `value` contains an invalid percent escape. 0848 0849 @param p The param to construct from. 0850 */ 0851 explicit 0852 param_pct_view( 0853 param_view const& p) 0854 : key(p.key) 0855 , value(p.has_value 0856 ? pct_string_view(p.value) 0857 : pct_string_view()) 0858 , has_value(p.has_value) 0859 { 0860 } 0861 0862 /** Conversion 0863 0864 This function performs a conversion from 0865 a reference-like query parameter to one 0866 retaining ownership of the strings by 0867 making a copy. 0868 0869 @par Complexity 0870 Linear in `this->key.size() + this->value.size()`. 0871 0872 @par Exception Safety 0873 Calls to allocate may throw. 0874 0875 @return A param object 0876 */ 0877 explicit 0878 operator 0879 param() const 0880 { 0881 return param( 0882 static_cast<std::string>(key), 0883 static_cast<std::string>(value), 0884 has_value); 0885 } 0886 0887 /** Conversion to param_view 0888 0889 This function performs a conversion from 0890 a pct_string_view query parameter to one 0891 using a simple string_view. 0892 0893 @par Exception Safety 0894 Calls to allocate may throw. 0895 0896 @return A param_view object 0897 */ 0898 operator 0899 param_view() const noexcept 0900 { 0901 return param_view( 0902 key, value, has_value); 0903 } 0904 0905 /** Arrow support 0906 0907 This operator returns the address of the 0908 object so that it can be used in pointer 0909 contexts. 0910 0911 @return A pointer to this object 0912 */ 0913 param_pct_view const* 0914 operator->() const noexcept 0915 { 0916 return this; 0917 } 0918 0919 /** Aggregate construction 0920 0921 @param key The key 0922 @param value The value 0923 @param has_value True if a value is present 0924 */ 0925 param_pct_view( 0926 pct_string_view key, 0927 pct_string_view value, 0928 bool has_value) noexcept 0929 : key(key) 0930 , value(has_value 0931 ? value 0932 : pct_string_view()) 0933 , has_value(has_value) 0934 { 0935 } 0936 0937 private: 0938 param_pct_view( 0939 pct_string_view key, 0940 detail::optional_string const& value) 0941 : param_pct_view(key, value.s, value.b) 0942 { 0943 } 0944 }; 0945 0946 //------------------------------------------------ 0947 0948 inline 0949 param& 0950 param:: 0951 operator=( 0952 param_view const& other) 0953 { 0954 // VFALCO operator= assignment 0955 // causes a loss of original capacity: 0956 // https://godbolt.org/z/nYef8445K 0957 // 0958 // key = other.key; 0959 // value = other.value; 0960 0961 // preserve capacity 0962 key.assign( 0963 other.key.data(), 0964 other.key.size()); 0965 value.assign( 0966 other.value.data(), 0967 other.value.size()); 0968 has_value = other.has_value; 0969 return *this; 0970 } 0971 0972 inline 0973 param& 0974 param:: 0975 operator=( 0976 param_pct_view const& other) 0977 { 0978 // preserve capacity 0979 key.assign( 0980 other.key.data(), 0981 other.key.size()); 0982 value.assign( 0983 other.value.data(), 0984 other.value.size()); 0985 has_value = other.has_value; 0986 return *this; 0987 } 0988 0989 } // urls 0990 } // boost 0991 0992 #endif
| [ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
|
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
|