<commit_before>#include "indexer/feature_covering.hpp"
#include "indexer/cell_coverer.hpp"
#include "indexer/cell_id.hpp"
#include "indexer/feature.hpp"
#include "indexer/scales.hpp"

#include "geometry/covering_utils.hpp"

#include "std/vector.hpp"

namespace
{

// This class should only be used with covering::CoverObject()!
class FeatureIntersector
{
public:
  struct Trg
  {
    m2::PointD m_a, m_b, m_c;
    Trg(m2::PointD const & a, m2::PointD const & b, m2::PointD const & c)
      : m_a(a), m_b(b), m_c(c) {}
  };

  vector<m2::PointD> m_polyline;
  vector<Trg> m_trg;
  m2::RectD m_rect;

  // Note:
  // 1. Here we don't need to differentiate between CELL_OBJECT_INTERSECT and OBJECT_INSIDE_CELL.
  // 2. We can return CELL_OBJECT_INTERSECT instead of CELL_INSIDE_OBJECT - it's just
  //    a performance penalty.
  covering::CellObjectIntersection operator() (RectId const & cell) const
  {
    using namespace covering;

    m2::RectD cellRect;
    {
      // Check for limit rect intersection.
      pair<uint32_t, uint32_t> const xy = cell.XY();
      uint32_t const r = cell.Radius();
      ASSERT_GREATER_OR_EQUAL(xy.first, r, ());
      ASSERT_GREATER_OR_EQUAL(xy.second, r, ());

      cellRect = m2::RectD(xy.first - r, xy.second - r, xy.first + r, xy.second + r);
      if (!cellRect.IsIntersect(m_rect))
        return CELL_OBJECT_NO_INTERSECTION;
    }

    for (size_t i = 0; i < m_trg.size(); ++i)
    {
      m2::RectD r;
      r.Add(m_trg[i].m_a);
      r.Add(m_trg[i].m_b);
      r.Add(m_trg[i].m_c);
      if (!cellRect.IsIntersect(r))
        continue;

      CellObjectIntersection const res =
          IntersectCellWithTriangle(cell, m_trg[i].m_a, m_trg[i].m_b, m_trg[i].m_c);

      switch (res)
      {
      case CELL_OBJECT_NO_INTERSECTION:
        break;
      case CELL_INSIDE_OBJECT:
        return CELL_INSIDE_OBJECT;
      case CELL_OBJECT_INTERSECT:
      case OBJECT_INSIDE_CELL:
        return CELL_OBJECT_INTERSECT;
      }
    }

    for (size_t i = 1; i < m_polyline.size(); ++i)
    {
      CellObjectIntersection const res =
          IntersectCellWithLine(cell, m_polyline[i], m_polyline[i-1]);
      switch (res)
      {
      case CELL_OBJECT_NO_INTERSECTION:
        break;
      case CELL_INSIDE_OBJECT:
        ASSERT(false, (cell, i, m_polyline));
        return CELL_OBJECT_INTERSECT;
      case CELL_OBJECT_INTERSECT:
      case OBJECT_INSIDE_CELL:
        return CELL_OBJECT_INTERSECT;
      }
    }

    return CELL_OBJECT_NO_INTERSECTION;
  }

  typedef CellIdConverter<MercatorBounds, RectId> CellIdConverterType;

  m2::PointD ConvertPoint(m2::PointD const & p)
  {
    m2::PointD const pt(CellIdConverterType::XToCellIdX(p.x),
                        CellIdConverterType::YToCellIdY(p.y));
    m_rect.Add(pt);
    return pt;
  }

  void operator() (m2::PointD const & pt)
  {
    m_polyline.push_back(ConvertPoint(pt));
  }

  void operator() (m2::PointD const & a, m2::PointD const & b, m2::PointD const & c)
  {
    m_trg.emplace_back(ConvertPoint(a), ConvertPoint(b), ConvertPoint(c));
  }
};

}

namespace covering
{

vector<int64_t> CoverFeature(FeatureType const & f, int cellDepth, uint64_t cellPenaltyArea)
{
  // We need to cover feature for the best geometry, because it's indexed once for the
  // first top level scale. Do reset current cached geometry first.
  f.ResetGeometry();
  int const scale = FeatureType::BEST_GEOMETRY;

  FeatureIntersector fIsect;
  f.ForEachPointRef(fIsect, scale);
  f.ForEachTriangleRef(fIsect, scale);

  CHECK(!(fIsect.m_trg.empty() && fIsect.m_polyline.empty()) &&
        f.GetLimitRect(scale).IsValid(), (f.DebugString(scale)));

  if (fIsect.m_trg.empty() && fIsect.m_polyline.size() == 1)
  {
    m2::PointD const pt = fIsect.m_polyline[0];
    return vector<int64_t>(
          1, RectId::FromXY(static_cast<uint32_t>(pt.x), static_cast<uint32_t>(pt.y),
                            RectId::DEPTH_LEVELS - 1).ToInt64(cellDepth));
  }

  vector<RectId> cells;
  covering::CoverObject(fIsect, cellPenaltyArea, cells, cellDepth, RectId::Root());

  vector<int64_t> res(cells.size());
  for (size_t i = 0; i < cells.size(); ++i)
    res[i] = cells[i].ToInt64(cellDepth);

  return res;
}

void SortAndMergeIntervals(IntervalsT v, IntervalsT & res)
{
#ifdef DEBUG
  ASSERT ( res.empty(), () );
  for (size_t i = 0; i < v.size(); ++i)
    ASSERT_LESS(v[i].first, v[i].second, (i));
#endif

  sort(v.begin(), v.end());

  res.reserve(v.size());
  for (size_t i = 0; i < v.size(); ++i)
  {
    if (i == 0 || res.back().second < v[i].first)
      res.push_back(v[i]);
    else
      res.back().second = max(res.back().second, v[i].second);
  }
}

IntervalsT SortAndMergeIntervals(IntervalsT const & v)
{
  IntervalsT res;
  SortAndMergeIntervals(v, res);
  return res;
}

void AppendLowerLevels(RectId id, int cellDepth, IntervalsT & intervals)
{
  int64_t idInt64 = id.ToInt64(cellDepth);
  intervals.push_back(make_pair(idInt64, idInt64 + id.SubTreeSize(cellDepth)));
  while (id.Level() > 0)
  {
    id = id.Parent();
    idInt64 = id.ToInt64(cellDepth);
    intervals.push_back(make_pair(idInt64, idInt64 + 1));
  }
}

void CoverViewportAndAppendLowerLevels(m2::RectD const & r, int cellDepth, IntervalsT & res)
{
  vector<RectId> ids;
  CoverRect<MercatorBounds, RectId>(r.minX(), r.minY(), r.maxX(), r.maxY(), 8, cellDepth, ids);

  IntervalsT intervals;
  intervals.reserve(ids.size() * 4);

  for (size_t i = 0; i < ids.size(); ++i)
    AppendLowerLevels(ids[i], cellDepth, intervals);

  SortAndMergeIntervals(intervals, res);
}

RectId GetRectIdAsIs(m2::RectD const & r)
{
  double const eps = MercatorBounds::GetCellID2PointAbsEpsilon();

  typedef CellIdConverter<MercatorBounds, RectId> TConverter;
  RectId const id = TConverter::Cover2PointsWithCell(
    MercatorBounds::ClampX(r.minX() + eps),
    MercatorBounds::ClampY(r.minY() + eps),
    MercatorBounds::ClampX(r.maxX() - eps),
    MercatorBounds::ClampY(r.maxY() - eps));

  // Calling this function make sence only for rects that are equal with index cells.
  // Check it here ...
#ifdef DEBUG
  double minX, minY, maxX, maxY;
  TConverter::GetCellBounds(id, minX, minY, maxX, maxY);
  m2::RectD dbgR(minX, minY, maxX, maxY);
  ASSERT(m2::IsEqual(dbgR, r, eps, eps), (r, dbgR));
#endif

  return id;
}

int GetCodingDepth(int scale)
{
  int const delta = scales::GetUpperScale() - scale;
  ASSERT_GREATER_OR_EQUAL ( delta, 0, () );

  return (RectId::DEPTH_LEVELS - delta);
}

IntervalsT const & CoveringGetter::Get(int scale)
{
  int const cellDepth = GetCodingDepth(scale);
  int const ind = (cellDepth == RectId::DEPTH_LEVELS ? 0 : 1);

  if (m_res[ind].empty())
  {
    switch (m_mode)
    {
    case ViewportWithLowLevels:
      CoverViewportAndAppendLowerLevels(m_rect, cellDepth, m_res[ind]);
      break;

    case LowLevelsOnly:
    {
      RectId id = GetRectIdAsIs(m_rect);
      while (id.Level() >= cellDepth)
        id = id.Parent();
      AppendLowerLevels(id, cellDepth, m_res[ind]);
/*
#ifdef DEBUG
      size_t oldSize = m_res[ind].size();
      IntervalsT res;
      SortAndMergeIntervals(m_res[ind], res);
      if (res.size() != oldSize)
        LOG(LDEBUG, ("Old =", oldSize, "; New =", res.size()));
      res.swap(m_res[ind]);
#endif
*/
      break;
    }

    case FullCover:
      m_res[ind].push_back(IntervalsT::value_type(0, static_cast<int64_t>((1ULL << 63) - 1)));
      break;
    }
  }

  return m_res[ind];
}

}
<commit_msg>Review fixes.<commit_after>#include "indexer/feature_covering.hpp"
#include "indexer/cell_coverer.hpp"
#include "indexer/cell_id.hpp"
#include "indexer/feature.hpp"
#include "indexer/scales.hpp"

#include "geometry/covering_utils.hpp"

#include "std/vector.hpp"

namespace
{

// This class should only be used with covering::CoverObject()!
class FeatureIntersector
{
public:
  struct Trg
  {
    m2::PointD m_a, m_b, m_c;
    Trg(m2::PointD const & a, m2::PointD const & b, m2::PointD const & c)
      : m_a(a), m_b(b), m_c(c) {}
  };

  vector<m2::PointD> m_polyline;
  vector<Trg> m_trg;
  m2::RectD m_rect;

  // Note:
  // 1. Here we don't need to differentiate between CELL_OBJECT_INTERSECT and OBJECT_INSIDE_CELL.
  // 2. We can return CELL_OBJECT_INTERSECT instead of CELL_INSIDE_OBJECT - it's just
  //    a performance penalty.
  covering::CellObjectIntersection operator() (RectId const & cell) const
  {
    using namespace covering;

    m2::RectD cellRect;
    {
      // Check for limit rect intersection.
      pair<uint32_t, uint32_t> const xy = cell.XY();
      uint32_t const r = cell.Radius();
      ASSERT_GREATER_OR_EQUAL(xy.first, r, ());
      ASSERT_GREATER_OR_EQUAL(xy.second, r, ());

      cellRect = m2::RectD(xy.first - r, xy.second - r, xy.first + r, xy.second + r);
      if (!cellRect.IsIntersect(m_rect))
        return CELL_OBJECT_NO_INTERSECTION;
    }

    for (size_t i = 0; i < m_trg.size(); ++i)
    {
      m2::RectD r;
      r.Add(m_trg[i].m_a);
      r.Add(m_trg[i].m_b);
      r.Add(m_trg[i].m_c);
      if (!cellRect.IsIntersect(r))
        continue;

      CellObjectIntersection const res =
          IntersectCellWithTriangle(cell, m_trg[i].m_a, m_trg[i].m_b, m_trg[i].m_c);

      switch (res)
      {
      case CELL_OBJECT_NO_INTERSECTION:
        break;
      case CELL_INSIDE_OBJECT:
        return CELL_INSIDE_OBJECT;
      case CELL_OBJECT_INTERSECT:
      case OBJECT_INSIDE_CELL:
        return CELL_OBJECT_INTERSECT;
      }
    }

    for (size_t i = 1; i < m_polyline.size(); ++i)
    {
      CellObjectIntersection const res =
          IntersectCellWithLine(cell, m_polyline[i], m_polyline[i-1]);
      switch (res)
      {
      case CELL_OBJECT_NO_INTERSECTION:
        break;
      case CELL_INSIDE_OBJECT:
        ASSERT(false, (cell, i, m_polyline));
        return CELL_OBJECT_INTERSECT;
      case CELL_OBJECT_INTERSECT:
      case OBJECT_INSIDE_CELL:
        return CELL_OBJECT_INTERSECT;
      }
    }

    return CELL_OBJECT_NO_INTERSECTION;
  }

