<commit_before>/*ckwg +5
 * Copyright 2011 by Kitware, Inc. All Rights Reserved. Please refer to
 * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel,
 * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065.
 */

#include "number_process.h"

#include <vistk/pipeline/config.h>
#include <vistk/pipeline/datum.h>
#include <vistk/pipeline/process_exception.h>

namespace vistk
{

class number_process::priv
{
  public:
    typedef uint32_t number_t;

    priv(number_t s, number_t e);
    ~priv();

    number_t const start;
    number_t const end;
    number_t current;

    edges_t output_edges;

    static number_t const DEFAULT_START_VALUE;
    static number_t const DEFAULT_END_VALUE;
    static config::key_t const START_CONFIG_NAME;
    static config::key_t const END_CONFIG_NAME;
    static port_t const OUTPUT_PORT_NAME;
};

number_process::priv::number_t const number_process::priv::DEFAULT_START_VALUE = 0;
number_process::priv::number_t const number_process::priv::DEFAULT_END_VALUE = 100;
config::key_t const number_process::priv::START_CONFIG_NAME = config::key_t("start");
config::key_t const number_process::priv::END_CONFIG_NAME = config::key_t("end");
process::port_t const number_process::priv::OUTPUT_PORT_NAME = process::port_t("number");

number_process
::number_process(config_t const& config)
  : process(config)
{
  priv::number_t start = config->get_value<priv::number_t>(priv::START_CONFIG_NAME, priv::DEFAULT_START_VALUE);
  priv::number_t end = config->get_value<priv::number_t>(priv::END_CONFIG_NAME, priv::DEFAULT_END_VALUE);

  d = boost::shared_ptr<priv>(new priv(start, end));
}

number_process
::~number_process()
{
}

config::keys_t
number_process
::available_config() const
{
  config::keys_t keys;

  keys.push_back(priv::START_CONFIG_NAME);
  keys.push_back(priv::END_CONFIG_NAME);

  return keys;
}

config::value_t
number_process
::config_default(config::key_t const& key) const
{
  if (key == priv::START_CONFIG_NAME)
  {
    return boost::lexical_cast<config::value_t>(priv::DEFAULT_START_VALUE);
  }
  if (key == priv::END_CONFIG_NAME)
  {
    return boost::lexical_cast<config::value_t>(priv::DEFAULT_END_VALUE);
  }

  return process::config_default(key);
}

config::description_t
number_process
::config_description(config::key_t const& key) const
{
  if (key == priv::START_CONFIG_NAME)
  {
    return config::description_t("The value to start counting at");
  }
  if (key == priv::END_CONFIG_NAME)
  {
    return config::description_t("The value to stop counting at");
  }

  return process::config_description(key);
}

process_registry::type_t
number_process
::type() const
{
  return process_registry::type_t("number_process");
}

void
number_process
::_init()
{
  // Check the configuration.
  if (d->end <= d->start)
  {
    throw invalid_configuration(name(), "The start value must be greater than the end value");
  }

  // Ensure the output port is connected.
  if (!d->output_edges.size())
  {
    throw missing_connection(name(), priv::OUTPUT_PORT_NAME, "The " + type() + " process is not much use without something out output to");
  }
}

void
number_process
::_step()
{
  datum_t dat;

  if (d->current == d->end)
  {
    dat = datum::complete_datum();
  }
  else
  {
    dat = datum::new_datum(d->current);

    ++d->current;
  }

  edge_datum_t const edat = edge_datum_t(dat, heartbeat_stamp());

  push_to_edges(d->output_edges, edat);

  process::_step();
}

void
number_process
::_connect_output_port(port_t const& port, edge_t edge)
{
  if (port == priv::OUTPUT_PORT_NAME)
  {
    d->output_edges.push_back(edge);

    return;
  }

  process::_connect_output_port(port, edge);
}

process::port_type_t
number_process
::_output_port_type(port_t const& port) const
{
  if (port == priv::OUTPUT_PORT_NAME)
  {
    port_flags_t flags;

    flags.insert(flag_required);

    return port_type_t("unsigned", flags);
  }

  process::_output_port_type(port);
}

process::port_description_t
number_process
::_output_port_description(port_t const& port) const
{
  if (port == priv::OUTPUT_PORT_NAME)
  {
    return port_description_t();
  }

  process::_output_port_description(port);
}

process::ports_t
number_process
::_output_ports() const
{
  ports_t ports;

  ports.push_back(priv::OUTPUT_PORT_NAME);

  return ports;
}

number_process::priv
::priv(number_t s, number_t e)
  : start(s)
  , end(e)
  , current(s)
{
}

number_process::priv
::~priv()
{
}

} // end namespace vistk
<commit_msg>Add a TODO item<commit_after>/*ckwg +5
 * Copyright 2011 by Kitware, Inc. All Rights Reserved. Please refer to
 * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel,
 * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065.
 */

#include "number_process.h"

#include <vistk/pipeline/config.h>
#include <vistk/pipeline/datum.h>
#include <vistk/pipeline/process_exception.h>

namespace vistk
{

class number_process::priv
{
  public:
    typedef uint32_t number_t;

    priv(number_t s, number_t e);
    ~priv();

    number_t const start;
    number_t const end;
    number_t current;

    edges_t output_edges;

    static number_t const DEFAULT_START_VALUE;
    static number_t const DEFAULT_END_VALUE;
    static config::key_t const START_CONFIG_NAME;
    static config::key_t const END_CONFIG_NAME;
    static port_t const OUTPUT_PORT_NAME;
};

number_process::priv::number_t const number_process::priv::DEFAULT_START_VALUE = 0;
number_process::priv::number_t const number_process::priv::DEFAULT_END_VALUE = 100;
config::key_t const number_process::priv::START_CONFIG_NAME = config::key_t("start");
config::key_t const number_process::priv::END_CONFIG_NAME = config::key_t("end");
process::port_t const number_process::priv::OUTPUT_PORT_NAME = process::port_t("number");

number_process
::number_process(config_t const& config)
  : process(config)
{
  priv::number_t start = config->get_value<priv::number_t>(priv::START_CONFIG_NAME, priv::DEFAULT_START_VALUE);
  priv::number_t end = config->get_value<priv::number_t>(priv::END_CONFIG_NAME, priv::DEFAULT_END_VALUE);

  d = boost::shared_ptr<priv>(new priv(start, end));
}

number_process
::~number_process()
{
}

config::keys_t
number_process
::available_config() const
{
  config::keys_t keys;

  keys.push_back(priv::START_CONFIG_NAME);
  keys.push_back(priv::END_CONFIG_NAME);

  return keys;
}

config::value_t
number_process
::config_default(config::key_t const& key) const
{
  if (key == priv::START_CONFIG_NAME)
  {
    return boost::lexical_cast<config::value_t>(priv::DEFAULT_START_VALUE);
  }
  if (key == priv::END_CONFIG_NAME)
  {
    return boost::lexical_cast<config::value_t>(priv::DEFAULT_END_VALUE);
  }

  return process::config_default(key);
}

config::description_t
number_process
::config_description(config::key_t const& key) const
{
  if (key == priv::START_CONFIG_NAME)
  {
    return config::description_t("The value to start counting at");
  }
  if (key == priv::END_CONFIG_NAME)
  {
    return config::description_t("The value to stop counting at");
  }

  return process::config_description(key);
}

process_registry::type_t
number_process
::type() const
{
  return process_registry::type_t("number_process");
}

void
number_process
::_init()
{
  // Check the configuration.
  if (d->end <= d->start)
  {
    throw invalid_configuration(name(), "The start value must be greater than the end value");
  }

  // Ensure the output port is connected.
  if (!d->output_edges.size())
  {
    throw missing_connection(name(), priv::OUTPUT_PORT_NAME, "The " + type() + " process is not much use without something out output to");
  }
}

void
number_process
::_step()
{
  datum_t dat;

  if (d->current == d->end)
  {
    dat = datum::complete_datum();
  }
  else
  {
    dat = datum::new_datum(d->current);

    ++d->current;
  }

  edge_datum_t const edat = edge_datum_t(dat, heartbeat_stamp());

  push_to_edges(d->output_edges, edat);

  process::_step();
}

void
number_process
::_connect_output_port(port_t const& port, edge_t edge)
{
  if (port == priv::OUTPUT_PORT_NAME)
  {
    d->output_edges.push_back(edge);

    return;
  }

  process::_connect_output_port(port, edge);
}

process::port_type_t
number_process
::_output_port_type(port_t const& port) const
{
  if (port == priv::OUTPUT_PORT_NAME)
  {
    port_flags_t flags;

    flags.insert(flag_required);

    return port_type_t("unsigned", flags);
  }

  process::_output_port_type(port);
}

process::port_description_t
number_process
::_output_port_description(port_t const& port) const
{
  if (port == priv::OUTPUT_PORT_NAME)
  {
    /// \todo Describe this port.
    return port_description_t();
  }

  process::_output_port_description(port);
}

process::ports_t
number_process
::_output_ports() const
{
  ports_t ports;

  ports.push_back(priv::OUTPUT_PORT_NAME);

  return ports;
}

number_process::priv
::priv(number_t s, number_t e)
  : start(s)
  , end(e)
  , current(s)
{
}

number_process::priv
::~priv()
{
}

} // end namespace vistk
<|endoftext|>


<commit_before>
#include <fcntl.h>
#include <string>
#include <tuple>
#include <algorithm>
#include <map>
#include <iostream>
#include <functional>
#include <google/protobuf/message.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/io/gzip_stream.h>
#include "example1.pb.h"

using namespace std;
using namespace google::protobuf;

template <typename T, typename It>
static string join(const string& delim, It begin, It end,
    const function<string(const T&)>& to_str) {
  string joined;
  if (begin != end) {
     joined += to_str(*begin);
     while ((++begin) != end) {
       joined += delim;
       joined += to_str(*begin);
     }
  }
  return joined;
}

template <typename T>
static string joinVec(const string& delim, const vector<T>& ts,
    const function<string(const T&)>& to_str) {
  return join<T>(delim, ts.cbegin(), ts.cend(), to_str);
}

