text:
//
// Copyright (c) 2015, Deutsches Forschungszentrum für Künstliche Intelligenz GmbH.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

#pragma once

#include <type_traits>

#include <envire_core/events/GraphEventPublisher.hpp>
#include <envire_core/events/FrameEvents.hpp>
#include <envire_core/events/EdgeEvents.hpp>

#include <boost/graph/filtered_graph.hpp>
#include <boost/graph/copy.hpp>
#include <boost/concept_check.hpp>

#include <envire_core/graph/GraphTypes.hpp>
#include <envire_core/graph/TreeView.hpp>
#include <envire_core/graph/GraphExceptions.hpp>
#include <envire_core/graph/GraphVisitors.hpp>
#include <envire_core/graph/Path.hpp>

namespace envire { namespace core
{

/**A double connected labeled graph structure.
 * Each vertex (frame) is identified by a unique FrameId.
 * Each edge is accompanied by it's inverse edge.
 *
 *
 * Methods Naming convention
 * Overloading boost methods uses _ delimited separated
 * words, new methods use Camel Case separated words
 *
 * @param FRAME_PROP should follow the FramePropertyConcept (see GraphTypes.hpp)
 * @param EDGE_PROP should follow the EdgePropertyConcept concept (see GraphTypes.hpp) */
template <class FRAME_PROP, class EDGE_PROP>
class Graph : public GraphBase<FRAME_PROP, EDGE_PROP>,
              public envire::core::GraphEventPublisher,
              public envire::core::TreeUpdatePublisher
{
public:
     using Base = GraphBase<FRAME_PROP, EDGE_PROP>;
     using vertex_descriptor = GraphTraits::vertex_descriptor;
     using edge_descriptor = GraphTraits::edge_descriptor;
     using EdgePair = std::pair<edge_descriptor, bool>;
     using vertices_size_type = GraphTraits::vertices_size_type;
     using edges_size_type = GraphTraits::edges_size_type;
     using out_edge_iterator = typename Base::out_edge_iterator;
     using vertex_iterator = typename Base::vertex_iterator;
     using edge_iterator = typename Base::edge_iterator;
     using Base::vertex;

    Graph();

    /**Creates a ***deep*** copy of @p other including edge and frame properties.
     * @note The usual boost copy constructors do create shallow copies, this one
     *       is an exception! If you want the shallow copy, call the base class
     *       copy ctor instead.
     * @note Does **not** copy the event subscribers or TreeView update subscribers.
     *       Only the graph data is copied*/
    explicit Graph(const Graph& other);

    /**Adds an unconnected frame to the graph.
    *  The frame property is default constructed and the id is set to @p frame.
    *
    *  Causes FrameAddedEvent.
    *
    *  @throw FrameAlreadyExistsException if the frame already exists
    *  @return A handle to the newly created frame.
    */
    vertex_descriptor addFrame(const FrameId& frame);

    /**Adds an unconnected frame to the graph.
     * The frame property is constructed using @p args and the id is set to
     * @p frame.
     *
     * Causes FrameAddedEvent.
     *
    *  @throw FrameAlreadyExistsException if the frame already exists
    *  @return A handle to the newly created frame.
     */
    template <class... Args>
    vertex_descriptor emplaceFrame(const FrameId&, Args&&... args);

    /**Disconnects @p frame from the Graph.
    *  I.e. all edges from and to @p frame will be removed.
    *
    * Causes EdgeRemovedEvent for each edge that is removed.
    *
    *  @throw UnknownFrameException if the frame does not exist. */
    void disconnectFrame(const FrameId& frame);

    /**Removes @p frame from the Graph.
    *  A frame can only be removed if there are no edges connected to
    *  or coming from that frame.
    *
    *  Causes FrameRemovedEvent.
    *
    *  @throw UnknownFrameException if the frame does not exist.
    *  @throw FrameStillConnectedException if there are still edges
    *                                      coming from or leading to this
    *                                      frame. */
    virtual void removeFrame(const FrameId& frame);

    /** @return the id of the specified @p vertex
     *  @throw NullVertexException if vertex is null_vertex */
    const FrameId& getFrameId(const vertex_descriptor vertex) const;

    /** @return true if this graph contains a frame with id @p frameId, false
     *          otherwise.*/
    bool containsFrame(const FrameId& frameId) const;

    /**@return true if the graph contains a direct edge between @p origin and
     *         @p target.
     * @throw UnknownFrameException if @p origin or @p target are not part of them
     *                              graph.*/
    bool containsEdge(const FrameId& origin, const FrameId& target) const;
    bool containsEdge(const vertex_descriptor origin, const vertex_descriptor target) const;

    /**Add an edge between two frames.
    *  If the frames do not exist they are created.
    *  The inverse edge is created automatically and added as well.
    *
    *  Causes EdgeAddedEvent.
    *  Causes FrameAddedEvent for each frame if it did not exist before.
    *
    *  @param origin Source of the edge.
    *  @param target Target of the edge.
    *  @param edgeProperty The value that should be stored in the edge.
    *  @throw EdgeAlreadyExistsException if the edge already exists
    *  @note The edge will also be added to all subscribed TreeViews
    */
    void add_edge(const vertex_descriptor origin,
                  const vertex_descriptor target,
                  const EDGE_PROP& edgeProperty);

    void add_edge(const FrameId& origin,
                  const FrameId& target,
                  const EDGE_PROP& edgeProperty);

    /**Removes the specified edge from the graph.
    * Also removes the inverse edge.
    * Emits EdgeRemovedEvent for the origin->target edge only.
    * @throw UnknownEdgeException if there is no edge from @p origin to @p target
    **/
    void remove_edge(const vertex_descriptor origin,
                    const vertex_descriptor target);
    /** @throw UnknownFrameException if one of the frames does not exist.*/
    void remove_edge(const FrameId& origin,
                    const FrameId& target);

    /** @return the edge between frame @p origin and @p target
      * @throw UnknownFrameException If @p orign or @p target do not exist.
      * @throw UnknownEdgeException if there is no such edge  */
    edge_descriptor getEdge(const FrameId& origin, const FrameId& target) const;
    edge_descriptor getEdge(const vertex_descriptor origin, const vertex_descriptor target) const;

    /** @throw UnknownFrameException if @p origin or @p target do not exist
     *  @return The edge property of the edge from origin to target*/
    const EDGE_PROP& getEdgeProperty(const FrameId& origin, const FrameId& target) const;
    const EDGE_PROP& getEdgeProperty(const vertex_descriptor origin, const vertex_descriptor target) const;
    const EDGE_PROP& getEdgeProperty(const edge_descriptor edge) const;

    /** @throw UnknownFrameException if @p frame does not exist.*/
    const FRAME_PROP& getFrameProperty(const FrameId& frame) const;

    /** Sets the property of the edge from @p orign to @p target to @p prop.
     *  Sets the property of the edge from @p target to @p origin to prop.inverse().
     *
     *  Causes EdgeModifiedEvent.
     *
     *  @throw UnknownEdgeException If there is no edge between origin and target.
     *  @note There is no setEdgeProperty(edge_descriptor) because it is not trivial
     *        to figure out the inverse edge soley based on the edge_descriptor.*/
    virtual void setEdgeProperty(const vertex_descriptor origin,
                                 const vertex_descriptor target,
                                 const EDGE_PROP& prop);

    /** @throw UnknownFrameException If @p orign or @p target do not exist */
    virtual void setEdgeProperty(const FrameId& origin,
                                 const FrameId& target,
                                 const EDGE_PROP& prop);

    /** @return the source vertex of the @p edge*/
    const vertex_descriptor getSourceVertex(const edge_descriptor edge) const;

    /** @return the target vertex of the @p edge*/
    const vertex_descriptor getTargetVertex(const edge_descriptor edge) const;

    /**Gets the vertex with id @p frameId.
    * @throw UnknownFrameException if the frame does not exist */
    vertex_descriptor getVertex(const FrameId& frameId) const;

    /**Returns a pair of iterators containing all vertices  */
    std::pair<vertex_iterator, vertex_iterator>
    getVertices() const;

    /** Returns a pair of iterators containing all edges*/
    std::pair<edge_iterator, edge_iterator>
    getEdges() const;

    /**Builds a TreeView containing all vertices that are accessible starting
      * from @p root.
      * @note The tree is ***not** updated when the Graph changes. */
    TreeView getTree(const vertex_descriptor root) const;

    /**Builds a TreeView containing all vertices that are accessible starting
      * from @p rootId.
      * @note The tree is ***not** updated when the Graph changes.
      * @throw UnknownFrameException if the frame does not exist */
    TreeView getTree(const FrameId rootId) const;
    void getTree(const FrameId rootId, TreeView* outView) const;
    void getTree(const vertex_descriptor root, TreeView* outView) const;

    /**Builds a TreeView containing all vertices that are accessible starting
      * from @p root and writes it to @p outView.
      * if @p keepTreeUpdated is true, the Graph will retain a pointer
      * to @p outView and update it whenever edges are added or removed.
      * If the TreeView is destroyed it will automatically unsubscribe from
      * the graph. The view can also be unsubscribed manually by calling
      * unsubscribeTreeView()*/
    void getTree(const vertex_descriptor root, const bool keepTreeUpdated, TreeView* outView);
    void getTree(const FrameId rootId, const bool keepTreeUpdated, TreeView* outView);

    /**Unsubscribe @p view from TreeView updates */
    virtual void unsubscribeTreeView(TreeView* view);

    /**Subscribe @p view to TreeView updates.
      * @note Make sure that the tree is not out of sync already when
      *       subscribing*/
    virtual void subscribeTreeView(TreeView* view);

    /**Returns all frames on the shortest path from @p origin to @p target.
     * Returns an empty vector if no path exists.
     * @throw UnknownFrameException if @p origin or @p target don't exist */
    std::vector<FrameId> getFrames(FrameId origin, FrameId target) const;

    /**Returns the shortest path from @p origin to @p target.
     * Returns an empty path if no path exists.
     *
     * @throw UnknownFrameException if @p origin or @p target don't exist.
     * @param autoUpdating If true, an auto updating path will be returned.
     *                     I.e. a path that is subscribed to graph and
     *                     notices when an edge on the path is removed*/
    Path::Ptr getPath(const FrameId& origin, const FrameId& target,
                                  const bool autoUpdating);

    /** @return number of frames in this graph*/
    vertices_size_type num_vertices() const;

    /** @return number of edges in this graph*/
    edges_size_type num_edges() const;

    /**
     * A vertex_descriptor that does not point to any vertex.
     * "A nullptr for vertices" */
    static vertex_descriptor null_vertex();

    /** Visit Graph in bfs order.
     * This is a wrapper around boost::breadth_first_search that correctly
     * parameterizes boost::breadth_first_search to work with this graph. */
    template <class VISITOR>
    void breadthFirstSearch(const vertex_descriptor root, VISITOR visitor) const;

    /** Visit Graph in bfs order.
     * This is a wrapper around boost::breadth_first_search that correctly
     * parameterizes boost::breadth_first_search to work with this graph. */
    template <class GRAPH, class VISITOR>
    void breadthFirstSearch(GRAPH& graph, const vertex_descriptor root, VISITOR visitor) const;

    template <class VISITOR>
    EIGEN_DEPRECATED
    void breathFirstSearch(const vertex_descriptor root, VISITOR visitor) const { breadthFirstSearch(root, visitor); }

    template <class GRAPH, class VISITOR>
    EIGEN_DEPRECATED
    void breathFirstSearch(GRAPH& graph, const vertex_descriptor root, VISITOR visitor) const { breadthFirstSearch(graph, root, visitor); }

    /** Visits all vertices in no particular order. The visitation order is
     * @p visitor can be anything that is callable and takes a vertex_descriptor as parameter. I.e. that fulfills visitor(GraphTraits::vertex_descriptor)*/
    template <class VISITOR>
    void visitVertices(VISITOR visitor);

protected:
    using map_type = typename GraphBase<FRAME_PROP, EDGE_PROP>::map_type;
    using GraphBase<FRAME_PROP, EDGE_PROP>::graph;
    using GraphBase<FRAME_PROP, EDGE_PROP>::_map;
    /**@brief Add a vertex
    * @note the frame's name must be unique. */
    vertex_descriptor add_vertex(const FrameId& frameId, const FRAME_PROP& frame);

    /**Update all subscribed TreeViews with the new edge.
    * @note When adding a new edge the back-edge is added to the graph
    *       as well. I.e. for each edge two edges are added to the graph.
    *       However only one of the edges should be added to the tree,
    *       because the tree does not care about edge direction.
    *       It does not matter whether you add the back-edge or the edge
    *       to the tree as long as you do ***not*** add both.
    */
    void addEdgeToTreeViews(edge_descriptor newEdge) const;
    void addEdgeToTreeView(edge_descriptor newEdge, TreeView* view) const;

    void removeEdgeFromTreeViews(vertex_descriptor origin, vertex_descriptor target) const;

    /**Rebuild all subscribed TreeViews */
    void rebuildTreeViews() const;

    /**Removes the specified edge.*/
    void remove_edge(const FrameId& origin, const FrameId& target,
                     const vertex_descriptor originDesc,
                     const vertex_descriptor targetDesc);

    /**
     * @brief Publishes the current state of the graph.
     */
    virtual void publishCurrentState(GraphEventSubscriber* pSubscriber);

    /**
     * @brief Unpublishes the current state of the graph.
     *        Basically the reverse process of publishCurrentState
     */
    virtual void unpublishCurrentState(GraphEventSubscriber* pSubscriber);

    /**Re-generates the content of _map based on the FrameIds.
     * This method is used when de-serializing or copying the graph.*/
    void regenerateLabelMap();

    /**TreeViews that need to be updated when the graph is modified */
    std::vector<TreeView*> subscribedTreeViews;

private:
    /**Grants access to boost serialization */
    friend class boost::serialization::access;

    /**Unserializes this class*/
    template <typename Archive>
    void load(Archive &ar, const unsigned int version);

    /**Serializes this class. Only the directed graph is serialized,
      * subscribers are excluded and the mapping of the labeled graph
      * is regenerated. */
    template <typename Archive>
    void save(Archive &ar, const unsigned int version) const;

    /**Splits boost serialize into a load and save method */
    template <typename Archive>
    void serialize(Archive &ar, const unsigned int version);
};

template <class F, class E>
Graph<F,E>::Graph()
{
    /**These asserts are important because:
     *   * we are handing out vertex_descriptor and edge_descriptor to the user.
     *     If the underlying datatype is a random-access container the vertex_descriptors
     *     could be invalidated each time a vertex/edge is added or removed.
     *   * The null_vertex() definition is different for random_access containers
     *     The definition provided in GraphTypes only works for pointer based containers.*/
    static_assert(std::is_same<typename GraphBase<F, E>::graph_type::graph_type::graph_type::vertex_list_selector, boost::listS>::value,
                  "vertex list type should be listS to ensure that vertex_descriptors remain valid");
    static_assert(std::is_same<typename GraphBase<F, E>::graph_type::graph_type::graph_type::edge_list_selector, boost::listS>::value,
                  "edge list type should be listS to ensure that vertex_descriptors remain valid");

    BOOST_CONCEPT_ASSERT((EdgePropertyConcept<E>));
    BOOST_CONCEPT_ASSERT((FramePropertyConcept<F>));
}

template <class F, class E>
Graph<F,E>::Graph(const Graph<F, E>& other) : Base()
{
  //NOTE: we are explicitly avoiding calling any copy constructor because boost
  //      graphs are not deep copied by default. To achieve deep copies
  //      we should use boost::copy_graph but copy_graph does not work for
  //      boost_labeled graph because labeled_graph does not define an
  //      add_vertex(G) method.
  //
  //      Therefore, we use copy_graph to copy the graph structure and add the
  //      labels manually

  //copy structure from other into the base directed_graph
  boost::copy_graph(other, graph());
  //copy the labels
  regenerateLabelMap();
}

template <class F, class E>
typename Graph<F,E>::vertex_descriptor Graph<F,E>::addFrame(const FrameId& frame)
{
    vertex_descriptor desc = vertex(frame);
    if(desc == null_vertex())
    {
        F frameProp;
        frameProp.setId(frame);
        desc = add_vertex(frame, frameProp);
    }
    else
    {
      throw FrameAlreadyExistsException(frame);
    }
    return desc;
}

template <class F, class E>
typename Graph<F,E>::vertex_descriptor Graph<F,E>::add_vertex(const FrameId& frameId,
                                                              const F& frame)
{
    vertex_descriptor v = GraphBase<F, E>::add_vertex(frameId, frame);
    notify(FrameAddedEvent(frameId));
    return v;
}

template <class F, class E>
typename Graph<F,E>::edge_descriptor Graph<F,E>::getEdge(const FrameId& origin,
                                                         const FrameId& target) const
{
    vertex_descriptor originDesc = getVertex(origin); //throws
    vertex_descriptor targetDesc = getVertex(target);
    return getEdge(originDesc, targetDesc);
}

template <class F, class E>
typename Graph<F,E>::edge_descriptor Graph<F,E>::getEdge(const vertex_descriptor origin,
                                                         const vertex_descriptor target) const
{
    EdgePair e = boost::edge(origin, target, graph());
    if(!e.second)
    {
        throw UnknownEdgeException(getFrameId(origin), getFrameId(target));
    }
    return e.first;
}

template <class F, class E>
const FrameId& Graph<F,E>::getFrameId(const vertex_descriptor vertex) const
{
    if(vertex == GraphTraits::null_vertex())
      throw NullVertexException();
    return graph()[vertex].getId();
}

template <class F, class E>
typename Graph<F,E>::vertex_descriptor Graph<F,E>::getVertex(const FrameId& frame) const
{
    vertex_descriptor desc = vertex(frame);
    if(desc == null_vertex())
    {
        throw UnknownFrameException(frame);
    }
    return desc;
}

template <class F, class E>
void Graph<F,E>::disconnectFrame(const FrameId& frame)
{
    const vertex_descriptor frameDesc = getVertex(frame);

    out_edge_iterator begin, end;

    //removing an edge invalidates the out_edge_iterators, thus the strange
    //loop structure :-)
    do
    {
        boost::tie(begin, end) = boost::out_edges(frameDesc, *this);
        if(begin != end)
        {
            const vertex_descriptor target = boost::target(*begin, *this);
            remove_edge(frameDesc, target); //this will remove the inv edge as well.
        }
    } while(begin != end);
}

template <class F, class E>
void Graph<F,E>::removeFrame(const FrameId& frame)
{
    vertex_descriptor desc = getVertex(frame); //will throw
    if(boost::degree(desc, *this) > 0)
    {
        throw FrameStillConnectedException(frame);
    }

    boost::remove_vertex(desc, graph());//If the HACK is removed, remove_vertex needs to be called with frame as first parameter
    //HACK this is a workaround for bug https://svn.boost.org/trac/boost/ticket/9493
    //If the bug is fixed also remove the #define private protected in GraphTypes.hpp
    typename map_type::iterator it = _map.find(frame);
    if(it != _map.end())
    {
        _map.erase(it);
    }
    notify(envire::core::FrameRemovedEvent(frame));
}

template <class F, class E>
envire::core::TreeView Graph<F,E>::getTree(const vertex_descriptor root) const
{
    TreeView view(root);
    getTree(root, &view);
    return std::move(view);
}

template <class F, class E>
envire::core::TreeView Graph<F,E>::getTree(const FrameId rootId) const
{
    const vertex_descriptor root = getVertex(rootId);
    return getTree(root);
}

template <class F, class E>
void Graph<F,E>::getTree(const FrameId rootId, TreeView* outView) const
{
    vertex_descriptor root = getVertex(rootId);
    getTree(root, outView);
}

template <class F, class E>
void Graph<F,E>::getTree(const vertex_descriptor root, const bool keepTreeUpdated, TreeView* outView)
{
    getTree(root, outView);
    if(keepTreeUpdated)
    {
      subscribeTreeView(outView);
    }
}

template <class F, class E>
void Graph<F,E>::getTree(const FrameId rootId, const bool keepTreeUpdated, TreeView* outView)
{
    const vertex_descriptor root = getVertex(rootId);
    getTree(root, keepTreeUpdated, outView);
}

template <class F, class E>
void Graph<F,E>::getTree(const vertex_descriptor root, TreeView* outView) const
{
    outView->addRoot(root);
    TreeBuilderVisitor<Graph<F,E>> visitor(*outView, *this);
    breadthFirstSearch(root, boost::visitor(visitor));
}

template <class F, class E>
void Graph<F,E>::unsubscribeTreeView(TreeView* view)
{
    subscribedTreeViews.erase(std::remove(subscribedTreeViews.begin(),
                                          subscribedTreeViews.end(), view),
                              subscribedTreeViews.end());
}

template <class F, class E>
void Graph<F,E>::subscribeTreeView(TreeView* view)
{
    assert(view != nullptr);
    subscribedTreeViews.push_back(view);
    view->setPublisher(this); //now the TreeView will automatically unsubscribe on destruction
}

template <class F, class E>
std::vector<FrameId> Graph<F,E>::getFrames(FrameId origin, FrameId target) const
{
    vertex_descriptor fromDesc = getVertex(origin); //may throw
    vertex_descriptor toDesc = getVertex(target); //may throw

    std::vector<FrameId> path;
    envire::core::GraphBFSVisitor <vertex_descriptor>visit(toDesc, this->graph());
    try
    {
        breadthFirstSearch(fromDesc, boost::visitor(visit));

    }catch(const FoundFrameException &e)
    {
        typename std::deque<vertex_descriptor>::iterator it = visit.tree->begin();
        for (; (it+1) != visit.tree->end(); ++it)
        {
            path.push_back(getFrameId(*it));
        }
    }
    if(path.size() > 0)
    {
        path.push_back(target);
    }
    //return is fine, compiler will detect this and move instead of copy
    return path;
}

template <class F, class E>
const typename Graph<F,E>::vertex_descriptor Graph<F,E>::getSourceVertex(const edge_descriptor edge) const
{
    return boost::source(edge, graph());
}

template <class F, class E>
const typename Graph<F,E>::vertex_descriptor Graph<F,E>::getTargetVertex(const edge_descriptor edge) const
{
    return boost::target(edge, graph());
}

template <class F, class E>
void Graph<F,E>::add_edge(const vertex_descriptor origin,
                          const vertex_descriptor target,
                          const E& edgeProperty)
{
    //check if an edge already exists
    //If a->b exists, b->a also exist. Therefore we need to check only one direction
    EdgePair e = boost::edge(origin, target, *this);
    if(e.second)// edge already exists
    {
        throw EdgeAlreadyExistsException(getFrameId(origin), getFrameId(target));
    }

    EdgePair edge_pair =  boost::add_edge(origin, target, edgeProperty, *this);
    EdgePair edge_pair_inv =  boost::add_edge(target, origin, edgeProperty.inverse(), *this);
    assert(edge_pair_inv.second);//origin->target has already been checkd before

    //note: we only need to add one of the edges to the tree, because the tree
    //      does not care about the edge direction.
    //      In fact: if we add both, both will end up in the cross edges list
    //      which might lead to infinite recursion when updating edges
    addEdgeToTreeViews(edge_pair.first);
    notify(envire::core::EdgeAddedEvent(getFrameId(origin), getFrameId(target), edge_pair.first));
}

template <class F, class E>
void Graph<F,E>::add_edge(const FrameId& origin,
                          const FrameId& target,
                          const E& edgeProperty)
{
    vertex_descriptor originDesc = vertex(origin);
    vertex_descriptor targetDesc = vertex(target);

      //if they don't exist create them
    if(originDesc == null_vertex())
    {
        F originProp;
        originProp.setId(origin);
        originDesc = add_vertex(origin, originProp);
    }
    if(targetDesc == null_vertex())
    {
        F targetProp;
        targetProp.setId(target);
        targetDesc = add_vertex(target, targetProp);
    }

    return add_edge(originDesc, targetDesc, edgeProperty);
}

template <class F, class E>
void Graph<F,E>::remove_edge(const FrameId& origin, const FrameId& target,
                             const vertex_descriptor originDesc,
                             const vertex_descriptor targetDesc)
{
    //note: do not use boost::edge_by_label as it will segfault if one of the
    //frames is not part of the tree.
    EdgePair originToTarget = boost::edge(originDesc, targetDesc, graph());
    EdgePair targetToOrigin = boost::edge(targetDesc, originDesc, graph());
    if(!originToTarget.second || !targetToOrigin.second)
    {
        throw UnknownEdgeException(origin, target);
    }

    boost::remove_edge(originToTarget.first, *this);
    notify(envire::core::EdgeRemovedEvent(origin, target));

    boost::remove_edge(targetToOrigin.first, *this);

    removeEdgeFromTreeViews(originDesc, targetDesc);

}

template <class F, class E>
void Graph<F,E>::remove_edge(const vertex_descriptor origin,
                             const vertex_descriptor target)
{
    remove_edge(getFrameId(origin), getFrameId(target), origin, target);
}

template <class F, class E>
void Graph<F,E>::remove_edge(const FrameId& origin,
                             const FrameId& target)
{
    remove_edge(origin, target, getVertex(origin), getVertex(target));
}

template <class F, class E>
void Graph<F,E>::removeEdgeFromTreeViews(vertex_descriptor origin, vertex_descriptor target) const
{
    for(TreeView* view : subscribedTreeViews)
    {
        if(view->edgeExists(origin, target))
            view->removeEdge(origin, target);
    }
}

template <class F, class E>
void Graph<F,E>::addEdgeToTreeViews(edge_descriptor newEdge) const
{
    for(TreeView* view : subscribedTreeViews)
    {
        addEdgeToTreeView(newEdge, view);
    }
}

template <class F, class E>
void Graph<F,E>::addEdgeToTreeView(edge_descriptor newEdge, TreeView* view) const
{

    //We only need to add the edge to the tree, if one of the two vertices is already part
    //of the tree

    const vertex_descriptor src = getSourceVertex(newEdge);
    const vertex_descriptor tar = getTargetVertex(newEdge);
    const bool srcInView = view->vertexExists(src);
    const bool tarInView = view->vertexExists(tar);

    vertex_descriptor inView = null_vertex(); //the vertex that is already in the tree
    vertex_descriptor notInView = null_vertex(); //the vertex that is not yet in the tree

    //this is either a cross- or back-edge
    if(srcInView && tarInView)
    {
        if(!view->edgeExists(src, tar))
        {
            //if both vertices are in the tree but there is no edge between them
            //this is a cross edge
            view->addCrossEdge(src, tar, newEdge);
            return;
        }
        else
        {
            //otherwise it's a back-edge that can be ignored
            return;
        }
    }
    else if(srcInView && !tarInView)
    {
        inView = src;
        notInView = tar;
    }
    else if(tarInView && !srcInView)
    {
        inView = tar;
        notInView = src;
    }
    else if(!srcInView && !tarInView)
    {
        //an edge was added to a different subtree that is not connected to this one
        return;
    }
    else
    {
        //this should never happen if the above logic is correct
        assert(false);
    }

    view->addEdge(inView, notInView);

    //there might be a whole tree connected to notInView which should
    //be added to the tree
    //There are exactly two edges connected to the new vertex if it is only
    //connected to 'inView'.
    //If there are more edges we need to follow them and add the whole graph
    if(boost::degree(notInView, graph()) > 2)
    {
        /* (1) Create a filtered graph that does not contain the edges between
        *     inView and notInView
        * (2) Build a bfs tree starting from notInView on the filtered graph
        * (3) merge the two trees
        */

        //the two edges that should be filtered
        const FrameId id1 = getFrameId(inView);
        const FrameId id2 = getFrameId(notInView);

        //create a filter that can be used to hide the two edges from the graph
        EdgeFilter filter;
        filter.edge1 = getEdge(id1, id2);
        filter.edge2 = getEdge(id2, id1);

        //everything in this graph will be visible except edge1 and edge2
        boost::filtered_graph<const Graph<F,E>, EdgeFilter> fg(*this, filter);

        //use TreeBuilderVisitor to generate a new tree starting from notInView.
        //This tree will only contain vertices that are part of the sub tree that
        //below to notInView because the edges leading to inView are hidden by the filter
        //and thus the bfs will not follow those edges.
        //the visitor will add those edges directly to the view
        TreeBuilderVisitor<Graph<F,E>> visitor(*view, *this);
        breadthFirstSearch(fg, notInView, boost::visitor(visitor));
    }
}

template <class F, class E>
void Graph<F,E>::rebuildTreeViews() const
{
    for(TreeView* view : subscribedTreeViews)
    {
        view->clear();
        getTree(view->root, view);
    }
}

template <class F, class E>
typename Graph<F,E>::vertices_size_type Graph<F,E>::num_vertices() const
{
    return boost::num_vertices(*this);
}

template <class F, class E>
typename Graph<F,E>::edges_size_type Graph<F,E>::num_edges() const
{
    return boost::num_edges(*this);
}

template <class F, class E>
const E& Graph<F,E>::getEdgeProperty(const FrameId& origin, const FrameId& target) const
{
    const edge_descriptor edge = getEdge(origin, target);
    return getEdgeProperty(edge);
}

template <class F, class E>
const E& Graph<F,E>::getEdgeProperty(const vertex_descriptor origin, const vertex_descriptor target) const
{
    const edge_descriptor edge = getEdge(origin, target);
    return getEdgeProperty(edge);
}

template <class F, class E>
const E& Graph<F,E>::getEdgeProperty(const edge_descriptor edge) const
{
  return (*this)[edge];
}

template <class F, class E>
void Graph<F,E>::setEdgeProperty(const FrameId& origin,
                                 const FrameId& target,
                                 const E& prop)
{
    const vertex_descriptor originDesc = getVertex(origin);
    const vertex_descriptor targetDesc = getVertex(target);
    setEdgeProperty(originDesc, targetDesc, prop);
}

template <class F, class E>
void Graph<F,E>::setEdgeProperty(const vertex_descriptor origin,
                                 const vertex_descriptor target,
                                 const E& prop)
{
    EdgePair originToTarget = boost::edge(origin, target, *this);

    if(!originToTarget.second)
    {
      throw UnknownEdgeException(getFrameId(origin), getFrameId(target));
    }
    (*this)[originToTarget.first] = prop;

    EdgePair targetToOrigin = boost::edge(target, origin, *this);
    assert(targetToOrigin.second); //there should always be an inverse edge
    (*this)[targetToOrigin.first] = prop.inverse();

    notify(EdgeModifiedEvent(getFrameId(origin), getFrameId(target), originToTarget.first, targetToOrigin.first));
}

template <class F, class E>
typename Graph<F,E>::vertex_descriptor Graph<F,E>::null_vertex()
{
    return GraphTraits::null_vertex();
}

template <class F, class E>
template <class... Args>
typename Graph<F,E>::vertex_descriptor Graph<F,E>::emplaceFrame(const FrameId& frame,
                                                                Args&&... args)
{
    vertex_descriptor desc = vertex(frame);
    if(desc == null_vertex())
    {
        F frameProp(std::forward<Args>(args)...);
        frameProp.setId(frame);
        desc = add_vertex(frame, frameProp);
    }
    else
    {
      throw FrameAlreadyExistsException(frame);
    }
    return desc;
}

template <class F, class E>
const F& Graph<F,E>::getFrameProperty(const FrameId& frame) const
{
    vertex_descriptor v = getVertex(frame);
    return graph()[v];
}

template <class F, class E>
void Graph<F,E>::publishCurrentState(GraphEventSubscriber* pSubscriber)
{
    // publish frames
    typename boost::graph_traits<Graph<F,E>>::vertex_iterator vertex_it, vertex_end;
    for (boost::tie( vertex_it, vertex_end ) = boost::vertices( graph() ); vertex_it != vertex_end; ++vertex_it)
    {
        notifySubscriber(pSubscriber, FrameAddedEvent(getFrameId(*vertex_it)));
    }

    // publish edges
    typename boost::graph_traits<Graph<F,E>>::edge_iterator edge_it, edge_end;
    std::vector<edge_descriptor> published_edges;
    for (boost::tie( edge_it, edge_end ) = boost::edges( graph() ); edge_it != edge_end; ++edge_it)
    {
        std::vector<edge_descriptor>::const_iterator it = std::find(published_edges.begin(), published_edges.end(), *edge_it);
        if(it == published_edges.end())
        {
            const vertex_descriptor src = getSourceVertex(*edge_it);
            const vertex_descriptor tar = getTargetVertex(*edge_it);
            notifySubscriber(pSubscriber, EdgeAddedEvent(getFrameId(src), getFrameId(tar), *edge_it));

            // save inverse edge in order to not send it twice
            published_edges.push_back(getEdge(tar, src));
        }
    }
}

template <class F, class E>
void Graph<F,E>::unpublishCurrentState(GraphEventSubscriber* pSubscriber)
{
    // unpublish edges
    typename boost::graph_traits<Graph<F,E>>::edge_iterator edge_it, edge_end;
    std::vector<edge_descriptor> unpublished_edges;
    for (boost::tie( edge_it, edge_end ) = boost::edges( graph() ); edge_it != edge_end; ++edge_it)
    {
        std::vector<edge_descriptor>::const_iterator it = std::find(unpublished_edges.begin(), unpublished_edges.end(), *edge_it);
        if(it == unpublished_edges.end())
        {
            const vertex_descriptor src = getSourceVertex(*edge_it);
            const vertex_descriptor tar = getTargetVertex(*edge_it);
            notifySubscriber(pSubscriber, EdgeRemovedEvent(getFrameId(src), getFrameId(tar)));

            // save inverse edge in order to not send it twice
            unpublished_edges.push_back(getEdge(tar, src));
        }
    }

    // unpublish frames
    typename boost::graph_traits<Graph<F,E>>::vertex_iterator vertex_it, vertex_end;
    for (boost::tie( vertex_it, vertex_end ) = boost::vertices( graph() ); vertex_it != vertex_end; ++vertex_it)
    {
        notifySubscriber(pSubscriber, FrameRemovedEvent(getFrameId(*vertex_it)));
    }
}

template<class F, class E>
template <typename Archive>
void Graph<F,E>::load(Archive &ar, const unsigned int version)
{
    ar >> boost::serialization::make_nvp("directed_graph",  graph());

    // regenerate mapping of the labeled graph
    regenerateLabelMap();
}

template<class F, class E>
template <typename Archive>
void Graph<F,E>::save(Archive &ar, const unsigned int version) const
{
    ar << boost::serialization::make_nvp("directed_graph", graph());
}

template<class F, class E>
template <typename Archive>
void Graph<F,E>::serialize(Archive &ar, const unsigned int version)
{
    boost::serialization::split_member(ar, *this, version);
}

template<class F, class E>
std::pair<typename Graph<F,E>::vertex_iterator, typename Graph<F,E>::vertex_iterator>
Graph<F,E>::getVertices() const
{
  return boost::vertices(*this);
}

template<class F, class E>
bool Graph<F,E>::containsFrame(const FrameId& frameId) const
{
    const vertex_descriptor v = Base::vertex(frameId);
    return v != null_vertex();
}

template<class F, class E>
void Graph<F,E>::regenerateLabelMap()
{
    typename boost::graph_traits<Graph<F,E>>::vertex_iterator it, end;
    for (boost::tie( it, end ) = boost::vertices( graph()); it != end; ++it)
    {
        const FrameId id = getFrameId(*it);
        _map[id] = *it;
    }
}

template<class F, class E>
std::pair<typename Graph<F,E>::edge_iterator, typename Graph<F,E>::edge_iterator>
Graph<F,E>::getEdges() const
{
    return boost::edges(graph());
}

template<class F, class E>
bool Graph<F,E>::containsEdge(const FrameId& origin, const FrameId& target) const
{
    return containsEdge(getVertex(origin), getVertex(target));
}

template<class F, class E>
bool Graph<F,E>::containsEdge(const vertex_descriptor origin, const vertex_descriptor target) const
{
    EdgePair e = boost::edge(origin, target, graph());
    return e.second;
}

template<class F, class E>
Path::Ptr Graph<F,E>::getPath(const FrameId& origin, const FrameId& target,
                                          const bool autoUpdating)
{
    if(autoUpdating)
    {
        return Path::Ptr(new Path(getFrames(origin, target), this));
    }
    else
    {
        return Path::Ptr(new Path(getFrames(origin, target)));
    }
}

template<class F, class E>
template<class VISITOR>
void Graph<F,E>::breadthFirstSearch(vertex_descriptor root, VISITOR visitor) const
{
    breadthFirstSearch(graph(), root, visitor);
}

template<class F, class E>
template <class GRAPH, class VISITOR>
void Graph<F,E>::breadthFirstSearch(GRAPH& graph, const vertex_descriptor root, VISITOR visitor) const
{
    // breadth first search uses a std::vector of default_color_type as default,
    // which is fine for graphs using boost::vecS. Since we are using listS,
    // we need to provide a colormap:
    std::unordered_map<vertex_descriptor, boost::default_color_type> colors;
    auto colorMap = boost::make_assoc_property_map(colors);
    // the alternative would be to provide a custom vertex_index_map which maps
    // each vertex to an integer in [0, num_vertices(g))

    boost::breadth_first_search(graph, root, visitor.color_map(colorMap));
}

template<class F, class E>
template <class VISITOR>
void Graph<F,E>::visitVertices(VISITOR visitor)
{
    auto vertices =  getVertices();
    for (auto vert = vertices.first; vert != vertices.second; ++vert){
        visitor(*vert);
    }
}

}}


