<commit_before>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include "Support/CmdLine.h"
#include "Support/PrettyPrint.h"
#include "Support/StringSplit.h"

#include "CmdLineQt.h"
#include "CmdLineWithIndex.h"

#include <iostream>
#include <set>

using namespace support;

namespace support
{
namespace cl
{

    template <class T, class U>
    struct Parser<std::pair<T, U>>
    {
        bool operator ()(StringRef value, size_t i, std::pair<T, U>& result) const
        {
            auto p = strings::split(value, ":")();

            return Parser<T>()(p.first.trim(), i, result.first)
                   && Parser<U>()(p.second.trim(), i, result.second);
        }
    };

    template <class T, class P>
    void prettyPrint(std::ostream& stream, Option<T, P> const& option)
    {
        stream << option.getName() << ":\n";
        stream << "  count = " << option.getCount() << "\n";
        stream << "  value = " << pretty(option.get());
    }

    template <class T>
    void prettyPrint(std::ostream& stream, WithIndex<T> const& x)
    {
        stream << "(" << x.index << ": " << pretty(x.value) << ")";
    }

} // namespace cl
} // namespace support

int main(int argc, char* argv[])
{
    //----------------------------------------------------------------------------------------------

    cl::CmdLine cmd(argv[0], "A short description.\n\nA long description.");

                    //------------------------------------------------------------------------------

    double y = -1.0;

    auto y_ref = cl::makeOption<double&>(cmd, "y",
        cl::ArgName("float"),
        cl::Desc("Enter a floating-point number"),
        cl::ArgRequired,
        cl::init(y)
        );

                    //------------------------------------------------------------------------------

    auto g  = cl::makeOption<bool>(cmd, "g", cl::Grouping, cl::ZeroOrMore);
    auto h  = cl::makeOption<bool>(cmd, "h", cl::Grouping, cl::ZeroOrMore);
    auto gh = cl::makeOption<bool>(cmd, "gh", cl::Prefix, cl::ArgRequired);

                    //------------------------------------------------------------------------------

    auto z = cl::makeOption<std::set<int>>(cmd, "z",
        cl::ArgName("int"),
        cl::Desc("A list of integers"),
        cl::ZeroOrMore,
        cl::ArgRequired,
        cl::CommaSeparated
        );

                    //------------------------------------------------------------------------------

    std::initializer_list<cl::WithIndex<std::string>> Iinit {
        "eins", "zwei", "drei", "vier", "funf"
    };

    auto I = cl::makeOption<std::vector<cl::WithIndex<std::string>>>(cmd, "I",
        cl::ArgName("dir"),
        cl::Desc("Add the directory dir to the list of directories to be searched for header files."),
        cl::Prefix,
        cl::ArgRequired,
        cl::ZeroOrMore,
        cl::init(Iinit)
        );

                    //------------------------------------------------------------------------------

    auto files = cl::makeOption<std::vector<std::string>>(cmd, "files",
        cl::Positional,
        cl::ZeroOrMore
        );

                    //------------------------------------------------------------------------------

    enum OptimizationLevel {
        OL_None,
        OL_Trivial,
        OL_Default,
        OL_Expensive
    };

    auto optParser = cl::MapParser<OptimizationLevel>({
        { "O0", OL_None        },
        { "O1", OL_Trivial     },
        { "O2", OL_Default     },
        { "O3", OL_Expensive   }
    });

    auto opt = cl::makeOptionWithParser<OptimizationLevel>(
        optParser,
        cmd,
        cl::Required,
        cl::ArgDisallowed,
        cl::Desc("Choose an optimization level"),
        cl::init(OL_None)
        );

                    //------------------------------------------------------------------------------

    enum Simpson {
        Homer, Marge, Bart, Lisa, Maggie, SideshowBob
    };

    auto simpsonParser = cl::MapParser<Simpson>({
        { "homer",       Homer  },
        { "marge",       Marge  },
        { "bart",        Bart   },
        { "el barto",    Bart   },
        { "lisa",        Lisa   },
        { "maggie",      Maggie }
    });

    auto simpson = cl::makeOptionWithParser<Simpson>(
        simpsonParser,
        cmd, "simpson",
        cl::Desc("Choose a Simpson"),
        cl::ArgRequired,
        cl::init(SideshowBob)
        );

                    //------------------------------------------------------------------------------

    auto f = cl::makeOption<std::map<std::string, int>>(
        cmd, "f",
        cl::CommaSeparated,
        cl::ArgRequired
        );

                    //------------------------------------------------------------------------------

    auto helpParser = [&](StringRef /*value*/, size_t /*i*/, bool & /*result*/)->bool
    {
        cmd.help();
        exit(0);
    };

    auto help = cl::makeOptionWithParser<bool>(
        helpParser,
        cmd, "help",
        cl::Optional
        );

    //----------------------------------------------------------------------------------------------

    if (!cmd.parse({ argv + 1, argv + argc }, /*ignoreUnknowns*/ false))
    {
        for (auto const& s : cmd.getErrors())
            std::cout << "error: " << s << "\n";

        return -1;
    }

    //----------------------------------------------------------------------------------------------

    std::cout << pretty(f) << std::endl;
    std::cout << pretty(g) << std::endl;
    std::cout << pretty(gh) << std::endl;
    std::cout << pretty(h) << std::endl;
    std::cout << pretty(help) << std::endl;
    std::cout << pretty(I) << std::endl;
    std::cout << pretty(opt) << std::endl;
    std::cout << pretty(simpson) << std::endl;
    std::cout << pretty(y_ref) << std::endl;
    std::cout << pretty(z) << std::endl;

    std::cout << "files:\n";
    for (auto& s : files.get())
        std::cout << "  \"" << s << "\"" << std::endl;

    //----------------------------------------------------------------------------------------------

    return 0;
}
<commit_msg>Test with std::bind<commit_after>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include "Support/CmdLine.h"
#include "Support/PrettyPrint.h"
#include "Support/StringSplit.h"

#include "CmdLineQt.h"
#include "CmdLineWithIndex.h"

#include <functional>
#include <iostream>
#include <set>

using namespace support;

namespace support
{
namespace cl
{

    template <class T, class U>
    struct Parser<std::pair<T, U>>
    {
        bool operator ()(StringRef value, size_t i, std::pair<T, U>& result) const
        {
            auto p = strings::split(value, ":")();

            return Parser<T>()(p.first.trim(), i, result.first)
                   && Parser<U>()(p.second.trim(), i, result.second);
        }
    };

    template <class T, class P>
    void prettyPrint(std::ostream& stream, Option<T, P> const& option)
    {
        stream << option.getName() << ":\n";
        stream << "  count = " << option.getCount() << "\n";
        stream << "  value = " << pretty(option.get());
    }

    template <class T>
    void prettyPrint(std::ostream& stream, WithIndex<T> const& x)
    {
        stream << "(" << x.index << ": " << pretty(x.value) << ")";
    }

} // namespace cl
} // namespace support

int main(int argc, char* argv[])
{
    //----------------------------------------------------------------------------------------------

    cl::CmdLine cmd(argv[0], "A short description.\n\nA long description.");

                    //------------------------------------------------------------------------------

    double y = -1.0;

    auto y_ref = cl::makeOption<double&>(cmd, "y",
        cl::ArgName("float"),
        cl::Desc("Enter a floating-point number"),
        cl::ArgRequired,
        cl::init(y)
        );

                    //------------------------------------------------------------------------------

    auto g  = cl::makeOption<bool>(cmd, "g", cl::Grouping, cl::ZeroOrMore);
    auto h  = cl::makeOption<bool>(cmd, "h", cl::Grouping, cl::ZeroOrMore);
    auto gh = cl::makeOption<bool>(cmd, "gh", cl::Prefix, cl::ArgRequired);

                    //------------------------------------------------------------------------------

    auto z = cl::makeOption<std::set<int>>(cmd, "z",
        cl::ArgName("int"),
        cl::Desc("A list of integers"),
        cl::ZeroOrMore,
        cl::ArgRequired,
        cl::CommaSeparated
        );

                    //------------------------------------------------------------------------------

    std::initializer_list<cl::WithIndex<std::string>> Iinit {
        "eins", "zwei", "drei", "vier", "funf"
    };

    auto I = cl::makeOption<std::vector<cl::WithIndex<std::string>>>(cmd, "I",
        cl::ArgName("dir"),
        cl::Desc("Add the directory dir to the list of directories to be searched for header files."),
        cl::Prefix,
        cl::ArgRequired,
        cl::ZeroOrMore,
        cl::init(Iinit)
        );

                    //------------------------------------------------------------------------------

    auto files = cl::makeOption<std::vector<std::string>>(cmd, "files",
        cl::Positional,
        cl::ZeroOrMore
        );

                    //------------------------------------------------------------------------------

    enum OptimizationLevel {
        OL_None,
        OL_Trivial,
        OL_Default,
        OL_Expensive
    };

    auto optParser = cl::MapParser<OptimizationLevel>({
        { "O0", OL_None        },
        { "O1", OL_Trivial     },
        { "O2", OL_Default     },
        { "O3", OL_Expensive   }
    });

    auto opt = cl::makeOptionWithParser<OptimizationLevel>(
        optParser,
        cmd,
        cl::Required,
        cl::ArgDisallowed,
        cl::Desc("Choose an optimization level"),
        cl::init(OL_None)
        );

                    //------------------------------------------------------------------------------

    enum Simpson {
        Homer, Marge, Bart, Lisa, Maggie, SideshowBob
    };

    auto simpsonParser = cl::MapParser<Simpson>({
        { "homer",       Homer  },
        { "marge",       Marge  },
        { "bart",        Bart   },
        { "el barto",    Bart   },
        { "lisa",        Lisa   },
        { "maggie",      Maggie }
    });

    auto simpson = cl::makeOptionWithParser<Simpson>(
        simpsonParser,
        cmd, "simpson",
        cl::Desc("Choose a Simpson"),
        cl::ArgRequired,
        cl::init(SideshowBob)
        );

                    //------------------------------------------------------------------------------

    auto f = cl::makeOption<std::map<std::string, int>>(
        cmd, "f",
        cl::CommaSeparated,
        cl::ArgRequired
        );

                    //------------------------------------------------------------------------------

    auto helpParser = [&](StringRef value) -> bool
    {
        std::cout << "Showing help for \"" << value << "\"\n";
        cmd.help();
        return true;
    };

    auto help = cl::makeOptionWithParser<std::string/*subcommand*/>(
        std::bind(helpParser, std::placeholders::_1),
        cmd, "help",
        cl::Optional,
        cl::Prefix,
        cl::ArgOptional
        );

    //----------------------------------------------------------------------------------------------

    bool success = cmd.parse({ argv + 1, argv + argc }, /*ignoreUnknowns*/ false);

    if (help.getCount())
        return 0;

    if (!success)
    {
        for (auto const& s : cmd.getErrors())
            std::cout << "error: " << s << "\n";

        return -1;
    }

    //----------------------------------------------------------------------------------------------

    std::cout << pretty(f) << std::endl;
    std::cout << pretty(g) << std::endl;
    std::cout << pretty(gh) << std::endl;
    std::cout << pretty(h) << std::endl;
    std::cout << pretty(help) << std::endl;
    std::cout << pretty(I) << std::endl;
    std::cout << pretty(opt) << std::endl;
    std::cout << pretty(simpson) << std::endl;
    std::cout << pretty(y_ref) << std::endl;
    std::cout << pretty(z) << std::endl;

    std::cout << "files:\n";
    for (auto& s : files.get())
        std::cout << "  \"" << s << "\"" << std::endl;

    //----------------------------------------------------------------------------------------------

    return 0;
}
<|endoftext|>


<commit_before>#include <pcl/ModelCoefficients.h>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/features/normal_3d.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/kdtree/kdtree.h>
#include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/segmentation/extract_clusters.h>

