| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| **/ |
| |
| #ifndef QUICKSTEP_UTILITY_TREE_STRING_SERIALIZABLE_HPP_ |
| #define QUICKSTEP_UTILITY_TREE_STRING_SERIALIZABLE_HPP_ |
| |
| #include <cstddef> |
| #include <string> |
| #include <vector> |
| |
| #include "utility/Macros.hpp" |
| |
| #include "glog/logging.h" |
| |
| namespace quickstep { |
| |
| /** \addtogroup Utility |
| * @{ |
| */ |
| |
| /** |
| * @brief Based class for a tree-structured type. |
| * It provides common logic in string serialization. |
| * |
| * @note This class is templated so that it may be used in both parser |
| * tree and optimizer trees. The parser tree uses unique_ptr to |
| * manage the ownership of fields, while the optimizer tree uses shared_ptr. |
| * TreeNodeType for a parser tree is a constant pointer, whereas it is |
| * a shared_ptr for an optimizer tree. |
| */ |
| template<class TreeNodeType> |
| class TreeStringSerializable { |
| public: |
| static const int kMaxLineWidth = 80; |
| |
| virtual ~TreeStringSerializable() { |
| } |
| |
| /** |
| * @return The node name in the tree-structured string. |
| */ |
| virtual std::string getName() const = 0; |
| |
| /** |
| * @return A tree-structured string representation. |
| */ |
| std::string toString() const { |
| return getNodeString(true /* is_root */, |
| true /* is_last */, |
| "" /* parent_prefix */, |
| "" /* node_name */); |
| } |
| |
| /** |
| * @return A short one-line string representation. |
| */ |
| std::string getShortString() const { |
| std::vector<std::string> inline_field_names; |
| std::vector<std::string> inline_field_values; |
| std::vector<std::string> non_container_child_field_names; |
| std::vector<TreeNodeType> non_container_child_fields; |
| std::vector<std::string> container_child_field_names; |
| std::vector<std::vector<TreeNodeType>> container_child_fields; |
| |
| getFieldStringItems(&inline_field_names, |
| &inline_field_values, |
| &non_container_child_field_names, |
| &non_container_child_fields, |
| &container_child_field_names, |
| &container_child_fields); |
| return getHeadString(true /* is_root */, |
| true /* is_last */, |
| "" /* prefix */, |
| "" /* node_name */, |
| inline_field_names, |
| inline_field_values, |
| false /* multi_line */); |
| } |
| |
| protected: |
| TreeStringSerializable() { |
| } |
| |
| /** |
| * |
| * @brief Returns all the fields (the name and the value) that need to be shown in the output string. There are |
| * three types of fields: |
| * 1) Inline fields are those that are not represented as tree nodes. |
| * 2) Each non-container field corresponds to a tree node in the string representation. Each field corresponds |
| * to a single subtree. |
| * 3) A container field contains a vector of non-container fields. They are all attached to a common child tree |
| * node. |
| * |
| * @param inline_field_names A list of inline field names. |
| * @param inline_field_values A list of inline field string value representation. |
| * 1:1 matching with inline_field_names. |
| * @param non_container_child_field_names A list of names for non-container fields. |
| * @param non_container_child_fields A list of non-container fields. |
| * @param container_child_field_names A list of names for container fields. |
| * @param container_child_fields A list of container fields. |
| */ |
| virtual void getFieldStringItems(std::vector<std::string> *inline_field_names, |
| std::vector<std::string> *inline_field_values, |
| std::vector<std::string> *non_container_child_field_names, |
| std::vector<TreeNodeType> *non_container_child_fields, |
| std::vector<std::string> *container_child_field_names, |
| std::vector<std::vector<TreeNodeType>> *container_child_fields) const = 0; |
| |
| /** |
| * @brief Returns a tree-structured string representation. |
| * |
| * @param is_root Whether this node is a root. |
| * @param is_last Whether this node is the last child node of its parent. |
| * @param parent_prefix The prefix of the parent node. |
| * @param node_name The name of this node in the parent node. This is different from getName() where the name is |
| * independent of the parent. |
| * @return A tree-structured string representation. |
| */ |
| std::string getNodeString(bool is_root, |
| bool is_last, |
| const std::string &parent_prefix, |
| const std::string &node_name) const { |
| std::vector<std::string> inline_field_names; |
| std::vector<std::string> inline_field_values; |
| std::vector<std::string> non_container_child_field_names; |
| std::vector<TreeNodeType> non_container_child_fields; |
| std::vector<std::string> container_child_field_names; |
| std::vector<std::vector<TreeNodeType>> container_child_fields; |
| |
| getFieldStringItems(&inline_field_names, |
| &inline_field_values, |
| &non_container_child_field_names, |
| &non_container_child_fields, |
| &container_child_field_names, |
| &container_child_fields); |
| DCHECK_EQ(non_container_child_field_names.size(), non_container_child_fields.size()); |
| DCHECK_EQ(container_child_field_names.size(), container_child_field_names.size()); |
| DCHECK(!is_root || parent_prefix.empty()); |
| DCHECK(!is_root || is_last); |
| |
| std::string ret = getHeadString(is_root, is_last, parent_prefix, node_name, inline_field_names, inline_field_values, |
| true /* multi_line */); |
| ret.append("\n"); |
| |
| std::string child_prefix; |
| if (!is_root) { |
| child_prefix = GetNewIndentedStringPrefix(is_last, parent_prefix); |
| } |
| |
| // Append strings of non_container child fields. |
| for (int i = 0; i < static_cast<int>(non_container_child_fields.size()) - 1; ++i) { |
| ret.append( |
| non_container_child_fields[i]->getNodeString(false /* is_root */, |
| false /* is_last */, |
| child_prefix, |
| non_container_child_field_names[i])); |
| } |
| |
| if (!non_container_child_fields.empty()) { |
| if (container_child_fields.empty()) { |
| ret.append( |
| non_container_child_fields.back()->getNodeString(false /* is_root */, |
| true /* is_last */, |
| child_prefix, |
| non_container_child_field_names.back())); |
| } else { |
| ret.append( |
| non_container_child_fields.back()->getNodeString(false /* is_root */, |
| false /* is_last */, |
| child_prefix, |
| non_container_child_field_names.back())); |
| } |
| } |
| |
| // Append strings of container child fields. |
| for (int i = 0; i < static_cast<int>(container_child_fields.size()) - 1; ++i) { |
| ret.append(GetNodeListString(false /* is_last */, |
| child_prefix, |
| container_child_field_names[i], |
| container_child_fields[i])); |
| } |
| |
| if (!container_child_fields.empty()) { |
| ret.append( |
| GetNodeListString(true /* is_last */, |
| child_prefix, |
| container_child_field_names.back(), |
| container_child_fields.back())); |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * @brief Creates a tree node with a list of child tree nodes: |
| * +-NodeList |
| * +- Item1 |
| * +- Item2 |
| * +- Item3 |
| * |
| * @param is_last Whether this is the last child node of the parent. |
| * @param prefix The initial prefix of this node. |
| * @param node_name The node name. |
| * @param node_list The list of nodes. |
| * @return A tree node string with a list of . |
| */ |
| static std::string GetNodeListString(bool is_last, |
| const std::string &prefix, |
| const std::string &node_name, |
| const std::vector<TreeNodeType> &node_list) { |
| std::string ret; |
| std::string item_prefix = prefix; |
| if (!node_name.empty()) { |
| ret = item_prefix; |
| ret.append("+-").append(node_name).append("=").append("\n"); |
| // Indent the child fields only if the node name is not empty. |
| item_prefix = GetNewIndentedStringPrefix(is_last, prefix); |
| } |
| |
| if (node_list.empty()) { |
| ret.append(item_prefix).append("+-[]\n"); |
| return ret; |
| } |
| |
| for (int i = 0; i < static_cast<int>(node_list.size()) - 1; ++i) { |
| // Do not provide the node name so that there will not be an extra indent. |
| ret.append(node_list[i]->getNodeString(false /* is_root */, |
| false /* is_last */, |
| item_prefix, |
| "" /* node_name */)); |
| } |
| if (!node_name.empty()) { |
| ret.append(node_list.back()->getNodeString(false /* is_root */, |
| true /* is_last */, |
| item_prefix, |
| "" /* node_name */)); |
| } else { |
| ret.append(node_list.back()->getNodeString(false /* is_root */, |
| (node_name.empty() ? is_last : true), |
| item_prefix, "" /* node_name */)); |
| } |
| return ret; |
| } |
| |
| /** |
| * @brief Returns the node head string with the form like NodeName[inline_field=value1,inline_field=value2]. |
| * |
| * @param is_root Whether this node is the root. |
| * @param is_last Whether this node is the last child of its parent. |
| * @param parent_prefix The prefix of its parent node. |
| * @param node_name The node name in the parent node. |
| * @param field_names The names of inline fields. |
| * @param field_values The string values of the inline fields. |
| * @param multi_line Whether the returned string is allowed to have multiple lines. |
| * @return The node head string. |
| */ |
| std::string getHeadString(bool is_root, |
| bool is_last, |
| const std::string &parent_prefix, |
| const std::string &node_name, |
| const std::vector<std::string> &field_names, |
| const std::vector<std::string> &field_values, |
| bool multi_line) const { |
| DCHECK_EQ(field_names.size(), field_values.size()); |
| std::string ret; |
| |
| const std::string name = getName(); |
| std::string prefix = GetNewIndentedStringPrefix(is_last, parent_prefix); |
| std::string current_line = parent_prefix; |
| if (!is_root) { |
| current_line.append("+-"); |
| } |
| |
| if (!node_name.empty()) { |
| current_line.append(node_name).append("="); |
| if (current_line.size() + name.size() > kMaxLineWidth && multi_line) { |
| ret.append(current_line).append("\n"); |
| current_line = prefix; |
| } |
| } |
| |
| current_line.append(name); |
| |
| if (!field_names.empty()) { |
| // "[" and "]" are considered as inline fields |
| // so that we can add newlines for them. |
| AppendInlineString(prefix, "[" /* inline_field */, multi_line, ¤t_line, &ret); |
| for (size_t i = 0; i < field_names.size(); ++i) { |
| std::string inline_field = field_names[i]; |
| inline_field.append("=").append(field_values[i]); |
| if (i < field_names.size() - 1) { |
| inline_field.append(","); |
| } |
| AppendInlineString(prefix, inline_field, multi_line, ¤t_line, &ret); |
| } |
| AppendInlineString(prefix, "]" /* inline_field */, false /* multi_line */, ¤t_line, &ret); |
| } |
| |
| if (!current_line.empty()) { |
| ret.append(current_line); |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * @brief Appends an inline field to \p output if the length of the current line is greater than MAX_LINE_WIDTH. |
| * Otherwise updates \p current_line. |
| */ |
| static void AppendInlineString(const std::string &prefix, |
| const std::string &inline_field, |
| bool multi_line, |
| std::string *current_line, |
| std::string *output) { |
| if (current_line->size() + inline_field.size() > kMaxLineWidth && multi_line) { |
| output->append(*current_line).append("\n"); |
| *current_line = prefix; |
| } |
| current_line->append(inline_field); |
| } |
| |
| /** |
| * @brief Returns a new prefix string indented by 2. |
| * |
| * @param is_last Whether this node is the last child of the parent. |
| * @param prefix The current prefix. |
| * @return A new prefix string indented by 2. |
| */ |
| static std::string GetNewIndentedStringPrefix(bool is_last, const std::string &prefix) { |
| std::string new_prefix(prefix); |
| if (!is_last) { |
| new_prefix.append("|").append(" "); |
| } else { |
| new_prefix.append(2, ' '); |
| } |
| return new_prefix; |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(TreeStringSerializable); |
| }; |
| |
| /** @} */ |
| |
| } // namespace quickstep |
| |
| #endif /* QUICKSTEP_UTILITY_TREE_STRING_SERIALIZABLE_HPP_ */ |