const function<string(const string&)> string2str = [](const string& str) {return str;};

struct Proto {
  const Message* defaultInstance;
  const char* protoNamespace;
};

void getProtoDetails(const string& protoName, Proto& proto) {
  if (protoName == "Example1.Company") {
    static Example1::Company defaultInstance;
    proto.defaultInstance = &defaultInstance;
    proto.protoNamespace = "Example1";
  } else {
    throw runtime_error("No mapping defined for protoName: " + protoName);
  }
}

struct Field {
  vector<const FieldDescriptor*> fieldParts;

  bool operator<(const Field& other) const {
    return fieldParts < other.fieldParts;
  }

  string type() const {
    if (fieldParts.empty()) {
      throw runtime_error("Can't determine type of empty field");
    }
    const FieldDescriptor* lastPart = fieldParts.back();
    if (lastPart->type() == FieldDescriptor::Type::TYPE_MESSAGE) {
      const Descriptor* msgType = lastPart->message_type();
      return msgType->name();
    } else {
      return lastPart->cpp_type_name();
    }
  }

  string accessor(const string& objName) const {
    string str = objName;
    for (const FieldDescriptor* part : fieldParts) {
      str += "." + part->name() + "()";
    }
    return str;
  }

  string hasAccessor(const string& objName, uint32_t end) const {
    string str = objName;
    for (uint32_t i=0; i<end; i++) {
      str += string(".") + ((i==(end-1)) ? "has_" : "") +
             fieldParts[i]->name() + "()";
    }
    return str;
  }

  string sizeAccessor(const string& objName) const {
    string str = objName;
    for (uint32_t i=0; i<fieldParts.size(); i++) {
      str += string(".") + fieldParts[i]->name() +
             ((i==(fieldParts.size()-1)) ? "_size" : "") + "()";
    }
    return str;
  }
};

enum NodeType {
  ROOT, REPEATED_MESSAGE, REPEATED_PRIMITIVE
};

// Root will be a node and all repeated fields will be nodes
struct Node {
  // only the ROOT node will be marked ROOT
  NodeType type;
  // this is the variable name in the generated code
  string objName;
  // this will be set for non root nodes
  Field repeatedField;
  // children of this node, repeated fields, repeated field => Node.
  map<Field, Node> children;
  // list of non-repeating select fields for this node.
  // Only populated for ROOT, REPEATED_MESSAGE
  vector<Field> selectFields;
};

using SelectFieldsFn = function<void(int indent, const Node& node)>;
using StartForAllFn = function<void(int indent, const Node& node, const Node& parent)>;
using EndForAllFn = function<void(int indent, const Node& node)>;

struct QueryGraph {
  Proto proto;
  Node root;
};

struct Query {
  vector<vector<string>> selects;
  string fromFile;
  string fromRootProto;
  vector<vector<string>> wheres;
  vector<vector<string>> groupbys;
  vector<vector<string>> havings;
  vector<vector<string>> orderbys;
};

void walkNode(const Node& node,
              int& indent,
              const SelectFieldsFn& selectFieldsFn,
              const StartForAllFn& startForAllFn,
              const uint32_t indentInc,
              map<int, const Node*>& endFieldsMap) {
  selectFieldsFn(indent, node);
  for (const auto& e : node.children) {
    const Node& child = e.second;
    startForAllFn(indent, child, node);
    endFieldsMap.emplace(-indent, &child);
    indent += indentInc;
    walkNode(child, indent, selectFieldsFn, startForAllFn,
             indentInc, endFieldsMap);
  }
}

void walkNode(const Node& root,
              const SelectFieldsFn& selectFieldsFn,
              const StartForAllFn& startForAllFn,
              const EndForAllFn& endForAllFn,
              const uint32_t indentInc = 2) {
  int indent = 0;
  map<int, const Node*> endNodeMap;
  walkNode(root, indent, selectFieldsFn, startForAllFn,
           indentInc, endNodeMap);
  for (const auto& e : endNodeMap) {
    int indent = -e.first;
    const Node* node = e.second;
    endForAllFn(indent, *node);
  }
}

void printPlan(QueryGraph& queryGraph) {
  SelectFieldsFn selectFieldsFn = [](int indent, const Node& node) {
    if (node.type == REPEATED_PRIMITIVE) {
      cout << string(indent, ' ') << "print " << node.objName << endl;
    } else {
      for (const Field& field : node.selectFields) {
        cout << string(indent, ' ') << "print " << field.accessor(node.objName) << endl;
      }
    }
  };
  StartForAllFn startForAllFn = [](int indent, const Node& node, const Node& parent) {
    cout << string(indent, ' ') << "for each " << node.objName << " in "
        << node.repeatedField.accessor(parent.objName) << " {" << endl;
  };
  EndForAllFn endForAllFn = [](int indent, const Node& node) {
    cout << string(indent, ' ') << "} //" << node.objName << endl;
  };
  walkNode(queryGraph.root, selectFieldsFn, startForAllFn, endForAllFn);
}

void printCode(QueryGraph& queryGraph) {
  cout << "using namespace " << queryGraph.proto.protoNamespace << ";" << endl;
  SelectFieldsFn selectFieldsFn = [](int indent, const Node& node) {};
  StartForAllFn startForAllFn = [](int indent, const Node& node, const Node& parent) {};
  EndForAllFn endForAllFn = [](int indent, const Node& node) {};

  vector<const Field*> allSelectFields;
  selectFieldsFn = [&](int indent, const Node& node) {
    if (node.type == REPEATED_PRIMITIVE) {
      allSelectFields.push_back(&node.repeatedField);
    } else {
      for (const Field& field : node.selectFields) {
        allSelectFields.push_back(&field);
      }
    }
  };
  walkNode(queryGraph.root, selectFieldsFn, startForAllFn, endForAllFn);

  map<const Field*, string> selectFieldVarMap;
  for (uint32_t i=0; i<allSelectFields.size(); i++) {
    const Field* field = allSelectFields[i];
    selectFieldVarMap[field] = "s" + to_string(i);
  }
  string tupleType = "using TupleType = tuple<";
  tupleType += joinVec<const Field*>(
      ",\n" + string(tupleType.size(), ' '),
      allSelectFields,
      [&](const Field* field) {
          string type = field->type();
          return type + string(6-type.size(), ' ') +
                 " /*" + field->accessor("") + "*/";
      });
  tupleType += ">;";
  cout << tupleType << endl;
  cout << endl;
  cout << "void runSelect(const "
      << queryGraph.proto.defaultInstance->GetDescriptor()->name() << "& "
      << queryGraph.root.objName << ", vector<TupleType>& tuples) {" << endl;
  vector<const Field*> selectFieldsProcessed;
  selectFieldsFn = [&](int indent, const Node& node) {
    string ind = string(indent+2, ' ');
    if (node.type == REPEATED_PRIMITIVE) {
      const Field* field = &node.repeatedField;
      cout << ind << "const " << field->type() << "* "
          << selectFieldVarMap[field] << " = &(" << node.objName << ");" << endl;
      selectFieldsProcessed.push_back(field);
    } else {
      for (const Field& field : node.selectFields) {
        cout << ind << "const " << field.type() << "* "
            << selectFieldVarMap[&field] << " = nullptr;" << endl;
        vector<string> checks;
        for (uint32_t i=0; i<field.fieldParts.size(); i++) {
          checks.push_back(field.hasAccessor(node.objName, i+1));
        }
        cout << ind << "if(" << joinVec<string>(" && ", checks, string2str)
            << ") {" << endl;
        cout << ind << "  " << selectFieldVarMap[&field] << " = &("
            << field.accessor(node.objName) << ");" << endl;
        cout << ind << "}" << endl;
        selectFieldsProcessed.push_back(&field);
      }
    }
    if (selectFieldsProcessed.size() == allSelectFields.size()) {
      string selectList = joinVec<const Field*>(", ", allSelectFields,
          [&](const Field* field) {return selectFieldVarMap[field];});
      cout << ind << "tuples.emplace(" + selectList + ");" << endl;
    }
  };
  startForAllFn = [](int indent, const Node& node, const Node& parent) {
    string ind = string(indent+2, ' ');
    cout << ind << "if (" << node.repeatedField.sizeAccessor(parent.objName)
        << " > 0) {" << endl;
    cout << ind << "  for (const " << node.repeatedField.type() << "& " <<
        node.objName << " : " << node.repeatedField.accessor(parent.objName) <<
        ") {" << endl;
  };
  endForAllFn = [&](int indent, const Node& node) {
    string ind = string(indent+2, ' ');
    cout << ind << "  }" << endl;
    cout << ind << "} else { // no " << node.objName << endl;
    if (node.type == REPEATED_PRIMITIVE) {
      auto it = std::find(
          selectFieldsProcessed.begin(), selectFieldsProcessed.end(),
          &node.repeatedField);
      *it = nullptr;
    } else {
      for (const Field& field : node.selectFields) {
        auto it = std::find(
            selectFieldsProcessed.begin(), selectFieldsProcessed.end(),
            &field);
        *it = nullptr;
      }
    }
    string selectList = joinVec<const Field*>(
        ", ", selectFieldsProcessed,
        [&](const Field* field) {
            return field ? selectFieldVarMap[field] : "nullptr";
        });
    cout << ind << "  " << "tuples.emplace(" + selectList + ");" << endl;
    cout << ind << "}" << endl;
  };
  walkNode(queryGraph.root, selectFieldsFn, startForAllFn, endForAllFn, 4);
  cout << "}" << endl;
}

string constructObjNameForRepeated(const FieldDescriptor* field) {
  string fieldName = field->name();
  return (fieldName[fieldName.size()-1] == 's') ?
      fieldName.substr(0, fieldName.size()-1) : fieldName;
}