  typedef CellIdConverter<MercatorBounds, RectId> CellIdConverterType;

  m2::PointD ConvertPoint(m2::PointD const & p)
  {
    m2::PointD const pt(CellIdConverterType::XToCellIdX(p.x),
                        CellIdConverterType::YToCellIdY(p.y));
    m_rect.Add(pt);
    return pt;
  }

  void operator() (m2::PointD const & pt)
  {
    m_polyline.push_back(ConvertPoint(pt));
  }

  void operator() (m2::PointD const & a, m2::PointD const & b, m2::PointD const & c)
  {
    m_trg.emplace_back(ConvertPoint(a), ConvertPoint(b), ConvertPoint(c));
  }
};

}

namespace covering
{

vector<int64_t> CoverFeature(FeatureType const & f, int cellDepth, uint64_t cellPenaltyArea)
{
  // We need to cover feature for the best geometry, because it's indexed once for the
  // first top level scale. Do reset current cached geometry first.
  f.ResetGeometry();
  int const scale = FeatureType::BEST_GEOMETRY;

  FeatureIntersector fIsect;
  f.ForEachPointRef(fIsect, scale);
  f.ForEachTriangleRef(fIsect, scale);

  CHECK(!(fIsect.m_trg.empty() && fIsect.m_polyline.empty()) &&
        f.GetLimitRect(scale).IsValid(), (f.DebugString(scale)));

  if (fIsect.m_trg.empty() && fIsect.m_polyline.size() == 1)
  {
    m2::PointD const pt = fIsect.m_polyline[0];
    return vector<int64_t>(
          1, RectId::FromXY(static_cast<uint32_t>(pt.x), static_cast<uint32_t>(pt.y),
                            RectId::DEPTH_LEVELS - 1).ToInt64(cellDepth));
  }

  vector<RectId> cells;
  covering::CoverObject(fIsect, cellPenaltyArea, cells, cellDepth, RectId::Root());

  vector<int64_t> res(cells.size());
  for (size_t i = 0; i < cells.size(); ++i)
    res[i] = cells[i].ToInt64(cellDepth);

  return res;
}

void SortAndMergeIntervals(IntervalsT v, IntervalsT & res)
{
#ifdef DEBUG
  ASSERT ( res.empty(), () );
  for (size_t i = 0; i < v.size(); ++i)
    ASSERT_LESS(v[i].first, v[i].second, (i));
#endif

  sort(v.begin(), v.end());

  res.reserve(v.size());
  for (size_t i = 0; i < v.size(); ++i)
  {
    if (i == 0 || res.back().second < v[i].first)
      res.push_back(v[i]);
    else
      res.back().second = max(res.back().second, v[i].second);
  }
}

IntervalsT SortAndMergeIntervals(IntervalsT const & v)
{
  IntervalsT res;
  SortAndMergeIntervals(v, res);
  return res;
}

void AppendLowerLevels(RectId id, int cellDepth, IntervalsT & intervals)
{
  int64_t idInt64 = id.ToInt64(cellDepth);
  intervals.push_back(make_pair(idInt64, idInt64 + id.SubTreeSize(cellDepth)));
  while (id.Level() > 0)
  {
    id = id.Parent();
    idInt64 = id.ToInt64(cellDepth);
    intervals.push_back(make_pair(idInt64, idInt64 + 1));
  }
}

void CoverViewportAndAppendLowerLevels(m2::RectD const & r, int cellDepth, IntervalsT & res)
{
  vector<RectId> ids;
  CoverRect<MercatorBounds, RectId>(r.minX(), r.minY(), r.maxX(), r.maxY(), 8, cellDepth, ids);

  IntervalsT intervals;
  intervals.reserve(ids.size() * 4);

  for (size_t i = 0; i < ids.size(); ++i)
    AppendLowerLevels(ids[i], cellDepth, intervals);

  SortAndMergeIntervals(intervals, res);
}

RectId GetRectIdAsIs(m2::RectD const & r)
{
  double const eps = MercatorBounds::GetCellID2PointAbsEpsilon();
  using TConverter = CellIdConverter<MercatorBounds, RectId>;

  RectId const id = TConverter::Cover2PointsWithCell(
    MercatorBounds::ClampX(r.minX() + eps),
    MercatorBounds::ClampY(r.minY() + eps),
    MercatorBounds::ClampX(r.maxX() - eps),
    MercatorBounds::ClampY(r.maxY() - eps));

  // Calling this function makes sence only for rects that are equal with index cells.
  // Check it here ...
#ifdef DEBUG
  double minX, minY, maxX, maxY;
  TConverter::GetCellBounds(id, minX, minY, maxX, maxY);
  m2::RectD dbgR(minX, minY, maxX, maxY);
  ASSERT(m2::IsEqual(dbgR, r, eps, eps), (r, dbgR));
#endif

  return id;
}

int GetCodingDepth(int scale)
{
  int const delta = scales::GetUpperScale() - scale;
  ASSERT_GREATER_OR_EQUAL ( delta, 0, () );

  return (RectId::DEPTH_LEVELS - delta);
}

IntervalsT const & CoveringGetter::Get(int scale)
{
  int const cellDepth = GetCodingDepth(scale);
  int const ind = (cellDepth == RectId::DEPTH_LEVELS ? 0 : 1);

  if (m_res[ind].empty())
  {
    switch (m_mode)
    {
    case ViewportWithLowLevels:
      CoverViewportAndAppendLowerLevels(m_rect, cellDepth, m_res[ind]);
      break;

    case LowLevelsOnly:
    {
      RectId id = GetRectIdAsIs(m_rect);
      while (id.Level() >= cellDepth)
        id = id.Parent();
      AppendLowerLevels(id, cellDepth, m_res[ind]);

      // Check for optimal result intervals.
#if 0
      size_t oldSize = m_res[ind].size();
      IntervalsT res;
      SortAndMergeIntervals(m_res[ind], res);
      if (res.size() != oldSize)
        LOG(LINFO, ("Old =", oldSize, "; New =", res.size()));
      res.swap(m_res[ind]);
#endif
      break;
    }

    case FullCover:
      m_res[ind].push_back(IntervalsT::value_type(0, static_cast<int64_t>((1ULL << 63) - 1)));
      break;
    }
  }

  return m_res[ind];
}

}
<|endoftext|>


<commit_before>/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2014 Paul Asmuth, Google Inc.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <fnord/base/stringutil.h>
#include <fnordmetric/environment.h>
#include <fnordmetric/metricdb/httpapi.h>
#include <fnordmetric/query/queryservice.h>
#include <fnordmetric/metricdb/metricrepository.h>
#include <fnordmetric/metricdb/metrictablerepository.h>
#include <fnordmetric/sql/backends/csv/csvbackend.h>
#include <fnordmetric/sql/backends/mysql/mysqlbackend.h>

namespace fnordmetric {
namespace metricdb {

static const char kMetricsUrl[] = "/metrics";
static const char kMetricsUrlPrefix[] = "/metrics/";
static const char kQueryUrl[] = "/query";
static const char kLabelParamPrefix[] = "label[";

HTTPAPI::HTTPAPI(IMetricRepository* metric_repo) : metric_repo_(metric_repo) {}

bool HTTPAPI::handleHTTPRequest(
    http::HTTPRequest* request,
    http::HTTPResponse* response) {
  fnord::URI uri(request->getUrl());
  auto path = uri.path();
  fnord::StringUtil::stripTrailingSlashes(&path);

  response->addHeader("Access-Control-Allow-Origin", "*");

  // PATH: ^/metrics/?$
  if (path == kMetricsUrl) {
    switch (request->method()) {
      case http::HTTPRequest::M_GET:
        renderMetricList(request, response, &uri);
        return true;
      case http::HTTPRequest::M_POST:
        insertSample(request, response, &uri);
        return true;
      default:
        return false;
    }
  }

  // PATH: ^/metrics/.*
  if (path.compare(0, sizeof(kMetricsUrlPrefix) - 1, kMetricsUrlPrefix) == 0) {
    // PATH: ^/metrics/(.*)$
    switch (request->method()) {
      case http::HTTPRequest::M_GET:
        renderMetricSampleScan(request, response, &uri);
        return true;
      default:
        return false;
    }
  }

  // PATH: ^/query/?*
  if (path == kQueryUrl) {
    switch (request->method()) {
      case http::HTTPRequest::M_GET:
      case http::HTTPRequest::M_POST:
        executeQuery(request, response, &uri);
        return true;
      default:
        return false;
    }
    return true;
  }

  return false;
}

void HTTPAPI::renderMetricList(
    http::HTTPRequest* request,
    http::HTTPResponse* response,
    fnord::URI* uri) {
  response->setStatus(http::kStatusOK);
  response->addHeader("Content-Type", "application/json; charset=utf-8");
  util::JSONOutputStream json(response->getBodyOutputStream());

  json.beginObject();
  json.addObjectEntry("metrics");
  json.beginArray();

  int i = 0;
  for (const auto& metric : metric_repo_->listMetrics()) {
    if (i++ > 0) { json.addComma(); }
    renderMetricJSON(metric, &json);
  }

  json.endArray();
  json.endObject();
}

void HTTPAPI::insertSample(
    http::HTTPRequest* request,
    http::HTTPResponse* response,
    fnord::URI* uri) {
  const auto& postbody = request->getBody();
  fnord::URI::ParamList params;

  if (postbody.size() > 0) {
    fnord::URI::parseQueryString(postbody, &params);
  } else {
    params = uri->queryParams();
  }

  std::string metric_key;
  if (!fnord::URI::getParam(params, "metric", &metric_key)) {
    response->addBody("error: invalid metric key: " + metric_key);
    response->setStatus(http::kStatusBadRequest);
    return;
  }

  std::string value_str;
  if (!fnord::URI::getParam(params, "value", &value_str)) {
    response->addBody("error: missing ?value=... parameter");
    response->setStatus(http::kStatusBadRequest);
    return;
  }

  std::vector<std::pair<std::string, std::string>> labels;
  for (const auto& param : params) {
    const auto& key = param.first;
    const auto& value = param.second;

    if (key.compare(0, sizeof(kLabelParamPrefix) - 1, kLabelParamPrefix) == 0 &&
        key.back() == ']') {
      auto label_key = key.substr(
          sizeof(kLabelParamPrefix) - 1,
          key.size() - sizeof(kLabelParamPrefix));

      labels.emplace_back(label_key, value);
    }
  }

  double sample_value;
  try {
    sample_value = std::stod(value_str);
  } catch (std::exception& e) {
    response->addBody("error: invalid value: " + value_str);
    response->setStatus(http::kStatusBadRequest);
    return;
  }

  auto metric = metric_repo_->findOrCreateMetric(metric_key);
  metric->insertSample(sample_value, labels);
  response->setStatus(http::kStatusCreated);
}

void HTTPAPI::renderMetricSampleScan(
    http::HTTPRequest* request,
    http::HTTPResponse* response,
    fnord::URI* uri) {
  auto metric_key = uri->path().substr(sizeof(kMetricsUrlPrefix) - 1);
  if (metric_key.size() < 3) {
    response->addBody("error: invalid metric key: " + metric_key);
    response->setStatus(http::kStatusBadRequest);
    return;
  }

  auto metric = metric_repo_->findMetric(metric_key);
  if (metric == nullptr) {
    response->addBody("metric not found: " + metric_key);
    response->setStatus(http::kStatusNotFound);
    return;
  }

  response->setStatus(http::kStatusOK);
  response->addHeader("Content-Type", "application/json; charset=utf-8");
  util::JSONOutputStream json(response->getBodyOutputStream());

  json.beginObject();

  json.addObjectEntry("metric");
  renderMetricJSON(metric, &json);
  json.addComma();

  json.addObjectEntry("samples");
  json.beginArray();

  int i = 0;
  metric->scanSamples(
      fnord::util::DateTime::epoch(),
      fnord::util::DateTime::now(),
      [&json, &i] (Sample* sample) -> bool {
        if (i++ > 0) { json.addComma(); }
        json.beginObject();

        json.addObjectEntry("time");
        json.addLiteral<uint64_t>(static_cast<uint64_t>(sample->time()));
        json.addComma();

        json.addObjectEntry("value");
        json.addLiteral<double>(sample->value());
        json.addComma();

        json.addObjectEntry("labels");
        json.beginObject();
        auto labels = sample->labels();
        for (int n = 0; n < labels.size(); n++) {
          if (n > 0) {
            json.addComma();
          }

          json.addObjectEntry(labels[n].first);
          json.addString(labels[n].second);
        }
        json.endObject();

        json.endObject();
        return true;
      });

  json.endArray();
  json.endObject();
}

void HTTPAPI::executeQuery(
    http::HTTPRequest* request,
    http::HTTPResponse* response,
    fnord::URI* uri) {
  auto params = uri->queryParams();

  std::shared_ptr<util::InputStream> input_stream;
  std::string get_query;
  if (fnord::URI::getParam(params, "q", &get_query)) {
    input_stream.reset(new util::StringInputStream(get_query));
  } else {
    input_stream = request->getBodyInputStream();
  }

  std::shared_ptr<util::OutputStream> output_stream =
      response->getBodyOutputStream();

  query::QueryService query_service;
  std::unique_ptr<query::TableRepository> table_repo(
      new MetricTableRepository(metric_repo_));

  if (!env()->flags()->isSet("disable_external_sources")) {
    query_service.registerBackend(
        std::unique_ptr<fnordmetric::query::Backend>(
            new fnordmetric::query::mysql_backend::MySQLBackend));

    query_service.registerBackend(
        std::unique_ptr<fnordmetric::query::Backend>(
            new fnordmetric::query::csv_backend::CSVBackend));
  }

  query::QueryService::kFormat resp_format = query::QueryService::FORMAT_JSON;
  std::string format_param;
  if (fnord::URI::getParam(params, "format", &format_param)) {
    if (format_param == "svg") {
      resp_format = query::QueryService::FORMAT_SVG;
    }
  }

  response->setStatus(http::kStatusOK);

  switch (resp_format) {
    case query::QueryService::FORMAT_JSON:
      response->addHeader("Content-Type", "application/json; charset=utf-8");
      break;
    case query::QueryService::FORMAT_SVG:
      response->addHeader("Content-Type", "text/html; charset=utf-8");
      break;
    default:
      break;
  }

  int width = -1;
  std::string width_param;
  if (fnord::URI::getParam(params, "width", &width_param)) {
    width = std::stoi(width_param);
  }

  int height = -1;
  std::string height_param;
  if (fnord::URI::getParam(params, "height", &height_param)) {
    height = std::stoi(height_param);
  }

  try {
    query_service.executeQuery(
        input_stream,
        resp_format,
        output_stream,
        std::move(table_repo),
        width,
        height);

  } catch (fnord::Exception e) {
    response->clearBody();

    util::JSONOutputStream json(std::move(output_stream));
    json.beginObject();
    json.addObjectEntry("status");
    json.addString("error");
    json.addComma();
    json.addObjectEntry("error");
    json.addString(e.getMessage());
    json.endObject();
  }
}

void HTTPAPI::renderMetricJSON(
    IMetric* metric,
    util::JSONOutputStream* json) const {
  json->beginObject();

  json->addObjectEntry("key");
  json->addString(metric->key());
  json->addComma();

  json->addObjectEntry("total_bytes");
  json->addLiteral<size_t>(metric->totalBytes());
  json->addComma();

  json->addObjectEntry("last_insert");
  json->addLiteral<uint64_t>(static_cast<uint64_t>(metric->lastInsertTime()));
  json->addComma();

  json->addObjectEntry("labels");
  json->beginArray();
  auto labels = metric->labels();
  for (auto cur = labels.begin(); cur != labels.end(); ++cur) {
    if (cur != labels.begin()) {
      json->addComma();
    }
    json->addString(*cur);
  }
  json->endArray();

  json->endObject();
}

}
}
<commit_msg>filter renderMetricJSON<commit_after>/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2014 Paul Asmuth, Google Inc.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <fnord/base/stringutil.h>
#include <fnordmetric/environment.h>
#include <fnordmetric/metricdb/httpapi.h>
#include <fnordmetric/query/queryservice.h>
#include <fnordmetric/metricdb/metricrepository.h>
#include <fnordmetric/metricdb/metrictablerepository.h>
#include <fnordmetric/sql/backends/csv/csvbackend.h>
#include <fnordmetric/sql/backends/mysql/mysqlbackend.h>

