xaizek / dit (License: GPLv3) (since 2018-12-07)
Command-line task keeper that remembers all old values and is meant to combine several orthogonal features to be rather flexible in managing items.
<root> / src / utils / propsRange.hpp (d18291fb66f594976cda4163a8b3c5f25ec859e8) (4,805B) (mode 100644) [raw]
// Copyright (C) 2015 xaizek <xaizek@posteo.net>
//
// This file is part of dit.
//
// dit is free software: you can redistribute it and/or modify
// it under the terms of version 3 of the GNU Affero General Public
// License as published by the Free Software Foundation.
//
// dit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with dit.  If not, see <http://www.gnu.org/licenses/>.

#ifndef DIT__UTILS__PROPSRANGE_HPP__
#define DIT__UTILS__PROPSRANGE_HPP__

#include <algorithm>
#include <iterator>
#include <sstream>
#include <stack>
#include <string>
#include <utility>

#include <boost/property_tree/ptree.hpp>
#include <boost/range/iterator_range.hpp>

namespace detail {

namespace pt = boost::property_tree;

/**
 * @brief Storage for range iterator data.
 */
class propsRangeData
{
public:
    /**
     * @brief Constructs data out of property tree.
     *
     * @param props Property tree to iterate over.
     */
    explicit propsRangeData(pt::ptree &props)
    {
        it.emplace(props.ordered_begin(), props.not_found());
    }

    /**
     * @brief Holds tree visit iterators as begin-end pairs.
     */
    std::stack<
        std::pair<
            pt::ptree::assoc_iterator,
            pt::ptree::assoc_iterator
        >
    > it;
    /**
     * @brief Names of all parent nodes of the current one.
     */
    std::vector<std::string> parents;
    /**
     * @brief Full name of the current node.
     */
    std::string fullName;
};


/**
 * @brief Iterator for property tree items.
 */
class propsIterator
  : public boost::iterator_facade<
        propsIterator,
        std::string,
        std::input_iterator_tag
    >
{
    friend class boost::iterator_core_access;

public:
    /**
     * @brief Constructs empty iterator ("end" iterator).
     */
    propsIterator() : rd(nullptr)
    {
    }

    /**
     * @brief Constructs non-empty iterator.
     *
     * @param rd Pointer to data storage that outlives the iterator.
     */
    propsIterator(propsRangeData *rd) : rd(rd)
    {
        // Special case of empty property tree.
        if (rd->it.top().first == rd->it.top().second) {
            rd->it.pop();
        }
        increment();
    }

private:
    /**
     * @brief Advances to the next property node.
     */
    void increment()
    {
        // Turn into "end" iterator and boil out if visit stack is empty.
        if (rd->it.empty()) {
            *this = propsIterator();
            return;
        }

        // Go down until a leaf.
        while (!rd->it.top().first->second.empty()) {
            pt::ptree::assoc_iterator &it = rd->it.top().first;

            rd->parents.emplace_back(it->first);
            rd->it.emplace(it->second.ordered_begin(), it->second.not_found());
        }

        // Format full name for the property.
        std::ostringstream oss;
        std::copy(rd->parents.cbegin(), rd->parents.cend(),
                  std::ostream_iterator<std::string>(oss, "."));
        rd->fullName = oss.str() + rd->it.top().first->first;

        // Go up until first unfinished level.
        while (!rd->it.empty() && ++rd->it.top().first == rd->it.top().second) {
            rd->it.pop();
            // Parents vector is empty for top-level elements.
            if (!rd->parents.empty()) {
                rd->parents.pop_back();
            }
        }
    }

    /**
     * @brief Checks whether two iterators are equal.
     *
     * @param that Iterator to compare against @c *this.
     *
     * @returns @c true if equal, @c false otherwise.
     */
    bool equal(const propsIterator &that) const
    {
        return rd == that.rd;
    }

    /**
     * @brief Retrieves value of a valid iterator (not "end" iterator).
     *
     * @returns The value.
     */
    std::string & dereference() const
    {
        return rd->fullName;
    }

private:
    /**
     * @brief Pointer to data storage.
     */
    propsRangeData *rd;
};

using propsRangeBase = boost::iterator_range<propsIterator>;

}

/**
 * @brief Range iterator over sorted names of property tree items.
 *
 * Each element is returned in fully qualified form: dot-separated path of
 * nodes, e.g. "root.middle.end".
 */
class propsRange : private detail::propsRangeData, public detail::propsRangeBase
{
public:
    explicit propsRange(boost::property_tree::ptree &props)
        : detail::propsRangeData(props),
          detail::propsRangeBase(detail::propsIterator(this),
                                 detail::propsIterator())
    {}
};

#endif // DIT__UTILS__PROPSRANGE_HPP__
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/dit

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/dit

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master