void calculateQueryGraph(const Query& query, QueryGraph& queryGraph) {
  getProtoDetails(query.fromRootProto, queryGraph.proto);
  const Descriptor* rootDescriptor =
      queryGraph.proto.defaultInstance->GetDescriptor();
  queryGraph.root.type = ROOT;
  queryGraph.root.objName = rootDescriptor->name();
  std::transform(queryGraph.root.objName.begin(), queryGraph.root.objName.end(),
                 queryGraph.root.objName.begin(), ::tolower);
  for (const vector<string>& select : query.selects) {
    const Descriptor* parentDescriptor = rootDescriptor;
    Node* parent = &(queryGraph.root);
    Field field;
    for (size_t i=0; i<select.size(); i++) {
      const FieldDescriptor* fieldPart =
          parentDescriptor->FindFieldByName(select[i]);
      if (fieldPart == nullptr) {
        throw runtime_error("No fieldPart by name " + select[i]);
      }
      field.fieldParts.push_back(fieldPart);
      if (i != (select.size()-1)) {
        if (fieldPart->type() != FieldDescriptor::Type::TYPE_MESSAGE) {
          throw runtime_error(
              "FieldPart " + select[i] + " expected to be message but found" +
              fieldPart->type_name());
        }
        if (fieldPart->label() == FieldDescriptor::LABEL_REPEATED) {
          Node& child = parent->children[field];
          child.type = REPEATED_MESSAGE;
          child.objName = constructObjNameForRepeated(fieldPart);
          child.repeatedField = field;
          parent = &child;
          field.fieldParts.clear();
        }
        parentDescriptor = fieldPart->message_type();
      } else {
        if (fieldPart->type() == FieldDescriptor::Type::TYPE_MESSAGE) {
          throw runtime_error(
              "FieldPart " + select[i] + " not expected to be message ");
        }
        if (fieldPart->label() == FieldDescriptor::LABEL_REPEATED) {
          Node& child = parent->children[field];
          child.type = REPEATED_PRIMITIVE;
          child.objName = constructObjNameForRepeated(fieldPart);
          child.repeatedField = field;
        } else {
          parent->selectFields.push_back(field);
        }
      }
    }
  }
}

void parseQuery(const string&, Query& query) {
  query.selects = {
      {"founded"},
      {"board_of_directors"},
      {"financial", "quarterly_profits"},
      {"financial", "quarterly_revenues"},
      {"all_employees", "id"},
      {"all_employees", "name"},
      {"all_employees", "active"},
      {"all_employees", "active_direct_reports"},
  };
  query.fromFile = "/tmp/example1.proto";
  query.fromRootProto = "Example1.Company";
  // TODO: do some validation on query
}

int main(int /*argc*/, char** /*argv*/) {
  Query query;
  parseQuery("", query);
  QueryGraph queryGraph;
  calculateQueryGraph(query, queryGraph);
  cout << "/*" << endl;
  printPlan(queryGraph);
  cout << "*/" << endl;
  printCode(queryGraph);
}

<commit_msg>add some #includes<commit_after>
#include <fcntl.h>
#include <string>
#include <tuple>
#include <algorithm>
#include <map>
#include <iostream>
#include <functional>
#include <google/protobuf/message.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/io/gzip_stream.h>
#include "example1.pb.h"

using namespace std;
using namespace google::protobuf;

template <typename T, typename It>
static string join(const string& delim, It begin, It end,
    const function<string(const T&)>& to_str) {
  string joined;
  if (begin != end) {
     joined += to_str(*begin);
     while ((++begin) != end) {
       joined += delim;
       joined += to_str(*begin);
     }
  }
  return joined;
}

template <typename T>
static string joinVec(const string& delim, const vector<T>& ts,
    const function<string(const T&)>& to_str) {
  return join<T>(delim, ts.cbegin(), ts.cend(), to_str);
}

const function<string(const string&)> string2str = [](const string& str) {return str;};

struct Proto {
  const Message* defaultInstance;
  const char* protoNamespace;
  const char* protoHeaderInclude;
};

void getProtoDetails(const string& protoName, Proto& proto) {
  if (protoName == "Example1.Company") {
    static Example1::Company defaultInstance;
    proto.defaultInstance = &defaultInstance;
    proto.protoNamespace = "Example1";
    proto.protoHeaderInclude = "example1.pb.h";
  } else {
    throw runtime_error("No mapping defined for protoName: " + protoName);
  }
}

struct Field {
  vector<const FieldDescriptor*> fieldParts;

  bool operator<(const Field& other) const {
    return fieldParts < other.fieldParts;
  }

  string type() const {
    if (fieldParts.empty()) {
      throw runtime_error("Can't determine type of empty field");
    }
    const FieldDescriptor* lastPart = fieldParts.back();
    if (lastPart->type() == FieldDescriptor::Type::TYPE_MESSAGE) {
      const Descriptor* msgType = lastPart->message_type();
      return msgType->name();
    } else {
      return lastPart->cpp_type_name();
    }
  }

  string accessor(const string& objName) const {
    string str = objName;
    for (const FieldDescriptor* part : fieldParts) {
      str += "." + part->name() + "()";
    }
    return str;
  }

  string hasAccessor(const string& objName, uint32_t end) const {
    string str = objName;
    for (uint32_t i=0; i<end; i++) {
      str += string(".") + ((i==(end-1)) ? "has_" : "") +
             fieldParts[i]->name() + "()";
    }
    return str;
  }

  string sizeAccessor(const string& objName) const {
    string str = objName;
    for (uint32_t i=0; i<fieldParts.size(); i++) {
      str += string(".") + fieldParts[i]->name() +
             ((i==(fieldParts.size()-1)) ? "_size" : "") + "()";
    }
    return str;
  }
};

enum NodeType {
  ROOT, REPEATED_MESSAGE, REPEATED_PRIMITIVE
};

// Root will be a node and all repeated fields will be nodes
struct Node {
  // only the ROOT node will be marked ROOT
  NodeType type;
  // this is the variable name in the generated code
  string objName;
  // this will be set for non root nodes
  Field repeatedField;
  // children of this node, repeated fields, repeated field => Node.
  map<Field, Node> children;
  // list of non-repeating select fields for this node.
  // Only populated for ROOT, REPEATED_MESSAGE
  vector<Field> selectFields;
};

using SelectFieldsFn = function<void(int indent, const Node& node)>;
using StartForAllFn = function<void(int indent, const Node& node, const Node& parent)>;
using EndForAllFn = function<void(int indent, const Node& node)>;

struct QueryGraph {
  Proto proto;
  Node root;
};

struct Query {
  vector<vector<string>> selects;
  string fromFile;
  string fromRootProto;
  vector<vector<string>> wheres;
  vector<vector<string>> groupbys;
  vector<vector<string>> havings;
  vector<vector<string>> orderbys;
};

void walkNode(const Node& node,
              int& indent,
              const SelectFieldsFn& selectFieldsFn,
              const StartForAllFn& startForAllFn,
              const uint32_t indentInc,
              map<int, const Node*>& endFieldsMap) {
  selectFieldsFn(indent, node);
  for (const auto& e : node.children) {
    const Node& child = e.second;
    startForAllFn(indent, child, node);
    endFieldsMap.emplace(-indent, &child);
    indent += indentInc;
    walkNode(child, indent, selectFieldsFn, startForAllFn,
             indentInc, endFieldsMap);
  }
}

void walkNode(const Node& root,
              const SelectFieldsFn& selectFieldsFn,
              const StartForAllFn& startForAllFn,
              const EndForAllFn& endForAllFn,
              const uint32_t indentInc = 2) {
  int indent = 0;
  map<int, const Node*> endNodeMap;
  walkNode(root, indent, selectFieldsFn, startForAllFn,
           indentInc, endNodeMap);
  for (const auto& e : endNodeMap) {
    int indent = -e.first;
    const Node* node = e.second;
    endForAllFn(indent, *node);
  }
}

void printPlan(QueryGraph& queryGraph) {
  SelectFieldsFn selectFieldsFn = [](int indent, const Node& node) {
    if (node.type == REPEATED_PRIMITIVE) {
      cout << string(indent, ' ') << "print " << node.objName << endl;
    } else {
      for (const Field& field : node.selectFields) {
        cout << string(indent, ' ') << "print " << field.accessor(node.objName) << endl;
      }
    }
  };
  StartForAllFn startForAllFn = [](int indent, const Node& node, const Node& parent) {
    cout << string(indent, ' ') << "for each " << node.objName << " in "
        << node.repeatedField.accessor(parent.objName) << " {" << endl;
  };
  EndForAllFn endForAllFn = [](int indent, const Node& node) {
    cout << string(indent, ' ') << "} //" << node.objName << endl;
  };
  walkNode(queryGraph.root, selectFieldsFn, startForAllFn, endForAllFn);
}