namespace fnordmetric {
namespace metricdb {

static const char kMetricsUrl[] = "/metrics";
static const char kMetricsUrlPrefix[] = "/metrics/";
static const char kQueryUrl[] = "/query";
static const char kLabelParamPrefix[] = "label[";

HTTPAPI::HTTPAPI(IMetricRepository* metric_repo) : metric_repo_(metric_repo) {}

bool HTTPAPI::handleHTTPRequest(
    http::HTTPRequest* request,
    http::HTTPResponse* response) {
  fnord::URI uri(request->getUrl());
  auto path = uri.path();
  fnord::StringUtil::stripTrailingSlashes(&path);

  response->addHeader("Access-Control-Allow-Origin", "*");

  // PATH: ^/metrics/?$
  if (path == kMetricsUrl) {
    switch (request->method()) {
      case http::HTTPRequest::M_GET:
        renderMetricList(request, response, &uri);
        return true;
      case http::HTTPRequest::M_POST:
        insertSample(request, response, &uri);
        return true;
      default:
        return false;
    }
  }

  // PATH: ^/metrics/.*
  if (path.compare(0, sizeof(kMetricsUrlPrefix) - 1, kMetricsUrlPrefix) == 0) {
    // PATH: ^/metrics/(.*)$
    switch (request->method()) {
      case http::HTTPRequest::M_GET:
        renderMetricSampleScan(request, response, &uri);
        return true;
      default:
        return false;
    }
  }

  // PATH: ^/query/?*
  if (path == kQueryUrl) {
    switch (request->method()) {
      case http::HTTPRequest::M_GET:
      case http::HTTPRequest::M_POST:
        executeQuery(request, response, &uri);
        return true;
      default:
        return false;
    }
    return true;
  }

  return false;
}

void HTTPAPI::renderMetricList(
    http::HTTPRequest* request,
    http::HTTPResponse* response,
    fnord::URI* uri) {
  response->setStatus(http::kStatusOK);
  response->addHeader("Content-Type", "application/json; charset=utf-8");
  util::JSONOutputStream json(response->getBodyOutputStream());

  json.beginObject();
  json.addObjectEntry("metrics");
  json.beginArray();

  fnord::URI::ParamList params = uri->queryParams();

  /*const std::string& filter;
  const std::string& limit;
  if (fnord::URI::getParam(params, "filter", &metric_key)) {
    for (const auto& param : params) {
      if (param.first == "filter") {
        filter = param.second;
      } else if (param.first == "limit") {
        limit = param.second;
      }
    }
  }*/

  int i = 0;
  std::string metric_key;
  for (const auto& metric : metric_repo_->listMetrics()) {
    if (fnord::URI::getParam(params, "filter", &metric_key)) {
      for (const auto& param : params) {
        const auto& key = param.first;
        const auto& value = param.second;

        if (key == "filter") {
          if ((metric->key()).find(value) != std::string::npos) {
            if (i++ > 0) { json.addComma(); }
            renderMetricJSON(metric, &json);
          }
        }
      }

    } else {
      if (i++ > 0) { json.addComma(); }
      renderMetricJSON(metric, &json);
    }
  }

  json.endArray();
  json.endObject();
}

void HTTPAPI::insertSample(
    http::HTTPRequest* request,
    http::HTTPResponse* response,
    fnord::URI* uri) {
  const auto& postbody = request->getBody();
  fnord::URI::ParamList params;

  if (postbody.size() > 0) {
    fnord::URI::parseQueryString(postbody, &params);
  } else {
    params = uri->queryParams();
  }

  std::string metric_key;
  if (!fnord::URI::getParam(params, "metric", &metric_key)) {
    response->addBody("error: invalid metric key: " + metric_key);
    response->setStatus(http::kStatusBadRequest);
    return;
  }

  std::string value_str;
  if (!fnord::URI::getParam(params, "value", &value_str)) {
    response->addBody("error: missing ?value=... parameter");
    response->setStatus(http::kStatusBadRequest);
    return;
  }

  std::vector<std::pair<std::string, std::string>> labels;
  for (const auto& param : params) {
    const auto& key = param.first;
    const auto& value = param.second;

    if (key.compare(0, sizeof(kLabelParamPrefix) - 1, kLabelParamPrefix) == 0 &&
        key.back() == ']') {
      auto label_key = key.substr(
          sizeof(kLabelParamPrefix) - 1,
          key.size() - sizeof(kLabelParamPrefix));

      labels.emplace_back(label_key, value);
    }
  }

  double sample_value;
  try {
    sample_value = std::stod(value_str);
  } catch (std::exception& e) {
    response->addBody("error: invalid value: " + value_str);
    response->setStatus(http::kStatusBadRequest);
    return;
  }

  auto metric = metric_repo_->findOrCreateMetric(metric_key);
  metric->insertSample(sample_value, labels);
  response->setStatus(http::kStatusCreated);
}

void HTTPAPI::renderMetricSampleScan(
    http::HTTPRequest* request,
    http::HTTPResponse* response,
    fnord::URI* uri) {
  auto metric_key = uri->path().substr(sizeof(kMetricsUrlPrefix) - 1);
  if (metric_key.size() < 3) {
    response->addBody("error: invalid metric key: " + metric_key);
    response->setStatus(http::kStatusBadRequest);
    return;
  }

  auto metric = metric_repo_->findMetric(metric_key);
  if (metric == nullptr) {
    response->addBody("metric not found: " + metric_key);
    response->setStatus(http::kStatusNotFound);
    return;
  }

  response->setStatus(http::kStatusOK);
  response->addHeader("Content-Type", "application/json; charset=utf-8");
  util::JSONOutputStream json(response->getBodyOutputStream());

  json.beginObject();

  json.addObjectEntry("metric");
  renderMetricJSON(metric, &json);
  json.addComma();

  json.addObjectEntry("samples");
  json.beginArray();

  int i = 0;
  metric->scanSamples(
      fnord::util::DateTime::epoch(),
      fnord::util::DateTime::now(),
      [&json, &i] (Sample* sample) -> bool {
        if (i++ > 0) { json.addComma(); }
        json.beginObject();

        json.addObjectEntry("time");
        json.addLiteral<uint64_t>(static_cast<uint64_t>(sample->time()));
        json.addComma();

        json.addObjectEntry("value");
        json.addLiteral<double>(sample->value());
        json.addComma();

        json.addObjectEntry("labels");
        json.beginObject();
        auto labels = sample->labels();
        for (int n = 0; n < labels.size(); n++) {
          if (n > 0) {
            json.addComma();
          }

          json.addObjectEntry(labels[n].first);
          json.addString(labels[n].second);
        }
        json.endObject();

        json.endObject();
        return true;
      });

  json.endArray();
  json.endObject();
}

void HTTPAPI::executeQuery(
    http::HTTPRequest* request,
    http::HTTPResponse* response,
    fnord::URI* uri) {
  auto params = uri->queryParams();

  std::shared_ptr<util::InputStream> input_stream;
  std::string get_query;
  if (fnord::URI::getParam(params, "q", &get_query)) {
    input_stream.reset(new util::StringInputStream(get_query));
  } else {
    input_stream = request->getBodyInputStream();
  }

  std::shared_ptr<util::OutputStream> output_stream =
      response->getBodyOutputStream();

  query::QueryService query_service;
  std::unique_ptr<query::TableRepository> table_repo(
      new MetricTableRepository(metric_repo_));

  if (!env()->flags()->isSet("disable_external_sources")) {
    query_service.registerBackend(
        std::unique_ptr<fnordmetric::query::Backend>(
            new fnordmetric::query::mysql_backend::MySQLBackend));

    query_service.registerBackend(
        std::unique_ptr<fnordmetric::query::Backend>(
            new fnordmetric::query::csv_backend::CSVBackend));
  }

  query::QueryService::kFormat resp_format = query::QueryService::FORMAT_JSON;
  std::string format_param;
  if (fnord::URI::getParam(params, "format", &format_param)) {
    if (format_param == "svg") {
      resp_format = query::QueryService::FORMAT_SVG;
    }
  }

  response->setStatus(http::kStatusOK);

  switch (resp_format) {
    case query::QueryService::FORMAT_JSON:
      response->addHeader("Content-Type", "application/json; charset=utf-8");
      break;
    case query::QueryService::FORMAT_SVG:
      response->addHeader("Content-Type", "text/html; charset=utf-8");
      break;
    default:
      break;
  }

  int width = -1;
  std::string width_param;
  if (fnord::URI::getParam(params, "width", &width_param)) {
    width = std::stoi(width_param);
  }

  int height = -1;
  std::string height_param;
  if (fnord::URI::getParam(params, "height", &height_param)) {
    height = std::stoi(height_param);
  }

  try {
    query_service.executeQuery(
        input_stream,
        resp_format,
        output_stream,
        std::move(table_repo),
        width,
        height);

  } catch (fnord::Exception e) {
    response->clearBody();

    util::JSONOutputStream json(std::move(output_stream));
    json.beginObject();
    json.addObjectEntry("status");
    json.addString("error");
    json.addComma();
    json.addObjectEntry("error");
    json.addString(e.getMessage());
    json.endObject();
  }
}

void HTTPAPI::renderMetricJSON(
    IMetric* metric,
    util::JSONOutputStream* json) const {
  json->beginObject();

  json->addObjectEntry("key");
  json->addString(metric->key());
  json->addComma();

  json->addObjectEntry("total_bytes");
  json->addLiteral<size_t>(metric->totalBytes());
  json->addComma();

  json->addObjectEntry("last_insert");
  json->addLiteral<uint64_t>(static_cast<uint64_t>(metric->lastInsertTime()));
  json->addComma();

  json->addObjectEntry("labels");
  json->beginArray();
  auto labels = metric->labels();
  for (auto cur = labels.begin(); cur != labels.end(); ++cur) {
    if (cur != labels.begin()) {
      json->addComma();
    }
    json->addString(*cur);
  }
  json->endArray();

  json->endObject();
}

}
}
<|endoftext|>


