mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 05:55:28 +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
|
||||
// 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
|
||||
Element::get(const int) const {
|
||||
throwTypeError("get(int) called on a non-list Element");
|
||||
throwTypeError("get(int) called on a non-container Element");
|
||||
}
|
||||
|
||||
ElementPtr
|
||||
@@ -158,7 +158,7 @@ Element::size() const {
|
||||
|
||||
bool
|
||||
Element::empty() const {
|
||||
throwTypeError("empty() called on a non-list Element");
|
||||
throwTypeError("empty() called on a non-container Element");
|
||||
}
|
||||
|
||||
ConstElementPtr
|
||||
@@ -215,6 +215,25 @@ bool operator!=(const Element& a, const Element& 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
|
||||
//
|
||||
@@ -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
|
||||
MapElement::equals(const Element& other) const {
|
||||
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
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@@ -7,12 +7,16 @@
|
||||
#ifndef ISC_DATA_H
|
||||
#define ISC_DATA_H 1
|
||||
|
||||
#include <stdint.h>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <exceptions/exceptions.h>
|
||||
|
||||
namespace isc { namespace data {
|
||||
@@ -263,8 +267,6 @@ public:
|
||||
virtual bool setValue(const std::map<std::string, ConstElementPtr>& v);
|
||||
//@}
|
||||
|
||||
|
||||
|
||||
// Other functions for specific subtypes
|
||||
|
||||
/// @name ListElement functions
|
||||
@@ -352,7 +354,6 @@ public:
|
||||
virtual bool find(const std::string& identifier, ConstElementPtr& t) const;
|
||||
//@}
|
||||
|
||||
|
||||
/// @name Factory functions
|
||||
|
||||
// TODO: should we move all factory functions to a different class
|
||||
@@ -403,7 +404,6 @@ public:
|
||||
static ElementPtr createMap(const Position& pos = ZERO_POSITION());
|
||||
//@}
|
||||
|
||||
|
||||
/// @name Compound factory functions
|
||||
|
||||
/// @brief These functions will parse the given string (JSON)
|
||||
@@ -529,6 +529,50 @@ public:
|
||||
/// @return ElementPtr with the data that is parsed.
|
||||
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.
|
||||
@@ -543,8 +587,6 @@ public:
|
||||
///
|
||||
class IntElement : public Element {
|
||||
int64_t i;
|
||||
private:
|
||||
|
||||
public:
|
||||
IntElement(int64_t v, const Position& pos = ZERO_POSITION())
|
||||
: Element(integer, pos), i(v) { }
|
||||
@@ -641,6 +683,17 @@ public:
|
||||
size_t size() const { return (l.size()); }
|
||||
bool empty() const { return (l.empty()); }
|
||||
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 {
|
||||
@@ -668,6 +721,19 @@ public:
|
||||
auto found = m.find(s);
|
||||
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;
|
||||
void set(const std::string& key, ConstElementPtr value);
|
||||
using Element::remove;
|
||||
@@ -783,7 +849,6 @@ void prettyPrint(ConstElementPtr element, std::ostream& out,
|
||||
std::string prettyPrint(ConstElementPtr element,
|
||||
unsigned indent = 0, unsigned step = 2);
|
||||
|
||||
///
|
||||
/// @brief Insert Element::Position as a string into stream.
|
||||
///
|
||||
/// 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.
|
||||
std::ostream& operator<<(std::ostream& out, const Element::Position& pos);
|
||||
|
||||
///
|
||||
/// @brief Insert the Element as a string into stream.
|
||||
///
|
||||
/// 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);
|
||||
|
||||
} // namespace data
|
||||
} // namespace isc
|
||||
|
||||
#endif // ISC_DATA_H
|
||||
|
||||
// Local Variables:
|
||||
|
@@ -1390,4 +1390,47 @@ TEST(Element, empty) {
|
||||
l->remove(0);
|
||||
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