text:
#include <fstream>
#include <iostream>
#include <vector>

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/adjacency_matrix.hpp>
#include <boost/graph/random.hpp>

#include "range.hpp"

using namespace std;

template<class Graph, class Generator>
Graph random_graph(int n, double p, Generator generator) {
  Graph g(n);
  bernoulli_distribution distribution(p);
  auto trial = bind(distribution, generator);
  for(int i = 0; i < n; ++i)
    for(int j = i + 1; j < n; ++j)
      if(trial()) add_edge(i, j, g);
  return g;
}

class Timing {
  timespec c_start, c_end;
  clockid_t c_id;
  vector<time_t> times;
public:
  Timing(clockid_t id) : c_id(id) {}
  void start() {
    clock_gettime(c_id, &c_start);
  }
  void stop() {
    clock_gettime(c_id, &c_end);
  }
  void tick() {
    stop();
    times.push_back(elapsed());
    start();
  }
  time_t begin() const {
    return c_start.tv_sec * 1e9 + c_start.tv_nsec;
  }
  time_t end() const {
    return c_end.tv_sec * 1e9 + c_end.tv_nsec;
  }
  time_t elapsed() const {
    return end() - begin();
  }
  const vector<time_t>& elems() {
    return times;
  }
  void clear() {
    times.clear();
  }
};