void printCode(QueryGraph& queryGraph) {
  cout << "#include <tuple>" << endl;
  cout << "#include \"" << queryGraph.proto.protoHeaderInclude << "\"" << endl;
  cout << "using namespace std;" << endl;
  cout << "using namespace " << queryGraph.proto.protoNamespace << ";" << endl;
  SelectFieldsFn selectFieldsFn = [](int indent, const Node& node) {};
  StartForAllFn startForAllFn = [](int indent, const Node& node, const Node& parent) {};
  EndForAllFn endForAllFn = [](int indent, const Node& node) {};

  vector<const Field*> allSelectFields;
  selectFieldsFn = [&](int indent, const Node& node) {
    if (node.type == REPEATED_PRIMITIVE) {
      allSelectFields.push_back(&node.repeatedField);
    } else {
      for (const Field& field : node.selectFields) {
        allSelectFields.push_back(&field);
      }
    }
  };
  walkNode(queryGraph.root, selectFieldsFn, startForAllFn, endForAllFn);

  map<const Field*, string> selectFieldVarMap;
  for (uint32_t i=0; i<allSelectFields.size(); i++) {
    const Field* field = allSelectFields[i];
    selectFieldVarMap[field] = "s" + to_string(i);
  }
  string tupleType = "using TupleType = tuple<";
  tupleType += joinVec<const Field*>(
      ",\n" + string(tupleType.size(), ' '),
      allSelectFields,
      [&](const Field* field) {
          string type = field->type();
          string spaces((((8-type.size()) > 0) ? (8-type.size()) : 0), ' ');
          return type + spaces + " /*" + field->accessor("") + "*/";
      });
  tupleType += ">;";
  cout << tupleType << endl;
  cout << endl;
  cout << "void runSelect(const "
      << queryGraph.proto.defaultInstance->GetDescriptor()->name() << "& "
      << queryGraph.root.objName << ", vector<TupleType>& tuples) {" << endl;
  vector<const Field*> selectFieldsProcessed;
  selectFieldsFn = [&](int indent, const Node& node) {
    string ind = string(indent+2, ' ');
    if (node.type == REPEATED_PRIMITIVE) {
      const Field* field = &node.repeatedField;
      cout << ind << "const " << field->type() << "* "
          << selectFieldVarMap[field] << " = &(" << node.objName << ");" << endl;
      selectFieldsProcessed.push_back(field);
    } else {
      for (const Field& field : node.selectFields) {
        cout << ind << "const " << field.type() << "* "
            << selectFieldVarMap[&field] << " = nullptr;" << endl;
        vector<string> checks;
        for (uint32_t i=0; i<field.fieldParts.size(); i++) {
          checks.push_back(field.hasAccessor(node.objName, i+1));
        }
        cout << ind << "if(" << joinVec<string>(" && ", checks, string2str)
            << ") {" << endl;
        cout << ind << "  " << selectFieldVarMap[&field] << " = &("
            << field.accessor(node.objName) << ");" << endl;
        cout << ind << "}" << endl;
        selectFieldsProcessed.push_back(&field);
      }
    }
    if (selectFieldsProcessed.size() == allSelectFields.size()) {
      string selectList = joinVec<const Field*>(", ", allSelectFields,
          [&](const Field* field) {return selectFieldVarMap[field];});
      cout << ind << "tuples.emplace(" + selectList + ");" << endl;
    }
  };
  startForAllFn = [](int indent, const Node& node, const Node& parent) {
    string ind = string(indent+2, ' ');
    cout << ind << "if (" << node.repeatedField.sizeAccessor(parent.objName)
        << " > 0) {" << endl;
    cout << ind << "  for (const " << node.repeatedField.type() << "& " <<
        node.objName << " : " << node.repeatedField.accessor(parent.objName) <<
        ") {" << endl;
  };
  endForAllFn = [&](int indent, const Node& node) {
    string ind = string(indent+2, ' ');
    cout << ind << "  }" << endl;
    cout << ind << "} else { // no " << node.objName << endl;
    if (node.type == REPEATED_PRIMITIVE) {
      auto it = std::find(
          selectFieldsProcessed.begin(), selectFieldsProcessed.end(),
          &node.repeatedField);
      *it = nullptr;
    } else {
      for (const Field& field : node.selectFields) {
        auto it = std::find(
            selectFieldsProcessed.begin(), selectFieldsProcessed.end(),
            &field);
        *it = nullptr;
      }
    }
    string selectList = joinVec<const Field*>(
        ", ", selectFieldsProcessed,
        [&](const Field* field) {
            return field ? selectFieldVarMap[field] : "nullptr";
        });
    cout << ind << "  " << "tuples.emplace(" + selectList + ");" << endl;
    cout << ind << "}" << endl;
  };
  walkNode(queryGraph.root, selectFieldsFn, startForAllFn, endForAllFn, 4);
  cout << "}" << endl;
}

string constructObjNameForRepeated(const FieldDescriptor* field) {
  string fieldName = field->name();
  return (fieldName[fieldName.size()-1] == 's') ?
      fieldName.substr(0, fieldName.size()-1) : fieldName;
}

void calculateQueryGraph(const Query& query, QueryGraph& queryGraph) {
  getProtoDetails(query.fromRootProto, queryGraph.proto);
  const Descriptor* rootDescriptor =
      queryGraph.proto.defaultInstance->GetDescriptor();
  queryGraph.root.type = ROOT;
  queryGraph.root.objName = rootDescriptor->name();
  std::transform(queryGraph.root.objName.begin(), queryGraph.root.objName.end(),
                 queryGraph.root.objName.begin(), ::tolower);
  for (const vector<string>& select : query.selects) {
    const Descriptor* parentDescriptor = rootDescriptor;
    Node* parent = &(queryGraph.root);
    Field field;
    for (size_t i=0; i<select.size(); i++) {
      const FieldDescriptor* fieldPart =
          parentDescriptor->FindFieldByName(select[i]);
      if (fieldPart == nullptr) {
        throw runtime_error("No fieldPart by name " + select[i]);
      }
      field.fieldParts.push_back(fieldPart);
      if (i != (select.size()-1)) {
        if (fieldPart->type() != FieldDescriptor::Type::TYPE_MESSAGE) {
          throw runtime_error(
              "FieldPart " + select[i] + " expected to be message but found" +
              fieldPart->type_name());
        }
        if (fieldPart->label() == FieldDescriptor::LABEL_REPEATED) {
          Node& child = parent->children[field];
          child.type = REPEATED_MESSAGE;
          child.objName = constructObjNameForRepeated(fieldPart);
          child.repeatedField = field;
          parent = &child;
          field.fieldParts.clear();
        }
        parentDescriptor = fieldPart->message_type();
      } else {
        if (fieldPart->type() == FieldDescriptor::Type::TYPE_MESSAGE) {
          throw runtime_error(
              "FieldPart " + select[i] + " not expected to be message ");
        }
        if (fieldPart->label() == FieldDescriptor::LABEL_REPEATED) {
          Node& child = parent->children[field];
          child.type = REPEATED_PRIMITIVE;
          child.objName = constructObjNameForRepeated(fieldPart);
          child.repeatedField = field;
        } else {
          parent->selectFields.push_back(field);
        }
      }
    }
  }
}

void parseQuery(const string&, Query& query) {
  query.selects = {
      {"founded"},
      {"board_of_directors"},
      {"financial", "quarterly_profits"},
      {"financial", "quarterly_revenues"},
      {"all_employees", "id"},
      {"all_employees", "name"},
      {"all_employees", "active"},
      {"all_employees", "active_direct_reports"},
  };
  query.fromFile = "/tmp/example1.proto";
  query.fromRootProto = "Example1.Company";
  // TODO: do some validation on query
}

int main(int /*argc*/, char** /*argv*/) {
  Query query;
  parseQuery("", query);
  QueryGraph queryGraph;
  calculateQueryGraph(query, queryGraph);
  cout << "/*" << endl;
  printPlan(queryGraph);
  cout << "*/" << endl;
  printCode(queryGraph);
}

<|endoftext|>


<commit_before>/*
 * (C) Copyright 2015 ETH Zurich Systems Group (http://www.systems.ethz.ch/) and others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.
 *
 * Contributors:
 *     Markus Pilman <mpilman@inf.ethz.ch>
 *     Simon Loesing <sloesing@inf.ethz.ch>
 *     Thomas Etter <etterth@gmail.com>
 *     Kevin Bocksrocker <kevin.bocksrocker@gmail.com>
 *     Lucas Braun <braunl@inf.ethz.ch>
 */
#pragma once

#include <tellstore/ClientConfig.hpp>
#include <tellstore/ClientSocket.hpp>
#include <tellstore/GenericTuple.hpp>
#include <tellstore/ScanMemory.hpp>
#include <tellstore/Table.hpp>
#include <tellstore/TransactionType.hpp>

#include <commitmanager/ClientSocket.hpp>
#include <commitmanager/SnapshotDescriptor.hpp>

#include <crossbow/infinio/InfinibandService.hpp>
#include <crossbow/infinio/Fiber.hpp>
#include <crossbow/logger.hpp>
#include <crossbow/non_copyable.hpp>
#include <crossbow/string.hpp>

#include <boost/functional/hash.hpp>

#include <atomic>
#include <cstdint>
#include <functional>
#include <memory>
#include <system_error>
#include <thread>
#include <tuple>
#include <type_traits>
#include <vector>

namespace tell {
namespace store {

struct ClientConfig;
class BaseClientProcessor;
class Record;

/**
 * @brief Class to interact with the TellStore from within a fiber
 */
class ClientHandle : crossbow::non_copyable, crossbow::non_movable {
public:
    static std::unique_ptr<commitmanager::SnapshotDescriptor> createNonTransactionalSnapshot(uint64_t baseVersion);

    static std::unique_ptr<commitmanager::SnapshotDescriptor> createAnalyticalSnapshot(uint64_t lowestActiveVersion,
            uint64_t baseVersion);

    ClientHandle(BaseClientProcessor& processor, crossbow::infinio::Fiber& fiber)
            : mProcessor(processor),
              mFiber(fiber) {
    }

    crossbow::infinio::Fiber& fiber() {
        return mFiber;
    }

    std::unique_ptr<commitmanager::SnapshotDescriptor> startTransaction(
            TransactionType type = TransactionType::READ_WRITE);

    void commit(const commitmanager::SnapshotDescriptor& snapshot);

    Table createTable(const crossbow::string& name, Schema schema);

    std::shared_ptr<GetTableResponse> getTable(const crossbow::string& name);

    std::shared_ptr<GetResponse> get(const Table& table, uint64_t key);

    std::shared_ptr<GetResponse> get(const Table& table, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot);

    std::shared_ptr<ModificationResponse> insert(const Table& table, uint64_t key, uint64_t version, GenericTuple data);

    std::shared_ptr<ModificationResponse> insert(const Table& table, uint64_t key, uint64_t version,
            const AbstractTuple& tuple);

    std::shared_ptr<ModificationResponse> insert(const Table& table, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot, GenericTuple data);

    std::shared_ptr<ModificationResponse> insert(const Table& table, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot, const AbstractTuple& tuple);

    std::shared_ptr<ModificationResponse> update(const Table& table, uint64_t key, uint64_t version, GenericTuple data);

    std::shared_ptr<ModificationResponse> update(const Table& table, uint64_t key, uint64_t version,
            const AbstractTuple& tuple);