<commit_before>/******************************************************************************

  This source file is part of the Avogadro project.

  Copyright 2012 Kitware, Inc.

  This source code is released under the New BSD License, (the "License").

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

******************************************************************************/

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <avogadro/qtgui/molecule.h>
#include <avogadro/core/elements.h>
#include <avogadro/io/cmlformat.h>
#include <avogadro/io/cjsonformat.h>
#include <avogadro/qtopengl/editor.h>
#include <avogadro/qtopengl/glwidget.h>
#include <avogadro/qtplugins/pluginmanager.h>
#include <avogadro/qtgui/sceneplugin.h>
#include <avogadro/qtgui/scenepluginmodel.h>
#include <avogadro/qtgui/extensionplugin.h>
#include <avogadro/rendering/glrenderer.h>
#include <avogadro/rendering/scene.h>

#include <QtCore/QCoreApplication>
#include <QtCore/QString>
#include <QtCore/QDebug>
#include <QtCore/QFileInfo>
#include <QtCore/QSettings>
#include <QtGui/QCloseEvent>
#include <QtGui/QMenu>
#include <QtGui/QMenuBar>
#include <QtGui/QFileDialog>
#include <QtGui/QStatusBar>
#include <QtGui/QToolBar>

#include <QtGui/QDockWidget>
#include <QtGui/QTreeView>
#include <QtGui/QHeaderView>

namespace Avogadro {

using QtGui::Molecule;

MainWindow::MainWindow(const QString &fileName)
  : m_ui(new Ui::MainWindow),
    m_molecule(0),
    m_scenePluginModel(0)
{
  m_ui->setupUi(this);

  QIcon icon(":/icons/avogadro.png");
  setWindowIcon(icon);

  // Create the scene plugin model
  m_scenePluginModel = new QtGui::ScenePluginModel(m_ui->scenePluginTreeView);
  m_ui->scenePluginTreeView->setModel(m_scenePluginModel);
  m_ui->scenePluginTreeView->setAlternatingRowColors(true);
  m_ui->scenePluginTreeView->header()->stretchLastSection();
  m_ui->scenePluginTreeView->header()->setVisible(false);
  connect(m_scenePluginModel,
          SIGNAL(pluginStateChanged(Avogadro::QtGui::ScenePlugin*)),
          SLOT(updateScenePlugins()));
  /// @todo HACK the molecule should trigger the update
  connect(&m_ui->glWidget->editor(), SIGNAL(moleculeChanged()),
          SLOT(updateScenePlugins()));
  connect(&m_ui->glWidget->manipulator(), SIGNAL(moleculeChanged()),
          SLOT(updateScenePlugins()));

  // Connect the menu actions
  connect(m_ui->actionNewMolecule, SIGNAL(triggered()), SLOT(newMolecule()));
  connect(m_ui->actionOpen, SIGNAL(triggered()), SLOT(openFile()));
  connect(m_ui->actionQuit, SIGNAL(triggered()), qApp, SLOT(quit()));

  // Connect the temporary tool/element selectors
  updateTool();
  buildElements();
  updateElement();
  connect(m_ui->toolComboBox, SIGNAL(currentIndexChanged(int)),
          SLOT(updateTool()));
  connect(m_ui->elementComboBox, SIGNAL(currentIndexChanged(int)),
          SLOT(updateElement()));

  readSettings();

  QtGui::PluginManager *plugin = QtGui::PluginManager::instance();
  plugin->load();
  QList<QtGui::ScenePluginFactory *> scenePluginFactories =
      plugin->pluginFactories<QtGui::ScenePluginFactory>();
  foreach (QtGui::ScenePluginFactory *factory, scenePluginFactories) {
    QtGui::ScenePlugin *scenePlugin = factory->createInstance();
    if (scenePlugin) {
      scenePlugin->setParent(this);
      m_scenePluginModel->addItem(scenePlugin);
    }
  }

  // Call this a second time, not needed but ensures plugins only load once.
  plugin->load();

  QList<QtGui::ExtensionPluginFactory *> extensions =
      plugin->pluginFactories<QtGui::ExtensionPluginFactory>();
  qDebug() << "Extension plugins dynamically found..." << extensions.size();
  foreach (QtGui::ExtensionPluginFactory *factory, extensions) {
    QtGui::ExtensionPlugin *extension = factory->createInstance();
    if (extension) {
      extension->setParent(this);
      qDebug() << "extension:" << extension->name() << extension->menuPath();
      connect(this, SIGNAL(moleculeChanged(QtGui::Molecule*)),
              extension, SLOT(setMolecule(QtGui::Molecule*)));
      connect(extension, SIGNAL(moleculeReady(int)), SLOT(moleculeReady(int)));
      buildMenu(extension);
    }
  }

  // try to open the file passed in. If opening fails, create a new molecule.
  openFile(fileName);
  if (!m_molecule)
    newMolecule();
  statusBar()->showMessage(tr("Ready..."), 2000);
}

MainWindow::~MainWindow()
{
  writeSettings();
  delete m_molecule;
}

void MainWindow::closeEvent(QCloseEvent *e)
{
  writeSettings();
  QMainWindow::closeEvent(e);
}

void MainWindow::moleculeReady(int)
{
  QtGui::ExtensionPlugin *extension =
      qobject_cast<QtGui::ExtensionPlugin *>(sender());
  if (extension) {
    QtGui::Molecule *mol = new QtGui::Molecule(this);
    if (extension->readMolecule(*mol))
      setMolecule(mol);
  }
}

void MainWindow::newMolecule()
{
  setMolecule(new QtGui::Molecule);
}

void MainWindow::setMolecule(QtGui::Molecule *mol)
{
  if (m_molecule == mol)
    return;

  // Clear the scene to prevent dangling identifiers:
  m_ui->glWidget->renderer().scene().clear();

  // Set molecule
  if (m_molecule)
    delete m_molecule;
  m_molecule = mol;

  emit moleculeChanged(m_molecule);

  connect(m_molecule, SIGNAL(changed(unsigned int)),
          SLOT(updateScenePlugins()));

  m_ui->glWidget->editor().setMolecule(mol);
  m_ui->glWidget->manipulator().setMolecule(mol);
  updateScenePlugins();
  m_ui->glWidget->resetCamera();
}

void MainWindow::writeSettings()
{
  QSettings settings;
  settings.beginGroup("MainWindow");
  settings.setValue("size", size());
  settings.setValue("pos", pos());
  settings.endGroup();
  settings.setValue("recentFiles", m_recentFiles);
}

void MainWindow::readSettings()
{
  QSettings settings;
  settings.beginGroup("MainWindow");
  resize(settings.value("size", QSize(400, 300)).toSize());
  move(settings.value("pos", QPoint(20, 20)).toPoint());
  settings.endGroup();
  m_recentFiles = settings.value("recentFiles", QStringList()).toStringList();
  updateRecentFiles();
}

void MainWindow::openFile()
{
  QString fileName = QFileDialog::getOpenFileName(this, tr("Open chemical file"),
    "", tr("Chemical files (*.cml *.cjson)"));
  openFile(fileName);
}

void MainWindow::openFile(const QString &fileName)
{
  if (fileName.isEmpty())
    return;

  QFileInfo info(fileName);
  Molecule *molecule_(NULL);
  bool success(false);
  if (info.suffix() == "cml") {
    Io::CmlFormat cml;
    molecule_ = new Molecule;
    success = cml.readFile(fileName.toStdString(), *molecule_);
  }
  else if (info.suffix() == "cjson") {
    Io::CjsonFormat cjson;
    molecule_ = new Molecule;
    success = cjson.readFile(fileName.toStdString(), *molecule_);
  }
  if (success) {
    m_recentFiles.prepend(fileName);
    updateRecentFiles();
    setMolecule(molecule_);
    statusBar()->showMessage(tr("Molecule loaded (%1 atoms, %2 bonds)")
                             .arg(molecule_->atomCount())
                             .arg(molecule_->bondCount()), 2500);
    setWindowTitle(tr("Avogadro - %1").arg(fileName));
  }
  else {
    statusBar()->showMessage(tr("Failed to read %1").arg(fileName), 2500);
    delete molecule_;
  }
}

void MainWindow::openRecentFile()
{
  QAction *action = qobject_cast<QAction *>(sender());
  if (action)
    openFile(action->data().toString());
}

void MainWindow::updateRecentFiles()
{
  m_recentFiles.removeDuplicates();
  while (m_recentFiles.size() > 10)
    m_recentFiles.removeLast();

  // Populate the recent file actions list if necessary.
  if (m_actionRecentFiles.isEmpty()) {
    for (int i = 0; i < 10; ++i) {
      m_actionRecentFiles.push_back(m_ui->menuRecentFiles->addAction(""));
      m_actionRecentFiles.back()->setVisible(false);
      connect(m_actionRecentFiles.back(), SIGNAL(triggered()),
              SLOT(openRecentFile()));
    }
    m_actionRecentFiles[0]->setText(tr("No recent files"));
    m_actionRecentFiles[0]->setVisible(true);
    m_actionRecentFiles[0]->setEnabled(false);
  }
  int i = 0;
  foreach (const QString &file, m_recentFiles) {
    QFileInfo fileInfo(file);
    QAction *recentFile = m_actionRecentFiles[i++];
    recentFile->setText(fileInfo.fileName());
    recentFile->setData(file);
    recentFile->setVisible(true);
    recentFile->setEnabled(true);
  }
  for (; i < 10; ++i)
    m_actionRecentFiles[i]->setVisible(false);
}

void MainWindow::updateScenePlugins()
{
  Rendering::Scene &scene = m_ui->glWidget->renderer().scene();
  scene.clear();
  if (m_molecule) {
    foreach (QtGui::ScenePlugin *scenePlugin,
             m_scenePluginModel->activeScenePlugins()) {
      scenePlugin->process(*m_molecule, scene);
    }
  }
  m_ui->glWidget->update();
}

void MainWindow::updateTool()
{
  m_ui->glWidget->setActiveTool(static_cast<QtOpenGL::GLWidget::Tool>(
                                  m_ui->toolComboBox->currentIndex()));
  m_ui->elementComboBox->setEnabled(
        m_ui->glWidget->activeTool() == QtOpenGL::GLWidget::EditTool);
}

void MainWindow::updateElement()
{
  m_ui->glWidget->editor().setAtomicNumber(
        m_elementLookup.at(m_ui->elementComboBox->currentIndex()));
}

void MainWindow::buildMenu(QtGui::ExtensionPlugin *extension)
{
  foreach (QAction *action, extension->actions()) {
    QStringList path = extension->menuPath(action);
    qDebug() << "Menu:" << extension->name() << path;
    if (path.size() < 1)
      continue;
    // First ensure the top-level menu is present (create it if needed).
    QMenu *menu(NULL);
    foreach (QAction *topMenu, menuBar()->actions()) {
      if (topMenu->text() == path.at(0)) {
        menu = topMenu->menu();
        break;
      }
    }
    if (!menu)
      menu = menuBar()->addMenu(path.at(0));

    // Build up submenus if necessary.
    QMenu *nextMenu(NULL);
    for (int i = 1; i < path.size(); ++i) {
      if (nextMenu) {
        menu = nextMenu;
        nextMenu = NULL;
      }
      const QString menuText = path[i];
      foreach (QAction *menuAction, menu->actions()) {
        if (menuAction->text() == menuText) {
          nextMenu = menuAction->menu();
          break;
        }
      }
      if (!nextMenu)
        nextMenu = menu->addMenu(path.at(i));
      menu = nextMenu;
      nextMenu = NULL;
    }
    // Now we actually add the action we got (it should have set the text etc).
    menu->addAction(action);
  }

}

void MainWindow::buildElements()
{
  m_ui->elementComboBox->clear();
  m_elementLookup.clear();

  // Add common elements to the top.
  addElement(1); // Hydrogen
  addElement(5); // Boron
  addElement(6); // Carbon
  addElement(7); // Nitrogen
  addElement(8); // Oxygen
  addElement(9); // Fluorine
  addElement(15); // Phosphorus
  addElement(16); // Sulfur
  addElement(17); // Chlorine
  addElement(35); // Bromine

  m_ui->elementComboBox->insertSeparator(m_ui->elementComboBox->count());

  // And the rest...
  for (unsigned char i = 1; i < Core::Elements::elementCount(); ++i)
    addElement(i);
}

void MainWindow::addElement(unsigned char atomicNum)
{
  m_ui->elementComboBox->addItem(QString("%1 (%2)")
                                 .arg(Core::Elements::name(atomicNum))
                                 .arg(QString::number(atomicNum)));
  m_elementLookup.push_back(atomicNum);
}

} // End of Avogadro namespace
<commit_msg>Fix bug in editor atom-type lookup.<commit_after>/******************************************************************************

  This source file is part of the Avogadro project.

  Copyright 2012 Kitware, Inc.

  This source code is released under the New BSD License, (the "License").

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

******************************************************************************/

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <avogadro/qtgui/molecule.h>
#include <avogadro/core/elements.h>
#include <avogadro/io/cmlformat.h>
#include <avogadro/io/cjsonformat.h>
#include <avogadro/qtopengl/editor.h>
#include <avogadro/qtopengl/glwidget.h>
#include <avogadro/qtplugins/pluginmanager.h>
#include <avogadro/qtgui/sceneplugin.h>
#include <avogadro/qtgui/scenepluginmodel.h>
#include <avogadro/qtgui/extensionplugin.h>
#include <avogadro/rendering/glrenderer.h>
#include <avogadro/rendering/scene.h>