template <class Graph>
void test(string name) {
  default_random_engine gen;
  ofstream file(name);
  Timing t(CLOCK_PROCESS_CPUTIME_ID);
  for(int n = 100; n <= 1000; n += 100) {
    cerr << n << endl;
    for(int j = 0; j < 100; ++j) {
      auto g = random_graph<Graph>(n, 0.1, gen);

      int foo = 0;
      t.start();
      for(auto v : range(vertices(g))) ++foo;
      t.stop();
      auto t1 = t.elapsed();

      t.start();
      for(auto e : range(edges(g))) --foo;
      t.stop();
      auto t2 = t.elapsed();

      for(int i = 0; i < 10; ++i) {
        auto v = random_vertex(g, gen);
        auto w = random_vertex(g, gen);

        t.start();
        auto e = edge(v, w, g);
        t.tick();
        auto d = out_degree(v, g);
        t.tick();
        for(auto x : range(adjacent_vertices(v, g))) --d;
        t.tick();

        if(e.second) remove_edge(v, w, g);

        t.start();
        add_edge(v, w, g);
        t.tick();
        remove_edge(v, w, g);
        t.tick();

        if(e.second) add_edge(v, w, g);

        t.start();
        source(e.first, g);
        t.tick();
        target(e.first, g);
        t.tick();

        assert(d == 0 && foo < 0);

        file << n << ' ' << t1 << ' ' << t2 << ' ';
        for(auto x : t.elems()) file << x << ' ';
        file << endl;
        t.clear();
      }
    }
  }
  file.close();
}