    std::shared_ptr<ModificationResponse> update(const Table& table, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot, GenericTuple data);

    std::shared_ptr<ModificationResponse> update(const Table& table, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot, const AbstractTuple& tuple);

    std::shared_ptr<ModificationResponse> remove(const Table& table, uint64_t key, uint64_t version);

    std::shared_ptr<ModificationResponse> remove(const Table& table, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot);

    std::shared_ptr<ModificationResponse> revert(const Table& table, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot);

    std::shared_ptr<ScanIterator> scan(const Table& table, const commitmanager::SnapshotDescriptor& snapshot,
            ScanMemoryManager& memoryManager, ScanQueryType queryType, uint32_t selectionLength, const char* selection,
            uint32_t queryLength, const char* query);

private:
    BaseClientProcessor& mProcessor;
    crossbow::infinio::Fiber& mFiber;
};

/**
 * @brief Class managing all running TellStore fibers
 */
class BaseClientProcessor : crossbow::non_copyable, crossbow::non_movable {
public:
    void shutdown();

    std::unique_ptr<commitmanager::SnapshotDescriptor> start(crossbow::infinio::Fiber& fiber, TransactionType type);

    void commit(crossbow::infinio::Fiber& fiber, const commitmanager::SnapshotDescriptor& snapshot);

    Table createTable(crossbow::infinio::Fiber& fiber, const crossbow::string& name, Schema schema);

    std::shared_ptr<GetTableResponse> getTable(crossbow::infinio::Fiber& fiber, const crossbow::string& name) {
        return mTellStoreSocket.at(0)->getTable(fiber, name);
    }

    std::shared_ptr<GetResponse> get(crossbow::infinio::Fiber& fiber, uint64_t tableId, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot) {
        return shard(tableId, key)->get(fiber, tableId, key, snapshot);
    }

    std::shared_ptr<ModificationResponse> insert(crossbow::infinio::Fiber& fiber, uint64_t tableId, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot, const AbstractTuple& tuple) {
        return shard(tableId, key)->insert(fiber, tableId, key, snapshot, tuple);
    }

    std::shared_ptr<ModificationResponse> update(crossbow::infinio::Fiber& fiber, uint64_t tableId, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot, const AbstractTuple& tuple) {
        return shard(tableId, key)->update(fiber, tableId, key, snapshot, tuple);
    }

    std::shared_ptr<ModificationResponse> remove(crossbow::infinio::Fiber& fiber, uint64_t tableId, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot) {
        return shard(tableId, key)->remove(fiber, tableId, key, snapshot);
    }

    std::shared_ptr<ModificationResponse> revert(crossbow::infinio::Fiber& fiber, uint64_t tableId, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot) {
        return shard(tableId, key)->revert(fiber, tableId, key, snapshot);
    }

    std::shared_ptr<ScanIterator> scan(crossbow::infinio::Fiber& fiber, uint64_t tableId,
            const commitmanager::SnapshotDescriptor& snapshot, Record record, ScanMemoryManager& memoryManager,
            ScanQueryType queryType, uint32_t selectionLength, const char* selection, uint32_t queryLength,
            const char* query);

protected:
    BaseClientProcessor(crossbow::infinio::InfinibandService& service, const ClientConfig& config,
            uint64_t processorNum);

    ~BaseClientProcessor() = default;

    template <typename Fun>
    void executeFiber(Fun fun) {
        // TODO Starting a fiber without the fiber cache takes ~500us - Investigate why
        mProcessor->executeFiber(std::move(fun));
    }

private:
    /**
     * @brief The socket associated with the shard for the given table and key
     */
    store::ClientSocket* shard(uint64_t tableId, uint64_t key) {
        auto hash = std::hash<uint64_t>()(tableId);
        boost::hash_combine(hash, std::hash<uint64_t>()(key));
        return mTellStoreSocket.at(hash % mTellStoreSocket.size()).get();
    }

    std::unique_ptr<crossbow::infinio::InfinibandProcessor> mProcessor;

    commitmanager::ClientSocket mCommitManagerSocket;
    std::vector<std::unique_ptr<store::ClientSocket>> mTellStoreSocket;

    uint64_t mProcessorNum;

    uint16_t mScanId;
};

/**
 * @brief Class managing all running TellStore fibers and its associated context
 */
template <typename Context>
class ClientProcessor : public BaseClientProcessor {
public:
    template <typename... Args>
    ClientProcessor(crossbow::infinio::InfinibandService& service, const ClientConfig& config, uint64_t processorNum,
            Args&&... contextArgs)
            : BaseClientProcessor(service, config, processorNum),
              mTransactionCount(0),
              mContext(std::forward<Args>(contextArgs)...) {
    }

    uint64_t transactionCount() const {
        return mTransactionCount.load();
    }

    template <typename Fun>
    void execute(Fun fun);

private:
    template <typename Fun, typename C = Context>
    typename std::enable_if<std::is_void<C>::value, void>::type executeHandler(Fun& fun, ClientHandle& handle) {
        fun(handle);
    }

    template <typename Fun, typename C = Context>
    typename std::enable_if<!std::is_void<C>::value, void>::type executeHandler(Fun& fun, ClientHandle& handle) {
        fun(handle, mContext);
    }

    std::atomic<uint64_t> mTransactionCount;

    /// The user defined context associated with this processor
    /// In case the context is void we simply allocate a 0-sized array
    typename std::conditional<std::is_void<Context>::value, char[0], Context>::type mContext;
};

template <typename Context>
template <typename Fun>
void ClientProcessor<Context>::execute(Fun fun) {
    ++mTransactionCount;

    executeFiber([this, fun] (crossbow::infinio::Fiber& fiber) mutable {
        ClientHandle handle(*this, fiber);
        executeHandler(fun, handle);

        --mTransactionCount;
    });
}

/**
 * @brief Class managing all TellStore client processors
 *
 * Dispatches new client functions to the processor with the least amout of load.
 */
template <typename Context>
class ClientManager : crossbow::non_copyable, crossbow::non_movable {
public:
    template <typename... Args>
    ClientManager(const ClientConfig& config, Args... contextArgs);

    void shutdown();

    template <typename Fun>
    void execute(Fun fun);

    template <typename Fun>
    void execute(size_t num, Fun fun) {
        mProcessor.at(num)->execute(std::move(fun));
    }

    std::unique_ptr<ScanMemoryManager> allocateScanMemory(size_t chunkCount, size_t chunkLength) {
        return std::unique_ptr<ScanMemoryManager>(new ScanMemoryManager(mService, chunkCount, chunkLength));
    }

private:
    crossbow::infinio::InfinibandService mService;

    std::thread mServiceThread;

    std::vector<std::unique_ptr<ClientProcessor<Context>>> mProcessor;
};

template <typename Context>
template <typename... Args>
ClientManager<Context>::ClientManager(const ClientConfig& config, Args... contextArgs)
        : mService(config.infinibandConfig) {
    LOG_INFO("Starting client manager");

    // TODO Move the service thread into the Infiniband Service itself
    mServiceThread = std::thread([this] () {
        mService.run();
    });

    mProcessor.reserve(config.numNetworkThreads);
    for (decltype(config.numNetworkThreads) i = 0; i < config.numNetworkThreads; ++i) {
        mProcessor.emplace_back(new ClientProcessor<Context>(mService, config, i, contextArgs...));
    }
}

template <typename Context>
void ClientManager<Context>::shutdown() {
    LOG_INFO("Shutting down client manager");
    for (auto& proc : mProcessor) {
        proc->shutdown();
    }

    LOG_INFO("Waiting for transactions to terminate");
    for (auto& proc : mProcessor) {
        while (proc->transactionCount() != 0) {
            std::this_thread::yield();
        }
    }
}

template <typename Context>
template <typename Fun>
void ClientManager<Context>::execute(Fun fun) {
    ClientProcessor<Context>* processor = nullptr;
    uint64_t minCount = std::numeric_limits<uint64_t>::max();
    for (auto& proc : mProcessor) {
        auto count = proc->transactionCount();
        if (minCount < count) {
            continue;
        }
        processor = proc.get();
        minCount = count;
    }
    LOG_ASSERT(processor != nullptr, "Found no processor");

    processor->execute(std::move(fun));
}

} // namespace store
} // namespace tell
<commit_msg>destroy ClientManager<commit_after>/*
 * (C) Copyright 2015 ETH Zurich Systems Group (http://www.systems.ethz.ch/) and others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.
 *
 * Contributors:
 *     Markus Pilman <mpilman@inf.ethz.ch>
 *     Simon Loesing <sloesing@inf.ethz.ch>
 *     Thomas Etter <etterth@gmail.com>
 *     Kevin Bocksrocker <kevin.bocksrocker@gmail.com>
 *     Lucas Braun <braunl@inf.ethz.ch>
 */
#pragma once

#include <tellstore/ClientConfig.hpp>
#include <tellstore/ClientSocket.hpp>
#include <tellstore/GenericTuple.hpp>
#include <tellstore/ScanMemory.hpp>
#include <tellstore/Table.hpp>
#include <tellstore/TransactionType.hpp>

#include <commitmanager/ClientSocket.hpp>
#include <commitmanager/SnapshotDescriptor.hpp>

#include <crossbow/infinio/InfinibandService.hpp>
#include <crossbow/infinio/Fiber.hpp>
#include <crossbow/logger.hpp>
#include <crossbow/non_copyable.hpp>
#include <crossbow/string.hpp>

#include <boost/functional/hash.hpp>

#include <atomic>
#include <cstdint>
#include <functional>
#include <memory>
#include <system_error>
#include <thread>
#include <tuple>
#include <type_traits>
#include <vector>

namespace tell {
namespace store {

struct ClientConfig;
class BaseClientProcessor;
class Record;

/**
 * @brief Class to interact with the TellStore from within a fiber
 */
class ClientHandle : crossbow::non_copyable, crossbow::non_movable {
public:
    static std::unique_ptr<commitmanager::SnapshotDescriptor> createNonTransactionalSnapshot(uint64_t baseVersion);