#include <QtCore/QCoreApplication>
#include <QtCore/QString>
#include <QtCore/QDebug>
#include <QtCore/QFileInfo>
#include <QtCore/QSettings>
#include <QtGui/QCloseEvent>
#include <QtGui/QMenu>
#include <QtGui/QMenuBar>
#include <QtGui/QFileDialog>
#include <QtGui/QStatusBar>
#include <QtGui/QToolBar>

#include <QtGui/QDockWidget>
#include <QtGui/QTreeView>
#include <QtGui/QHeaderView>

namespace Avogadro {

using QtGui::Molecule;

MainWindow::MainWindow(const QString &fileName)
  : m_ui(new Ui::MainWindow),
    m_molecule(0),
    m_scenePluginModel(0)
{
  m_ui->setupUi(this);

  QIcon icon(":/icons/avogadro.png");
  setWindowIcon(icon);

  // Create the scene plugin model
  m_scenePluginModel = new QtGui::ScenePluginModel(m_ui->scenePluginTreeView);
  m_ui->scenePluginTreeView->setModel(m_scenePluginModel);
  m_ui->scenePluginTreeView->setAlternatingRowColors(true);
  m_ui->scenePluginTreeView->header()->stretchLastSection();
  m_ui->scenePluginTreeView->header()->setVisible(false);
  connect(m_scenePluginModel,
          SIGNAL(pluginStateChanged(Avogadro::QtGui::ScenePlugin*)),
          SLOT(updateScenePlugins()));
  /// @todo HACK the molecule should trigger the update
  connect(&m_ui->glWidget->editor(), SIGNAL(moleculeChanged()),
          SLOT(updateScenePlugins()));
  connect(&m_ui->glWidget->manipulator(), SIGNAL(moleculeChanged()),
          SLOT(updateScenePlugins()));

  // Connect the menu actions
  connect(m_ui->actionNewMolecule, SIGNAL(triggered()), SLOT(newMolecule()));
  connect(m_ui->actionOpen, SIGNAL(triggered()), SLOT(openFile()));
  connect(m_ui->actionQuit, SIGNAL(triggered()), qApp, SLOT(quit()));

  // Connect the temporary tool/element selectors
  updateTool();
  buildElements();
  updateElement();
  connect(m_ui->toolComboBox, SIGNAL(currentIndexChanged(int)),
          SLOT(updateTool()));
  connect(m_ui->elementComboBox, SIGNAL(currentIndexChanged(int)),
          SLOT(updateElement()));

  readSettings();

  QtGui::PluginManager *plugin = QtGui::PluginManager::instance();
  plugin->load();
  QList<QtGui::ScenePluginFactory *> scenePluginFactories =
      plugin->pluginFactories<QtGui::ScenePluginFactory>();
  foreach (QtGui::ScenePluginFactory *factory, scenePluginFactories) {
    QtGui::ScenePlugin *scenePlugin = factory->createInstance();
    if (scenePlugin) {
      scenePlugin->setParent(this);
      m_scenePluginModel->addItem(scenePlugin);
    }
  }

  // Call this a second time, not needed but ensures plugins only load once.
  plugin->load();

  QList<QtGui::ExtensionPluginFactory *> extensions =
      plugin->pluginFactories<QtGui::ExtensionPluginFactory>();
  qDebug() << "Extension plugins dynamically found..." << extensions.size();
  foreach (QtGui::ExtensionPluginFactory *factory, extensions) {
    QtGui::ExtensionPlugin *extension = factory->createInstance();
    if (extension) {
      extension->setParent(this);
      qDebug() << "extension:" << extension->name() << extension->menuPath();
      connect(this, SIGNAL(moleculeChanged(QtGui::Molecule*)),
              extension, SLOT(setMolecule(QtGui::Molecule*)));
      connect(extension, SIGNAL(moleculeReady(int)), SLOT(moleculeReady(int)));
      buildMenu(extension);
    }
  }

  // try to open the file passed in. If opening fails, create a new molecule.
  openFile(fileName);
  if (!m_molecule)
    newMolecule();
  statusBar()->showMessage(tr("Ready..."), 2000);
}

MainWindow::~MainWindow()
{
  writeSettings();
  delete m_molecule;
}

void MainWindow::closeEvent(QCloseEvent *e)
{
  writeSettings();
  QMainWindow::closeEvent(e);
}

void MainWindow::moleculeReady(int)
{
  QtGui::ExtensionPlugin *extension =
      qobject_cast<QtGui::ExtensionPlugin *>(sender());
  if (extension) {
    QtGui::Molecule *mol = new QtGui::Molecule(this);
    if (extension->readMolecule(*mol))
      setMolecule(mol);
  }
}

void MainWindow::newMolecule()
{
  setMolecule(new QtGui::Molecule);
}

void MainWindow::setMolecule(QtGui::Molecule *mol)
{
  if (m_molecule == mol)
    return;

  // Clear the scene to prevent dangling identifiers:
  m_ui->glWidget->renderer().scene().clear();

  // Set molecule
  if (m_molecule)
    delete m_molecule;
  m_molecule = mol;

  emit moleculeChanged(m_molecule);

  connect(m_molecule, SIGNAL(changed(unsigned int)),
          SLOT(updateScenePlugins()));

  m_ui->glWidget->editor().setMolecule(mol);
  m_ui->glWidget->manipulator().setMolecule(mol);
  updateScenePlugins();
  m_ui->glWidget->resetCamera();
}

void MainWindow::writeSettings()
{
  QSettings settings;
  settings.beginGroup("MainWindow");
  settings.setValue("size", size());
  settings.setValue("pos", pos());
  settings.endGroup();
  settings.setValue("recentFiles", m_recentFiles);
}

void MainWindow::readSettings()
{
  QSettings settings;
  settings.beginGroup("MainWindow");
  resize(settings.value("size", QSize(400, 300)).toSize());
  move(settings.value("pos", QPoint(20, 20)).toPoint());
  settings.endGroup();
  m_recentFiles = settings.value("recentFiles", QStringList()).toStringList();
  updateRecentFiles();
}

void MainWindow::openFile()
{
  QString fileName = QFileDialog::getOpenFileName(this, tr("Open chemical file"),
    "", tr("Chemical files (*.cml *.cjson)"));
  openFile(fileName);
}

void MainWindow::openFile(const QString &fileName)
{
  if (fileName.isEmpty())
    return;

  QFileInfo info(fileName);
  Molecule *molecule_(NULL);
  bool success(false);
  if (info.suffix() == "cml") {
    Io::CmlFormat cml;
    molecule_ = new Molecule;
    success = cml.readFile(fileName.toStdString(), *molecule_);
  }
  else if (info.suffix() == "cjson") {
    Io::CjsonFormat cjson;
    molecule_ = new Molecule;
    success = cjson.readFile(fileName.toStdString(), *molecule_);
  }
  if (success) {
    m_recentFiles.prepend(fileName);
    updateRecentFiles();
    setMolecule(molecule_);
    statusBar()->showMessage(tr("Molecule loaded (%1 atoms, %2 bonds)")
                             .arg(molecule_->atomCount())
                             .arg(molecule_->bondCount()), 2500);
    setWindowTitle(tr("Avogadro - %1").arg(fileName));
  }
  else {
    statusBar()->showMessage(tr("Failed to read %1").arg(fileName), 2500);
    delete molecule_;
  }
}

void MainWindow::openRecentFile()
{
  QAction *action = qobject_cast<QAction *>(sender());
  if (action)
    openFile(action->data().toString());
}

void MainWindow::updateRecentFiles()
{
  m_recentFiles.removeDuplicates();
  while (m_recentFiles.size() > 10)
    m_recentFiles.removeLast();

  // Populate the recent file actions list if necessary.
  if (m_actionRecentFiles.isEmpty()) {
    for (int i = 0; i < 10; ++i) {
      m_actionRecentFiles.push_back(m_ui->menuRecentFiles->addAction(""));
      m_actionRecentFiles.back()->setVisible(false);
      connect(m_actionRecentFiles.back(), SIGNAL(triggered()),
              SLOT(openRecentFile()));
    }
    m_actionRecentFiles[0]->setText(tr("No recent files"));
    m_actionRecentFiles[0]->setVisible(true);
    m_actionRecentFiles[0]->setEnabled(false);
  }
  int i = 0;
  foreach (const QString &file, m_recentFiles) {
    QFileInfo fileInfo(file);
    QAction *recentFile = m_actionRecentFiles[i++];
    recentFile->setText(fileInfo.fileName());
    recentFile->setData(file);
    recentFile->setVisible(true);
    recentFile->setEnabled(true);
  }
  for (; i < 10; ++i)
    m_actionRecentFiles[i]->setVisible(false);
}

void MainWindow::updateScenePlugins()
{
  Rendering::Scene &scene = m_ui->glWidget->renderer().scene();
  scene.clear();
  if (m_molecule) {
    foreach (QtGui::ScenePlugin *scenePlugin,
             m_scenePluginModel->activeScenePlugins()) {
      scenePlugin->process(*m_molecule, scene);
    }
  }
  m_ui->glWidget->update();
}

void MainWindow::updateTool()
{
  m_ui->glWidget->setActiveTool(static_cast<QtOpenGL::GLWidget::Tool>(
                                  m_ui->toolComboBox->currentIndex()));
  m_ui->elementComboBox->setEnabled(
        m_ui->glWidget->activeTool() == QtOpenGL::GLWidget::EditTool);
}

void MainWindow::updateElement()
{
  m_ui->glWidget->editor().setAtomicNumber(
        m_elementLookup.at(m_ui->elementComboBox->currentIndex()));
}

void MainWindow::buildMenu(QtGui::ExtensionPlugin *extension)
{
  foreach (QAction *action, extension->actions()) {
    QStringList path = extension->menuPath(action);
    qDebug() << "Menu:" << extension->name() << path;
    if (path.size() < 1)
      continue;
    // First ensure the top-level menu is present (create it if needed).
    QMenu *menu(NULL);
    foreach (QAction *topMenu, menuBar()->actions()) {
      if (topMenu->text() == path.at(0)) {
        menu = topMenu->menu();
        break;
      }
    }
    if (!menu)
      menu = menuBar()->addMenu(path.at(0));

    // Build up submenus if necessary.
    QMenu *nextMenu(NULL);
    for (int i = 1; i < path.size(); ++i) {
      if (nextMenu) {
        menu = nextMenu;
        nextMenu = NULL;
      }
      const QString menuText = path[i];
      foreach (QAction *menuAction, menu->actions()) {
        if (menuAction->text() == menuText) {
          nextMenu = menuAction->menu();
          break;
        }
      }
      if (!nextMenu)
        nextMenu = menu->addMenu(path.at(i));
      menu = nextMenu;
      nextMenu = NULL;
    }
    // Now we actually add the action we got (it should have set the text etc).
    menu->addAction(action);
  }

}

void MainWindow::buildElements()
{
  m_ui->elementComboBox->clear();
  m_elementLookup.clear();

  // Add common elements to the top.
  addElement(1); // Hydrogen
  addElement(5); // Boron
  addElement(6); // Carbon
  addElement(7); // Nitrogen
  addElement(8); // Oxygen
  addElement(9); // Fluorine
  addElement(15); // Phosphorus
  addElement(16); // Sulfur
  addElement(17); // Chlorine
  addElement(35); // Bromine

  m_ui->elementComboBox->insertSeparator(m_ui->elementComboBox->count());
  m_elementLookup.push_back(0); // for the separator

  // And the rest...
  for (unsigned char i = 1; i < Core::Elements::elementCount(); ++i)
    addElement(i);
}

void MainWindow::addElement(unsigned char atomicNum)
{
  m_ui->elementComboBox->addItem(QString("%1 (%2)")
                                 .arg(Core::Elements::name(atomicNum))
                                 .arg(QString::number(atomicNum)));
  m_elementLookup.push_back(atomicNum);
}

} // End of Avogadro namespace
<|endoftext|>