int main() {
  using namespace boost;
  test<adjacency_list<vecS, vecS, undirectedS>>("vec.txt");
  test<adjacency_list<listS, vecS, undirectedS>>("list.txt");
  test<adjacency_list<slistS, vecS, undirectedS>>("slist.txt");
  test<adjacency_list<setS, vecS, undirectedS>>("set.txt");
  test<adjacency_list<multisetS, vecS, undirectedS>>("multiset.txt");
  test<adjacency_list<hash_setS, vecS, undirectedS>>("hashset.txt");
  test<adjacency_matrix<undirectedS>>("matrix.txt");
  return 0;
}

ext: cpp

max_stars_repo_name: arekolek/MaxIST

max_issues_repo_name: arekolek/MaxIST

max_forks_repo_name: arekolek/MaxIST


text:
// Copyright 2010-2014 RethinkDB, all rights reserved.
#ifndef HTTP_HTTP_HPP_
#define HTTP_HTTP_HPP_

#include <openssl/ssl.h>

#include <map>
#include <string>
#include <stdexcept>
#include <vector>
#include <set>

#include "errors.hpp"
#include <boost/tokenizer.hpp>
#include <boost/shared_array.hpp>
#include <boost/optional.hpp>

#include "arch/address.hpp"
#include "arch/types.hpp"
#include "concurrency/auto_drainer.hpp"
#include "containers/scoped.hpp"
#include "parsing/util.hpp"

enum class http_method_t {
    HEAD = 0,
    GET,
    POST,
    PUT,
    DELETE,
    TRACE,
    OPTIONS,
    CONNECT,
    PATCH
};