int
main (int argc, char** argv)
{
  // Read in the cloud data
  pcl::PCDReader reader;
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
  reader.read ("table_scene_lms400.pcd", *cloud);
  std::cout << "PointCloud before filtering has: " << cloud->points.size () << " data points." << std::endl; //*

  // Create the filtering object: downsample the dataset using a leaf size of 1cm
  pcl::VoxelGrid<pcl::PointXYZ> vg;
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);
  vg.setInputCloud (cloud);
  vg.setLeafSize (0.01f, 0.01f, 0.01f);
  vg.filter (*cloud_filtered);
  std::cout << "PointCloud after filtering has: " << cloud_filtered->points.size ()  << " data points." << std::endl; //*

  // Create the segmentation object for the planar model and set all the parameters
  pcl::SACSegmentation<pcl::PointXYZ> seg;
  pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
  pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_plane (new pcl::PointCloud<pcl::PointXYZ> ());
  pcl::PCDWriter writer;
  seg.setOptimizeCoefficients (true);
  seg.setModelType (pcl::SACMODEL_PLANE);
  seg.setMethodType (pcl::SAC_RANSAC);
  seg.setMaxIterations (100);
  seg.setDistanceThreshold (0.02);

  int i=0, nr_points = (int) cloud_filtered->points.size ();
  while (cloud_filtered->points.size () > 0.3 * nr_points)
  {
    // Segment the largest planar component from the remaining cloud
    seg.setInputCloud(cloud_filtered);
    seg.segment (*inliers, *coefficients); //*
    if (inliers->indices.size () == 0)
    {
      std::cout << "Could not estimate a planar model for the given dataset." << std::endl;
      break;
    }

    // Extract the planar inliers from the input cloud
    pcl::ExtractIndices<pcl::PointXYZ> extract;
    extract.setInputCloud (cloud_filtered);
    extract.setIndices (inliers);
    extract.setNegative (false);

    // Write the planar inliers to disk
    extract.filter (*cloud_plane); //*
    std::cout << "PointCloud representing the planar component: " << cloud_plane->points.size () << " data points." << std::endl;

    // Remove the planar inliers, extract the rest
    extract.setNegative (true);
    extract.filter (*cloud_filtered); //*
  }

  // Creating the KdTree object for the search method of the extraction
  pcl::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::KdTreeFLANN<pcl::PointXYZ>);
  tree->setInputCloud (cloud_filtered);

  std::vector<pcl::PointIndices> cluster_indices;
  pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;
  ec.setClusterTolerance (0.02); // 2cm
  ec.setMinClusterSize (100);
  ec.setMaxClusterSize (25000);
  ec.setSearchMethod (tree);
  ec.setInputCloud( cloud_filtered);
  ec.extract (cluster_indices);

  int j = 0;
  for (std::vector<pcl::PointIndices>::const_iterator it = cluster_indices.begin (); it != cluster_indices.end (); ++it)
  {
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cluster (new pcl::PointCloud<pcl::PointXYZ>);
    for (std::vector<int>::const_iterator pit = it->indices.begin (); pit != it->indices.end (); pit++)
      cloud_cluster->points.push_back (cloud_filtered->points[*pit]); //*

    std::cout << "PointCloud representing the Cluster: " << cloud_cluster->points.size () << " data points." << std::endl;
    std::stringstream ss;
    ss << "cloud_cluster_" << j << ".pcd";
    writer.write<pcl::PointXYZ> (ss.str (), *cloud_cluster, false); //*
    j++;
  }

  return (0);
}
<commit_msg>fix for the cluster_extraction tutorial, per #357 (thanks Anthony!)<commit_after>#include <pcl/ModelCoefficients.h>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/features/normal_3d.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/kdtree/kdtree.h>
#include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/segmentation/extract_clusters.h>

int
main (int argc, char** argv)
{
  // Read in the cloud data
  pcl::PCDReader reader;
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
  reader.read ("table_scene_lms400.pcd", *cloud);
  std::cout << "PointCloud before filtering has: " << cloud->points.size () << " data points." << std::endl; //*

  // Create the filtering object: downsample the dataset using a leaf size of 1cm
  pcl::VoxelGrid<pcl::PointXYZ> vg;
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);
  vg.setInputCloud (cloud);
  vg.setLeafSize (0.01f, 0.01f, 0.01f);
  vg.filter (*cloud_filtered);
  std::cout << "PointCloud after filtering has: " << cloud_filtered->points.size ()  << " data points." << std::endl; //*

  // Create the segmentation object for the planar model and set all the parameters
  pcl::SACSegmentation<pcl::PointXYZ> seg;
  pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
  pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients);
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_plane (new pcl::PointCloud<pcl::PointXYZ> ());
  pcl::PCDWriter writer;
  seg.setOptimizeCoefficients (true);
  seg.setModelType (pcl::SACMODEL_PLANE);
  seg.setMethodType (pcl::SAC_RANSAC);
  seg.setMaxIterations (100);
  seg.setDistanceThreshold (0.02);

  int i=0, nr_points = (int) cloud_filtered->points.size ();
  while (cloud_filtered->points.size () > 0.3 * nr_points)
  {
    // Segment the largest planar component from the remaining cloud
    seg.setInputCloud(cloud_filtered);
    seg.segment (*inliers, *coefficients); //*
    if (inliers->indices.size () == 0)
    {
      std::cout << "Could not estimate a planar model for the given dataset." << std::endl;
      break;
    }

    // Extract the planar inliers from the input cloud
    pcl::ExtractIndices<pcl::PointXYZ> extract;
    extract.setInputCloud (cloud_filtered);
    extract.setIndices (inliers);
    extract.setNegative (false);

    // Write the planar inliers to disk
    extract.filter (*cloud_plane); //*
    std::cout << "PointCloud representing the planar component: " << cloud_plane->points.size () << " data points." << std::endl;

    // Remove the planar inliers, extract the rest
    extract.setNegative (true);
    extract.filter (*cloud_filtered); //*
  }

  // Creating the KdTree object for the search method of the extraction
  pcl::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::KdTreeFLANN<pcl::PointXYZ>);
  tree->setInputCloud (cloud_filtered);

  std::vector<pcl::PointIndices> cluster_indices;
  pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;
  ec.setClusterTolerance (0.02); // 2cm
  ec.setMinClusterSize (100);
  ec.setMaxClusterSize (25000);
  ec.setSearchMethod (tree);
  ec.setInputCloud( cloud_filtered);
  ec.extract (cluster_indices);

  int j = 0;
  for (std::vector<pcl::PointIndices>::const_iterator it = cluster_indices.begin (); it != cluster_indices.end (); ++it)
  {
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_cluster (new pcl::PointCloud<pcl::PointXYZ>);
    for (std::vector<int>::const_iterator pit = it->indices.begin (); pit != it->indices.end (); pit++)
      cloud_cluster->points.push_back (cloud_filtered->points[*pit]); //*
    cloud_cluster->width = cloud_cluster->points.size ();
    cloud_cluster->height = 1;
    cloud_cluster->is_dense = true;

    std::cout << "PointCloud representing the Cluster: " << cloud_cluster->points.size () << " data points." << std::endl;
    std::stringstream ss;
    ss << "cloud_cluster_" << j << ".pcd";
    writer.write<pcl::PointXYZ> (ss.str (), *cloud_cluster, false); //*
    j++;
  }

  return (0);
}
<|endoftext|>