    static std::unique_ptr<commitmanager::SnapshotDescriptor> createAnalyticalSnapshot(uint64_t lowestActiveVersion,
            uint64_t baseVersion);

    ClientHandle(BaseClientProcessor& processor, crossbow::infinio::Fiber& fiber)
            : mProcessor(processor),
              mFiber(fiber) {
    }

    crossbow::infinio::Fiber& fiber() {
        return mFiber;
    }

    std::unique_ptr<commitmanager::SnapshotDescriptor> startTransaction(
            TransactionType type = TransactionType::READ_WRITE);

    void commit(const commitmanager::SnapshotDescriptor& snapshot);

    Table createTable(const crossbow::string& name, Schema schema);

    std::shared_ptr<GetTableResponse> getTable(const crossbow::string& name);

    std::shared_ptr<GetResponse> get(const Table& table, uint64_t key);

    std::shared_ptr<GetResponse> get(const Table& table, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot);

    std::shared_ptr<ModificationResponse> insert(const Table& table, uint64_t key, uint64_t version, GenericTuple data);

    std::shared_ptr<ModificationResponse> insert(const Table& table, uint64_t key, uint64_t version,
            const AbstractTuple& tuple);

    std::shared_ptr<ModificationResponse> insert(const Table& table, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot, GenericTuple data);

    std::shared_ptr<ModificationResponse> insert(const Table& table, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot, const AbstractTuple& tuple);

    std::shared_ptr<ModificationResponse> update(const Table& table, uint64_t key, uint64_t version, GenericTuple data);

    std::shared_ptr<ModificationResponse> update(const Table& table, uint64_t key, uint64_t version,
            const AbstractTuple& tuple);

    std::shared_ptr<ModificationResponse> update(const Table& table, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot, GenericTuple data);

    std::shared_ptr<ModificationResponse> update(const Table& table, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot, const AbstractTuple& tuple);

    std::shared_ptr<ModificationResponse> remove(const Table& table, uint64_t key, uint64_t version);

    std::shared_ptr<ModificationResponse> remove(const Table& table, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot);

    std::shared_ptr<ModificationResponse> revert(const Table& table, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot);

    std::shared_ptr<ScanIterator> scan(const Table& table, const commitmanager::SnapshotDescriptor& snapshot,
            ScanMemoryManager& memoryManager, ScanQueryType queryType, uint32_t selectionLength, const char* selection,
            uint32_t queryLength, const char* query);

private:
    BaseClientProcessor& mProcessor;
    crossbow::infinio::Fiber& mFiber;
};

/**
 * @brief Class managing all running TellStore fibers
 */
class BaseClientProcessor : crossbow::non_copyable, crossbow::non_movable {
public:
    void shutdown();

    std::unique_ptr<commitmanager::SnapshotDescriptor> start(crossbow::infinio::Fiber& fiber, TransactionType type);

    void commit(crossbow::infinio::Fiber& fiber, const commitmanager::SnapshotDescriptor& snapshot);

    Table createTable(crossbow::infinio::Fiber& fiber, const crossbow::string& name, Schema schema);

    std::shared_ptr<GetTableResponse> getTable(crossbow::infinio::Fiber& fiber, const crossbow::string& name) {
        return mTellStoreSocket.at(0)->getTable(fiber, name);
    }

    std::shared_ptr<GetResponse> get(crossbow::infinio::Fiber& fiber, uint64_t tableId, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot) {
        return shard(tableId, key)->get(fiber, tableId, key, snapshot);
    }

    std::shared_ptr<ModificationResponse> insert(crossbow::infinio::Fiber& fiber, uint64_t tableId, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot, const AbstractTuple& tuple) {
        return shard(tableId, key)->insert(fiber, tableId, key, snapshot, tuple);
    }

    std::shared_ptr<ModificationResponse> update(crossbow::infinio::Fiber& fiber, uint64_t tableId, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot, const AbstractTuple& tuple) {
        return shard(tableId, key)->update(fiber, tableId, key, snapshot, tuple);
    }

    std::shared_ptr<ModificationResponse> remove(crossbow::infinio::Fiber& fiber, uint64_t tableId, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot) {
        return shard(tableId, key)->remove(fiber, tableId, key, snapshot);
    }

    std::shared_ptr<ModificationResponse> revert(crossbow::infinio::Fiber& fiber, uint64_t tableId, uint64_t key,
            const commitmanager::SnapshotDescriptor& snapshot) {
        return shard(tableId, key)->revert(fiber, tableId, key, snapshot);
    }

    std::shared_ptr<ScanIterator> scan(crossbow::infinio::Fiber& fiber, uint64_t tableId,
            const commitmanager::SnapshotDescriptor& snapshot, Record record, ScanMemoryManager& memoryManager,
            ScanQueryType queryType, uint32_t selectionLength, const char* selection, uint32_t queryLength,
            const char* query);

protected:
    BaseClientProcessor(crossbow::infinio::InfinibandService& service, const ClientConfig& config,
            uint64_t processorNum);

    ~BaseClientProcessor() = default;

    template <typename Fun>
    void executeFiber(Fun fun) {
        // TODO Starting a fiber without the fiber cache takes ~500us - Investigate why
        mProcessor->executeFiber(std::move(fun));
    }

private:
    /**
     * @brief The socket associated with the shard for the given table and key
     */
    store::ClientSocket* shard(uint64_t tableId, uint64_t key) {
        auto hash = std::hash<uint64_t>()(tableId);
        boost::hash_combine(hash, std::hash<uint64_t>()(key));
        return mTellStoreSocket.at(hash % mTellStoreSocket.size()).get();
    }

    std::unique_ptr<crossbow::infinio::InfinibandProcessor> mProcessor;

    commitmanager::ClientSocket mCommitManagerSocket;
    std::vector<std::unique_ptr<store::ClientSocket>> mTellStoreSocket;

    uint64_t mProcessorNum;

    uint16_t mScanId;
};

/**
 * @brief Class managing all running TellStore fibers and its associated context
 */
template <typename Context>
class ClientProcessor : public BaseClientProcessor {
public:
    template <typename... Args>
    ClientProcessor(crossbow::infinio::InfinibandService& service, const ClientConfig& config, uint64_t processorNum,
            Args&&... contextArgs)
            : BaseClientProcessor(service, config, processorNum),
              mTransactionCount(0),
              mContext(std::forward<Args>(contextArgs)...) {
    }

    uint64_t transactionCount() const {
        return mTransactionCount.load();
    }

    template <typename Fun>
    void execute(Fun fun);

private:
    template <typename Fun, typename C = Context>
    typename std::enable_if<std::is_void<C>::value, void>::type executeHandler(Fun& fun, ClientHandle& handle) {
        fun(handle);
    }

    template <typename Fun, typename C = Context>
    typename std::enable_if<!std::is_void<C>::value, void>::type executeHandler(Fun& fun, ClientHandle& handle) {
        fun(handle, mContext);
    }

    std::atomic<uint64_t> mTransactionCount;

    /// The user defined context associated with this processor
    /// In case the context is void we simply allocate a 0-sized array
    typename std::conditional<std::is_void<Context>::value, char[0], Context>::type mContext;
};

template <typename Context>
template <typename Fun>
void ClientProcessor<Context>::execute(Fun fun) {
    ++mTransactionCount;

    executeFiber([this, fun] (crossbow::infinio::Fiber& fiber) mutable {
        ClientHandle handle(*this, fiber);
        executeHandler(fun, handle);

        --mTransactionCount;
    });
}

/**
 * @brief Class managing all TellStore client processors
 *
 * Dispatches new client functions to the processor with the least amout of load.
 */
template <typename Context>
class ClientManager : crossbow::non_copyable, crossbow::non_movable {
public:
    template <typename... Args>
    ClientManager(const ClientConfig& config, Args... contextArgs);
    ~ClientManager();

    void shutdown();

    template <typename Fun>
    void execute(Fun fun);

    template <typename Fun>
    void execute(size_t num, Fun fun) {
        mProcessor.at(num)->execute(std::move(fun));
    }

    std::unique_ptr<ScanMemoryManager> allocateScanMemory(size_t chunkCount, size_t chunkLength) {
        return std::unique_ptr<ScanMemoryManager>(new ScanMemoryManager(mService, chunkCount, chunkLength));
    }

private:
    crossbow::infinio::InfinibandService mService;

    std::thread mServiceThread;

    std::vector<std::unique_ptr<ClientProcessor<Context>>> mProcessor;
};

template <typename Context>
template <typename... Args>
ClientManager<Context>::ClientManager(const ClientConfig& config, Args... contextArgs)
        : mService(config.infinibandConfig) {
    LOG_INFO("Starting client manager");

    // TODO Move the service thread into the Infiniband Service itself
    mServiceThread = std::thread([this] () {
        mService.run();
    });

    mProcessor.reserve(config.numNetworkThreads);
    for (decltype(config.numNetworkThreads) i = 0; i < config.numNetworkThreads; ++i) {
        mProcessor.emplace_back(new ClientProcessor<Context>(mService, config, i, contextArgs...));
    }
}

template<typename Context>
ClientManager<Context>::~ClientManager() {
    shutdown();
    mServiceThread.detach();
}

template <typename Context>
void ClientManager<Context>::shutdown() {
    LOG_INFO("Shutting down client manager");
    for (auto& proc : mProcessor) {
        proc->shutdown();
    }

    LOG_INFO("Waiting for transactions to terminate");
    for (auto& proc : mProcessor) {
        while (proc->transactionCount() != 0) {
            std::this_thread::yield();
        }
    }
}

template <typename Context>
template <typename Fun>
void ClientManager<Context>::execute(Fun fun) {
    ClientProcessor<Context>* processor = nullptr;
    uint64_t minCount = std::numeric_limits<uint64_t>::max();
    for (auto& proc : mProcessor) {
        auto count = proc->transactionCount();
        if (minCount < count) {
            continue;
        }
        processor = proc.get();
        minCount = count;
    }
    LOG_ASSERT(processor != nullptr, "Found no processor");

    processor->execute(std::move(fun));
}

} // namespace store
} // namespace tell
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// 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 "array.h"
#include "test.h"