struct http_req_t {
    class resource_t {
    public:
        typedef boost::tokenizer<boost::char_separator<char>, char *> tokenizer;
        typedef tokenizer::iterator iterator;

        resource_t();
        explicit resource_t(const std::string &_val);
        resource_t(const char* _val, size_t size);

        MUST_USE bool assign(const std::string &_val);
        MUST_USE bool assign(const char* _val, size_t size);
        iterator begin() const;
        iterator end() const;
        std::string as_string(const iterator &from) const;
        std::string as_string() const;
    private:
        resource_t(const resource_t &from, const iterator &resource_start);
        friend struct http_req_t;

        // This function returns a pointer to the beginning of the token (without the leading '/')
        const char* token_start_position(const iterator& it) const;

        boost::shared_array<char> val;
        size_t val_size;
        iterator b;
        iterator e;
    } resource;

    ip_and_port_t peer;

    http_method_t method;
    std::map<std::string, std::string> query_params;
    std::string version;
    std::map<std::string, std::string> header_lines;
    std::string body;
    std::string get_sanitized_body() const;

    http_req_t();
    explicit http_req_t(const std::string &resource_path);
    http_req_t(const http_req_t &from, const resource_t::iterator& resource_start);

    boost::optional<std::string> find_query_param(const std::string&) const;
    boost::optional<std::string> find_header_line(const std::string&) const;
    void add_header_line(const std::string&, const std::string&);
    bool has_header_line(const std::string&) const;
};

int content_length(const http_req_t&);

enum class http_status_code_t {
    OK = 200,
    BAD_REQUEST = 400,
    FORBIDDEN = 403,
    NOT_FOUND = 404,
    METHOD_NOT_ALLOWED = 405,
    INTERNAL_SERVER_ERROR = 500
};

class http_res_t {
public:
    std::string version;
    http_status_code_t code;
    std::map<std::string, std::string> header_lines;
    std::string body;

    void add_header_line(const std::string&, const std::string&);
    void set_body(const std::string&, const std::string&);

    http_res_t();
    explicit http_res_t(http_status_code_t rescode);
    http_res_t(http_status_code_t rescode, const std::string &content_type,
               const std::string &content);
};

bool maybe_gzip_response(const http_req_t &req, http_res_t *res);

http_res_t http_error_res(const std::string &content,
    http_status_code_t rescode = http_status_code_t::BAD_REQUEST);

class tcp_http_msg_parser_t {
public:
    tcp_http_msg_parser_t() {}
    bool parse(tcp_conn_t *conn, http_req_t *req, signal_t *closer) THROWS_ONLY(tcp_conn_read_closed_exc_t);
private:
    struct version_parser_t {
        std::string version;

        bool parse(const std::string &src);
    };

    struct resource_string_parser_t {
        std::string resource;
        std::map<std::string, std::string> query_params;

        bool parse(const std::string &src);
    };

    struct header_line_parser_t {
        std::string key;
        std::string val;

        bool parse(const std::string &src);
    };
};

class http_app_t {
public:
    virtual void handle(const http_req_t &request,
                        http_res_t *result,
                        signal_t *interruptor) = 0;
protected:
    virtual ~http_app_t() { }
};

/* creating an http server will bind to the specified port and listen for http
 * connections, the data from incoming connections will be parsed into
 * http_req_ts and passed to the handle function which must then return an http
 * msg that's a meaningful response */
class http_server_t {
public:
    http_server_t(
        SSL_CTX *tls_ctx, const std::set<ip_address_t> &local_addresses,
        int port, http_app_t *application);
    ~http_server_t();
    int get_port() const;
private:
    void handle_conn(const scoped_ptr_t<tcp_conn_descriptor_t> &conn, auto_drainer_t::lock_t);
    http_app_t *application;
    auto_drainer_t auto_drainer;
    scoped_ptr_t<tcp_listener_t> tcp_listener;
    SSL_CTX *tls_ctx;
};

std::string percent_escaped_string(const std::string &s);

bool percent_unescape_string(const std::string &s, std::string *out);

std::string http_format_date(const time_t date);

#endif /* HTTP_HTTP_HPP_ */

ext: hpp

max_stars_repo_name: sauter-hq/rethinkdb

max_issues_repo_name: sauter-hq/rethinkdb

max_forks_repo_name: sauter-hq/rethinkdb


text:
#ifndef BOOST_PREPROCESSOR_COMPARISON_GREATER_EQUAL_HPP
#define BOOST_PREPROCESSOR_COMPARISON_GREATER_EQUAL_HPP

/* Copyright (C) 2001
 * Housemarque Oy
 * http://www.housemarque.com
 *
 * Permission to copy, use, modify, sell and distribute this software is
 * granted provided this copyright notice appears in all copies. This
 * software is provided "as is" without express or implied warranty, and
 * with no claim as to its suitability for any purpose.
 *
 * See http://www.boost.org for most recent version.
 */

#include <boost/preprocessor/comparison/less_equal.hpp>

/**
Expands to 1 if X >= Y and 0
otherwise.

Both X and Y must expand to integer literals
in the range [0, BOOST_PP_LIMIT_MAG].

For example, BOOST_PP_GREATER_EQUAL(1,3) expands to 0.

Uses

BOOST_PP_WHILE()

Test

arithmetic_test.cpp

*/
#define BOOST_PP_GREATER_EQUAL(X,Y) BOOST_PP_GREATER_EQUAL_D(0,X,Y)

/**
Can be used inside BOOST_PP_WHILE().
 */
#define BOOST_PP_GREATER_EQUAL_D(D,X,Y) BOOST_PP_LESS_EQUAL_D(D,Y,X)

/*
Obsolete. Use BOOST_PP_GREATER_EQUAL().
 */
#define BOOST_PREPROCESSOR_GREATER_EQUAL(X,Y) BOOST_PP_GREATER_EQUAL(X,Y)
#endif


text:
/*************************************************************************
 * odil - Copyright (C) Universite de Strasbourg
 * Distributed under the terms of the CeCILL-B license, as published by
 * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
 * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
 * for details.
 ************************************************************************/

#include "odil/webservices/QIDORSResponse.h"

#include <sstream>
#include <vector>

#include <boost/property_tree/xml_parser.hpp>
#include <boost/version.hpp>

#include <json/json.h>

#include "odil/DataSet.h"
#include "odil/Exception.h"
#include "odil/StringStream.h"
#include "odil/json_converter.h"
#include "odil/webservices/HTTPResponse.h"
#include "odil/webservices/ItemWithParameters.h"
#include "odil/webservices/multipart_related.h"
#include "odil/webservices/Utils.h"
#include "odil/Writer.h"
#include "odil/xml_converter.h"

namespace odil
{

namespace webservices
{

QIDORSResponse
::QIDORSResponse()
:_data_sets(), _representation(Representation::DICOM_XML), _media_type("")
{
    // Nothing else.
}

QIDORSResponse
::QIDORSResponse(HTTPResponse const & response)
:_data_sets(), _representation(Representation::DICOM_XML), _media_type("")
{
    // Manage response status
    if(response.get_status() == 200)
    {
        // Query is ok and matching results are returned in the message body
    }
    else if(response.get_status() == 204)
    {
        // No matches found
        return;
    }
    else
    {
        throw Exception(
            "Invalid status: "+std::to_string(response.get_status()));
    }

    // Find the media type.
    auto const content_type = as<ItemWithParameters>(
        response.get_header("Content-Type"));

    if(content_type.name == "multipart/related")
    {
        auto const it = content_type.name_parameters.find("type");
        if(
            it == content_type.name_parameters.end()
            || it->second != "application/dicom+xml")
        {
            throw Exception("Invalid content type");
        }
        this->_media_type = it->second;
        this->_representation = Representation::DICOM_XML;
    }
    else if(content_type.name == "application/dicom+json")
    {
        this->_media_type = content_type.name;
        this->_representation = Representation::DICOM_JSON;
    }
    else
    {
        throw Exception("Unrecognized response representation");
    }

    if(this->_representation == Representation::DICOM_XML)
    {
        transform_parts(
            response, std::back_inserter(this->get_data_sets()),
            [](Message const & part)
            {
                IStringStream stream(
                    &part.get_body()[0], part.get_body().size());
                boost::property_tree::ptree xml;
                boost::property_tree::read_xml(stream, xml);
                return as_dataset(xml);
            });
    }
    else
    {
        IStringStream stream(
            &response.get_body()[0], response.get_body().size());
        Json::Value array;
        stream >> array;
        if(!array.isArray())
        {
            throw Exception("Body must be an array");
        }

        this->get_data_sets().reserve(array.size());
        std::transform(
            array.begin(), array.end(),
            std::back_inserter(this->get_data_sets()),
            static_cast<std::shared_ptr<DataSet>(*)(Json::Value const &)>(as_dataset));
    }
}

bool
QIDORSResponse
::operator==(QIDORSResponse const & other) const
{
    return(
        this->_data_sets == other._data_sets
        && this->_representation == other._representation
        && this->_media_type == other._media_type);
}

bool
QIDORSResponse
::operator!=(QIDORSResponse const & other) const
{
    return !(*this == other);
}

Value::DataSets const &
QIDORSResponse
::get_data_sets() const
{
    return this->_data_sets;
}

Value::DataSets &
QIDORSResponse
::get_data_sets()
{
    return this->_data_sets;
}

void
QIDORSResponse
::set_data_sets(Value::DataSets const & data_sets)
{
    this->_data_sets = data_sets;
}

Representation const &
QIDORSResponse
::get_representation() const
{
    return this->_representation;
}

void
QIDORSResponse
::set_representation(Representation const & representation)
{
    if(representation == Representation::DICOM_JSON)
    {
        this->_representation = representation;
        this->_media_type = "application/dicom+json";
    }
    else if(representation == Representation::DICOM_XML)
    {
        this->_representation = representation;
        this->_media_type = "application/dicom+xml";
    }
    else
    {
        throw Exception("Unknown representation");
    }
}

std::string const &
QIDORSResponse
::get_media_type() const
{
    return this->_media_type;
}

HTTPResponse
QIDORSResponse
::get_http_response() const
{
    HTTPResponse response;
    response.set_status(this->_data_sets.empty()?204:200);
    response.set_reason(this->_data_sets.empty()?"No Content":"OK");

    if(this->_representation == Representation::DICOM_XML)
    {
        auto const accumulator =
            [](std::shared_ptr<DataSet const> data_set)
            {
                auto const xml =
                    data_set?as_xml(data_set):boost::property_tree::ptree();

                std::string body;
                OStringStream stream(body);

                boost::property_tree::write_xml(
                    stream, xml,
#if BOOST_VERSION >= 105600
                    boost::property_tree::xml_writer_settings<std::string>()
#else
                    boost::property_tree::xml_writer_settings<char>()
#endif
                );
                stream.flush();

                return Message(
                    { { "Content-Type", "application/dicom+xml" } }, body);
            };

        auto const boundary = random_boundary();
        std::ostringstream body;
        accumulate_parts(
            this->get_data_sets().begin(), this->get_data_sets().end(),
            accumulator, body, boundary);
        response.set_body(body.str());

        response.set_header(
            "Content-Type",
            ItemWithParameters(
                "multipart/related", {
                    {"type", "application/dicom+xml"},
                    {"boundary", boundary}}));
    }
    else if(this->_representation == Representation::DICOM_JSON)
    {
        Json::Value json;
        json.resize(this->_data_sets.size());
        for(unsigned int i=0; i<this->_data_sets.size(); ++i)
        {
            json[i] = as_json(this->_data_sets[i]);
        }

        Json::FastWriter writer;
        response.set_body(writer.write(json));

        response.set_header("Content-Type", "application/dicom+json");
    }
    else
    {
        throw Exception("Unknown representation");
    }
    return response;
}

}

}