<commit_before>/*
 * This file is part of the Sqloxx project and is distributed under the
 * terms of the license contained in the file LICENSE.txt distributed
 * with this package.
 *
 * Author: Matthew Harvey <matthew@matthewharvey.net>
 *
 * Copyright (c) 2012-2013, Matthew Harvey.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "example.hpp"
#include "handle.hpp"
#include "sqloxx_tests_common.hpp"
#include <jewel/optional.hpp>
#include <UnitTest++/UnitTest++.h>
#include <utility>

using jewel::UninitializedOptionalException;
using std::move;

namespace sqloxx
{
namespace tests
{

TEST_FIXTURE(ExampleFixture, handle_constructors)
{
	JEWEL_LOG_TRACE();

	Handle<ExampleA> const dpo0;
	CHECK(!dpo0);
	CHECK_THROW(*dpo0, UnboundHandleException);
	CHECK_THROW(dpo0->x(), UnboundHandleException);

	Handle<ExampleA> const dpo1(*pdbc);
	dpo1->set_x(10);
	CHECK_EQUAL(dpo1->x(), 10);
	dpo1->set_y(50000.9812);
	CHECK_EQUAL(dpo1->y(), 50000.9812);
	dpo1->save();

	Handle<ExampleA> const dpo1b(*pdbc, 1);
	CHECK_EQUAL(dpo1b->x(), 10);
	CHECK_EQUAL(dpo1b->y(), 50000.9812);

	Handle<ExampleA> dpo2(*pdbc);
	dpo2->set_x(503);
	dpo2->set_y(-1.3);
	dpo2->save();

	Handle<ExampleA> const dpo2b(*pdbc, 2);
	CHECK_EQUAL(dpo2b->x(), 503);
	CHECK_EQUAL(dpo2b->y(), -1.3);

	Handle<ExampleA> dpo2c(*pdbc, 2);
	CHECK_EQUAL(dpo2c->y(), -1.3);
	CHECK_EQUAL(dpo2c->x(), 503);

	CHECK_THROW
	(	Handle<ExampleA> const dpo3(*pdbc, 3),
		sqloxx::BadIdentifier
	);

	CHECK_THROW
	(	Handle<ExampleA> dpo0(*pdbc, 0),
		sqloxx::BadIdentifier
	);
	JEWEL_LOG_TRACE();
}

TEST_FIXTURE(ExampleFixture, handle_create_unchecked)
{
	JEWEL_LOG_TRACE();

	Handle<ExampleA> const dpo1(*pdbc);
	dpo1->set_x(10);
	CHECK_EQUAL(dpo1->x(), 10);
	dpo1->set_y(50000.9812);
	CHECK_EQUAL(dpo1->y(), 50000.9812);
	dpo1->save();

	Handle<ExampleA> const dpo1b =
		Handle<ExampleA>::create_unchecked(*pdbc, 1);
	CHECK_EQUAL(dpo1b->x(), 10);
	CHECK_EQUAL(dpo1b->y(), 50000.9812);

	Handle<ExampleA> dpo2(*pdbc);
	dpo2->set_x(503);
	dpo2->set_y(-1.3);
	dpo2->save();

	CHECK_EQUAL(Handle<ExampleA>::create_unchecked(*pdbc, 2)->x(), 503);
	Handle<ExampleA> const dpo2b
	(	Handle<ExampleA>::create_unchecked(*pdbc, 2)
	);
	CHECK_EQUAL(dpo2b->y(), -1.3);

	JEWEL_LOG_TRACE();
}

TEST_FIXTURE(ExampleFixture, handle_copy_constructor_and_indirection)
{
	Handle<ExampleA> dpo1(*pdbc);
	dpo1->set_x(-9);
	Handle<ExampleA> dpo2(dpo1);
	dpo2->set_y(102928);
	CHECK_EQUAL(dpo2->x(), -9);
	dpo2->save();
	CHECK_EQUAL(dpo1->id(), 1);
	CHECK_EQUAL(dpo2->id(), dpo1->id());
	CHECK_EQUAL(dpo1->y(), 102928);
	Handle<ExampleA> dpo3(dpo1);
	CHECK_EQUAL(dpo3->id(), 1);
	CHECK_EQUAL(dpo3->y(), 102928);
	CHECK_EQUAL(dpo3->x(), dpo1->x());
}

TEST_FIXTURE(ExampleFixture, handle_move_constructor_and_indirection)
{
	Handle<ExampleA> dpo1(*pdbc);
	dpo1->set_x(-9);
	dpo1->set_y(102928);
	dpo1->save();
	Handle<ExampleA> dpo2(move(dpo1));

	// dpo1 is now "taboo".
	CHECK_EQUAL(dpo2->id(), 1);
	CHECK_EQUAL(dpo2->y(), 102928);
	CHECK_EQUAL(dpo2->x(), -9);
}

TEST_FIXTURE(ExampleFixture, handle_assignment_and_indirection)
{
	Handle<ExampleA> dpo1(*pdbc);
	Handle<ExampleA> dpo2(*pdbc);
	dpo2->set_x(100);
	dpo2->set_y(0.0112);
	dpo2->save();
	dpo1->set_x(897);
	dpo1->set_y(30978);
	dpo2 = dpo1;  // copy assignment
	CHECK_EQUAL(dpo2->x(), dpo1->x());
	CHECK_EQUAL(dpo2->y(), 30978);
	dpo1->save();
	CHECK_EQUAL(dpo2->id(), 2);
	Handle<ExampleA> dpo3(*pdbc, 1);
	CHECK_EQUAL(dpo3->id(), 1);
	dpo3->set_x(-188342392);
	dpo1 = dpo3;  // copy assignment
	CHECK_EQUAL(dpo1->x(), -188342392);
	dpo1->set_y(50);
	CHECK_EQUAL(dpo1->y(), 50);
	dpo1->save();
	CHECK_EQUAL(dpo3->id(), 1);

	Handle<ExampleA> dpo4(*pdbc);
	dpo4 = Handle<ExampleA>(*pdbc,  2);  // move assignment
	CHECK_EQUAL(dpo4->x(), 897);
	CHECK_EQUAL(dpo4->y(), 30978);
	dpo4 = Handle<ExampleA>();  // move assignment
	CHECK(!dpo4);
	dpo4 = Handle<ExampleA>(*pdbc);  // move assignment
	CHECK(dpo4);
}

TEST_FIXTURE(ExampleFixture, handle_dereferencing)
{
	Handle<ExampleA> dpo1(*pdbc);
	dpo1->set_x(10);
	dpo1->set_y(1278.90172);
	dpo1->save();
	ExampleA& dpo1_dereferenced(*dpo1);
	CHECK_EQUAL(dpo1_dereferenced.y(), dpo1->y());
	CHECK_EQUAL((*dpo1).y(), dpo1->y());
	CHECK_EQUAL(dpo1_dereferenced.id(), dpo1->id());
	CHECK_EQUAL(dpo1_dereferenced.x(), 10);
	dpo1_dereferenced.set_y(.504);
	CHECK_EQUAL(dpo1->y(), 0.504);
	Handle<ExampleA> dpo2(*pdbc);
	ExampleA& dpo2_dereferenced = *dpo2;
	dpo2_dereferenced.set_x(8000);
	dpo2_dereferenced.set_y(140);
	CHECK_EQUAL((*dpo2).x(), dpo2_dereferenced.x());
	CHECK_EQUAL(dpo2->y(), (*dpo2).y());
	CHECK_EQUAL(dpo2->y(), 140);

	Handle<ExampleA> dpo4;
	CHECK_THROW(dpo4->y(), UnboundHandleException);
	CHECK_THROW(*dpo4, UnboundHandleException);
}

TEST_FIXTURE(ExampleFixture, handle_conversion_to_bool)
{
	Handle<ExampleA> dpo1(*pdbc);
	CHECK(dpo1);
	CHECK_EQUAL(static_cast<bool>(dpo1), true);
	CHECK(dpo1);
	dpo1->set_y(139000000);
	dpo1->set_x(7);
	dpo1->save();
	CHECK(dpo1);

	// Handle is still valid after underlying object has
	// been removed from the database.
	dpo1->remove();
	CHECK(dpo1);
	CHECK_EQUAL(dpo1->x(), 7);
	CHECK_THROW(dpo1->id(), UninitializedOptionalException);
	CHECK_EQUAL(static_cast<bool>(dpo1), true);
	CHECK(static_cast<bool>(dpo1));

	// However handle is not valid if completely uninitialized.
	Handle<ExampleA> dpo2;
	CHECK_EQUAL(static_cast<bool>(dpo2), false);
	dpo1 = dpo2;
	CHECK_EQUAL(static_cast<bool>(dpo2), false);
	CHECK(!dpo2);

}

TEST_FIXTURE(ExampleFixture, handle_equality_and_inequality)
{
	Handle<ExampleA> dpo1(*pdbc);
	Handle<ExampleA> dpo2(dpo1);
	CHECK(dpo1 == dpo2);
	CHECK(dpo2 == dpo1);

	Handle<ExampleA> const dpo3(*pdbc);
	dpo3->set_x(109);
	dpo3->set_y(.5);
	dpo3->save();
	CHECK(dpo3 != dpo1);
	CHECK(dpo2 != dpo3);

	Handle<ExampleA> dpo4(*pdbc, 1);
	CHECK(dpo4 == dpo3);
	CHECK(dpo3 == dpo4);

	Handle<ExampleA> const dpo5(*pdbc);
	dpo5->save();

	Handle<ExampleA> const dpo6(*pdbc, 2);
	CHECK(dpo6 == dpo5);
	CHECK(dpo5 == dpo6);
	CHECK(dpo6 != dpo4);
	CHECK(dpo3 != dpo6);
	CHECK(dpo4 != dpo6);
}

TEST_FIXTURE(ExampleFixture, handle_cast_test)
{
	Handle<ExampleC> dpoc1(*pdbc);
	dpoc1->set_s("hello");
	dpoc1->set_p(3);
	dpoc1->set_q(50);
	dpoc1->save();

	Handle<ExampleC> dpoc2(*pdbc);
	dpoc2->set_s("goodbye");
	dpoc2->set_p(-309);
	dpoc2->set_q(501);
	dpoc2->save();

	Handle<ExampleB> dpob1;
	CHECK_THROW(*dpob1, UnboundHandleException);
	dpob1 = handle_cast<ExampleB>(dpoc2);
	CHECK(dpob1);
	CHECK_EQUAL(dpob1->id(), 2);
	CHECK_EQUAL(dpob1->s(), "goodbye");

	Handle<ExampleB> dpob2 = handle_cast<ExampleB>(dpob1);
	CHECK(dpob2);
	CHECK_EQUAL(dpob2->id(), 2);
	CHECK_EQUAL(dpob2->s(), "goodbye");
	dpob2->set_s("yeah");
	CHECK_EQUAL(dpoc2->s(), "yeah");
	CHECK_EQUAL(dpob1->s(), "yeah");
	dpoc2->set_s("mmm");
	CHECK_EQUAL(dpob2->s(), "mmm");

	Handle<ExampleC> dpoc3;
	CHECK(!dpoc3);
	dpoc3 = handle_cast<ExampleC>(dpob2);
	CHECK(dpoc3);
	CHECK_EQUAL(dpoc3->q(), 501);
	CHECK_EQUAL(dpoc3->id(), 2);
	CHECK_EQUAL(dpoc3->p(), -309);
	CHECK_EQUAL(dpoc3->s(), "mmm");

	Handle<ExampleB> dpoc4;
	dpoc3 = handle_cast<ExampleC>(dpoc4);
	CHECK_THROW(dpoc3->s(), UnboundHandleException);
	CHECK_THROW(dpoc3->p(), UnboundHandleException);
	CHECK(!dpoc3);
}

}  // namespace tests
}  // namespace sqloxx
<commit_msg>Fixed compilation warning re. shadowed variable in test.<commit_after>/*
 * This file is part of the Sqloxx project and is distributed under the
 * terms of the license contained in the file LICENSE.txt distributed
 * with this package.
 *
 * Author: Matthew Harvey <matthew@matthewharvey.net>
 *
 * Copyright (c) 2012-2013, Matthew Harvey.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "example.hpp"
#include "handle.hpp"
#include "sqloxx_tests_common.hpp"
#include <jewel/optional.hpp>
#include <UnitTest++/UnitTest++.h>
#include <utility>

using jewel::UninitializedOptionalException;
using std::move;

namespace sqloxx
{
namespace tests
{

TEST_FIXTURE(ExampleFixture, handle_constructors)
{
	JEWEL_LOG_TRACE();

	Handle<ExampleA> const dpo0;
	CHECK(!dpo0);
	CHECK_THROW(*dpo0, UnboundHandleException);
	CHECK_THROW(dpo0->x(), UnboundHandleException);

	Handle<ExampleA> const dpo1(*pdbc);
	dpo1->set_x(10);
	CHECK_EQUAL(dpo1->x(), 10);
	dpo1->set_y(50000.9812);
	CHECK_EQUAL(dpo1->y(), 50000.9812);
	dpo1->save();

	Handle<ExampleA> const dpo1b(*pdbc, 1);
	CHECK_EQUAL(dpo1b->x(), 10);
	CHECK_EQUAL(dpo1b->y(), 50000.9812);

	Handle<ExampleA> dpo2(*pdbc);
	dpo2->set_x(503);
	dpo2->set_y(-1.3);
	dpo2->save();

	Handle<ExampleA> const dpo2b(*pdbc, 2);
	CHECK_EQUAL(dpo2b->x(), 503);
	CHECK_EQUAL(dpo2b->y(), -1.3);

	Handle<ExampleA> dpo2c(*pdbc, 2);
	CHECK_EQUAL(dpo2c->y(), -1.3);
	CHECK_EQUAL(dpo2c->x(), 503);

	CHECK_THROW
	(	Handle<ExampleA> const dpo3(*pdbc, 3),
		sqloxx::BadIdentifier
	);

	CHECK_THROW
	(	Handle<ExampleA> dpo100(*pdbc, 0),
		sqloxx::BadIdentifier
	);
	JEWEL_LOG_TRACE();
}

TEST_FIXTURE(ExampleFixture, handle_create_unchecked)
{
	JEWEL_LOG_TRACE();

	Handle<ExampleA> const dpo1(*pdbc);
	dpo1->set_x(10);
	CHECK_EQUAL(dpo1->x(), 10);
	dpo1->set_y(50000.9812);
	CHECK_EQUAL(dpo1->y(), 50000.9812);
	dpo1->save();

	Handle<ExampleA> const dpo1b =
		Handle<ExampleA>::create_unchecked(*pdbc, 1);
	CHECK_EQUAL(dpo1b->x(), 10);
	CHECK_EQUAL(dpo1b->y(), 50000.9812);

	Handle<ExampleA> dpo2(*pdbc);
	dpo2->set_x(503);
	dpo2->set_y(-1.3);
	dpo2->save();

	CHECK_EQUAL(Handle<ExampleA>::create_unchecked(*pdbc, 2)->x(), 503);
	Handle<ExampleA> const dpo2b
	(	Handle<ExampleA>::create_unchecked(*pdbc, 2)
	);
	CHECK_EQUAL(dpo2b->y(), -1.3);

	JEWEL_LOG_TRACE();
}

TEST_FIXTURE(ExampleFixture, handle_copy_constructor_and_indirection)
{
	Handle<ExampleA> dpo1(*pdbc);
	dpo1->set_x(-9);
	Handle<ExampleA> dpo2(dpo1);
	dpo2->set_y(102928);
	CHECK_EQUAL(dpo2->x(), -9);
	dpo2->save();
	CHECK_EQUAL(dpo1->id(), 1);
	CHECK_EQUAL(dpo2->id(), dpo1->id());
	CHECK_EQUAL(dpo1->y(), 102928);
	Handle<ExampleA> dpo3(dpo1);
	CHECK_EQUAL(dpo3->id(), 1);
	CHECK_EQUAL(dpo3->y(), 102928);
	CHECK_EQUAL(dpo3->x(), dpo1->x());
}

TEST_FIXTURE(ExampleFixture, handle_move_constructor_and_indirection)
{
	Handle<ExampleA> dpo1(*pdbc);
	dpo1->set_x(-9);
	dpo1->set_y(102928);
	dpo1->save();
	Handle<ExampleA> dpo2(move(dpo1));

	// dpo1 is now "taboo".
	CHECK_EQUAL(dpo2->id(), 1);
	CHECK_EQUAL(dpo2->y(), 102928);
	CHECK_EQUAL(dpo2->x(), -9);
}

TEST_FIXTURE(ExampleFixture, handle_assignment_and_indirection)
{
	Handle<ExampleA> dpo1(*pdbc);
	Handle<ExampleA> dpo2(*pdbc);
	dpo2->set_x(100);
	dpo2->set_y(0.0112);
	dpo2->save();
	dpo1->set_x(897);
	dpo1->set_y(30978);
	dpo2 = dpo1;  // copy assignment
	CHECK_EQUAL(dpo2->x(), dpo1->x());
	CHECK_EQUAL(dpo2->y(), 30978);
	dpo1->save();
	CHECK_EQUAL(dpo2->id(), 2);
	Handle<ExampleA> dpo3(*pdbc, 1);
	CHECK_EQUAL(dpo3->id(), 1);
	dpo3->set_x(-188342392);
	dpo1 = dpo3;  // copy assignment
	CHECK_EQUAL(dpo1->x(), -188342392);
	dpo1->set_y(50);
	CHECK_EQUAL(dpo1->y(), 50);
	dpo1->save();
	CHECK_EQUAL(dpo3->id(), 1);

	Handle<ExampleA> dpo4(*pdbc);
	dpo4 = Handle<ExampleA>(*pdbc,  2);  // move assignment
	CHECK_EQUAL(dpo4->x(), 897);
	CHECK_EQUAL(dpo4->y(), 30978);
	dpo4 = Handle<ExampleA>();  // move assignment
	CHECK(!dpo4);
	dpo4 = Handle<ExampleA>(*pdbc);  // move assignment
	CHECK(dpo4);
}

TEST_FIXTURE(ExampleFixture, handle_dereferencing)
{
	Handle<ExampleA> dpo1(*pdbc);
	dpo1->set_x(10);
	dpo1->set_y(1278.90172);
	dpo1->save();
	ExampleA& dpo1_dereferenced(*dpo1);
	CHECK_EQUAL(dpo1_dereferenced.y(), dpo1->y());
	CHECK_EQUAL((*dpo1).y(), dpo1->y());
	CHECK_EQUAL(dpo1_dereferenced.id(), dpo1->id());
	CHECK_EQUAL(dpo1_dereferenced.x(), 10);
	dpo1_dereferenced.set_y(.504);
	CHECK_EQUAL(dpo1->y(), 0.504);
	Handle<ExampleA> dpo2(*pdbc);
	ExampleA& dpo2_dereferenced = *dpo2;
	dpo2_dereferenced.set_x(8000);
	dpo2_dereferenced.set_y(140);
	CHECK_EQUAL((*dpo2).x(), dpo2_dereferenced.x());
	CHECK_EQUAL(dpo2->y(), (*dpo2).y());
	CHECK_EQUAL(dpo2->y(), 140);

	Handle<ExampleA> dpo4;
	CHECK_THROW(dpo4->y(), UnboundHandleException);
	CHECK_THROW(*dpo4, UnboundHandleException);
}

TEST_FIXTURE(ExampleFixture, handle_conversion_to_bool)
{
	Handle<ExampleA> dpo1(*pdbc);
	CHECK(dpo1);
	CHECK_EQUAL(static_cast<bool>(dpo1), true);
	CHECK(dpo1);
	dpo1->set_y(139000000);
	dpo1->set_x(7);
	dpo1->save();
	CHECK(dpo1);

	// Handle is still valid after underlying object has
	// been removed from the database.
	dpo1->remove();
	CHECK(dpo1);
	CHECK_EQUAL(dpo1->x(), 7);
	CHECK_THROW(dpo1->id(), UninitializedOptionalException);
	CHECK_EQUAL(static_cast<bool>(dpo1), true);
	CHECK(static_cast<bool>(dpo1));

	// However handle is not valid if completely uninitialized.
	Handle<ExampleA> dpo2;
	CHECK_EQUAL(static_cast<bool>(dpo2), false);
	dpo1 = dpo2;
	CHECK_EQUAL(static_cast<bool>(dpo2), false);
	CHECK(!dpo2);

}

TEST_FIXTURE(ExampleFixture, handle_equality_and_inequality)
{
	Handle<ExampleA> dpo1(*pdbc);
	Handle<ExampleA> dpo2(dpo1);
	CHECK(dpo1 == dpo2);
	CHECK(dpo2 == dpo1);

	Handle<ExampleA> const dpo3(*pdbc);
	dpo3->set_x(109);
	dpo3->set_y(.5);
	dpo3->save();
	CHECK(dpo3 != dpo1);
	CHECK(dpo2 != dpo3);

	Handle<ExampleA> dpo4(*pdbc, 1);
	CHECK(dpo4 == dpo3);
	CHECK(dpo3 == dpo4);

	Handle<ExampleA> const dpo5(*pdbc);
	dpo5->save();

	Handle<ExampleA> const dpo6(*pdbc, 2);
	CHECK(dpo6 == dpo5);
	CHECK(dpo5 == dpo6);
	CHECK(dpo6 != dpo4);
	CHECK(dpo3 != dpo6);
	CHECK(dpo4 != dpo6);
}

TEST_FIXTURE(ExampleFixture, handle_cast_test)
{
	Handle<ExampleC> dpoc1(*pdbc);
	dpoc1->set_s("hello");
	dpoc1->set_p(3);
	dpoc1->set_q(50);
	dpoc1->save();

	Handle<ExampleC> dpoc2(*pdbc);
	dpoc2->set_s("goodbye");
	dpoc2->set_p(-309);
	dpoc2->set_q(501);
	dpoc2->save();

	Handle<ExampleB> dpob1;
	CHECK_THROW(*dpob1, UnboundHandleException);
	dpob1 = handle_cast<ExampleB>(dpoc2);
	CHECK(dpob1);
	CHECK_EQUAL(dpob1->id(), 2);
	CHECK_EQUAL(dpob1->s(), "goodbye");

	Handle<ExampleB> dpob2 = handle_cast<ExampleB>(dpob1);
	CHECK(dpob2);
	CHECK_EQUAL(dpob2->id(), 2);
	CHECK_EQUAL(dpob2->s(), "goodbye");
	dpob2->set_s("yeah");
	CHECK_EQUAL(dpoc2->s(), "yeah");
	CHECK_EQUAL(dpob1->s(), "yeah");
	dpoc2->set_s("mmm");
	CHECK_EQUAL(dpob2->s(), "mmm");

	Handle<ExampleC> dpoc3;
	CHECK(!dpoc3);
	dpoc3 = handle_cast<ExampleC>(dpob2);
	CHECK(dpoc3);
	CHECK_EQUAL(dpoc3->q(), 501);
	CHECK_EQUAL(dpoc3->id(), 2);
	CHECK_EQUAL(dpoc3->p(), -309);
	CHECK_EQUAL(dpoc3->s(), "mmm");

	Handle<ExampleB> dpoc4;
	dpoc3 = handle_cast<ExampleC>(dpoc4);
	CHECK_THROW(dpoc3->s(), UnboundHandleException);
	CHECK_THROW(dpoc3->p(), UnboundHandleException);
	CHECK(!dpoc3);
}

}  // namespace tests
}  // namespace sqloxx
<|endoftext|>


<commit_before>// This program takes two args:
// 1. the path to the sites file, like /foo/sites/sites.tsv
// 2. the path to the augmented pheno directory, like /foo/augmented_phenos/
// The augmented pheno files must look somewhat like:
//      chr    pos  ref  alt       rsids  nearest_genes      maf     pval
//        1  49298    T    C  rs10399793         OR4G4P  0.36596  0.73483
//        1  54676    C    T   rs2462492         OR4G4P   0.3857  0.52691
// The sites file is like that but with only [chr, pos, ref, alt, rsid, nearest_genes] and without headers.
// Every augmented pheno file must be a subsequence of the sites file.
// This program will print the full matrix to stdout.

#include <iterator>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <glob.h>
#include <assert.h>
#include <stdlib.h>
#include <sys/resource.h> // setrlimit
#include <stdio.h>
#include <errno.h>

std::vector<std::string> glob(const std::string& pat) {
  // From <http://stackoverflow.com/a/8615450/1166306>
  glob_t glob_result;
  glob(pat.c_str(),GLOB_TILDE,NULL,&glob_result);
  std::vector<std::string> ret;
  for(unsigned int i=0;i<glob_result.gl_pathc;++i){
    ret.push_back(std::string(glob_result.gl_pathv[i]));
  }
  globfree(&glob_result);
  return ret;
}

class CSVRow { // From <http://stackoverflow.com/questions/1120140/how-can-i-read-and-parse-csv-files-in-c>
public:
  std::string const& operator[](std::size_t index) const { return m_data[index]; }
  std::size_t size() const { return m_data.size(); }
  void readNextRow(std::istream& str) {
    std::string line;
    std::getline(str, line);
    // CR remover from <http://stackoverflow.com/a/2529011/1166306>
    if (!line.empty() && line[line.size() - 1] == '\r')
      line.erase(line.size() - 1);
    std::stringstream lineStream(line);
    std::string cell;
    m_data.clear();
    while(std::getline(lineStream, cell, '\t')) {
      m_data.push_back(cell);
    }
    if (!lineStream && cell.empty()) { //check for trailing empty cell
      m_data.push_back("");
    }
  }
private:
  std::vector<std::string> m_data;
};

std::istream& operator>>(std::istream& str, CSVRow& data) {
  data.readNextRow(str);
  return str;
}

class CSVIterator {
public:
  typedef std::input_iterator_tag iterator_category;
  typedef CSVRow value_type;
  typedef std::size_t difference_type;
  typedef CSVRow *pointer;
  typedef CSVRow &reference;
  CSVIterator(std::istream& stream) : m_str(stream.good()?&stream:NULL) { ++(*this); }
  CSVIterator(std::ifstream& stream) : m_str(stream.good()?&stream:NULL) { ++(*this); }
  CSVIterator() : m_str(NULL) {}
  CSVIterator& operator++() {
    if (m_str) {
      if (!((*m_str) >> m_row)){
        m_str = NULL;
      }
    }
    return *this;
  }
  CSVIterator operator++(int) {
    CSVIterator tmp(*this);
    ++(*this);
    return tmp;
  }
  CSVRow const& operator*() const { return m_row; }
  CSVRow const* operator->() const { return &m_row; }
  bool operator==(CSVIterator const& rhs) { return ((this == &rhs) || ((this->m_str == NULL) && (rhs.m_str == NULL))); }
  bool operator!=(CSVIterator const& rhs) { return !((*this) == rhs); }
private:
  std::istream *m_str;
  CSVRow m_row;
};

class ColIdx {
  // TODO: Long-term, how can we support arbitrary columns?
public:
  int CHR_COL = -1;
  int POS_COL = -1;
  int REF_COL = -1;
  int ALT_COL = -1;
  int RSID_COL = -1;
  int GENE_COL = -1;
  int MAF_COL = -1;
  int PVAL_COL = -1;
  int BETA_COL = -1;
  int SEBETA_COL = -1;
};

bool is_empty(std::ifstream& f)
{
  return f.peek() == std::ifstream::traits_type::eof();
}

void set_ulimit_num_files(unsigned num_files) {
  struct rlimit limit;
  limit.rlim_cur = num_files;
  limit.rlim_max = num_files;
  if (setrlimit(RLIMIT_NOFILE, &limit) != 0) {
    std::cerr << "setrlimit() failed with errno=" << errno << "\n";
    exit(1);
  }
}

int main(int argc, char** argv) {

  //sites file
  std::string sites_fname = argv[1];
  std::ifstream sites_file(sites_fname);
  CSVIterator sites_it(sites_file);
  const int SITES_CHR_COL = 0;
  const int SITES_POS_COL = 1;
  const int SITES_REF_COL = 2;
  const int SITES_ALT_COL = 3;
  const int SITES_RSID_COL = 4;
  const int SITES_GENE_COL = 5;

  // augmented_pheno files
  std::string phenos_dir = argv[2];
  phenos_dir += "/*";
  std::vector<std::string> aug_fpaths = glob(phenos_dir.c_str());
  std::vector<std::string> aug_fnames(aug_fpaths.size());
  std::vector<std::ifstream*> aug_files(aug_fpaths.size()); // ifstreams have a problem with copies, so just pre-allocate.
  std::vector<CSVIterator*> aug_its(aug_fpaths.size());
  std::vector<ColIdx> aug_colidx(aug_fpaths.size());
  set_ulimit_num_files(aug_fpaths.size() + 5); // stdin, stdout, stderr, sites_file, ???

  for (size_t i = 0; i != aug_fnames.size(); i++) {
    aug_fnames[i] = aug_fpaths[i];
    size_t last_slash_idx = aug_fnames[i].find_last_of("/");
    if (std::string::npos != last_slash_idx) {
      aug_fnames[i].erase(0, last_slash_idx + 1);
    }
    std::ifstream* f = new std::ifstream(aug_fpaths[i].c_str());
    aug_files[i] = f;
    CSVIterator *it = new CSVIterator(*f);
    aug_its[i] = it;

    CSVRow row = **aug_its[i];
    for (int fieldname_index = 0; fieldname_index < row.size(); fieldname_index++) {
      if (row[fieldname_index] == "chrom") {
        aug_colidx[i].CHR_COL = fieldname_index;
      } else if (row[fieldname_index] == "pos") {
        aug_colidx[i].POS_COL = fieldname_index;
      } else if (row[fieldname_index] == "ref") {
        aug_colidx[i].REF_COL = fieldname_index;
      } else if (row[fieldname_index] == "alt") {
        aug_colidx[i].ALT_COL = fieldname_index;
      } else if (row[fieldname_index] == "rsids") {
        aug_colidx[i].RSID_COL = fieldname_index;
      } else if (row[fieldname_index] == "nearest_genes") {
        aug_colidx[i].GENE_COL = fieldname_index;
      } else if (row[fieldname_index] == "maf") {
        aug_colidx[i].MAF_COL = fieldname_index;
      } else if (row[fieldname_index] == "pval") {
        aug_colidx[i].PVAL_COL = fieldname_index;
      } else if (row[fieldname_index] == "beta") {
        aug_colidx[i].BETA_COL = fieldname_index;
      } else if (row[fieldname_index] == "sebeta") {
        aug_colidx[i].SEBETA_COL = fieldname_index;
      } else {
        std::cerr << "What is this column \"" << row[fieldname_index] << "\"?" << std::endl;
        return 1;
      }
    }
    (*aug_its[i])++; // go past header.
    // TODO: can I: row = (*aug_its[i])++; ?
  }

  // Print header line.
  std::cout << "#chrom\tpos\tref\talt\trsids\tnearest_genes\tmaf";
  for (size_t i = 0; i != aug_fnames.size(); i++) {
    std::cout << "\tpval@" << aug_fnames[i];
    if (aug_colidx[i].BETA_COL != -1) {
      std::cout << "\tbeta@" << aug_fnames[i];
    }
    if (aug_colidx[i].SEBETA_COL != -1) {
      std::cout << "\tsebeta@" << aug_fnames[i];
    }
  }
  std::cout << "\n";

  // Combine the remaining lines of each file.
  for(; sites_it != CSVIterator(); ++sites_it) {

    CSVRow sites_row = *sites_it;
    std::cout << sites_row[SITES_CHR_COL] << "\t"
              << sites_row[SITES_POS_COL] << "\t"
              << sites_row[SITES_REF_COL] << "\t"
              << sites_row[SITES_ALT_COL] << "\t"
              << sites_row[SITES_RSID_COL] << "\t"
              << sites_row[SITES_GENE_COL] << "\t";

    int num_mafs = 0;
    double maf = 0;
    std::ostringstream all_the_rest; // gotta hold these while you calculate avg maf
    for (size_t i = 0; i < aug_fnames.size(); i++) {

      if (*aug_its[i] == CSVIterator()) {
        // just print blanks and continue
        all_the_rest << "\t.";
        if (aug_colidx[i].BETA_COL != -1) all_the_rest << "\t.";
        if (aug_colidx[i].SEBETA_COL != -1) all_the_rest << "\t.";
        continue;
      }

      // load current variant
      CSVRow row = **aug_its[i];

      if (row[aug_colidx[i].CHR_COL] == sites_row[SITES_CHR_COL] &&
          row[aug_colidx[i].POS_COL] == sites_row[SITES_POS_COL] &&
          row[aug_colidx[i].REF_COL] == sites_row[SITES_REF_COL] &&
          row[aug_colidx[i].ALT_COL] == sites_row[SITES_ALT_COL]) {

        // print row
        if (row[aug_colidx[i].RSID_COL] != sites_row[SITES_RSID_COL]) {
          std::cerr << "rsids disagree: [" << row[aug_colidx[i].RSID_COL] << "] [" << sites_row[SITES_RSID_COL] << "]" << std::endl;
          exit(1);
        }
        if (row[aug_colidx[i].GENE_COL] != sites_row[SITES_GENE_COL]) {
          std::cerr << "genes disagree: [" << row[aug_colidx[i].GENE_COL] << "] [" << sites_row[SITES_GENE_COL] << "]" << std::endl;
          exit(1);
        }

        maf += atof(row[aug_colidx[i].MAF_COL].c_str());
        num_mafs++;
        all_the_rest << "\t" << row[aug_colidx[i].PVAL_COL];
        if (aug_colidx[i].BETA_COL != -1) all_the_rest << "\t" << row[aug_colidx[i].BETA_COL];
        if (aug_colidx[i].SEBETA_COL != -1) all_the_rest << "\t" << row[aug_colidx[i].BETA_COL];
        (*aug_its[i])++;
      } else {
        // print blanks

        all_the_rest << "\t.";
        if (aug_colidx[i].BETA_COL != -1) all_the_rest << "\t.";
        if (aug_colidx[i].SEBETA_COL != -1) all_the_rest << "\t.";
      }
    }

    std::cout << maf/num_mafs << "\t" << all_the_rest.str() << "\n";
  }
}
<commit_msg>Remove extra tab from matrix.tsv.gz<commit_after>// This program takes two args:
// 1. the path to the sites file, like /foo/sites/sites.tsv
// 2. the path to the augmented pheno directory, like /foo/augmented_phenos/
// The augmented pheno files must look somewhat like:
//      chr    pos  ref  alt       rsids  nearest_genes      maf     pval
//        1  49298    T    C  rs10399793         OR4G4P  0.36596  0.73483
//        1  54676    C    T   rs2462492         OR4G4P   0.3857  0.52691
// The sites file is like that but with only [chr, pos, ref, alt, rsid, nearest_genes] and without headers.
// Every augmented pheno file must be a subsequence of the sites file.
// This program will print the full matrix to stdout.

#include <iterator>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <glob.h>
#include <assert.h>
#include <stdlib.h>
#include <sys/resource.h> // setrlimit
#include <stdio.h>
#include <errno.h>

std::vector<std::string> glob(const std::string& pat) {
  // From <http://stackoverflow.com/a/8615450/1166306>
  glob_t glob_result;
  glob(pat.c_str(),GLOB_TILDE,NULL,&glob_result);
  std::vector<std::string> ret;
  for(unsigned int i=0;i<glob_result.gl_pathc;++i){
    ret.push_back(std::string(glob_result.gl_pathv[i]));
  }
  globfree(&glob_result);
  return ret;
}

class CSVRow { // From <http://stackoverflow.com/questions/1120140/how-can-i-read-and-parse-csv-files-in-c>
public:
  std::string const& operator[](std::size_t index) const { return m_data[index]; }
  std::size_t size() const { return m_data.size(); }
  void readNextRow(std::istream& str) {
    std::string line;
    std::getline(str, line);
    // CR remover from <http://stackoverflow.com/a/2529011/1166306>
    if (!line.empty() && line[line.size() - 1] == '\r')
      line.erase(line.size() - 1);
    std::stringstream lineStream(line);
    std::string cell;
    m_data.clear();
    while(std::getline(lineStream, cell, '\t')) {
      m_data.push_back(cell);
    }
    if (!lineStream && cell.empty()) { //check for trailing empty cell
      m_data.push_back("");
    }
  }
private:
  std::vector<std::string> m_data;
};

std::istream& operator>>(std::istream& str, CSVRow& data) {
  data.readNextRow(str);
  return str;
}

class CSVIterator {
public:
  typedef std::input_iterator_tag iterator_category;
  typedef CSVRow value_type;
  typedef std::size_t difference_type;
  typedef CSVRow *pointer;
  typedef CSVRow &reference;
  CSVIterator(std::istream& stream) : m_str(stream.good()?&stream:NULL) { ++(*this); }
  CSVIterator(std::ifstream& stream) : m_str(stream.good()?&stream:NULL) { ++(*this); }
  CSVIterator() : m_str(NULL) {}
  CSVIterator& operator++() {
    if (m_str) {
      if (!((*m_str) >> m_row)){
        m_str = NULL;
      }
    }
    return *this;
  }
  CSVIterator operator++(int) {
    CSVIterator tmp(*this);
    ++(*this);
    return tmp;
  }
  CSVRow const& operator*() const { return m_row; }
  CSVRow const* operator->() const { return &m_row; }
  bool operator==(CSVIterator const& rhs) { return ((this == &rhs) || ((this->m_str == NULL) && (rhs.m_str == NULL))); }
  bool operator!=(CSVIterator const& rhs) { return !((*this) == rhs); }
private:
  std::istream *m_str;
  CSVRow m_row;
};

class ColIdx {
  // TODO: Long-term, how can we support arbitrary columns?
public:
  int CHR_COL = -1;
  int POS_COL = -1;
  int REF_COL = -1;
  int ALT_COL = -1;
  int RSID_COL = -1;
  int GENE_COL = -1;
  int MAF_COL = -1;
  int PVAL_COL = -1;
  int BETA_COL = -1;
  int SEBETA_COL = -1;
};

bool is_empty(std::ifstream& f)
{
  return f.peek() == std::ifstream::traits_type::eof();
}

void set_ulimit_num_files(unsigned num_files) {
  struct rlimit limit;
  limit.rlim_cur = num_files;
  limit.rlim_max = num_files;
  if (setrlimit(RLIMIT_NOFILE, &limit) != 0) {
    std::cerr << "setrlimit() failed with errno=" << errno << "\n";
    exit(1);
  }
}

int main(int argc, char** argv) {

  //sites file
  std::string sites_fname = argv[1];
  std::ifstream sites_file(sites_fname);
  CSVIterator sites_it(sites_file);
  const int SITES_CHR_COL = 0;
  const int SITES_POS_COL = 1;
  const int SITES_REF_COL = 2;
  const int SITES_ALT_COL = 3;
  const int SITES_RSID_COL = 4;
  const int SITES_GENE_COL = 5;

  // augmented_pheno files
  std::string phenos_dir = argv[2];
  phenos_dir += "/*";
  std::vector<std::string> aug_fpaths = glob(phenos_dir.c_str());
  std::vector<std::string> aug_fnames(aug_fpaths.size());
  std::vector<std::ifstream*> aug_files(aug_fpaths.size()); // ifstreams have a problem with copies, so just pre-allocate.
  std::vector<CSVIterator*> aug_its(aug_fpaths.size());
  std::vector<ColIdx> aug_colidx(aug_fpaths.size());
  set_ulimit_num_files(aug_fpaths.size() + 5); // stdin, stdout, stderr, sites_file, ???

  for (size_t i = 0; i != aug_fnames.size(); i++) {
    aug_fnames[i] = aug_fpaths[i];
    size_t last_slash_idx = aug_fnames[i].find_last_of("/");
    if (std::string::npos != last_slash_idx) {
      aug_fnames[i].erase(0, last_slash_idx + 1);
    }
    std::ifstream* f = new std::ifstream(aug_fpaths[i].c_str());
    aug_files[i] = f;
    CSVIterator *it = new CSVIterator(*f);
    aug_its[i] = it;

    CSVRow row = **aug_its[i];
    for (int fieldname_index = 0; fieldname_index < row.size(); fieldname_index++) {
      if (row[fieldname_index] == "chrom") {
        aug_colidx[i].CHR_COL = fieldname_index;
      } else if (row[fieldname_index] == "pos") {
        aug_colidx[i].POS_COL = fieldname_index;
      } else if (row[fieldname_index] == "ref") {
        aug_colidx[i].REF_COL = fieldname_index;
      } else if (row[fieldname_index] == "alt") {
        aug_colidx[i].ALT_COL = fieldname_index;
      } else if (row[fieldname_index] == "rsids") {
        aug_colidx[i].RSID_COL = fieldname_index;
      } else if (row[fieldname_index] == "nearest_genes") {
        aug_colidx[i].GENE_COL = fieldname_index;
      } else if (row[fieldname_index] == "maf") {
        aug_colidx[i].MAF_COL = fieldname_index;
      } else if (row[fieldname_index] == "pval") {
        aug_colidx[i].PVAL_COL = fieldname_index;
      } else if (row[fieldname_index] == "beta") {
        aug_colidx[i].BETA_COL = fieldname_index;
      } else if (row[fieldname_index] == "sebeta") {
        aug_colidx[i].SEBETA_COL = fieldname_index;
      } else {
        std::cerr << "What is this column \"" << row[fieldname_index] << "\"?" << std::endl;
        return 1;
      }
    }
    (*aug_its[i])++; // go past header.
    // TODO: can I: row = (*aug_its[i])++; ?
  }

  // Print header line.
  std::cout << "#chrom\tpos\tref\talt\trsids\tnearest_genes\tmaf";
  for (size_t i = 0; i != aug_fnames.size(); i++) {
    std::cout << "\tpval@" << aug_fnames[i];
    if (aug_colidx[i].BETA_COL != -1) {
      std::cout << "\tbeta@" << aug_fnames[i];
    }
    if (aug_colidx[i].SEBETA_COL != -1) {
      std::cout << "\tsebeta@" << aug_fnames[i];
    }
  }
  std::cout << "\n";

  // Combine the remaining lines of each file.
  for(; sites_it != CSVIterator(); ++sites_it) {

    CSVRow sites_row = *sites_it;
    std::cout << sites_row[SITES_CHR_COL] << "\t"
              << sites_row[SITES_POS_COL] << "\t"
              << sites_row[SITES_REF_COL] << "\t"
              << sites_row[SITES_ALT_COL] << "\t"
              << sites_row[SITES_RSID_COL] << "\t"
              << sites_row[SITES_GENE_COL];

    int num_mafs = 0;
    double maf = 0;
    std::ostringstream all_the_rest; // gotta hold these while you calculate avg maf
    for (size_t i = 0; i < aug_fnames.size(); i++) {

      if (*aug_its[i] == CSVIterator()) {
        // just print blanks and continue
        all_the_rest << "\t.";
        if (aug_colidx[i].BETA_COL != -1) all_the_rest << "\t.";
        if (aug_colidx[i].SEBETA_COL != -1) all_the_rest << "\t.";
        continue;
      }

      // load current variant
      CSVRow row = **aug_its[i];

      if (row[aug_colidx[i].CHR_COL] == sites_row[SITES_CHR_COL] &&
          row[aug_colidx[i].POS_COL] == sites_row[SITES_POS_COL] &&
          row[aug_colidx[i].REF_COL] == sites_row[SITES_REF_COL] &&
          row[aug_colidx[i].ALT_COL] == sites_row[SITES_ALT_COL]) {

        // print row
        if (row[aug_colidx[i].RSID_COL] != sites_row[SITES_RSID_COL]) {
          std::cerr << "rsids disagree: [" << row[aug_colidx[i].RSID_COL] << "] [" << sites_row[SITES_RSID_COL] << "]" << std::endl;
          exit(1);
        }
        if (row[aug_colidx[i].GENE_COL] != sites_row[SITES_GENE_COL]) {
          std::cerr << "genes disagree: [" << row[aug_colidx[i].GENE_COL] << "] [" << sites_row[SITES_GENE_COL] << "]" << std::endl;
          exit(1);
        }

        maf += atof(row[aug_colidx[i].MAF_COL].c_str());
        num_mafs++;
        all_the_rest << "\t" << row[aug_colidx[i].PVAL_COL];
        if (aug_colidx[i].BETA_COL != -1) all_the_rest << "\t" << row[aug_colidx[i].BETA_COL];
        if (aug_colidx[i].SEBETA_COL != -1) all_the_rest << "\t" << row[aug_colidx[i].BETA_COL];
        (*aug_its[i])++;
      } else {
        // print blanks

        all_the_rest << "\t.";
        if (aug_colidx[i].BETA_COL != -1) all_the_rest << "\t.";
        if (aug_colidx[i].SEBETA_COL != -1) all_the_rest << "\t.";
      }
    }

    std::cout << "\t" << maf/num_mafs << all_the_rest.str() << "\n";
  }
}
<|endoftext|>


<commit_before>//
//  FileDir.m
//  FileDir
//
//  Created by Daniel Cohen Gindi on 6/24/14.
//  Copyright (c) 2013 Daniel Cohen Gindi. All rights reserved.
//
//  https://github.com/danielgindi/FileDir
//
//  The MIT License (MIT)
//
//  Copyright (c) 2014 Daniel Cohen Gindi (danielgindi@gmail.com)
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in all
//  copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//  SOFTWARE.
//

#include "FileDir.h"

#include <stdlib.h>
#include <string.h>

#ifdef _MSC_VER
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif

#ifndef FILEDIR_CHAR

#ifdef _MSC_VER
#define FILEDIR_CHAR wchar_t
#define ustrrchr wcsrchr
#define ustrlen wcslen
#define ustrdup _wcsdup
#else
#define FILEDIR_CHAR char
#define ustrrchr strrchr
#define ustrlen strlen
#define ustrdup strdup
#endif

#endif

FileDir::FileDir(void)
{
	_fullPath = NULL;
	_fileName = NULL;
	_cachedFullPath = _cachedFileName = _cachedExtension = _cachedFileNameWithoutExtension = NULL;
	_isFolder = _isFile = false;
}

FileDir::~FileDir(void)
{
	if (_fullPath)
	{
		delete [] _fullPath;
		_fullPath = NULL;
	}

	if (_fileName)
	{
		delete [] _fileName;
		_fileName = NULL;
	}

	if (_cachedFileNameWithoutExtension)
	{
		delete [] _cachedFileNameWithoutExtension;
		_cachedFileNameWithoutExtension = NULL;
	}

	_cachedFullPath = _cachedFileName = _cachedExtension = NULL;
}

void FileDir::SetFullPath(const FILEDIR_CHAR *fullPath)
{
	if (_fullPath)
	{
		delete [] _fullPath;
		_fullPath = NULL;
	}

	if (_fileName)
	{
		delete [] _fileName;
		_fileName = NULL;
	}

	if (fullPath)
	{
		_fullPath = ustrdup(fullPath);

		const FILEDIR_CHAR *separator = ustrrchr(_fullPath, '/');
		if (!separator) separator = ustrrchr(_fullPath, '\\');
		if (separator && separator[1] == '\0')
		{
			while (separator > _fullPath && --separator && (separator[0] != '/' && separator[0] != '\\'));
		}
		if (separator && !(separator == _fullPath && separator[1] == '\0'))
		{
			_fileName = ustrdup(separator + 1);
		}
		else
		{
			_fileName = ustrdup(_fullPath);
		}
	}
}

const FILEDIR_CHAR * FileDir::GetExtension()
{
	if (_fullPath != _cachedFullPath)
	{
		_cachedFullPath = _fullPath;
		_cachedExtension = NULL;
	}

	if (!_fullPath) return NULL;

	if (!_cachedExtension)
	{
		FILEDIR_CHAR *period = ustrrchr(_fullPath, '.');
		if (period == NULL)
		{
			return &(_fullPath[ustrlen(_fullPath)]);
		}
		else
		{
			return period + 1;
		}
	}

	return _cachedExtension;
}

const FILEDIR_CHAR * FileDir::GetFileNameWithoutExtension()
{
	if (_fileName != _cachedFileName)
	{
		_cachedFileName = _fileName;
		if (_cachedFileNameWithoutExtension)
		{
			delete [] _cachedFileNameWithoutExtension;
			_cachedFileNameWithoutExtension = NULL;
		}
	}

	if (!_fileName) return NULL;

	if (!_cachedExtension)
	{
		FILEDIR_CHAR *period = ustrrchr(_fileName, '.');
		if (period == NULL)
		{
			_cachedFileNameWithoutExtension = ustrdup(_fileName);
		}
		else
		{
			int periodIndex = (int)(period - _fileName);
			FILEDIR_CHAR *fileNameWithoutExtension = new FILEDIR_CHAR[periodIndex + 1];
			memcpy(fileNameWithoutExtension, _fileName, sizeof(FILEDIR_CHAR) * periodIndex);
			fileNameWithoutExtension[periodIndex] = '\0';
			_cachedFileNameWithoutExtension = fileNameWithoutExtension;
		}
	}

	return _cachedFileNameWithoutExtension;
}<commit_msg>was not correctly caching extension<commit_after>//
//  FileDir.m
//  FileDir
//
//  Created by Daniel Cohen Gindi on 6/24/14.
//  Copyright (c) 2013 Daniel Cohen Gindi. All rights reserved.
//
//  https://github.com/danielgindi/FileDir
//
//  The MIT License (MIT)
//
//  Copyright (c) 2014 Daniel Cohen Gindi (danielgindi@gmail.com)
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in all
//  copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//  SOFTWARE.
//

#include "FileDir.h"

#include <stdlib.h>
#include <string.h>

#ifdef _MSC_VER
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif

#ifndef FILEDIR_CHAR

#ifdef _MSC_VER
#define FILEDIR_CHAR wchar_t
#define ustrrchr wcsrchr
#define ustrlen wcslen
#define ustrdup _wcsdup
#else
#define FILEDIR_CHAR char
#define ustrrchr strrchr
#define ustrlen strlen
#define ustrdup strdup
#endif

#endif

FileDir::FileDir(void)
{
	_fullPath = NULL;
	_fileName = NULL;
	_cachedFullPath = _cachedFileName = _cachedExtension = _cachedFileNameWithoutExtension = NULL;
	_isFolder = _isFile = false;
}

FileDir::~FileDir(void)
{
	if (_fullPath)
	{
		delete [] _fullPath;
		_fullPath = NULL;
	}

	if (_fileName)
	{
		delete [] _fileName;
		_fileName = NULL;
	}

	if (_cachedFileNameWithoutExtension)
	{
		delete [] _cachedFileNameWithoutExtension;
		_cachedFileNameWithoutExtension = NULL;
	}

	_cachedFullPath = _cachedFileName = _cachedExtension = NULL;
}

void FileDir::SetFullPath(const FILEDIR_CHAR *fullPath)
{
	if (_fullPath)
	{
		delete [] _fullPath;
		_fullPath = NULL;
	}

	if (_fileName)
	{
		delete [] _fileName;
		_fileName = NULL;
	}

	if (fullPath)
	{
		_fullPath = ustrdup(fullPath);

		const FILEDIR_CHAR *separator = ustrrchr(_fullPath, '/');
		if (!separator) separator = ustrrchr(_fullPath, '\\');
		if (separator && separator[1] == '\0')
		{
			while (separator > _fullPath && --separator && (separator[0] != '/' && separator[0] != '\\'));
		}
		if (separator && !(separator == _fullPath && separator[1] == '\0'))
		{
			_fileName = ustrdup(separator + 1);
		}
		else
		{
			_fileName = ustrdup(_fullPath);
		}
	}
}

const FILEDIR_CHAR * FileDir::GetExtension()
{
	if (_fullPath != _cachedFullPath)
	{
		_cachedFullPath = _fullPath;
		_cachedExtension = NULL;
	}

	if (!_fullPath) return NULL;

	if (!_cachedExtension)
	{
		FILEDIR_CHAR *period = ustrrchr(_fullPath, '.');
		if (period == NULL)
		{
			_cachedExtension = &(_fullPath[ustrlen(_fullPath)]);
		}
		else
		{
			_cachedExtension = period + 1;
		}
	}

	return _cachedExtension;
}

const FILEDIR_CHAR * FileDir::GetFileNameWithoutExtension()
{
	if (_fileName != _cachedFileName)
	{
		_cachedFileName = _fileName;
		if (_cachedFileNameWithoutExtension)
		{
			delete [] _cachedFileNameWithoutExtension;
			_cachedFileNameWithoutExtension = NULL;
		}
	}

	if (!_fileName) return NULL;

	if (!_cachedExtension)
	{
		FILEDIR_CHAR *period = ustrrchr(_fileName, '.');
		if (period == NULL)
		{
			_cachedFileNameWithoutExtension = ustrdup(_fileName);
		}
		else
		{
			int periodIndex = (int)(period - _fileName);
			FILEDIR_CHAR *fileNameWithoutExtension = new FILEDIR_CHAR[periodIndex + 1];
			memcpy(fileNameWithoutExtension, _fileName, sizeof(FILEDIR_CHAR) * periodIndex);
			fileNameWithoutExtension[periodIndex] = '\0';
			_cachedFileNameWithoutExtension = fileNameWithoutExtension;
		}
	}

	return _cachedFileNameWithoutExtension;
}<|endoftext|>


<commit_before>#include "vendor/docopt/docopt.hpp"
#include <thewizardplusplus/wizard_parser/lexer/lexeme.hpp>
#include <thewizardplusplus/wizard_parser/parser/parsers.hpp>
#include <thewizardplusplus/wizard_parser/parser/macroses.hpp>
#include <thewizardplusplus/wizard_parser/lexer/tokenizer.hpp>
#include <thewizardplusplus/wizard_parser/parser/parse.hpp>
#include <regex>
#include <vector>
#include <string>
#include <unordered_set>
#include <iostream>
#include <iterator>
#include <cstdlib>
#include <exception>

using namespace thewizardplusplus::wizard_parser::lexer;
using namespace thewizardplusplus::wizard_parser::parser;
using namespace thewizardplusplus::wizard_parser::parser::operators;

const auto usage =
R"(Usage:
  ./example -h | --help
  ./example [-t | --tokens] <expression>
  ./example [-t | --tokens] (-s | --stdin)

Options:
  -h, --help    - show this message;
  -t, --tokens  - show a token list instead an AST;
  -s, --stdin   - read an expression from stdin.)";
const auto lexemes = std::vector<lexeme>{
	{std::regex{"=="}, "equal"},
	{std::regex{"/="}, "not_equal"},
	{std::regex{"<"}, "less"},
	{std::regex{"<="}, "less_or_equal"},
	{std::regex{">"}, "great"},
	{std::regex{">="}, "great_or_equal"},
	{std::regex{R"(\+)"}, "plus"},
	{std::regex{"-"}, "minus"},
	{std::regex{R"(\*)"}, "star"},
	{std::regex{"/"}, "slash"},
	{std::regex{"%"}, "percent"},
	{std::regex{R"(\()"}, "opening_parenthesis"},
	{std::regex{R"(\))"}, "closing_parenthesis"},
	{std::regex{","}, "comma"},
	{std::regex{R"(\d+(?:\.\d+)?(?:e-?\d+)?)"}, "number_constant"},
	{std::regex{R"([A-Za-z_]\w*)"}, "base_identifier"},
	{std::regex{R"(\s+)"}, "whitespace"}
};
const auto ignorable_tokens = std::unordered_set<std::string>{"whitespace"};

namespace {

rule_parser::pointer make_atom_parser(const rule_parser::pointer& expression) {
	RULE(number_constant) = "number_constant"_t;
	RULE(key_words) = "and"_v | "not"_v | "or"_v;
	RULE(identifier) = "base_identifier"_t - key_words;
	IMPORTANT_RULE(function_call) = identifier >> &"("_v >>
		-(expression >> *(&","_v >> expression))
	>> &")"_v;
	return number_constant
		| identifier
		| function_call
		| (&"("_v >> expression >> &")"_v);
}

rule_parser::pointer make_expression_parser() {
	const auto expression_dummy = dummy();
	RULE(atom) = make_atom_parser(expression_dummy);
	RULE(unary) = *("-"_v | "not"_v) >> atom;
	RULE(product) = unary >> *(("*"_v | "/"_v | "%"_v) >> unary);
	RULE(sum) = product >> *(("+"_v | "-"_v) >> product);
	RULE(comparison) = sum >> *(("<"_v | "<="_v | ">"_v | ">="_v) >> sum);
	RULE(equality) = comparison >> *(("=="_v | "/="_v) >> comparison);
	RULE(conjunction) = equality >> *(&"and"_v >> equality);
	RULE(disjunction) = conjunction >> *(&"or"_v >> conjunction);
	expression_dummy->set_parser(disjunction);

	return disjunction >> eoi();
}

}

int main(int argc, char* argv[]) try {
	auto code = std::string{};
	const auto options = docopt::docopt(usage, {argv + 1, argv + argc}, true);
	if (!options.at("--stdin").asBool()) {
		code = options.at("<expression>").asString();
	} else {
		code = std::string{std::istreambuf_iterator<char>{std::cin}, {}};
	}

	const auto tokens = tokenizer{lexemes, ignorable_tokens, code}.tokenize();
	if (options.at("--tokens").asBool()) {
		std::cout << tokens << '\n';
		std::exit(EXIT_SUCCESS);
	}

	RULE(expression) = make_expression_parser();
	const auto ast = parse(expression, tokens, code.size());
	std::cout << ast << '\n';
} catch (const std::exception& exception) {
	std::cerr << "error: " << exception.what() << '\n';
	std::exit(EXIT_FAILURE);
}
<commit_msg>Issue #12: correct the example<commit_after>#include "vendor/docopt/docopt.hpp"
#include <thewizardplusplus/wizard_parser/lexer/lexeme.hpp>
#include <thewizardplusplus/wizard_parser/parser/parsers.hpp>
#include <thewizardplusplus/wizard_parser/parser/macroses.hpp>
#include <thewizardplusplus/wizard_parser/lexer/tokenize.hpp>
#include <thewizardplusplus/wizard_parser/parser/parse.hpp>
#include <regex>
#include <vector>
#include <string>
#include <unordered_set>
#include <iostream>
#include <iterator>
#include <cstdlib>
#include <exception>

using namespace thewizardplusplus::wizard_parser::lexer;
using namespace thewizardplusplus::wizard_parser::parser;
using namespace thewizardplusplus::wizard_parser::parser::operators;

const auto usage =
R"(Usage:
  ./example -h | --help
  ./example [-t | --tokens] <expression>
  ./example [-t | --tokens] (-s | --stdin)

Options:
  -h, --help    - show this message;
  -t, --tokens  - show a token list instead an AST;
  -s, --stdin   - read an expression from stdin.)";
const auto lexemes = std::vector<lexeme>{
	{std::regex{"=="}, "equal"},
	{std::regex{"/="}, "not_equal"},
	{std::regex{"<"}, "less"},
	{std::regex{"<="}, "less_or_equal"},
	{std::regex{">"}, "great"},
	{std::regex{">="}, "great_or_equal"},
	{std::regex{R"(\+)"}, "plus"},
	{std::regex{"-"}, "minus"},
	{std::regex{R"(\*)"}, "star"},
	{std::regex{"/"}, "slash"},
	{std::regex{"%"}, "percent"},
	{std::regex{R"(\()"}, "opening_parenthesis"},
	{std::regex{R"(\))"}, "closing_parenthesis"},
	{std::regex{","}, "comma"},
	{std::regex{R"(\d+(?:\.\d+)?(?:e-?\d+)?)"}, "number_constant"},
	{std::regex{R"([A-Za-z_]\w*)"}, "base_identifier"},
	{std::regex{R"(\s+)"}, "whitespace"}
};
const auto ignorable_tokens = std::unordered_set<std::string>{"whitespace"};

namespace {

rule_parser::pointer make_atom_parser(const rule_parser::pointer& expression) {
	RULE(number_constant) = "number_constant"_t;
	RULE(key_words) = "and"_v | "not"_v | "or"_v;
	RULE(identifier) = "base_identifier"_t - key_words;
	IMPORTANT_RULE(function_call) = identifier >> &"("_v >>
		-(expression >> *(&","_v >> expression))
	>> &")"_v;
	return number_constant
		| identifier
		| function_call
		| (&"("_v >> expression >> &")"_v);
}

rule_parser::pointer make_expression_parser() {
	const auto expression_dummy = dummy();
	RULE(atom) = make_atom_parser(expression_dummy);
	RULE(unary) = *("-"_v | "not"_v) >> atom;
	RULE(product) = unary >> *(("*"_v | "/"_v | "%"_v) >> unary);
	RULE(sum) = product >> *(("+"_v | "-"_v) >> product);
	RULE(comparison) = sum >> *(("<"_v | "<="_v | ">"_v | ">="_v) >> sum);
	RULE(equality) = comparison >> *(("=="_v | "/="_v) >> comparison);
	RULE(conjunction) = equality >> *(&"and"_v >> equality);
	RULE(disjunction) = conjunction >> *(&"or"_v >> conjunction);
	expression_dummy->set_parser(disjunction);

	return disjunction >> eoi();
}

}

int main(int argc, char* argv[]) try {
	auto code = std::string{};
	const auto options = docopt::docopt(usage, {argv + 1, argv + argc}, true);
	if (!options.at("--stdin").asBool()) {
		code = options.at("<expression>").asString();
	} else {
		code = std::string{std::istreambuf_iterator<char>{std::cin}, {}};
	}

	const auto tokens = tokenize(lexemes, ignorable_tokens, code);
	if (options.at("--tokens").asBool()) {
		std::cout << tokens << '\n';
		std::exit(EXIT_SUCCESS);
	}

	RULE(expression) = make_expression_parser();
	const auto ast = parse(expression, tokens, code.size());
	std::cout << ast << '\n';
} catch (const std::exception& exception) {
	std::cerr << "error: " << exception.what() << '\n';
	std::exit(EXIT_FAILURE);
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2021 dresden elektronik ingenieurtechnik gmbh.
 * All rights reserved.
 *
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE.txt file.
 *
 */