<commit_before>// Copyright (c) 2015, Galaxy Authors. All Rights Reserved
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Author: wangtaize@baidu.com

#include "agent/task_manager.h"

#include "common/logging.h"
#include "agent/cgroup.h"

extern std::string FLAGS_container;
namespace galaxy {
int TaskManager::Add(const ::galaxy::TaskInfo& task_info,
                     DefaultWorkspace *  workspace) {
    MutexLock lock(m_mutex);
    LOG(INFO, "add task with id %d", task_info.task_id());
    if (m_task_runner_map.find(task_info.task_id()) != m_task_runner_map.end()) {
        LOG(WARNING, "task with id %d has exist", task_info.task_id());
        return 0;
    }
    // do download

    TaskRunner* runner = NULL;
    if(FLAGS_container.compare("cgroup") == 0){
        LOG(INFO,"use cgroup task runner for task %d",task_info.task_id());
        runner = new ContainerTaskRunner(task_info,"/cgroup", workspace);
    }else{
        LOG(INFO,"use command task runner for task %d",task_info.task_id());
        runner = new CommandTaskRunner(task_info,workspace);
    }
    int ret = runner->Prepare();
    if(ret != 0 ){
        LOG(INFO,"fail to prepare runner ,ret is %d",ret);
        return ret;
    }
    m_task_runner_map[task_info.task_id()] = runner;
    //ret = runner->Start();
    //if (ret == 0) {
    //    LOG(INFO, "add task with id %d successfully", task_info.task_id());
    //    m_task_runner_map[task_info.task_id()] = runner;
    //} else {
    //    LOG(FATAL, "fail to add task with %d", task_info.task_id());
    //}
    return ret;
}

int TaskManager::Remove(const int64_t& task_info_id) {
    MutexLock lock(m_mutex);
    if (m_task_runner_map.find(task_info_id) == m_task_runner_map.end()) {
        LOG(WARNING, "task with id %d does not exist", task_info_id);
        return 0;
    }
    TaskRunner* runner = m_task_runner_map[task_info_id];
    if(NULL == runner){
        return 0;
    }
    int status = runner->Stop();
    if(status == 0){
        LOG(INFO,"stop task %d successfully",task_info_id);
    }
    m_task_runner_map.erase(task_info_id);
    delete runner;
    return status;
}

int TaskManager::Status(std::vector< TaskStatus >& task_status_vector) {
    MutexLock lock(m_mutex);
    std::map<int64_t, TaskRunner*>::iterator it = m_task_runner_map.begin();
    for (; it != m_task_runner_map.end(); ++it) {
        TaskStatus status;
        status.set_task_id(it->first);
        int ret = it->second->IsRunning();
        if(ret == 0){
            status.set_status(RUNNING);
        }else if(ret == 1){
            status.set_status(COMPLETE);
            Remove(it->first);
        }else{
            if (it->second->ReStart() == 0) {
                status.set_status(RESTART);
            } else {
                // if restart failed,
                // 1. retry times more than limit, no need retry any more.
                // 2. stop failed
                // 3. start failed
                status.set_status(ERROR);
            }
        }

        task_status_vector.push_back(status);
    }
    return 0;
}
}

<commit_msg>remove Remove Functiont<commit_after>// Copyright (c) 2015, Galaxy Authors. All Rights Reserved
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Author: wangtaize@baidu.com

#include "agent/task_manager.h"

#include "common/logging.h"
#include "agent/cgroup.h"

extern std::string FLAGS_container;
namespace galaxy {
int TaskManager::Add(const ::galaxy::TaskInfo& task_info,
                     DefaultWorkspace *  workspace) {
    MutexLock lock(m_mutex);
    LOG(INFO, "add task with id %d", task_info.task_id());
    if (m_task_runner_map.find(task_info.task_id()) != m_task_runner_map.end()) {
        LOG(WARNING, "task with id %d has exist", task_info.task_id());
        return 0;
    }
    // do download

    TaskRunner* runner = NULL;
    if(FLAGS_container.compare("cgroup") == 0){
        LOG(INFO,"use cgroup task runner for task %d",task_info.task_id());
        runner = new ContainerTaskRunner(task_info,"/cgroup", workspace);
    }else{
        LOG(INFO,"use command task runner for task %d",task_info.task_id());
        runner = new CommandTaskRunner(task_info,workspace);
    }
    int ret = runner->Prepare();
    if(ret != 0 ){
        LOG(INFO,"fail to prepare runner ,ret is %d",ret);
        return ret;
    }
    m_task_runner_map[task_info.task_id()] = runner;
    //ret = runner->Start();
    //if (ret == 0) {
    //    LOG(INFO, "add task with id %d successfully", task_info.task_id());
    //    m_task_runner_map[task_info.task_id()] = runner;
    //} else {
    //    LOG(FATAL, "fail to add task with %d", task_info.task_id());
    //}
    return ret;
}

int TaskManager::Remove(const int64_t& task_info_id) {
    MutexLock lock(m_mutex);
    if (m_task_runner_map.find(task_info_id) == m_task_runner_map.end()) {
        LOG(WARNING, "task with id %d does not exist", task_info_id);
        return 0;
    }
    TaskRunner* runner = m_task_runner_map[task_info_id];
    if(NULL == runner){
        return 0;
    }
    int status = runner->Stop();
    if(status == 0){
        LOG(INFO,"stop task %d successfully",task_info_id);
    }
    m_task_runner_map.erase(task_info_id);
    delete runner;
    return status;
}

int TaskManager::Status(std::vector< TaskStatus >& task_status_vector) {
    MutexLock lock(m_mutex);
    std::map<int64_t, TaskRunner*>::iterator it = m_task_runner_map.begin();
    for (; it != m_task_runner_map.end(); ++it) {
        TaskStatus status;
        status.set_task_id(it->first);
        int ret = it->second->IsRunning();
        if(ret == 0){
            status.set_status(RUNNING);
        }else if(ret == 1){
            status.set_status(COMPLETE);
        }else{
            if (it->second->ReStart() == 0) {
                status.set_status(RESTART);
            } else {
                // if restart failed,
                // 1. retry times more than limit, no need retry any more.
                // 2. stop failed
                // 3. start failed
                status.set_status(ERROR);
            }
        }

        task_status_vector.push_back(status);
    }
    return 0;
}
}

<|endoftext|>


<commit_before>///
/// @file      calculator.hpp
/// @brief     calculator::eval(const std::string&) evaluates an integer
///            arithmetic expression and returns the result. If an error
///            occurs a calculator::error exception is thrown.
///            <https://github.com/kimwalisch/calculator>
/// @author    Kim Walisch, <kim.walisch@gmail.com>
/// @copyright Copyright (C) 2013-2018 Kim Walisch
/// @license   BSD 2-Clause, https://opensource.org/licenses/BSD-2-Clause
/// @version   1.2 patched: `^' is raise to power instead of XOR.
///
/// == Supported operators ==
///
/// OPERATOR    NAME                     ASSOCIATIVITY    PRECEDENCE
///
/// |           Bitwise Inclusive OR    Left               4
/// &           Bitwise AND             Left               6
/// <<          Shift Left              Left               9
/// >>          Shift Right             Left               9
/// +           Addition                Left              10
/// -           Subtraction             Left              10
/// *           Multiplication          Left              20
/// /           Division                Left              20
/// %           Modulo                  Left              20
/// ^, **       Raise to power          Right             30
/// e, E        Scientific notation     Right             40
/// ~           Unary complement        Left              99
///
/// The operator precedence has been set according to (uses the C and
/// C++ operator precedence): https://en.wikipedia.org/wiki/Order_of_operations
/// Operators with higher precedence are evaluated before operators
/// with relatively lower precedence. Unary operators are set to have
/// the highest precedence, this is not strictly correct for the power
/// operator e.g. "-3**2" = 9 but a lot of software tools (Bash shell,
/// Microsoft Excel, GNU bc, ...) use the same convention.
///
/// == Examples of valid expressions ==
///
/// "65536 >> 15"                       = 2
/// "2**16"                             = 65536
/// "(0 + 0xDf234 - 1000)*3/2%999"      = 828
/// "-(2**2**2**2)"                     = -65536
/// "(0 + ~(0xDF234 & 1000) *3) /-2"    = 817
/// "(2**16) + (1 << 16) >> 0X5"        = 4096
/// "5*-(2**(9+7))/3+5*(1 & 0xFf123)"   = -109221
///
/// == About the algorithm used ==
///
/// calculator::eval(std::string&) relies on the ExpressionParser
/// class which is a simple C++ operator precedence parser with infix
/// notation for integer arithmetic expressions.
/// ExpressionParser has its roots in a JavaScript parser published
/// at: http://stackoverflow.com/questions/28256/equation-expression-parser-with-precedence/114961#114961
/// The same author has also published an article about his operator
/// precedence algorithm at PerlMonks:
/// http://www.perlmonks.org/?node_id=554516
///