max_stars_repo_name: genisysram/odil

max_issues_repo_name: genisysram/odil

max_forks_repo_name: genisysram/odil


text:
// Standard library:
#include <iostream>
#include <fstream>

// Third Party:
// - Boost:
#include <boost/serialization/nvp.hpp>
#include <boost/archive/xml_oarchive.hpp>

// Ourselves:
#include <XY/X.hpp>

int main(void)
{
  std::ofstream file("test_X_2.xml");
  boost::archive::xml_oarchive oa(file);
  xy::X x(666);
  std::clog << "x = " << x << std::endl;
  oa & BOOST_SERIALIZATION_NVP(x);
  return 0;
}

ext: cxx


text:
/******************************************************************************
 * Copyright © 2020 Kévin Lesénéchal <kevin.lesenechal@gmail.com>             *
 *                                                                            *
 * This file is part of the GLARE project, published under the terms of the   *
 * MIT license; see LICENSE file for more information.                        *
 ******************************************************************************/

#pragma once

#include <filesystem>
#include <boost/signals2.hpp>

namespace glare {

class FileWatcherInterface
{
public:
    using WatchSignal = boost::signals2::signal<void()>;

    enum class Event {
        Modified
    };

    virtual ~FileWatcherInterface() noexcept = default;

    virtual WatchSignal& add_watch(const std::filesystem::path& file_path,
                                   Event event) = 0;

    virtual void poll() = 0;
};

} // ns glare

max_stars_repo_name: kevin-lesenechal/glare

max_issues_repo_name: kevin-lesenechal/glare

max_forks_repo_name: kevin-lesenechal/glare


text:
/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2015 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_SQLITE_UTILS_HPP
#define MAPNIK_SQLITE_UTILS_HPP

// stl
#include <string>
#include <vector>
#include <algorithm>
#include <memory>

// mapnik
#include <mapnik/debug.hpp>
#include <mapnik/datasource.hpp>
#include <mapnik/params.hpp>
#include <mapnik/sql_utils.hpp>
#include <mapnik/util/fs.hpp>
#include <mapnik/geometry_is_empty.hpp>
#include <mapnik/geometry_envelope.hpp>

#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/algorithm/string.hpp>
#pragma GCC diagnostic pop

// sqlite
extern "C" {
#include <sqlite3.h>
}

#include "sqlite_resultset.hpp"
#include "sqlite_prepared.hpp"
#include "sqlite_connection.hpp"

//==============================================================================

class sqlite_utils
{
public:

    static bool needs_quoting(std::string const& name)
    {
        if (name.size() > 0)
        {
            const char first = name[0];
            if (is_quote_char(first))
            {
                return false;
            }
            if ((first >= '0' && first <= '9') ||
                (name.find("-") != std::string::npos)
                )
            {
                return true;
            }
        }
        return false;
    }

    static bool is_quote_char(const char z)
    {
        if (z == '"' || z == '\'' || z == '[' || z == '`')
        {
            return true;
        }
        return false;
    }

    static void dequote(std::string & z)
    {
        boost::algorithm::trim_if(z,boost::algorithm::is_any_of("[]'\"`"));
    }

    static std::string index_for_table(std::string const& table, std::string const& field)
    {
        std::string table_trimmed = table;
        dequote(table_trimmed);
        return "\"idx_" + table_trimmed + "_" + field + "\"";
    }

    static std::string index_for_db(std::string const& file)
    {
        //std::size_t idx = file.find_last_of(".");
        //if(idx != std::string::npos) {
        //    return file.substr(0,idx) + ".index" + file.substr(idx);
        //}
        //else
        //{
        return file + ".index";
        //}
    }

    static bool apply_spatial_filter(std::string & query,
                                     mapnik::box2d<double> const& e,
                                     std::string const& table,
                                     std::string const& key_field,
                                     std::string const& index_table,
                                     std::string const& geometry_table,
                                     std::string const& intersects_token)
    {
        std::ostringstream spatial_sql;
        spatial_sql << std::setprecision(16);
        spatial_sql << key_field << " IN (SELECT pkid FROM " << index_table;
        spatial_sql << " WHERE xmax>=" << e.minx() << " AND xmin<=" << e.maxx() ;
        spatial_sql << " AND ymax>=" << e.miny() << " AND ymin<=" << e.maxy() << ")";
        if (boost::algorithm::ifind_first(query,  intersects_token))
        {
            boost::algorithm::ireplace_all(query, intersects_token, spatial_sql.str());
            return true;
        }
        // substitute first WHERE found if not using JOIN
        // (because we can't know the WHERE is on the right table)
        else if (boost::algorithm::ifind_first(query, "WHERE")
                 && !boost::algorithm::ifind_first(query, "JOIN"))
        {
            std::string replace(" WHERE " + spatial_sql.str() + " AND ");
            boost::algorithm::ireplace_first(query, "WHERE", replace);
            return true;
        }
        // fallback to appending spatial filter at end of query
        else if (boost::algorithm::ifind_first(query, geometry_table))
        {
            query = table + " WHERE " + spatial_sql.str();
            return true;
        }
        return false;
    }

    static void get_tables(std::shared_ptr<sqlite_connection> ds,
                           std::vector<std::string> & tables)
    {
        std::ostringstream sql;
        // todo handle finding tables from attached db's
        sql << " SELECT name FROM sqlite_master"
            << " WHERE type IN ('table','view')"
            << " AND name NOT LIKE 'sqlite_%'"
            << " AND name NOT LIKE 'idx_%'"
            << " AND name NOT LIKE '%geometry_columns%'"
            << " AND name NOT LIKE '%ref_sys%'"
            << " UNION ALL"
            << " SELECT name FROM sqlite_temp_master"
            << " WHERE type IN ('table','view')"
            << " ORDER BY 1";
        sqlite3_stmt* stmt = 0;
        const int rc = sqlite3_prepare_v2 (*(*ds), sql.str().c_str(), -1, &stmt, 0);
        if (rc == SQLITE_OK)
        {
            std::shared_ptr<sqlite_resultset> rs = std::make_shared<sqlite_resultset>(stmt);
            while (rs->is_valid() && rs->step_next())
            {
                const int type_oid = rs->column_type(0);
                if (type_oid == SQLITE_TEXT)
                {
                    const char * data = rs->column_text(0);
                    if (data)
                    {
                        tables.push_back(std::string(data));
                    }
                }
            }
        }
    }

    static void query_extent(std::shared_ptr<sqlite_resultset> rs,
                             mapnik::box2d<double>& extent)
    {

        bool first = true;
        while (rs->is_valid() && rs->step_next())
        {
            int size;
            const char* data = static_cast<const char*>(rs->column_blob(0, size));
            if (data)
            {
                mapnik::geometry::geometry<double> geom = mapnik::geometry_utils::from_wkb(data, size, mapnik::wkbAuto);
                if (!mapnik::geometry::is_empty(geom))
                {
                    mapnik::box2d<double> bbox = mapnik::geometry::envelope(geom);
                    if (bbox.valid())
                    {
                        if (first)
                        {
                            first = false;
                            extent = bbox;
                        }
                        else
                        {
                            extent.expand_to_include(bbox);
                        }
                    }
                }
            }
        }
    }

    static bool create_spatial_index(std::string const& index_db,
                                     std::string const& index_table,
                                     std::shared_ptr<sqlite_resultset> rs)
    {
        /* TODO
           - speedups
           - return early/recover from failure
           - allow either in db or in separate
        */

        if (!rs->is_valid())
            return false;

#if SQLITE_VERSION_NUMBER >= 3005000
        int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
#else
        int flags;
#endif

        bool existed = mapnik::util::exists(index_db);
        std::shared_ptr<sqlite_connection> ds = std::make_shared<sqlite_connection>(index_db,flags);

        bool one_success = false;
        try
        {

            ds->execute("PRAGMA synchronous=OFF");
            ds->execute("BEGIN IMMEDIATE TRANSACTION");

            /*
              ds->execute("PRAGMA journal_mode=WAL");
              ds->execute("PRAGMA fullfsync=1");
              ds->execute("PRAGMA locking_mode = EXCLUSIVE");
              ds->execute("BEGIN EXCLUSIVE TRANSACTION");
            */

            // first drop the index if it already exists
            std::ostringstream spatial_index_drop_sql;
            spatial_index_drop_sql << "DROP TABLE IF EXISTS " << index_table;
            ds->execute(spatial_index_drop_sql.str());

            // create the spatial index
            std::ostringstream create_idx;
            create_idx << "create virtual table "
                       << index_table
                       << " using rtree(pkid, xmin, xmax, ymin, ymax)";

            // insert for prepared statement
            std::ostringstream insert_idx;
            insert_idx << "insert into "
                       << index_table
                       << " values (?,?,?,?,?)";

            ds->execute(create_idx.str());

            prepared_index_statement ps(ds,insert_idx.str());

            while (rs->is_valid() && rs->step_next())
            {
                int size;
                const char* data = (const char*) rs->column_blob(0, size);
                if (data)
                {
                    mapnik::geometry::geometry<double> geom = mapnik::geometry_utils::from_wkb(data, size, mapnik::wkbAuto);
                    if (!mapnik::geometry::is_empty(geom))
                    {
                        mapnik::box2d<double> bbox = mapnik::geometry::envelope(geom);
                        if (bbox.valid())
                        {
                            ps.bind(bbox);
                            const int type_oid = rs->column_type(1);
                            if (type_oid != SQLITE_INTEGER)
                            {
                                std::ostringstream error_msg;
                                error_msg << "Sqlite Plugin: invalid type for key field '"
                                          << rs->column_name(1) << "' when creating index '" << index_table
                                          << "' type was: " << type_oid << "";
                                throw mapnik::datasource_exception(error_msg.str());
                            }
                            const sqlite_int64 pkid = rs->column_integer64(1);
                            ps.bind(pkid);
                        }
                        else
                        {
                            std::ostringstream error_msg;
                            error_msg << "SQLite Plugin: encountered invalid bbox at '"
                                      << rs->column_name(1) << "' == " << rs->column_integer64(1);
                            throw mapnik::datasource_exception(error_msg.str());
                        }
                    }
                    ps.step_next();
                    one_success = true;
                }
            }
        }
        catch (mapnik::datasource_exception const& ex)
        {
            if (!existed)
            {
                try
                {
                    mapnik::util::remove(index_db);
                 }
                catch (...) {};
            }
            throw mapnik::datasource_exception(ex.what());
        }

        if (one_success)
        {
            ds->execute("COMMIT");
            return true;
        }
        else if (!existed)
        {
            try
            {
                mapnik::util::remove(index_db);
            }
            catch (...) {};
        }
        return false;
    }