#include <deconz/aps_controller.h>
#include <deconz/dbg_trace.h>
#include <deconz/zcl.h>
#include "zcl.h"

struct ZclDataType
{
    quint8 dataType;
    char type; // 'A' analog 'D' discrete
    quint8 size;
};

static ZclDataType _zclDataTypes[] = {
    { deCONZ::Zcl8BitUint,    'A',  1 },
    { deCONZ::Zcl16BitUint,   'A',  2 },
    { deCONZ::Zcl24BitUint,   'A',  3 },
    { deCONZ::Zcl32BitUint,   'A',  4 },
    { deCONZ::Zcl40BitUint,   'A',  5 },
    { deCONZ::Zcl48BitUint,   'A',  6 },
    { deCONZ::Zcl56BitUint,   'A',  7 },
    { deCONZ::Zcl64BitUint,   'A',  8 },
    { deCONZ::Zcl8BitInt,     'A',  1 },
    { deCONZ::Zcl16BitInt,    'A',  2 },
    { deCONZ::Zcl24BitInt,    'A',  3 },
    { deCONZ::Zcl32BitInt,    'A',  4 },
    { deCONZ::Zcl40BitInt,    'A',  5 },
    { deCONZ::Zcl48BitInt,    'A',  6 },
    { deCONZ::Zcl56BitInt,    'A',  7 },
    { deCONZ::Zcl64BitInt,    'A',  8 },
    { deCONZ::ZclSingleFloat, 'A',  4 },
    { deCONZ::ZclSemiFloat,   'A',  2 },
    { deCONZ::ZclDoubleFloat, 'A',  8 },
    { deCONZ::ZclTimeOfDay,   'A',  4 },
    { deCONZ::ZclDate,        'A',  4 },
    { deCONZ::ZclUtcTime,     'A',  4 },
    { deCONZ::ZclNoData,       0,   0 }
};