#include <vector>

namespace nda {

TEST(split_iterator_increment) {
  std::vector<interval<>> range;
  std::vector<interval<>> pre;
  std::vector<interval<>> post;

  interval<> test(0, 10);
  auto test_split = split<3>(interval<>(0, 10));
  for (auto i : test_split) {
    range.push_back(i);
  }
  for (auto i = std::begin(test_split); i != std::end(test_split); ++i) {
    pre.push_back(*i);
  }
  for (auto i = std::begin(test_split); i != std::end(test_split); i++) {
    post.push_back(*i);
  }

  ASSERT_EQ(pre.size(), range.size());
  ASSERT_EQ(post.size(), range.size());
  for (size_t i = 0; i < range.size(); i++) {
    ASSERT_EQ(pre[i], range[i]);
    ASSERT_EQ(post[i], range[i]);
  }
}

// Test compile-time constant splits that divide the extents of an array.
TEST(split_even_constant) {
  dense_array<int, 3> a({8, 9, 4});
  for (auto yo : split<3>(a.y())) {
    for (auto xo : split<4>(a.x())) {
      auto a_inner = a(xo, yo, _);
      // Compile-time constant splits should always be the same size.
      ASSERT_EQ(a_inner.size(), 48);
      fill_pattern(a_inner);
    }
  }
  check_pattern(a);
}

// Test compile-time constant splits that do not divide the extents of an array.
TEST(split_uneven_constant) {
  dense_array<int, 3> a({dense_dim<>(2, 8), dim<>(1, 9), 4});
  for (auto yo : split<4>(a.y())) {
    for (auto xo : split<5>(a.x())) {
      auto a_inner = a(xo, yo, _);
      // Compile-time constant splits should always be the same size.
      ASSERT_EQ(a_inner.size(), 80);
      fill_pattern(a_inner);
    }
  }
  check_pattern(a);
}

// Test splits that divide the extents of an array.
TEST(split_even_nonconstant) {
  dense_array<int, 3> a({4, 8, 9});
  size_t total_size = 0;
  for (auto zo : split(a.z(), 3)) {
    for (auto yo : split(a.y(), 4)) {
      auto a_inner = a(_, yo, zo);
      total_size += a_inner.size();
      fill_pattern(a_inner);
    }
  }
  // The total number of items in the inner splits should be equal to the size
  // of the array (no overlap among inner splits).
  ASSERT_EQ(total_size, a.size());
  check_pattern(a);
}

// Test splits that do not divide the extents of an array.
TEST(split_uneven_nonconstant) {
  dense_array<int, 3> a({dense_dim<>(2, 8), dim<>(1, 9), 4});
  size_t total_size = 0;
  for (auto zo : split(a.z(), 12)) {
    for (auto xo : split(a.x(), 5)) {
      auto a_inner = a(xo, _, zo);
      total_size += a_inner.size();
      fill_pattern(a_inner);
    }
  }
  // The total number of items in the inner splits should be equal to the size
  // of the array (no overlap among inner splits).
  ASSERT_EQ(total_size, a.size());
  check_pattern(a);
}

} // namespace nda
<commit_msg>Add reference result to split test.<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// 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 "array.h"
#include "test.h"

#include <vector>

namespace nda {

TEST(split_iterator_increment) {
  std::vector<interval<>> range;
  std::vector<interval<>> pre;
  std::vector<interval<>> post;

  auto test_split = split<3>(interval<>(0, 10));
  for (auto i : test_split) {
    range.push_back(i);
  }
  for (auto i = std::begin(test_split); i != std::end(test_split); ++i) {
    pre.push_back(*i);
  }
  for (auto i = std::begin(test_split); i != std::end(test_split); i++) {
    post.push_back(*i);
  }

  std::vector<interval<>> reference = {
      {0, 3},
      {3, 3},
      {6, 3},
      {7, 3},
  };

  ASSERT_EQ(range.size(), reference.size());
  ASSERT_EQ(pre.size(), reference.size());
  ASSERT_EQ(post.size(), reference.size());
  for (size_t i = 0; i < reference.size(); i++) {
    ASSERT_EQ(range[i], reference[i]);
    ASSERT_EQ(pre[i], reference[i]);
    ASSERT_EQ(post[i], reference[i]);
  }
}

// Test compile-time constant splits that divide the extents of an array.
TEST(split_even_constant) {
  dense_array<int, 3> a({8, 9, 4});
  for (auto yo : split<3>(a.y())) {
    for (auto xo : split<4>(a.x())) {
      auto a_inner = a(xo, yo, _);
      // Compile-time constant splits should always be the same size.
      ASSERT_EQ(a_inner.size(), 48);
      fill_pattern(a_inner);
    }
  }
  check_pattern(a);
}

// Test compile-time constant splits that do not divide the extents of an array.
TEST(split_uneven_constant) {
  dense_array<int, 3> a({dense_dim<>(2, 8), dim<>(1, 9), 4});
  for (auto yo : split<4>(a.y())) {
    for (auto xo : split<5>(a.x())) {
      auto a_inner = a(xo, yo, _);
      // Compile-time constant splits should always be the same size.
      ASSERT_EQ(a_inner.size(), 80);
      fill_pattern(a_inner);
    }
  }
  check_pattern(a);
}

// Test splits that divide the extents of an array.
TEST(split_even_nonconstant) {
  dense_array<int, 3> a({4, 8, 9});
  size_t total_size = 0;
  for (auto zo : split(a.z(), 3)) {
    for (auto yo : split(a.y(), 4)) {
      auto a_inner = a(_, yo, zo);
      total_size += a_inner.size();
      fill_pattern(a_inner);
    }
  }
  // The total number of items in the inner splits should be equal to the size
  // of the array (no overlap among inner splits).
  ASSERT_EQ(total_size, a.size());
  check_pattern(a);
}

// Test splits that do not divide the extents of an array.
TEST(split_uneven_nonconstant) {
  dense_array<int, 3> a({dense_dim<>(2, 8), dim<>(1, 9), 4});
  size_t total_size = 0;
  for (auto zo : split(a.z(), 12)) {
    for (auto xo : split(a.x(), 5)) {
      auto a_inner = a(xo, _, zo);
      total_size += a_inner.size();
      fill_pattern(a_inner);
    }
  }
  // The total number of items in the inner splits should be equal to the size
  // of the array (no overlap among inner splits).
  ASSERT_EQ(total_size, a.size());
  check_pattern(a);
}

} // namespace nda
<|endoftext|>


<commit_before>/***************************************************************************
 *   Copyright (c) 2014 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"
#ifndef _PreComp_
# include <algorithm>
# include <sstream>
# include <QListWidgetItem>
# include <QMessageBox>
#endif

#include <App/Application.h>
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <App/GeoFeature.h>

#include "DlgPropertyLink.h"
#include "Application.h"
#include "ViewProvider.h"
#include "ui_DlgPropertyLink.h"

using namespace Gui::Dialog;

/* TRANSLATOR Gui::Dialog::DlgPropertyLink */

DlgPropertyLink::DlgPropertyLink(const QStringList& list, QWidget* parent, Qt::WindowFlags fl)
  : QDialog(parent, fl), link(list), ui(new Ui_DlgPropertyLink)
{
#ifdef FC_DEBUG
    assert(list.size() >= 5);
#endif
    ui->setupUi(this);
    findObjects(ui->checkObjectType->isChecked(), QString());
}

/**
 *  Destroys the object and frees any allocated resources
 */
DlgPropertyLink::~DlgPropertyLink()
{
    // no need to delete child widgets, Qt does it all for us
    delete ui;
}

void DlgPropertyLink::setSelectionMode(QAbstractItemView::SelectionMode mode)
{
    ui->listWidget->setSelectionMode(mode);
    ui->listWidget->clear();
    findObjects(ui->checkObjectType->isChecked(), ui->searchBox->text());
}

void DlgPropertyLink::accept()
{
    if (ui->listWidget->selectionMode() == QAbstractItemView::SingleSelection) {
        QList<QListWidgetItem*> items = ui->listWidget->selectedItems();
        if (items.isEmpty()) {
            QMessageBox::warning(this, tr("No selection"), tr("Please select an object from the list"));
            return;
        }
    }

    QDialog::accept();
}

QStringList DlgPropertyLink::propertyLink() const
{
    QList<QListWidgetItem*> items = ui->listWidget->selectedItems();
    if (items.isEmpty()) {
        return link;
    }
    else {
        QStringList list = link;
        list[1] = items[0]->data(Qt::UserRole).toString();
        list[2] = items[0]->text();
        if (list[1].isEmpty())
            list[2] = QString::fromUtf8("");
        return list;
    }
}

QVariantList DlgPropertyLink::propertyLinkList() const
{
    QVariantList varList;
    QList<QListWidgetItem*> items = ui->listWidget->selectedItems();
    if (items.isEmpty()) {
        varList << link;
    }
    else {
        for (QList<QListWidgetItem*>::iterator it = items.begin(); it != items.end(); ++it) {
            QStringList list = link;
            list[1] = (*it)->data(Qt::UserRole).toString();
            list[2] = (*it)->text();
            if (list[1].isEmpty())
                list[2] = QString::fromUtf8("");
            varList << list;
        }
    }

    return varList;
}