#ifndef CALCULATOR_HPP
#define CALCULATOR_HPP

#include <stdexcept>
#include <string>
#include <sstream>
#include <stack>
#include <cstddef>
#include <cctype>

namespace calculator
{

/// calculator::eval() throws a calculator::error if it fails
/// to evaluate the expression string.
///
class error : public std::runtime_error
{
public:
  error(const std::string& expr, const std::string& message)
    : std::runtime_error(message),
      expr_(expr)
  { }
#if __cplusplus >= 201103L
  ~error() { }
#else
  ~error() throw() { }
#endif
  std::string expression() const
  {
    return expr_;
  }
private:
  std::string expr_;
};

template <typename T>
class ExpressionParser
{
public:
  /// Evaluate an integer arithmetic expression and return its result.
  /// @throw error if parsing fails.
  ///
  T eval(const std::string& expr)
  {
    T result = 0;
    index_ = 0;
    expr_ = expr;
    try
    {
      result = parseExpr();
      if (!isEnd())
        unexpected();
    }
    catch (const calculator::error&)
    {
      while(!stack_.empty())
        stack_.pop();
      throw;
    }
    return result;
  }

  /// Get the integer value of a character.
  T eval(char c)
  {
    std::string expr(1, c);
    return eval(expr);
  }

private:
  enum
  {
    OPERATOR_NULL,
    OPERATOR_BITWISE_OR,     /// |
    OPERATOR_BITWISE_XOR,    /// ^
    OPERATOR_BITWISE_AND,    /// &
    OPERATOR_BITWISE_SHL,    /// <<
    OPERATOR_BITWISE_SHR,    /// >>
    OPERATOR_ADDITION,       /// +
    OPERATOR_SUBTRACTION,    /// -
    OPERATOR_MULTIPLICATION, /// *
    OPERATOR_DIVISION,       /// /
    OPERATOR_MODULO,         /// %
    OPERATOR_POWER,          /// **
    OPERATOR_EXPONENT        /// e, E
  };

  struct Operator
  {
    /// Operator, one of the OPERATOR_* enum definitions
    int op;
    int precedence;
    /// 'L' = left or 'R' = right
    int associativity;
    Operator(int opr, int prec, int assoc) :
      op(opr),
      precedence(prec),
      associativity(assoc)
    { }
  };

  struct OperatorValue
  {
    Operator op;
    T value;
    OperatorValue(const Operator& opr, T val) :
      op(opr),
      value(val)
    { }
    int getPrecedence() const
    {
      return op.precedence;
    }
    bool isNull() const
    {
      return op.op == OPERATOR_NULL;
    }
  };

  /// Expression string
  std::string expr_;
  /// Current expression index, incremented whilst parsing
  std::size_t index_;
  /// The current operator and its left value
  /// are pushed onto the stack if the operator on
  /// top of the stack has lower precedence.
  std::stack<OperatorValue> stack_;

  /// Exponentiation by squaring, x^n.
  static T pow(T x, T n)
  {
    T res = 1;

    while (n > 0)
    {
      if (n % 2 != 0)
      {
        res *= x;
        n -= 1;
      }
      n /= 2;

      if (n > 0)
        x *= x;
    }

    return res;
  }

  T checkZero(T value) const
  {
    if (value == 0)
    {
      std::string divOperators("/%");
      std::size_t division = expr_.find_last_of(divOperators, index_ - 2);
      std::ostringstream msg;
      msg << "Parser error: division by 0";
      if (division != std::string::npos)
        msg << " (error token is \""
            << expr_.substr(division, expr_.size() - division)
            << "\")";
      throw calculator::error(expr_, msg.str());
    }
    return value;
  }

  T calculate(T v1, T v2, const Operator& op) const
  {
    switch (op.op)
    {
      case OPERATOR_BITWISE_OR:     return v1 | v2;
      case OPERATOR_BITWISE_XOR:    return v1 ^ v2;
      case OPERATOR_BITWISE_AND:    return v1 & v2;
      case OPERATOR_BITWISE_SHL:    return v1 << v2;
      case OPERATOR_BITWISE_SHR:    return v1 >> v2;
      case OPERATOR_ADDITION:       return v1 + v2;
      case OPERATOR_SUBTRACTION:    return v1 - v2;
      case OPERATOR_MULTIPLICATION: return v1 * v2;
      case OPERATOR_DIVISION:       return v1 / checkZero(v2);
      case OPERATOR_MODULO:         return v1 % checkZero(v2);
      case OPERATOR_POWER:          return pow(v1, v2);
      case OPERATOR_EXPONENT:       return v1 * pow(10, v2);
      default:                      return 0;
    }
  }

  bool isEnd() const
  {
    return index_ >= expr_.size();
  }

  /// Returns the character at the current expression index or
  /// 0 if the end of the expression is reached.
  ///
  char getCharacter() const
  {
    if (!isEnd())
      return expr_[index_];
    return 0;
  }

  /// Parse str at the current expression index.
  /// @throw error if parsing fails.
  ///
  void expect(const std::string& str)
  {
    if (expr_.compare(index_, str.size(), str) != 0)
      unexpected();
    index_ += str.size();
  }

  void unexpected() const
  {
    std::ostringstream msg;
    msg << "Syntax error: unexpected token \""
        << expr_.substr(index_, expr_.size() - index_)
        << "\" at index "
        << index_;
    throw calculator::error(expr_, msg.str());
  }

  /// Eat all white space characters at the
  /// current expression index.
  ///
  void eatSpaces()
  {
    while (std::isspace(getCharacter()) != 0)
      index_++;
  }

  /// Parse a binary operator at the current expression index.
  /// @return Operator with precedence and associativity.
  ///
  Operator parseOp()
  {
    eatSpaces();
    switch (getCharacter())
    {
      case '|': index_++;     return Operator(OPERATOR_BITWISE_OR,      4, 'L');
      case '&': index_++;     return Operator(OPERATOR_BITWISE_AND,     6, 'L');
      case '<': expect("<<"); return Operator(OPERATOR_BITWISE_SHL,     9, 'L');
      case '>': expect(">>"); return Operator(OPERATOR_BITWISE_SHR,     9, 'L');
      case '+': index_++;     return Operator(OPERATOR_ADDITION,       10, 'L');
      case '-': index_++;     return Operator(OPERATOR_SUBTRACTION,    10, 'L');
      case '/': index_++;     return Operator(OPERATOR_DIVISION,       20, 'L');
      case '%': index_++;     return Operator(OPERATOR_MODULO,         20, 'L');
      case '*': index_++; if (getCharacter() != '*')
                              return Operator(OPERATOR_MULTIPLICATION, 20, 'L');
                index_++;     return Operator(OPERATOR_POWER,          30, 'R');
      case '^': index_++;     return Operator(OPERATOR_POWER,          30, 'R');
      case 'e': index_++;     return Operator(OPERATOR_EXPONENT,       40, 'R');
      case 'E': index_++;     return Operator(OPERATOR_EXPONENT,       40, 'R');
      default :               return Operator(OPERATOR_NULL,            0, 'L');
    }
  }

  static T toInteger(char c)
  {
    if (c >= '0' && c <= '9') return c -'0';
    if (c >= 'a' && c <= 'f') return c -'a' + 0xa;
    if (c >= 'A' && c <= 'F') return c -'A' + 0xa;
    T noDigit = 0xf + 1;
    return noDigit;
  }

  T getInteger() const
  {
    return toInteger(getCharacter());
  }

  T parseDecimal()
  {
    T value = 0;
    for (T d; (d = getInteger()) <= 9; index_++)
      value = value * 10 + d;
    return value;
  }

  T parseHex()
  {
    index_ = index_ + 2;
    T value = 0;
    for (T h; (h = getInteger()) <= 0xf; index_++)
      value = value * 0x10 + h;
    return value;
  }

  bool isHex() const
  {
    if (index_ + 2 < expr_.size())
    {
      char x = expr_[index_ + 1];
      char h = expr_[index_ + 2];
      return (std::tolower(x) == 'x' && toInteger(h) <= 0xf);
    }
    return false;
  }

  /// Parse an integer value at the current expression index.
  /// The unary `+', `-' and `~' operators and opening
  /// parentheses `(' cause recursion.
  ///
  T parseValue()
  {
    T val = 0;
    eatSpaces();
    switch (getCharacter())
    {
      case '0': if (isHex())
                  val = parseHex();
                else
                  val = parseDecimal();
                break;
      case '1': case '2': case '3': case '4': case '5':
      case '6': case '7': case '8': case '9':
                val = parseDecimal();
                break;
      case '(': index_++;
                val = parseExpr();
                eatSpaces();
                if (getCharacter() != ')')
                {
                  if (!isEnd())
                    unexpected();
                  throw calculator::error(expr_, "Syntax error: `)' expected at end of expression");
                }
                index_++; break;
      case '~': index_++; val = ~parseValue(); break;
      case '+': index_++; val =  parseValue(); break;
      case '-': index_++; val =  parseValue() * static_cast<T>(-1);
                break;
      default : if (!isEnd())
                  unexpected();
                throw calculator::error(expr_, "Syntax error: value expected at end of expression");
    }
    return val;
  }

  /// Parse all operations of the current parenthesis
  /// level and the levels above, when done
  /// return the result (value).
  ///
  T parseExpr()
  {
    stack_.push(OperatorValue(Operator(OPERATOR_NULL, 0, 'L'), 0));
    // first parse value on the left
    T value = parseValue();

    while (!stack_.empty())
    {
      // parse an operator (+, -, *, ...)
      Operator op(parseOp());
      while (op.precedence  < stack_.top().getPrecedence() || (
             op.precedence == stack_.top().getPrecedence() &&
             op.associativity == 'L'))
      {
        // end reached
        if (stack_.top().isNull())
        {
          stack_.pop();
          return value;
        }
        // do the calculation ("reduce"), producing a new value
        value = calculate(stack_.top().value, value, stack_.top().op);
        stack_.pop();
      }

      // store on stack_ and continue parsing ("shift")
      stack_.push(OperatorValue(op, value));
      // parse value on the right
      value = parseValue();
    }
    return 0;
  }
};

template <typename T>
inline T eval(const std::string& expression)
{
  ExpressionParser<T> parser;
  return parser.eval(expression);
}

template <typename T>
inline T eval(char c)
{
  ExpressionParser<T> parser;
  return parser.eval(c);
}

inline int eval(const std::string& expression)
{
  return eval<int>(expression);
}

inline int eval(char c)
{
  return eval<int>(c);
}

} // namespace calculator