ZCL_Result ZCL_ReadAttributes(const ZCL_Param &param, quint64 extAddress, quint16 nwkAddress, deCONZ::ApsController *apsCtrl)
{
    ZCL_Result result{};

//    task.req.setTxOptions(deCONZ::ApsTxAcknowledgedTransmission);
    deCONZ::ApsDataRequest req;
    result.apsReqId = req.id();

    req.setDstEndpoint(param.endpoint);
    req.setDstAddressMode(deCONZ::ApsExtAddress);
    req.dstAddress().setExt(extAddress);
    req.dstAddress().setNwk(nwkAddress);
    req.setClusterId(param.clusterId);
    req.setProfileId(HA_PROFILE_ID);
    req.setSrcEndpoint(0x01); // todo dynamic

    deCONZ::ZclFrame zclFrame;

    zclFrame.setSequenceNumber(zclNextSequenceNumber());
    zclFrame.setCommandId(deCONZ::ZclReadAttributesId);

    DBG_Printf(DBG_INFO, "ZCL read attr 0x%016llX, ep: 0x%02X, cl: 0x%04X, attr: 0x%04X, mfcode: 0x%04X, aps.id: %u, zcl.seq: %u\n",
               extAddress, param.endpoint, param.clusterId, param.attributes.front(), param.manufacturerCode, req.id(), zclFrame.sequenceNumber());

    result.sequenceNumber = zclFrame.sequenceNumber();

    if (param.manufacturerCode)
    {
        zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand |
                                      deCONZ::ZclFCManufacturerSpecific |
                                      deCONZ::ZclFCDirectionClientToServer |
                                      deCONZ::ZclFCDisableDefaultResponse);
        zclFrame.setManufacturerCode(param.manufacturerCode);
    }
    else
    {
        zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand |
                                      deCONZ::ZclFCDirectionClientToServer |
                                      deCONZ::ZclFCDisableDefaultResponse);
    }

    { // payload
        QDataStream stream(&zclFrame.payload(), QIODevice::WriteOnly);
        stream.setByteOrder(QDataStream::LittleEndian);

        for (size_t i = 0; i < param.attributeCount; i++)
        {
            stream << param.attributes[i];
        }
    }

    { // ZCL frame
        QDataStream stream(&req.asdu(), QIODevice::WriteOnly);
        stream.setByteOrder(QDataStream::LittleEndian);
        zclFrame.writeToStream(stream);
    }

    if (apsCtrl->apsdeDataRequest(req) == deCONZ::Success)
    {
        result.isEnqueued = true;
    }

    return result;
}