    typedef struct {
        sqlite_int64 pkid;
        mapnik::box2d<double> bbox;
    } rtree_type;

    static void build_tree(std::shared_ptr<sqlite_resultset> rs,
                           std::vector<sqlite_utils::rtree_type> & rtree_list)
    {

        while (rs->is_valid() && rs->step_next())
        {
            int size;
            const char* data = static_cast<const char*>(rs->column_blob(0, size));
            if (data)
            {
                mapnik::geometry::geometry<double> geom = mapnik::geometry_utils::from_wkb(data, size, mapnik::wkbAuto);
                if (!mapnik::geometry::is_empty(geom))
                {
                    mapnik::box2d<double> bbox = mapnik::geometry::envelope(geom);
                    if (bbox.valid())
                    {
                        const int type_oid = rs->column_type(1);
                        if (type_oid != SQLITE_INTEGER)
                        {
                            std::ostringstream error_msg;
                            error_msg << "Sqlite Plugin: invalid type for key field '"
                                      << rs->column_name(1) << "' when creating index "
                                      << "type was: " << type_oid << "";
                            throw mapnik::datasource_exception(error_msg.str());
                        }
                        const sqlite_int64 pkid = rs->column_integer64(1);
                        rtree_type entry = rtree_type();
                        entry.pkid = pkid;
                        entry.bbox = bbox;
                        rtree_list.push_back(entry);
                    }
                    else
                    {
                        std::ostringstream error_msg;
                        error_msg << "SQLite Plugin: encountered invalid bbox at '"
                                  << rs->column_name(1) << "' == " << rs->column_integer64(1);
                        throw mapnik::datasource_exception(error_msg.str());
                    }
                }
            }
        }
    }

    static bool create_spatial_index2(std::string const& index_db,
                                      std::string const& index_table,
                                      std::vector<rtree_type> const& rtree_list)
    {

#if SQLITE_VERSION_NUMBER >= 3005000
        int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
#else
        int flags;
#endif

        bool existed = mapnik::util::exists(index_db);;

        std::shared_ptr<sqlite_connection> ds = std::make_shared<sqlite_connection>(index_db,flags);

        bool one_success = false;
        try
        {

            ds->execute("PRAGMA synchronous=OFF");
            ds->execute("BEGIN IMMEDIATE TRANSACTION");

            // first drop the index if it already exists
            std::ostringstream spatial_index_drop_sql;
            spatial_index_drop_sql << "DROP TABLE IF EXISTS " << index_table;
            ds->execute(spatial_index_drop_sql.str());

            // create the spatial index
            std::ostringstream create_idx;
            create_idx << "create virtual table "
                       << index_table
                       << " using rtree(pkid, xmin, xmax, ymin, ymax)";

            // insert for prepared statement
            std::ostringstream insert_idx;
            insert_idx << "insert into "
                       << index_table
                       << " values (?,?,?,?,?)";

            ds->execute(create_idx.str());

            prepared_index_statement ps(ds,insert_idx.str());

            std::vector<rtree_type>::const_iterator it = rtree_list.begin();
            std::vector<rtree_type>::const_iterator end = rtree_list.end();
            for (; it != end; ++it)
            {
                ps.bind(it->bbox);
                ps.bind(it->pkid);
                ps.step_next();
                one_success = true;
            }
        }
        catch (mapnik::datasource_exception const& ex)
        {
            if (!existed)
            {
                try
                {
                    mapnik::util::remove(index_db);
                }
                catch (...) {};
            }
            throw mapnik::datasource_exception(ex.what());
        }

        if (one_success)
        {
            ds->execute("COMMIT");
            return true;
        }
        else if (!existed)
        {
            try
            {
                mapnik::util::remove(index_db);
            }
            catch (...) {};
        }
        return false;
    }

    static bool detect_extent(std::shared_ptr<sqlite_connection> ds,
                              bool has_spatial_index,
                              mapnik::box2d<double> & extent,
                              std::string const& index_table,
                              std::string const& metadata,
                              std::string const& geometry_field,
                              std::string const& geometry_table,
                              std::string const& key_field,
                              std::string const& table
        )
    {
        if (! metadata.empty())
        {
            std::ostringstream s;
            s << "SELECT xmin, ymin, xmax, ymax FROM " << metadata;
            s << " WHERE LOWER(f_table_name) = LOWER('" << geometry_table << "')";
            MAPNIK_LOG_DEBUG(sqlite) << "sqlite_datasource: executing: '" << s.str() << "'";
            std::shared_ptr<sqlite_resultset> rs(ds->execute_query(s.str()));
            if (rs->is_valid() && rs->step_next())
            {
                double xmin = rs->column_double(0);
                double ymin = rs->column_double(1);
                double xmax = rs->column_double(2);
                double ymax = rs->column_double(3);
                extent.init (xmin, ymin, xmax, ymax);
                return true;
            }
        }
        else if (has_spatial_index)
        {
            std::ostringstream s;
            s << "SELECT MIN(xmin), MIN(ymin), MAX(xmax), MAX(ymax) FROM "
              << index_table;
            MAPNIK_LOG_DEBUG(sqlite) << "sqlite_datasource: executing: '" << s.str() << "'";
            std::shared_ptr<sqlite_resultset> rs(ds->execute_query(s.str()));
            if (rs->is_valid() && rs->step_next())
            {
                if (! rs->column_isnull(0))
                {
                    double xmin = rs->column_double(0);
                    double ymin = rs->column_double(1);
                    double xmax = rs->column_double(2);
                    double ymax = rs->column_double(3);
                    extent.init (xmin, ymin, xmax, ymax);
                    return true;
                }
            }
        }

        else if (! key_field.empty())
        {
            std::ostringstream s;
            s << "SELECT " << geometry_field << "," << key_field
              << " FROM (" << table << ")";
            MAPNIK_LOG_DEBUG(sqlite) << "sqlite_datasource: executing: '" << s.str() << "'";
            std::shared_ptr<sqlite_resultset> rs(ds->execute_query(s.str()));
            sqlite_utils::query_extent(rs,extent);
            return true;
        }
        return false;
    }

    static bool has_rtree(std::string const& index_table,std::shared_ptr<sqlite_connection> ds)
    {
        try
        {
            std::ostringstream s;
            s << "SELECT pkid,xmin,xmax,ymin,ymax FROM " << index_table << " LIMIT 1";
            std::shared_ptr<sqlite_resultset> rs = ds->execute_query(s.str());
            if (rs->is_valid() && rs->step_next())
            {
                return true;
            }
        }
        catch (std::exception const& ex)
        {
            MAPNIK_LOG_DEBUG(sqlite) << "has_rtree returned:" <<  ex.what();
            return false;
        }
        return false;
    }

    static bool detect_types_from_subquery(std::string const& query,
                                           std::string & geometry_field,
                                           mapnik::layer_descriptor & desc,
                                           std::shared_ptr<sqlite_connection> ds)
    {
        bool found = false;
        std::shared_ptr<sqlite_resultset> rs(ds->execute_query(query));
        if (rs->is_valid() && rs->step_next())
        {
            for (int i = 0; i < rs->column_count(); ++i)
            {
                found = true;
                const int type_oid = rs->column_type(i);
                const char* fld_name = rs->column_name(i);
                switch (type_oid)
                {
                case SQLITE_INTEGER:
                    desc.add_descriptor(mapnik::attribute_descriptor(fld_name, mapnik::Integer));
                    break;

                case SQLITE_FLOAT:
                    desc.add_descriptor(mapnik::attribute_descriptor(fld_name, mapnik::Double));
                    break;

                case SQLITE_TEXT:
                    desc.add_descriptor(mapnik::attribute_descriptor(fld_name, mapnik::String));
                    break;

                case SQLITE_NULL:
                    // sqlite reports based on value, not actual column type unless
                    // PRAGMA table_info is used so here we assume the column is a string
                    // which is a lesser evil than altogether dropping the column
                    desc.add_descriptor(mapnik::attribute_descriptor(fld_name, mapnik::String));
                    break;

                case SQLITE_BLOB:
                    if (geometry_field.empty()
                        && (boost::algorithm::icontains(fld_name, "geom") ||
                            boost::algorithm::icontains(fld_name, "point") ||
                            boost::algorithm::icontains(fld_name, "linestring") ||
                            boost::algorithm::icontains(fld_name, "polygon")))
                    {
                        geometry_field = std::string(fld_name);
                    }
                    break;

                default:
                    MAPNIK_LOG_DEBUG(sqlite) << "detect_types_from_subquery: unknown type_oid=" << type_oid;
                    break;
                }
            }
        }

        return found;
    }