#endif
<commit_msg>Workaround for MSVC bug<commit_after>///
/// @file      calculator.hpp
/// @brief     calculator::eval(const std::string&) evaluates an integer
///            arithmetic expression and returns the result. If an error
///            occurs a calculator::error exception is thrown.
///            <https://github.com/kimwalisch/calculator>
/// @author    Kim Walisch, <kim.walisch@gmail.com>
/// @copyright Copyright (C) 2013-2018 Kim Walisch
/// @license   BSD 2-Clause, https://opensource.org/licenses/BSD-2-Clause
/// @version   1.3 patched: `^' is raise to power instead of XOR.
///
/// == Supported operators ==
///
/// OPERATOR    NAME                     ASSOCIATIVITY    PRECEDENCE
///
/// |           Bitwise Inclusive OR    Left               4
/// &           Bitwise AND             Left               6
/// <<          Shift Left              Left               9
/// >>          Shift Right             Left               9
/// +           Addition                Left              10
/// -           Subtraction             Left              10
/// *           Multiplication          Left              20
/// /           Division                Left              20
/// %           Modulo                  Left              20
/// ^, **       Raise to power          Right             30
/// e, E        Scientific notation     Right             40
/// ~           Unary complement        Left              99
///
/// The operator precedence has been set according to (uses the C and
/// C++ operator precedence): https://en.wikipedia.org/wiki/Order_of_operations
/// Operators with higher precedence are evaluated before operators
/// with relatively lower precedence. Unary operators are set to have
/// the highest precedence, this is not strictly correct for the power
/// operator e.g. "-3**2" = 9 but a lot of software tools (Bash shell,
/// Microsoft Excel, GNU bc, ...) use the same convention.
///
/// == Examples of valid expressions ==
///
/// "65536 >> 15"                       = 2
/// "2**16"                             = 65536
/// "(0 + 0xDf234 - 1000)*3/2%999"      = 828
/// "-(2**2**2**2)"                     = -65536
/// "(0 + ~(0xDF234 & 1000) *3) /-2"    = 817
/// "(2**16) + (1 << 16) >> 0X5"        = 4096
/// "5*-(2**(9+7))/3+5*(1 & 0xFf123)"   = -109221
///
/// == About the algorithm used ==
///
/// calculator::eval(std::string&) relies on the ExpressionParser
/// class which is a simple C++ operator precedence parser with infix
/// notation for integer arithmetic expressions.
/// ExpressionParser has its roots in a JavaScript parser published
/// at: http://stackoverflow.com/questions/28256/equation-expression-parser-with-precedence/114961#114961
/// The same author has also published an article about his operator
/// precedence algorithm at PerlMonks:
/// http://www.perlmonks.org/?node_id=554516
///

#ifndef CALCULATOR_HPP
#define CALCULATOR_HPP

#include <stdexcept>
#include <string>
#include <sstream>
#include <stack>
#include <cstddef>
#include <cctype>

namespace calculator
{

/// calculator::eval() throws a calculator::error if it fails
/// to evaluate the expression string.
///
class error : public std::runtime_error
{
public:
  error(const std::string& expr, const std::string& message)
    : std::runtime_error(message),
      expr_(expr)
  { }
#if __cplusplus >= 201103L || \
    (defined(_MSC_VER) && _MSC_VER >= 1800)
  ~error() { }
#else
  ~error() throw() { }
#endif
  std::string expression() const
  {
    return expr_;
  }
private:
  std::string expr_;
};

template <typename T>
class ExpressionParser
{
public:
  /// Evaluate an integer arithmetic expression and return its result.
  /// @throw error if parsing fails.
  ///
  T eval(const std::string& expr)
  {
    T result = 0;
    index_ = 0;
    expr_ = expr;
    try
    {
      result = parseExpr();
      if (!isEnd())
        unexpected();
    }
    catch (const calculator::error&)
    {
      while(!stack_.empty())
        stack_.pop();
      throw;
    }
    return result;
  }

  /// Get the integer value of a character.
  T eval(char c)
  {
    std::string expr(1, c);
    return eval(expr);
  }

private:
  enum
  {
    OPERATOR_NULL,
    OPERATOR_BITWISE_OR,     /// |
    OPERATOR_BITWISE_XOR,    /// ^
    OPERATOR_BITWISE_AND,    /// &
    OPERATOR_BITWISE_SHL,    /// <<
    OPERATOR_BITWISE_SHR,    /// >>
    OPERATOR_ADDITION,       /// +
    OPERATOR_SUBTRACTION,    /// -
    OPERATOR_MULTIPLICATION, /// *
    OPERATOR_DIVISION,       /// /
    OPERATOR_MODULO,         /// %
    OPERATOR_POWER,          /// **
    OPERATOR_EXPONENT        /// e, E
  };

  struct Operator
  {
    /// Operator, one of the OPERATOR_* enum definitions
    int op;
    int precedence;
    /// 'L' = left or 'R' = right
    int associativity;
    Operator(int opr, int prec, int assoc) :
      op(opr),
      precedence(prec),
      associativity(assoc)
    { }
  };

  struct OperatorValue
  {
    Operator op;
    T value;
    OperatorValue(const Operator& opr, T val) :
      op(opr),
      value(val)
    { }
    int getPrecedence() const
    {
      return op.precedence;
    }
    bool isNull() const
    {
      return op.op == OPERATOR_NULL;
    }
  };

  /// Expression string
  std::string expr_;
  /// Current expression index, incremented whilst parsing
  std::size_t index_;
  /// The current operator and its left value
  /// are pushed onto the stack if the operator on
  /// top of the stack has lower precedence.
  std::stack<OperatorValue> stack_;

  /// Exponentiation by squaring, x^n.
  static T pow(T x, T n)
  {
    T res = 1;

    while (n > 0)
    {
      if (n % 2 != 0)
      {
        res *= x;
        n -= 1;
      }
      n /= 2;

      if (n > 0)
        x *= x;
    }

    return res;
  }

  T checkZero(T value) const
  {
    if (value == 0)
    {
      std::string divOperators("/%");
      std::size_t division = expr_.find_last_of(divOperators, index_ - 2);
      std::ostringstream msg;
      msg << "Parser error: division by 0";
      if (division != std::string::npos)
        msg << " (error token is \""
            << expr_.substr(division, expr_.size() - division)
            << "\")";
      throw calculator::error(expr_, msg.str());
    }
    return value;
  }

  T calculate(T v1, T v2, const Operator& op) const
  {
    switch (op.op)
    {
      case OPERATOR_BITWISE_OR:     return v1 | v2;
      case OPERATOR_BITWISE_XOR:    return v1 ^ v2;
      case OPERATOR_BITWISE_AND:    return v1 & v2;
      case OPERATOR_BITWISE_SHL:    return v1 << v2;
      case OPERATOR_BITWISE_SHR:    return v1 >> v2;
      case OPERATOR_ADDITION:       return v1 + v2;
      case OPERATOR_SUBTRACTION:    return v1 - v2;
      case OPERATOR_MULTIPLICATION: return v1 * v2;
      case OPERATOR_DIVISION:       return v1 / checkZero(v2);
      case OPERATOR_MODULO:         return v1 % checkZero(v2);
      case OPERATOR_POWER:          return pow(v1, v2);
      case OPERATOR_EXPONENT:       return v1 * pow(10, v2);
      default:                      return 0;
    }
  }

  bool isEnd() const
  {
    return index_ >= expr_.size();
  }

  /// Returns the character at the current expression index or
  /// 0 if the end of the expression is reached.
  ///
  char getCharacter() const
  {
    if (!isEnd())
      return expr_[index_];
    return 0;
  }

  /// Parse str at the current expression index.
  /// @throw error if parsing fails.
  ///
  void expect(const std::string& str)
  {
    if (expr_.compare(index_, str.size(), str) != 0)
      unexpected();
    index_ += str.size();
  }

  void unexpected() const
  {
    std::ostringstream msg;
    msg << "Syntax error: unexpected token \""
        << expr_.substr(index_, expr_.size() - index_)
        << "\" at index "
        << index_;
    throw calculator::error(expr_, msg.str());
  }

  /// Eat all white space characters at the
  /// current expression index.
  ///
  void eatSpaces()
  {
    while (std::isspace(getCharacter()) != 0)
      index_++;
  }

  /// Parse a binary operator at the current expression index.
  /// @return Operator with precedence and associativity.
  ///
  Operator parseOp()
  {
    eatSpaces();
    switch (getCharacter())
    {
      case '|': index_++;     return Operator(OPERATOR_BITWISE_OR,      4, 'L');
      case '&': index_++;     return Operator(OPERATOR_BITWISE_AND,     6, 'L');
      case '<': expect("<<"); return Operator(OPERATOR_BITWISE_SHL,     9, 'L');
      case '>': expect(">>"); return Operator(OPERATOR_BITWISE_SHR,     9, 'L');
      case '+': index_++;     return Operator(OPERATOR_ADDITION,       10, 'L');
      case '-': index_++;     return Operator(OPERATOR_SUBTRACTION,    10, 'L');
      case '/': index_++;     return Operator(OPERATOR_DIVISION,       20, 'L');
      case '%': index_++;     return Operator(OPERATOR_MODULO,         20, 'L');
      case '*': index_++; if (getCharacter() != '*')
                              return Operator(OPERATOR_MULTIPLICATION, 20, 'L');
                index_++;     return Operator(OPERATOR_POWER,          30, 'R');
      case '^': index_++;     return Operator(OPERATOR_POWER,          30, 'R');
      case 'e': index_++;     return Operator(OPERATOR_EXPONENT,       40, 'R');
      case 'E': index_++;     return Operator(OPERATOR_EXPONENT,       40, 'R');
      default :               return Operator(OPERATOR_NULL,            0, 'L');
    }
  }

  static T toInteger(char c)
  {
    if (c >= '0' && c <= '9') return c -'0';
    if (c >= 'a' && c <= 'f') return c -'a' + 0xa;
    if (c >= 'A' && c <= 'F') return c -'A' + 0xa;
    T noDigit = 0xf + 1;
    return noDigit;
  }

  T getInteger() const
  {
    return toInteger(getCharacter());
  }

  T parseDecimal()
  {
    T value = 0;
    for (T d; (d = getInteger()) <= 9; index_++)
      value = value * 10 + d;
    return value;
  }

  T parseHex()
  {
    index_ = index_ + 2;
    T value = 0;
    for (T h; (h = getInteger()) <= 0xf; index_++)
      value = value * 0x10 + h;
    return value;
  }

  bool isHex() const
  {
    if (index_ + 2 < expr_.size())
    {
      char x = expr_[index_ + 1];
      char h = expr_[index_ + 2];
      return (std::tolower(x) == 'x' && toInteger(h) <= 0xf);
    }
    return false;
  }

  /// Parse an integer value at the current expression index.
  /// The unary `+', `-' and `~' operators and opening
  /// parentheses `(' cause recursion.
  ///
  T parseValue()
  {
    T val = 0;
    eatSpaces();
    switch (getCharacter())
    {
      case '0': if (isHex())
                  val = parseHex();
                else
                  val = parseDecimal();
                break;
      case '1': case '2': case '3': case '4': case '5':
      case '6': case '7': case '8': case '9':
                val = parseDecimal();
                break;
      case '(': index_++;
                val = parseExpr();
                eatSpaces();
                if (getCharacter() != ')')
                {
                  if (!isEnd())
                    unexpected();
                  throw calculator::error(expr_, "Syntax error: `)' expected at end of expression");
                }
                index_++; break;
      case '~': index_++; val = ~parseValue(); break;
      case '+': index_++; val =  parseValue(); break;
      case '-': index_++; val =  parseValue() * static_cast<T>(-1);
                break;
      default : if (!isEnd())
                  unexpected();
                throw calculator::error(expr_, "Syntax error: value expected at end of expression");
    }
    return val;
  }

  /// Parse all operations of the current parenthesis
  /// level and the levels above, when done
  /// return the result (value).
  ///
  T parseExpr()
  {
    stack_.push(OperatorValue(Operator(OPERATOR_NULL, 0, 'L'), 0));
    // first parse value on the left
    T value = parseValue();

    while (!stack_.empty())
    {
      // parse an operator (+, -, *, ...)
      Operator op(parseOp());
      while (op.precedence  < stack_.top().getPrecedence() || (
             op.precedence == stack_.top().getPrecedence() &&
             op.associativity == 'L'))
      {
        // end reached
        if (stack_.top().isNull())
        {
          stack_.pop();
          return value;
        }
        // do the calculation ("reduce"), producing a new value
        value = calculate(stack_.top().value, value, stack_.top().op);
        stack_.pop();
      }

      // store on stack_ and continue parsing ("shift")
      stack_.push(OperatorValue(op, value));
      // parse value on the right
      value = parseValue();
    }
    return 0;
  }
};

template <typename T>
inline T eval(const std::string& expression)
{
  ExpressionParser<T> parser;
  return parser.eval(expression);
}

template <typename T>
inline T eval(char c)
{
  ExpressionParser<T> parser;
  return parser.eval(c);
}

inline int eval(const std::string& expression)
{
  return eval<int>(expression);
}

inline int eval(char c)
{
  return eval<int>(c);
}

} // namespace calculator

#endif
<|endoftext|>