ZCL_Result ZCL_ReadReportConfiguration(const ZCL_ReadReportConfigurationParam &param, deCONZ::ApsController *apsCtrl)
{
    ZCL_Result result{};

    deCONZ::ApsDataRequest req;
    result.apsReqId = req.id();

    req.setDstEndpoint(param.endpoint);
    req.setDstAddressMode(deCONZ::ApsExtAddress);
    req.dstAddress().setExt(param.extAddress);
    req.dstAddress().setNwk(param.nwkAddress);
    req.setClusterId(param.clusterId);
    req.setProfileId(HA_PROFILE_ID);
    req.setSrcEndpoint(0x01); // todo dynamic

    deCONZ::ZclFrame zclFrame;

    zclFrame.setSequenceNumber(zclNextSequenceNumber());
    zclFrame.setCommandId(deCONZ::ZclReadReportingConfigId);

    DBG_Printf(DBG_INFO, "ZCL read report config, ep: 0x%02X, cl: 0x%04X, mfcode: 0x%04X, aps.id: %u, zcl.seq: %u\n",
               param.endpoint, param.clusterId, param.manufacturerCode, req.id(), zclFrame.sequenceNumber());

    result.sequenceNumber = zclFrame.sequenceNumber();

    if (param.manufacturerCode)
    {
        zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand |
                                      deCONZ::ZclFCManufacturerSpecific |
                                      deCONZ::ZclFCDirectionClientToServer |
                                      deCONZ::ZclFCDisableDefaultResponse);
        zclFrame.setManufacturerCode(param.manufacturerCode);
    }
    else
    {
        zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand |
                                      deCONZ::ZclFCDirectionClientToServer |
                                      deCONZ::ZclFCDisableDefaultResponse);
    }

    { // payload
        QDataStream stream(&zclFrame.payload(), QIODevice::WriteOnly);
        stream.setByteOrder(QDataStream::LittleEndian);

        for (const auto &record : param.records)
        {
            stream << record.direction;
            stream << record.attributeId;
        }
    }

    { // ZCL frame
        QDataStream stream(&req.asdu(), QIODevice::WriteOnly);
        stream.setByteOrder(QDataStream::LittleEndian);
        zclFrame.writeToStream(stream);
    }

    if (apsCtrl->apsdeDataRequest(req) == deCONZ::Success)
    {
        result.isEnqueued = true;
    }

    return result;
}