void DlgPropertyLink::findObjects(bool on, const QString& searchText)
{
    QString docName = link[0]; // document name
    QString objName = link[1]; // internal object name
    QString parName = link[3]; // internal object name of the parent of the link property
    QString proName = link[4]; // property name

    bool isSingleSelection = (ui->listWidget->selectionMode() == QAbstractItemView::SingleSelection);
    App::Document* doc = App::GetApplication().getDocument((const char*)docName.toLatin1());
    if (doc) {
        Base::Type baseType = App::DocumentObject::getClassTypeId();
        if (!on) {
            App::DocumentObject* obj = doc->getObject((const char*)objName.toLatin1());
            if (obj) {
                Base::Type objType = obj->getTypeId();
                // get only geometric types
                if (objType.isDerivedFrom(App::GeoFeature::getClassTypeId()))
                    baseType = App::GeoFeature::getClassTypeId();

                // get the direct base class of App::DocumentObject which 'obj' is derived from
                while (!objType.isBad()) {
                    Base::Type parType = objType.getParent();
                    if (parType == baseType) {
                        baseType = objType;
                        break;
                    }
                    objType = parType;
                }
            }
        }

        std::vector<App::DocumentObject*> ignoreList;
        App::DocumentObject* par = doc->getObject((const char*)parName.toLatin1());
        if (par) {
            // for multi-selection we need all objects
            if (isSingleSelection) {
                App::Property* prop = par->getPropertyByName((const char*)proName.toLatin1());
                ignoreList = par->getOutListOfProperty(prop);
            }

            // add the inlist to the ignore list to avoid dependency loops
            std::vector<App::DocumentObject*> inList = par->getInListRecursive();
            ignoreList.insert(ignoreList.end(), inList.begin(), inList.end());
            ignoreList.push_back(par);
        }

        // Add a "None" entry on top
        QListWidgetItem* item = new QListWidgetItem(ui->listWidget);
        item->setText(tr("None (Remove link)"));
        QByteArray ba("");
        item->setData(Qt::UserRole, ba);

        std::vector<App::DocumentObject*> obj = doc->getObjectsOfType(baseType);
        for (std::vector<App::DocumentObject*>::iterator it = obj.begin(); it != obj.end(); ++it) {
            Gui::ViewProvider* vp = Gui::Application::Instance->getViewProvider(*it);
            bool nameOk = true;
            if (!searchText.isEmpty()) {
                QString label = QString::fromUtf8((*it)->Label.getValue());
                if (!label.contains(searchText,Qt::CaseInsensitive))
                    nameOk = false;
            }
            if (vp && nameOk) {
                // filter out the objects
                if (std::find(ignoreList.begin(), ignoreList.end(), *it) == ignoreList.end()) {
                    QListWidgetItem* item = new QListWidgetItem(ui->listWidget);
                    item->setIcon(vp->getIcon());
                    item->setText(QString::fromUtf8((*it)->Label.getValue()));
                    QByteArray ba((*it)->getNameInDocument());
                    item->setData(Qt::UserRole, ba);
                }
            }
        }
    }
}

void DlgPropertyLink::on_checkObjectType_toggled(bool on)
{
    ui->listWidget->clear();
    findObjects(on, ui->searchBox->text());
}

void DlgPropertyLink::on_searchBox_textChanged(const QString& search)
{
    ui->listWidget->clear();
    bool on = ui->checkObjectType->isChecked();
    findObjects(on, search);
}

#include "moc_DlgPropertyLink.cpp"
<commit_msg>Select items already present in PropertyLinkList edit dialog<commit_after>/***************************************************************************
 *   Copyright (c) 2014 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"
#ifndef _PreComp_
# include <algorithm>
# include <sstream>
# include <QListWidgetItem>
# include <QMessageBox>
#endif

#include <App/Application.h>
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <App/GeoFeature.h>

#include "DlgPropertyLink.h"
#include "Application.h"
#include "ViewProvider.h"
#include "ui_DlgPropertyLink.h"

using namespace Gui::Dialog;

/* TRANSLATOR Gui::Dialog::DlgPropertyLink */

DlgPropertyLink::DlgPropertyLink(const QStringList& list, QWidget* parent, Qt::WindowFlags fl)
  : QDialog(parent, fl), link(list), ui(new Ui_DlgPropertyLink)
{
#ifdef FC_DEBUG
    assert(list.size() >= 5);
#endif
    ui->setupUi(this);
    findObjects(ui->checkObjectType->isChecked(), QString());
}

/**
 *  Destroys the object and frees any allocated resources
 */
DlgPropertyLink::~DlgPropertyLink()
{
    // no need to delete child widgets, Qt does it all for us
    delete ui;
}

void DlgPropertyLink::setSelectionMode(QAbstractItemView::SelectionMode mode)
{
    ui->listWidget->setSelectionMode(mode);
    ui->listWidget->clear();
    findObjects(ui->checkObjectType->isChecked(), ui->searchBox->text());
}

void DlgPropertyLink::accept()
{
    if (ui->listWidget->selectionMode() == QAbstractItemView::SingleSelection) {
        QList<QListWidgetItem*> items = ui->listWidget->selectedItems();
        if (items.isEmpty()) {
            QMessageBox::warning(this, tr("No selection"), tr("Please select an object from the list"));
            return;
        }
    }

    QDialog::accept();
}

QStringList DlgPropertyLink::propertyLink() const
{
    QList<QListWidgetItem*> items = ui->listWidget->selectedItems();
    if (items.isEmpty()) {
        return link;
    }
    else {
        QStringList list = link;
        list[1] = items[0]->data(Qt::UserRole).toString();
        list[2] = items[0]->text();
        if (list[1].isEmpty())
            list[2] = QString::fromUtf8("");
        return list;
    }
}

QVariantList DlgPropertyLink::propertyLinkList() const
{
    QVariantList varList;
    QList<QListWidgetItem*> items = ui->listWidget->selectedItems();
    if (items.isEmpty()) {
        varList << link;
    }
    else {
        for (QList<QListWidgetItem*>::iterator it = items.begin(); it != items.end(); ++it) {
            QStringList list = link;
            list[1] = (*it)->data(Qt::UserRole).toString();
            list[2] = (*it)->text();
            if (list[1].isEmpty())
                list[2] = QString::fromUtf8("");
            varList << list;
        }
    }

    return varList;
}

void DlgPropertyLink::findObjects(bool on, const QString& searchText)
{
    QString docName = link[0]; // document name
    QString objName = link[1]; // internal object name
    QString parName = link[3]; // internal object name of the parent of the link property
    QString proName = link[4]; // property name

    bool isSingleSelection = (ui->listWidget->selectionMode() == QAbstractItemView::SingleSelection);
    App::Document* doc = App::GetApplication().getDocument((const char*)docName.toLatin1());
    if (doc) {
        Base::Type baseType = App::DocumentObject::getClassTypeId();
        if (!on) {
            App::DocumentObject* obj = doc->getObject((const char*)objName.toLatin1());
            if (obj) {
                Base::Type objType = obj->getTypeId();
                // get only geometric types
                if (objType.isDerivedFrom(App::GeoFeature::getClassTypeId()))
                    baseType = App::GeoFeature::getClassTypeId();

                // get the direct base class of App::DocumentObject which 'obj' is derived from
                while (!objType.isBad()) {
                    Base::Type parType = objType.getParent();
                    if (parType == baseType) {
                        baseType = objType;
                        break;
                    }
                    objType = parType;
                }
            }
        }

        // build list of objects names already in property so we can mark them as selected later on
        std::vector<const char *> selectedNames;

        // build ignore list
        std::vector<App::DocumentObject*> ignoreList;
        App::DocumentObject* par = doc->getObject((const char*)parName.toLatin1());
        App::Property* prop = par->getPropertyByName((const char*)proName.toLatin1());
        if (par) {
            // for multi-selection we need all objects
            if (isSingleSelection) {
                ignoreList = par->getOutListOfProperty(prop);
            } else {
                // gather names of objects currently in property
                if (prop->getTypeId().isDerivedFrom(App::PropertyLinkList::getClassTypeId())) {
                    const App::PropertyLinkList* propll = static_cast<const App::PropertyLinkList*>(prop);
                    std::vector<App::DocumentObject*> links = propll->getValues();
                    for (std::vector<App::DocumentObject*>::iterator it = links.begin(); it != links.end(); ++it) {
                        selectedNames.push_back((*it)->getNameInDocument());
                    }
                }
            }

            // add the inlist to the ignore list to avoid dependency loops
            std::vector<App::DocumentObject*> inList = par->getInListRecursive();
            ignoreList.insert(ignoreList.end(), inList.begin(), inList.end());
            ignoreList.push_back(par);
        }

        // Add a "None" entry on top
        QListWidgetItem* item = new QListWidgetItem(ui->listWidget);
        item->setText(tr("None (Remove link)"));
        QByteArray ba("");
        item->setData(Qt::UserRole, ba);

        std::vector<App::DocumentObject*> obj = doc->getObjectsOfType(baseType);
        for (std::vector<App::DocumentObject*>::iterator it = obj.begin(); it != obj.end(); ++it) {
            Gui::ViewProvider* vp = Gui::Application::Instance->getViewProvider(*it);
            bool nameOk = true;
            if (!searchText.isEmpty()) {
                QString label = QString::fromUtf8((*it)->Label.getValue());
                if (!label.contains(searchText,Qt::CaseInsensitive))
                    nameOk = false;
            }
            if (vp && nameOk) {
                // filter out the objects
                if (std::find(ignoreList.begin(), ignoreList.end(), *it) == ignoreList.end()) {
                    QListWidgetItem* item = new QListWidgetItem(ui->listWidget);
                    item->setIcon(vp->getIcon());
                    item->setText(QString::fromUtf8((*it)->Label.getValue()));
                    QByteArray ba((*it)->getNameInDocument());
                    item->setData(Qt::UserRole, ba);
                    // mark items as selected if needed
                    for (std::vector<const char *>::iterator nit = selectedNames.begin(); nit != selectedNames.end(); ++nit) {
                        if (strcmp(*nit,(*it)->getNameInDocument()) == 0) {
                            item->setSelected(true);
                            break;
                        }
                    }
                }
            }
        }
    }
}

void DlgPropertyLink::on_checkObjectType_toggled(bool on)
{
    ui->listWidget->clear();
    findObjects(on, ui->searchBox->text());
}

void DlgPropertyLink::on_searchBox_textChanged(const QString& search)
{
    ui->listWidget->clear();
    bool on = ui->checkObjectType->isChecked();
    findObjects(on, search);
}

#include "moc_DlgPropertyLink.cpp"
<|endoftext|>