    static bool table_info(std::string & key_field,
                           bool detected_types,
                           std::string & field,
                           std::string & table,
                           mapnik::layer_descriptor & desc,
                           std::shared_ptr<sqlite_connection> ds)
    {

        // http://www.sqlite.org/pragma.html#pragma_table_info
        // use pragma table_info to detect primary key
        // and to detect types if no subquery is used or
        // if the subquery-based type detection failed
        std::ostringstream s;
        s << "PRAGMA table_info(" << table << ")";
        std::shared_ptr<sqlite_resultset> rs(ds->execute_query(s.str()));
        bool found_table = false;
        bool found_pk = false;
        while (rs->is_valid() && rs->step_next())
        {
            found_table = true;
            const char* fld_name = rs->column_text(1);
            std::string fld_type(rs->column_text(2));
            sqlite_int64 fld_pk = rs->column_integer64(5);
            std::transform(fld_type.begin(), fld_type.end(), fld_type.begin(), ::tolower);
            // TODO - how to handle primary keys on multiple columns ?
            if (key_field.empty() && ! found_pk && fld_pk != 0)
            {
                key_field = fld_name;
                found_pk = true;
            }
            if (! detected_types)
            {
                // see 2.1 "Column Affinity" at http://www.sqlite.org/datatype3.html
                // TODO - refactor this somehow ?
                if (field.empty()
                    && ((boost::algorithm::contains(fld_type, "geom") ||
                         boost::algorithm::contains(fld_type, "point") ||
                         boost::algorithm::contains(fld_type, "linestring") ||
                         boost::algorithm::contains(fld_type, "polygon"))
                        ||
                        (boost::algorithm::icontains(fld_name, "geom") ||
                         boost::algorithm::icontains(fld_name, "point") ||
                         boost::algorithm::icontains(fld_name, "linestring") ||
                         boost::algorithm::icontains(fld_name, "polygon")))
                    )
                {
                    field = std::string(fld_name);
                }
                else if (boost::algorithm::contains(fld_type, "int"))
                {
                    desc.add_descriptor(mapnik::attribute_descriptor(fld_name, mapnik::Integer));
                }
                else if (boost::algorithm::contains(fld_type, "text") ||
                         boost::algorithm::contains(fld_type, "char") ||
                         boost::algorithm::contains(fld_type, "clob"))
                {
                    desc.add_descriptor(mapnik::attribute_descriptor(fld_name, mapnik::String));
                }
                else if (boost::algorithm::contains(fld_type, "real") ||
                         boost::algorithm::contains(fld_type, "floa") ||
                         boost::algorithm::contains(fld_type, "doub"))
                {
                    desc.add_descriptor(mapnik::attribute_descriptor(fld_name, mapnik::Double));
                }
                else if (boost::algorithm::contains(fld_type, "blob"))
                {
                    if (! field.empty())
                    {
                        desc.add_descriptor(mapnik::attribute_descriptor(fld_name, mapnik::String));
                    }
                }
                else
                {
                    // "Column Affinity" says default to "Numeric" but for now we pass..
                    //desc_.add_descriptor(attribute_descriptor(fld_name,mapnik::Double));

                    desc.add_descriptor(mapnik::attribute_descriptor(fld_name, mapnik::String));

#ifdef MAPNIK_LOG
                    // Do not fail when we specify geometry_field in XML file
                    if (field.empty())
                    {
                        MAPNIK_LOG_DEBUG(sqlite) << "Column '"
                                                 << std::string(fld_name)
                                                 << "' unhandled due to unknown type: '"
                                                 << fld_type
                                                 << "', using String";
                    }
#endif
                }
            }
        }

        return found_table;
    }
};

#endif // MAPNIK_SQLITE_UTILS_HPP


text:
//---------------------------------------------------------------------------//
//!
//! \file   Utility_GlobalMPISession.hpp
//! \author Alex Robinson
//! \brief  The global mpi session class declaration
//!
//---------------------------------------------------------------------------//

#ifndef UTILITY_GLOBAL_MPI_SESSION_HPP
#define UTILITY_GLOBAL_MPI_SESSION_HPP

// Std Lib Includes
#include <iostream>
#include <memory>

// Boost Includes
#include <boost/core/noncopyable.hpp>
#include <boost/shared_ptr.hpp>

// FRENSIE Includes
#include "Utility_Timer.hpp"
#include "Utility_Vector.hpp"

/*! \defgroup mpi MPI
 *
 * A custom MPI interface has been created using insights gained by using
 * both the boost::mpi library and the Teuchos::Comm library. Like the
 * Teuchos::Comm library, the FRENSIE MPI interface can be used whether or not
 * the external MPI libraries have been built (without having to enclose
 * interface code in preprocessor logic blocks). To accomplish this behavior
 * the FRENSIE MPI interface simply provides wrappers around various
 * boost::mpi methods and classes.
 * \ingroup traits
 */

namespace Utility{

/*! The global mpi session class
 *
 * This class is based off of the Teuchos::GlobalMPISession class and the
 * boost::mpi::environment class.
 * \ingroup mpi
 */
class GlobalMPISession : private boost::noncopyable
{

public:

  //! The mpi single thread support level tag
  struct SingleThreadingTag
  {
    operator int() const;
  };

  //! The mpi single thread support level tag object
  static SingleThreadingTag SingleThreading;

  //! The mpi funneled thread support level tag
  struct FunneledThreadingTag
  {
    operator int() const;
  };

  //! The mpi funneled thread support level tag object
  static FunneledThreadingTag FunneledThreading;

  //! The mpi serialized thread support level tag
  struct SerializedThreadingTag
  {
    operator int() const;
  };

  //! The mpi serialized thread support level tag object
  static SerializedThreadingTag SerializedThreading;

  //! The mpi multiple threading support level tag
  struct MultipleThreadingTag
  {
    operator int() const;
  };

  //! The mpi multiple threading support level tag object
  static MultipleThreadingTag MultipleThreading;

  //! Default constructor
  explicit GlobalMPISession( bool abort_on_exception = true );

  //! Constructor for single threading support level
  explicit GlobalMPISession( SingleThreadingTag level,
                             bool abort_on_exception = true );

  //! Constructor for funneled threading support level
  explicit GlobalMPISession( FunneledThreadingTag level,
                             bool abort_on_exception = true );

  //! Constructor for serialized threading support level
  explicit GlobalMPISession( SerializedThreadingTag level,
                             bool abort_on_exception = true );

  //! Constructor for multiple threading support level
  explicit GlobalMPISession( MultipleThreadingTag level,
                             bool abort_on_exception = true );

  //! Constructor with command-line inputs
  GlobalMPISession( int& argc, char**& argv, bool abort_on_exception = true );

  //! Constructor with command-line inputs for single threading support level
  GlobalMPISession( int& argc, char**& argv,
                    SingleThreadingTag level,
                    bool abort_on_exception = true );

  //! Constructor with command-line inputs for funneled threading support level
  GlobalMPISession( int& argc, char**& argv,
                    FunneledThreadingTag level,
                    bool abort_on_exception = true );

  //! Constructor with command-line inputs for serialized threading support level
  GlobalMPISession( int& argc, char**& argv,
                    SerializedThreadingTag level,
                    bool abort_on_exception = true );

  //! Constructor with command-line inputs for multiple threading support level
  GlobalMPISession( int& argc, char**& argv,
                    MultipleThreadingTag level,
                    bool abort_on_exception = true );

  //! Destructor
  ~GlobalMPISession();

  //! Initialize logs
  void initializeLogs( boost::shared_ptr<std::ostream> log_sink,
                       const int root_process = 0,
                       const bool limit_logging_to_root = false,
                       const bool synchronous_logging = true );

  //! Initialize error log
  void initializeErrorLog( boost::shared_ptr<std::ostream> log_sink,
                           const int root_process = 0,
                           const bool limit_logging_to_root = false,
                           const bool synchronous_logging = true );

  //! Initialize warning log
  void initializeWarningLog( boost::shared_ptr<std::ostream> log_sink,
                             const int root_process = 0,
                             const bool limit_logging_to_root = false,
                             const bool synchronous_logging = true );

  //! Initialize notification log
  void initializeNotificationLog( boost::shared_ptr<std::ostream> log_sink,
                                  const int root_process = 0,
                                  const bool limit_logging_to_root = false,
                                  const bool synchronous_logging = true );

  //! Initialize an output stream
  static void initializeOutputStream( boost::shared_ptr<std::ostream> os,
                                      const int root_process = 0,
                                      const bool limit_logging_to_root = false );

  //! Restore an output stream
  static void restoreOutputStream( boost::shared_ptr<std::ostream> os );

  //! Restore all output streams
  static void restoreOutputStreams();

  //! Check if MPI has been configured for use
  static bool isMPIUsed();

  //! Create a timer
  static std::shared_ptr<Timer> createTimer();

  //! Abort all MPI processes
  static void abort( int error_code );

  //! Check if mpi has been initialized
  static bool initialized();

  //! Check if MPI has been finalized
  static bool finalized();

  //! Get the maximum tag value
  static int maxTag();

  //! Get the maximum tag value (mirror boost::mpi interface)
  static int max_tag();

  //! Get the tag value used for collective operations
  static int collectivesTag();

  //! Get the tag value used for collective operations (mirror boost::mpi interface)
  static int collectives_tag();

  //! Get the name of the calling processor
  static std::string processorName();

  //! Get the name of the calling processor (mirror boost::mpi interface)
  static std::string processor_name();

  //! Get the current level of thread support
  static int threadLevel();

  //! Get the current level of thread support (mirror boost::mpi interface)
  static int thread_level();

  //! Check if the calling process is the main thread
  static bool isMainThread();

  //! Check if the calling process is the main thread (mirror boost::mpi interface)
  static bool is_main_thread();

  //! Return the rank of the calling process (w.r.t. MPI_COMM_WORLD)
  static int rank();

  //! Return the number of processes (w.r.t. MPI_COMM_WORLD)
  static int size();

  //! Create an mpi synchronization point
  static void barrier();

  //! Sum a set of integers across all processes
  static int sum( const int local_value );

  //! Sum a set of doubles across all processes
  static double sum( const double local_value );

  //! Check if a boolean is true on all processes
  static bool isGloballyTrue( const bool local_boolean );

  //! Check if a boolean is false on all processes
  static bool isGloballyFalse( const bool local_boolean );

  //! Gather messages on the desired process
  static std::vector<std::string> gatherMessages( const int root, const std::string& local_message );

  //! Gather bools on the desired process
  static std::vector<bool> gatherData( const int root, const bool local_data );

  //! Gather ints on the desired process
  static std::vector<int> gatherData( const int root, const int local_data );

  //! Gather doubles on the desired process
  static std::vector<double> gatherData( const int root, const double local_data );

private:

  // Initialize rank and size
  static void initializeRankAndSize();

  // The global mpi session implementation
  class GlobalMPISessionImpl;
  std::unique_ptr<GlobalMPISessionImpl> d_impl;

  // The MPI_COMM_WORLD rank
  static int s_rank;

  // The MPI_COMM_WORLD size
  static int s_size;
};

} // end Utility namespace

#endif // end UTILITY_GLOBAL_MPI_SESSION_HPP

//---------------------------------------------------------------------------//
// end Utility_GlobalMPISession.hpp
//---------------------------------------------------------------------------//