const ZclDataType *ZCL_GetDataType(deCONZ::ZclDataTypeId type)
{
    const auto *dt = &_zclDataTypes[0];
    while (dt->dataType != deCONZ::ZclNoData)
    {
        if (dt->dataType == type)
        {
            break;
        }
        dt++;
    }

    return dt; // deCONZ::ZclNoData if not found
}

bool ZCL_IsDataTypeAnalog(deCONZ::ZclDataTypeId type)
{
    const auto *dt = ZCL_GetDataType(type);
    return dt->type == 'A';
}

size_t ZCL_DataTypeSize(deCONZ::ZclDataTypeId type)
{
    const auto *dt = ZCL_GetDataType(type);
    return dt->size;
}

ZCL_ReadReportConfigurationRsp ZCL_ParseReadReportConfigurationRsp(const deCONZ::ApsDataIndication &ind, const deCONZ::ZclFrame &zclFrame)
{
    ZCL_ReadReportConfigurationRsp result{};

    result.sequenceNumber = zclFrame.sequenceNumber();
    result.endpoint = ind.srcEndpoint();
    result.clusterId = ind.clusterId();
    result.manufacturerCode = zclFrame.manufacturerCode();

    QDataStream stream(zclFrame.payload());
    stream.setByteOrder(QDataStream::LittleEndian);

    while (!stream.atEnd() && result.recordCount < ZCL_ReadReportConfigurationRsp::MaxRecords)
    {
        auto &record = result.records[result.recordCount];
        result.recordCount++;

        stream >> record.status;
        stream >> record.direction;
        stream >> record.attributeId;

        if (record.status != deCONZ::ZclSuccessStatus)
        {
            // If the status field is not set to SUCCESS, all fields except the direction and attribute identifier fields SHALL be omitted.
            continue;
        }

        stream >> record.dataType;
        stream >> record.minInterval;
        stream >> record.maxInterval;

        record.reportableChange = 0;

        if (ZCL_IsDataTypeAnalog(deCONZ::ZclDataTypeId(record.dataType)))
        {
            const auto size = ZCL_DataTypeSize(deCONZ::ZclDataTypeId(record.dataType));
            Q_ASSERT(size <= 8);
            if (size > 8)
            {
                break; // unsupported
            }

            for (size_t i = 0; i < size; i++)
            {
                quint8 tmp;
                stream >> tmp;
                record.reportableChange |= quint64(tmp) << (i * 8);
            }
        }
    }

    return result;
}

ZCL_Result ZCL_ConfigureReporting(const ZCL_ConfigureReportingParam &param, deCONZ::ApsController *apsCtrl)
{
    ZCL_Result result{};

    deCONZ::ApsDataRequest req;
    result.apsReqId = req.id();

    req.setDstEndpoint(param.endpoint);
    req.setDstAddressMode(deCONZ::ApsExtAddress);
    req.dstAddress().setExt(param.extAddress);
    req.dstAddress().setNwk(param.nwkAddress);
    req.setClusterId(param.clusterId);
    req.setProfileId(HA_PROFILE_ID);
    req.setSrcEndpoint(0x01); // todo dynamic

    deCONZ::ZclFrame zclFrame;

    zclFrame.setSequenceNumber(zclNextSequenceNumber());
    zclFrame.setCommandId(deCONZ::ZclConfigureReportingId);

    DBG_Printf(DBG_INFO, "ZCL configure reporting ep: 0x%02X, cl: 0x%04X, mfcode: 0x%04X, aps.id: %u, zcl.seq: %u\n",
               param.endpoint, param.clusterId, param.manufacturerCode, req.id(), zclFrame.sequenceNumber());

    result.sequenceNumber = zclFrame.sequenceNumber();

    if (param.manufacturerCode)
    {
        zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand |
                                      deCONZ::ZclFCManufacturerSpecific |
                                      deCONZ::ZclFCDirectionClientToServer |
                                      deCONZ::ZclFCDisableDefaultResponse);
        zclFrame.setManufacturerCode(param.manufacturerCode);
    }
    else
    {
        zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand |
                                      deCONZ::ZclFCDirectionClientToServer |
                                      deCONZ::ZclFCDisableDefaultResponse);
    }

    { // payload
        QDataStream stream(&zclFrame.payload(), QIODevice::WriteOnly);
        stream.setByteOrder(QDataStream::LittleEndian);

        for (const auto &record : param.records)
        {
            stream << record.direction;
            stream << record.attributeId;
            stream << record.dataType;
            stream << record.minInterval;
            stream << record.maxInterval;

            if (ZCL_IsDataTypeAnalog(deCONZ::ZclDataTypeId(record.dataType)))
            {
                const auto size = ZCL_DataTypeSize(deCONZ::ZclDataTypeId(record.dataType));
                Q_ASSERT(size <= 8);
                if (size > 8)
                {
                    return result; // unsupported
                }

                auto reportableChange = record.reportableChange;
                for (size_t i = 0; i < size; i++)
                {
                    stream << static_cast<quint8>(reportableChange & 0xff);
                    reportableChange >>= 8;
                }
            }

//            stream << record.timeout; // TODO?
        }
    }

    { // ZCL frame
        QDataStream stream(&req.asdu(), QIODevice::WriteOnly);
        stream.setByteOrder(QDataStream::LittleEndian);
        zclFrame.writeToStream(stream);
    }

    if (apsCtrl->apsdeDataRequest(req) == deCONZ::Success)
    {
        result.isEnqueued = true;
    }

    return result;
}
<commit_msg>For device ZCL functions change DBG_INFO to DBG_ZCL<commit_after>/*
 * Copyright (c) 2021 dresden elektronik ingenieurtechnik gmbh.
 * All rights reserved.
 *
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE.txt file.
 *
 */

