mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-03 15:35:17 +00:00
committed by
Tomek Mrugalski
parent
8a733f3074
commit
2f6c06c2b7
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2010-2020 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2010-2021 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@@ -128,7 +128,7 @@ Element::setValue(const std::map<std::string, ConstElementPtr>&) {
|
|||||||
|
|
||||||
ConstElementPtr
|
ConstElementPtr
|
||||||
Element::get(const int) const {
|
Element::get(const int) const {
|
||||||
throwTypeError("get(int) called on a non-list Element");
|
throwTypeError("get(int) called on a non-container Element");
|
||||||
}
|
}
|
||||||
|
|
||||||
ElementPtr
|
ElementPtr
|
||||||
@@ -158,7 +158,7 @@ Element::size() const {
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
Element::empty() const {
|
Element::empty() const {
|
||||||
throwTypeError("empty() called on a non-list Element");
|
throwTypeError("empty() called on a non-container Element");
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstElementPtr
|
ConstElementPtr
|
||||||
@@ -215,6 +215,25 @@ bool operator!=(const Element& a, const Element& b) {
|
|||||||
return (!a.equals(b));
|
return (!a.equals(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator<(Element const& a, Element const& b) {
|
||||||
|
if (a.getType() != b.getType()) {
|
||||||
|
isc_throw(BadValue, "cannot compare Elements of different types");
|
||||||
|
}
|
||||||
|
switch (a.getType()) {
|
||||||
|
case Element::integer:
|
||||||
|
return a.intValue() < b.intValue();
|
||||||
|
case Element::real:
|
||||||
|
return a.doubleValue() < b.doubleValue();
|
||||||
|
case Element::boolean:
|
||||||
|
return b.boolValue() || !a.boolValue();
|
||||||
|
case Element::string:
|
||||||
|
return std::strcmp(a.stringValue().c_str(), b.stringValue().c_str()) < 0;
|
||||||
|
}
|
||||||
|
isc_throw(BadValue, "cannot compare Elements of type " <<
|
||||||
|
std::to_string(a.getType()));
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// factory functions
|
// factory functions
|
||||||
//
|
//
|
||||||
@@ -1002,6 +1021,42 @@ ListElement::equals(const Element& other) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ListElement::sort(std::string const& index /* = std::string() */) {
|
||||||
|
if (l.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int const t(l.at(0)->getType());
|
||||||
|
std::function<bool(ElementPtr, ElementPtr)> comparator;
|
||||||
|
if (t == map) {
|
||||||
|
if (index.empty()) {
|
||||||
|
isc_throw(BadValue, "index required when sorting maps");
|
||||||
|
}
|
||||||
|
comparator = [&](ElementPtr const& a, ElementPtr const& b) {
|
||||||
|
ConstElementPtr const& ai(a->get(index));
|
||||||
|
ConstElementPtr const& bi(b->get(index));
|
||||||
|
if (ai && bi) {
|
||||||
|
return *ai < *bi;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
} else if (t == list) {
|
||||||
|
// Nested lists. Not supported.
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// Assume scalars.
|
||||||
|
if (!index.empty()) {
|
||||||
|
isc_throw(BadValue, "index given when sorting scalars?");
|
||||||
|
}
|
||||||
|
comparator = [&](ElementPtr const& a, ElementPtr const& b) {
|
||||||
|
return *a < *b;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(l.begin(), l.end(), comparator);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
MapElement::equals(const Element& other) const {
|
MapElement::equals(const Element& other) const {
|
||||||
if (other.getType() == Element::map) {
|
if (other.getType() == Element::map) {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2010-2020 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2010-2021 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@@ -7,12 +7,16 @@
|
|||||||
#ifndef ISC_DATA_H
|
#ifndef ISC_DATA_H
|
||||||
#define ISC_DATA_H 1
|
#define ISC_DATA_H 1
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
#include <stdexcept>
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
|
|
||||||
namespace isc { namespace data {
|
namespace isc { namespace data {
|
||||||
@@ -263,8 +267,6 @@ public:
|
|||||||
virtual bool setValue(const std::map<std::string, ConstElementPtr>& v);
|
virtual bool setValue(const std::map<std::string, ConstElementPtr>& v);
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Other functions for specific subtypes
|
// Other functions for specific subtypes
|
||||||
|
|
||||||
/// @name ListElement functions
|
/// @name ListElement functions
|
||||||
@@ -352,7 +354,6 @@ public:
|
|||||||
virtual bool find(const std::string& identifier, ConstElementPtr& t) const;
|
virtual bool find(const std::string& identifier, ConstElementPtr& t) const;
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
|
|
||||||
/// @name Factory functions
|
/// @name Factory functions
|
||||||
|
|
||||||
// TODO: should we move all factory functions to a different class
|
// TODO: should we move all factory functions to a different class
|
||||||
@@ -403,7 +404,6 @@ public:
|
|||||||
static ElementPtr createMap(const Position& pos = ZERO_POSITION());
|
static ElementPtr createMap(const Position& pos = ZERO_POSITION());
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
|
|
||||||
/// @name Compound factory functions
|
/// @name Compound factory functions
|
||||||
|
|
||||||
/// @brief These functions will parse the given string (JSON)
|
/// @brief These functions will parse the given string (JSON)
|
||||||
@@ -529,6 +529,50 @@ public:
|
|||||||
/// @return ElementPtr with the data that is parsed.
|
/// @return ElementPtr with the data that is parsed.
|
||||||
static ElementPtr fromWire(const std::string& s);
|
static ElementPtr fromWire(const std::string& s);
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
|
/// @brief Remove all empty maps and lists from this Element and its
|
||||||
|
/// descendants.
|
||||||
|
void removeEmptyContainersRecursively() {
|
||||||
|
if (type_ == list || type_ == map) {
|
||||||
|
size_t s(size());
|
||||||
|
for (size_t i = 0; i < s; ++i) {
|
||||||
|
// Get child.
|
||||||
|
ElementPtr child;
|
||||||
|
if (type_ == list) {
|
||||||
|
child = getNonConst(i);
|
||||||
|
} else if (type_ == map) {
|
||||||
|
std::string const key(get(i)->stringValue());
|
||||||
|
// The ElementPtr - ConstElementPtr disparity between
|
||||||
|
// ListElement and MapElement is forcing a const cast here.
|
||||||
|
// It's undefined behavior to modify it after const casting.
|
||||||
|
// The options are limited. I've tried templating, moving
|
||||||
|
// this function from a member function to free-standing and
|
||||||
|
// taking the Element template as argument. I've tried
|
||||||
|
// making it a virtual function with overriden
|
||||||
|
// implementations in ListElement and MapElement. Nothing
|
||||||
|
// works.
|
||||||
|
child = boost::const_pointer_cast<Element>(get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes no sense to continue for non-container children.
|
||||||
|
if (child->getType() != list && child->getType() != map) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recurse if not empty.
|
||||||
|
if (!child->empty()){
|
||||||
|
child->removeEmptyContainersRecursively();
|
||||||
|
}
|
||||||
|
|
||||||
|
// When returning from recursion, remove if empty.
|
||||||
|
if (child->empty()) {
|
||||||
|
remove(i);
|
||||||
|
--i;
|
||||||
|
--s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Notes: IntElement type is changed to int64_t.
|
/// Notes: IntElement type is changed to int64_t.
|
||||||
@@ -543,8 +587,6 @@ public:
|
|||||||
///
|
///
|
||||||
class IntElement : public Element {
|
class IntElement : public Element {
|
||||||
int64_t i;
|
int64_t i;
|
||||||
private:
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
IntElement(int64_t v, const Position& pos = ZERO_POSITION())
|
IntElement(int64_t v, const Position& pos = ZERO_POSITION())
|
||||||
: Element(integer, pos), i(v) { }
|
: Element(integer, pos), i(v) { }
|
||||||
@@ -641,6 +683,17 @@ public:
|
|||||||
size_t size() const { return (l.size()); }
|
size_t size() const { return (l.size()); }
|
||||||
bool empty() const { return (l.empty()); }
|
bool empty() const { return (l.empty()); }
|
||||||
bool equals(const Element& other) const;
|
bool equals(const Element& other) const;
|
||||||
|
|
||||||
|
/// @brief Sorts the elements inside the list.
|
||||||
|
///
|
||||||
|
/// The list must contain elments of the same type.
|
||||||
|
/// Call with the key by which you want to sort when the list contains maps.
|
||||||
|
/// Nested lists are not supported.
|
||||||
|
/// Call without a parameter when sorting any other type.
|
||||||
|
///
|
||||||
|
/// @param index the key by which you want to sort when the list contains
|
||||||
|
/// maps
|
||||||
|
void sort(std::string const& index = std::string());
|
||||||
};
|
};
|
||||||
|
|
||||||
class MapElement : public Element {
|
class MapElement : public Element {
|
||||||
@@ -668,6 +721,19 @@ public:
|
|||||||
auto found = m.find(s);
|
auto found = m.find(s);
|
||||||
return (found != m.end() ? found->second : ConstElementPtr());
|
return (found != m.end() ? found->second : ConstElementPtr());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Get the i-th element in the map.
|
||||||
|
///
|
||||||
|
/// Useful when required to iterate with an index.
|
||||||
|
///
|
||||||
|
/// @param i the position of the element you want to return
|
||||||
|
/// @return the element at position i
|
||||||
|
ConstElementPtr get(int const i) const override {
|
||||||
|
auto it(m.begin());
|
||||||
|
std::advance(it, i);
|
||||||
|
return create(it->first);
|
||||||
|
}
|
||||||
|
|
||||||
using Element::set;
|
using Element::set;
|
||||||
void set(const std::string& key, ConstElementPtr value);
|
void set(const std::string& key, ConstElementPtr value);
|
||||||
using Element::remove;
|
using Element::remove;
|
||||||
@@ -783,7 +849,6 @@ void prettyPrint(ConstElementPtr element, std::ostream& out,
|
|||||||
std::string prettyPrint(ConstElementPtr element,
|
std::string prettyPrint(ConstElementPtr element,
|
||||||
unsigned indent = 0, unsigned step = 2);
|
unsigned indent = 0, unsigned step = 2);
|
||||||
|
|
||||||
///
|
|
||||||
/// @brief Insert Element::Position as a string into stream.
|
/// @brief Insert Element::Position as a string into stream.
|
||||||
///
|
///
|
||||||
/// This operator converts the @c Element::Position into a string and
|
/// This operator converts the @c Element::Position into a string and
|
||||||
@@ -796,7 +861,6 @@ std::string prettyPrint(ConstElementPtr element,
|
|||||||
/// parameter @c out after the insertion operation.
|
/// parameter @c out after the insertion operation.
|
||||||
std::ostream& operator<<(std::ostream& out, const Element::Position& pos);
|
std::ostream& operator<<(std::ostream& out, const Element::Position& pos);
|
||||||
|
|
||||||
///
|
|
||||||
/// @brief Insert the Element as a string into stream.
|
/// @brief Insert the Element as a string into stream.
|
||||||
///
|
///
|
||||||
/// This method converts the @c ElementPtr into a string with
|
/// This method converts the @c ElementPtr into a string with
|
||||||
@@ -815,7 +879,11 @@ std::ostream& operator<<(std::ostream& out, const Element& e);
|
|||||||
|
|
||||||
bool operator==(const Element& a, const Element& b);
|
bool operator==(const Element& a, const Element& b);
|
||||||
bool operator!=(const Element& a, const Element& b);
|
bool operator!=(const Element& a, const Element& b);
|
||||||
} }
|
bool operator<(const Element& a, const Element& b);
|
||||||
|
|
||||||
|
} // namespace data
|
||||||
|
} // namespace isc
|
||||||
|
|
||||||
#endif // ISC_DATA_H
|
#endif // ISC_DATA_H
|
||||||
|
|
||||||
// Local Variables:
|
// Local Variables:
|
||||||
|
@@ -1390,4 +1390,47 @@ TEST(Element, empty) {
|
|||||||
l->remove(0);
|
l->remove(0);
|
||||||
EXPECT_TRUE(l->empty());
|
EXPECT_TRUE(l->empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Element, sortIntegers) {
|
||||||
|
ElementPtr l(Element::fromJSON("[5, 7, 4, 2, 8, 6, 1, 9, 0, 3]"));
|
||||||
|
ElementPtr expected(Element::fromJSON("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]"));
|
||||||
|
boost::dynamic_pointer_cast<ListElement>(l)->sort();
|
||||||
|
EXPECT_EQ(*l, *expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Element, sortFloatingPoint) {
|
||||||
|
ElementPtr l(Element::fromJSON("[2.1, 3.2, 2.1, 2.2, 4.1, 3.2, 1.1, 4.2, 0.1, 1.2]"));
|
||||||
|
ElementPtr expected(Element::fromJSON("[0.1, 1.1, 1.2, 2.1, 2.1, 2.2, 3.2, 3.2, 4.1, 4.2]"));
|
||||||
|
boost::dynamic_pointer_cast<ListElement>(l)->sort();
|
||||||
|
EXPECT_EQ(*l, *expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Element, sortBooleans) {
|
||||||
|
ElementPtr l(Element::fromJSON("[false, true, false, true]"));
|
||||||
|
ElementPtr expected(Element::fromJSON("[false, false, true, true]"));
|
||||||
|
boost::dynamic_pointer_cast<ListElement>(l)->sort();
|
||||||
|
EXPECT_EQ(*l, *expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Element, sortStrings) {
|
||||||
|
ElementPtr l(Element::fromJSON(R"(["hello", "world", "lorem", "ipsum", "dolor", "sit", "amet"])"));
|
||||||
|
ElementPtr expected(Element::fromJSON(R"(["amet", "dolor", "hello", "ipsum", "lorem", "sit", "world"])"));
|
||||||
|
boost::dynamic_pointer_cast<ListElement>(l)->sort();
|
||||||
|
EXPECT_EQ(*l, *expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Element, sortMaps) {
|
||||||
|
ElementPtr e1(Element::fromJSON(R"({"id": 1, "subnet": "10.0.1.0/24"})"));
|
||||||
|
ElementPtr e2(Element::fromJSON(R"({"id": 2, "subnet": "10.0.1.0/24"})"));
|
||||||
|
ElementPtr l(Element::createList());
|
||||||
|
l->add(e2);
|
||||||
|
l->add(e1);
|
||||||
|
EXPECT_EQ(*l->get(0), *e2);
|
||||||
|
EXPECT_EQ(*l->get(1), *e1);
|
||||||
|
boost::dynamic_pointer_cast<ListElement>(l)->sort("id");
|
||||||
|
ASSERT_EQ(l->size(), 2);
|
||||||
|
EXPECT_EQ(*l->get(0), *e1);
|
||||||
|
EXPECT_EQ(*l->get(1), *e2);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
Reference in New Issue
Block a user