#include <deconz/aps_controller.h>
#include <deconz/dbg_trace.h>
#include <deconz/zcl.h>
#include "zcl.h"

struct ZclDataType
{
    quint8 dataType;
    char type; // 'A' analog 'D' discrete
    quint8 size;
};

static ZclDataType _zclDataTypes[] = {
    { deCONZ::Zcl8BitUint,    'A',  1 },
    { deCONZ::Zcl16BitUint,   'A',  2 },
    { deCONZ::Zcl24BitUint,   'A',  3 },
    { deCONZ::Zcl32BitUint,   'A',  4 },
    { deCONZ::Zcl40BitUint,   'A',  5 },
    { deCONZ::Zcl48BitUint,   'A',  6 },
    { deCONZ::Zcl56BitUint,   'A',  7 },
    { deCONZ::Zcl64BitUint,   'A',  8 },
    { deCONZ::Zcl8BitInt,     'A',  1 },
    { deCONZ::Zcl16BitInt,    'A',  2 },
    { deCONZ::Zcl24BitInt,    'A',  3 },
    { deCONZ::Zcl32BitInt,    'A',  4 },
    { deCONZ::Zcl40BitInt,    'A',  5 },
    { deCONZ::Zcl48BitInt,    'A',  6 },
    { deCONZ::Zcl56BitInt,    'A',  7 },
    { deCONZ::Zcl64BitInt,    'A',  8 },
    { deCONZ::ZclSingleFloat, 'A',  4 },
    { deCONZ::ZclSemiFloat,   'A',  2 },
    { deCONZ::ZclDoubleFloat, 'A',  8 },
    { deCONZ::ZclTimeOfDay,   'A',  4 },
    { deCONZ::ZclDate,        'A',  4 },
    { deCONZ::ZclUtcTime,     'A',  4 },
    { deCONZ::ZclNoData,       0,   0 }
};

ZCL_Result ZCL_ReadAttributes(const ZCL_Param &param, quint64 extAddress, quint16 nwkAddress, deCONZ::ApsController *apsCtrl)
{
    ZCL_Result result{};

//    task.req.setTxOptions(deCONZ::ApsTxAcknowledgedTransmission);
    deCONZ::ApsDataRequest req;
    result.apsReqId = req.id();

    req.setDstEndpoint(param.endpoint);
    req.setDstAddressMode(deCONZ::ApsExtAddress);
    req.dstAddress().setExt(extAddress);
    req.dstAddress().setNwk(nwkAddress);
    req.setClusterId(param.clusterId);
    req.setProfileId(HA_PROFILE_ID);
    req.setSrcEndpoint(0x01); // todo dynamic

    deCONZ::ZclFrame zclFrame;

    zclFrame.setSequenceNumber(zclNextSequenceNumber());
    zclFrame.setCommandId(deCONZ::ZclReadAttributesId);

    DBG_Printf(DBG_ZCL, "ZCL read attr 0x%016llX, ep: 0x%02X, cl: 0x%04X, attr: 0x%04X, mfcode: 0x%04X, aps.id: %u, zcl.seq: %u\n",
               extAddress, param.endpoint, param.clusterId, param.attributes.front(), param.manufacturerCode, req.id(), zclFrame.sequenceNumber());

    result.sequenceNumber = zclFrame.sequenceNumber();

    if (param.manufacturerCode)
    {
        zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand |
                                      deCONZ::ZclFCManufacturerSpecific |
                                      deCONZ::ZclFCDirectionClientToServer |
                                      deCONZ::ZclFCDisableDefaultResponse);
        zclFrame.setManufacturerCode(param.manufacturerCode);
    }
    else
    {
        zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand |
                                      deCONZ::ZclFCDirectionClientToServer |
                                      deCONZ::ZclFCDisableDefaultResponse);
    }

    { // payload
        QDataStream stream(&zclFrame.payload(), QIODevice::WriteOnly);
        stream.setByteOrder(QDataStream::LittleEndian);

        for (size_t i = 0; i < param.attributeCount; i++)
        {
            stream << param.attributes[i];
        }
    }

    { // ZCL frame
        QDataStream stream(&req.asdu(), QIODevice::WriteOnly);
        stream.setByteOrder(QDataStream::LittleEndian);
        zclFrame.writeToStream(stream);
    }

    if (apsCtrl->apsdeDataRequest(req) == deCONZ::Success)
    {
        result.isEnqueued = true;
    }

    return result;
}

ZCL_Result ZCL_ReadReportConfiguration(const ZCL_ReadReportConfigurationParam &param, deCONZ::ApsController *apsCtrl)
{
    ZCL_Result result{};

    deCONZ::ApsDataRequest req;
    result.apsReqId = req.id();

    req.setDstEndpoint(param.endpoint);
    req.setDstAddressMode(deCONZ::ApsExtAddress);
    req.dstAddress().setExt(param.extAddress);
    req.dstAddress().setNwk(param.nwkAddress);
    req.setClusterId(param.clusterId);
    req.setProfileId(HA_PROFILE_ID);
    req.setSrcEndpoint(0x01); // todo dynamic

    deCONZ::ZclFrame zclFrame;

    zclFrame.setSequenceNumber(zclNextSequenceNumber());
    zclFrame.setCommandId(deCONZ::ZclReadReportingConfigId);

    DBG_Printf(DBG_ZCL, "ZCL read report config, ep: 0x%02X, cl: 0x%04X, mfcode: 0x%04X, aps.id: %u, zcl.seq: %u\n",
               param.endpoint, param.clusterId, param.manufacturerCode, req.id(), zclFrame.sequenceNumber());

    result.sequenceNumber = zclFrame.sequenceNumber();

    if (param.manufacturerCode)
    {
        zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand |
                                      deCONZ::ZclFCManufacturerSpecific |
                                      deCONZ::ZclFCDirectionClientToServer |
                                      deCONZ::ZclFCDisableDefaultResponse);
        zclFrame.setManufacturerCode(param.manufacturerCode);
    }
    else
    {
        zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand |
                                      deCONZ::ZclFCDirectionClientToServer |
                                      deCONZ::ZclFCDisableDefaultResponse);
    }

    { // payload
        QDataStream stream(&zclFrame.payload(), QIODevice::WriteOnly);
        stream.setByteOrder(QDataStream::LittleEndian);

        for (const auto &record : param.records)
        {
            stream << record.direction;
            stream << record.attributeId;
        }
    }

    { // ZCL frame
        QDataStream stream(&req.asdu(), QIODevice::WriteOnly);
        stream.setByteOrder(QDataStream::LittleEndian);
        zclFrame.writeToStream(stream);
    }

    if (apsCtrl->apsdeDataRequest(req) == deCONZ::Success)
    {
        result.isEnqueued = true;
    }

    return result;
}

const ZclDataType *ZCL_GetDataType(deCONZ::ZclDataTypeId type)
{
    const auto *dt = &_zclDataTypes[0];
    while (dt->dataType != deCONZ::ZclNoData)
    {
        if (dt->dataType == type)
        {
            break;
        }
        dt++;
    }

    return dt; // deCONZ::ZclNoData if not found
}

bool ZCL_IsDataTypeAnalog(deCONZ::ZclDataTypeId type)
{
    const auto *dt = ZCL_GetDataType(type);
    return dt->type == 'A';
}

size_t ZCL_DataTypeSize(deCONZ::ZclDataTypeId type)
{
    const auto *dt = ZCL_GetDataType(type);
    return dt->size;
}

ZCL_ReadReportConfigurationRsp ZCL_ParseReadReportConfigurationRsp(const deCONZ::ApsDataIndication &ind, const deCONZ::ZclFrame &zclFrame)
{
    ZCL_ReadReportConfigurationRsp result{};

    result.sequenceNumber = zclFrame.sequenceNumber();
    result.endpoint = ind.srcEndpoint();
    result.clusterId = ind.clusterId();
    result.manufacturerCode = zclFrame.manufacturerCode();

    QDataStream stream(zclFrame.payload());
    stream.setByteOrder(QDataStream::LittleEndian);

    while (!stream.atEnd() && result.recordCount < ZCL_ReadReportConfigurationRsp::MaxRecords)
    {
        auto &record = result.records[result.recordCount];
        result.recordCount++;

        stream >> record.status;
        stream >> record.direction;
        stream >> record.attributeId;

        if (record.status != deCONZ::ZclSuccessStatus)
        {
            // If the status field is not set to SUCCESS, all fields except the direction and attribute identifier fields SHALL be omitted.
            continue;
        }

        stream >> record.dataType;
        stream >> record.minInterval;
        stream >> record.maxInterval;

        record.reportableChange = 0;

        if (ZCL_IsDataTypeAnalog(deCONZ::ZclDataTypeId(record.dataType)))
        {
            const auto size = ZCL_DataTypeSize(deCONZ::ZclDataTypeId(record.dataType));
            Q_ASSERT(size <= 8);
            if (size > 8)
            {
                break; // unsupported
            }

            for (size_t i = 0; i < size; i++)
            {
                quint8 tmp;
                stream >> tmp;
                record.reportableChange |= quint64(tmp) << (i * 8);
            }
        }
    }

    return result;
}

ZCL_Result ZCL_ConfigureReporting(const ZCL_ConfigureReportingParam &param, deCONZ::ApsController *apsCtrl)
{
    ZCL_Result result{};

    deCONZ::ApsDataRequest req;
    result.apsReqId = req.id();

    req.setDstEndpoint(param.endpoint);
    req.setDstAddressMode(deCONZ::ApsExtAddress);
    req.dstAddress().setExt(param.extAddress);
    req.dstAddress().setNwk(param.nwkAddress);
    req.setClusterId(param.clusterId);
    req.setProfileId(HA_PROFILE_ID);
    req.setSrcEndpoint(0x01); // todo dynamic

    deCONZ::ZclFrame zclFrame;

    zclFrame.setSequenceNumber(zclNextSequenceNumber());
    zclFrame.setCommandId(deCONZ::ZclConfigureReportingId);

    DBG_Printf(DBG_ZCL, "ZCL configure reporting ep: 0x%02X, cl: 0x%04X, mfcode: 0x%04X, aps.id: %u, zcl.seq: %u\n",
               param.endpoint, param.clusterId, param.manufacturerCode, req.id(), zclFrame.sequenceNumber());

    result.sequenceNumber = zclFrame.sequenceNumber();

    if (param.manufacturerCode)
    {
        zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand |
                                      deCONZ::ZclFCManufacturerSpecific |
                                      deCONZ::ZclFCDirectionClientToServer |
                                      deCONZ::ZclFCDisableDefaultResponse);
        zclFrame.setManufacturerCode(param.manufacturerCode);
    }
    else
    {
        zclFrame.setFrameControl(deCONZ::ZclFCProfileCommand |
                                      deCONZ::ZclFCDirectionClientToServer |
                                      deCONZ::ZclFCDisableDefaultResponse);
    }

    { // payload
        QDataStream stream(&zclFrame.payload(), QIODevice::WriteOnly);
        stream.setByteOrder(QDataStream::LittleEndian);

        for (const auto &record : param.records)
        {
            stream << record.direction;
            stream << record.attributeId;
            stream << record.dataType;
            stream << record.minInterval;
            stream << record.maxInterval;

            if (ZCL_IsDataTypeAnalog(deCONZ::ZclDataTypeId(record.dataType)))
            {
                const auto size = ZCL_DataTypeSize(deCONZ::ZclDataTypeId(record.dataType));
                Q_ASSERT(size <= 8);
                if (size > 8)
                {
                    return result; // unsupported
                }

                auto reportableChange = record.reportableChange;
                for (size_t i = 0; i < size; i++)
                {
                    stream << static_cast<quint8>(reportableChange & 0xff);
                    reportableChange >>= 8;
                }
            }

//            stream << record.timeout; // TODO?
        }
    }

    { // ZCL frame
        QDataStream stream(&req.asdu(), QIODevice::WriteOnly);
        stream.setByteOrder(QDataStream::LittleEndian);
        zclFrame.writeToStream(stream);
    }

    if (apsCtrl->apsdeDataRequest(req) == deCONZ::Success)
    {
        result.isEnqueued = true;
    }

    return result;
}
<|endoftext|>