You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@thrift.apache.org by je...@apache.org on 2013/05/13 22:14:29 UTC

git commit: THRIFT-1965 Adds Graphviz (graph description language) generator

Updated Branches:
  refs/heads/master 2d2b3b2be -> d7b457927


THRIFT-1965 Adds Graphviz (graph description language) generator

Patch: Rodrigo Setti


Project: http://git-wip-us.apache.org/repos/asf/thrift/repo
Commit: http://git-wip-us.apache.org/repos/asf/thrift/commit/d7b45792
Tree: http://git-wip-us.apache.org/repos/asf/thrift/tree/d7b45792
Diff: http://git-wip-us.apache.org/repos/asf/thrift/diff/d7b45792

Branch: refs/heads/master
Commit: d7b4579272a86d49adcc8805d2624ce1c757b263
Parents: 2d2b3b2
Author: Jens Geyer <je...@apache.org>
Authored: Mon May 13 22:11:46 2013 +0200
Committer: Jens Geyer <je...@apache.org>
Committed: Mon May 13 22:11:46 2013 +0200

----------------------------------------------------------------------
 compiler/cpp/Makefile.am                    |    1 +
 compiler/cpp/src/generate/t_gv_generator.cc |  319 ++++++++++++++++++++++
 2 files changed, 320 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/thrift/blob/d7b45792/compiler/cpp/Makefile.am
----------------------------------------------------------------------
diff --git a/compiler/cpp/Makefile.am b/compiler/cpp/Makefile.am
index 8a8f394..a70129a 100644
--- a/compiler/cpp/Makefile.am
+++ b/compiler/cpp/Makefile.am
@@ -85,6 +85,7 @@ thrift_SOURCES += src/generate/t_c_glib_generator.cc \
                   src/generate/t_javame_generator.cc \
                   src/generate/t_delphi_generator.cc \
                   src/generate/t_go_generator.cc \
+                  src/generate/t_gv_generator.cc \
                   src/generate/t_d_generator.cc
 
 thrift_CPPFLAGS = -I$(srcdir)/src

http://git-wip-us.apache.org/repos/asf/thrift/blob/d7b45792/compiler/cpp/src/generate/t_gv_generator.cc
----------------------------------------------------------------------
diff --git a/compiler/cpp/src/generate/t_gv_generator.cc b/compiler/cpp/src/generate/t_gv_generator.cc
new file mode 100644
index 0000000..41207f1
--- /dev/null
+++ b/compiler/cpp/src/generate/t_gv_generator.cc
@@ -0,0 +1,319 @@
+/*
+ * 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.
+ */
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <map>
+#include <list>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sstream>
+#include "t_generator.h"
+#include "platform.h"
+
+using std::map;
+using std::ofstream;
+using std::ostringstream;
+using std::pair;
+using std::string;
+using std::stringstream;
+using std::vector;
+
+static const string endl = "\n";  // avoid ostream << std::endl flushes
+
+/**
+ * Graphviz code generator
+ */
+class t_gv_generator : public t_generator {
+  public:
+    t_gv_generator(
+        t_program* program,
+        const std::map<std::string, std::string>& parsed_options,
+        const std::string& option_string)
+      : t_generator(program)
+    {
+      (void) parsed_options;
+      (void) option_string;
+      out_dir_base_ = "gen-gv";
+    }
+
+    /**
+     * Init and end of generator
+     */
+    void init_generator();
+    void close_generator();
+
+    /**
+     * Program-level generation functions
+     */
+    void generate_typedef (t_typedef*  ttypedef);
+    void generate_enum    (t_enum*     tenum);
+    void generate_const   (t_const*    tconst);
+    void generate_struct  (t_struct*   tstruct);
+    void generate_service (t_service*  tservice);
+
+  protected:
+    /**
+     * Helpers
+     */
+    void print_type(t_type* ttype, string struct_field_ref);
+    void print_const_value(t_const_value* tvalue);
+
+  private:
+    std::ofstream f_out_;
+    std::list<string> edges;
+};
+
+/**
+ * Init generator:
+ * - Adds some escaping for the Graphviz domain.
+ * - Create output directory and open file for writting.
+ * - Write the file header.
+ */
+void t_gv_generator::init_generator() {
+  escape_['{']  = "\\{";
+  escape_['}']  = "\\}";
+
+  // Make output directory
+  MKDIR(get_out_dir().c_str());
+  string fname = get_out_dir() + program_->get_name() + ".gv";
+  f_out_.open(fname.c_str());
+  f_out_ << "digraph \""  << escape_string(program_name_) << "\" {" << endl;
+  f_out_ << "node [style=filled, shape=record];" << endl;
+  f_out_ << "rankdir=LR" << endl;
+}
+
+/**
+ * Closes generator:
+ * - Print accumulated nodes connections.
+ * - Print footnote.
+ * - Closes file.
+ */
+void t_gv_generator::close_generator() {
+  // Print edges
+  std::list<string>::iterator iter = edges.begin();
+  for ( ; iter != edges.end(); iter++) {
+    f_out_ << (*iter) << endl;
+  }
+
+  // Print graph end } and close file
+  f_out_ << "}" << endl;
+  f_out_.close();
+}
+
+void t_gv_generator::generate_typedef (t_typedef* ttypedef) {
+  string name = ttypedef->get_name();
+  f_out_ << "node [fillcolor=azure];" << endl;
+  f_out_ << "type_" << name << " [label=\"";
+
+  f_out_ << escape_string(name);
+  f_out_ << " :: ";
+  print_type(ttypedef->get_type(), "type_" + name);
+
+  f_out_ << "\"];" << endl;
+}
+
+void t_gv_generator::generate_enum (t_enum* tenum) {
+  string name = tenum->get_name();
+  f_out_ << "node [fillcolor=white];" << endl;
+  f_out_ << "type_" << name << " [label=\"enum " << escape_string(name);
+
+  vector<t_enum_value*> values = tenum->get_constants();
+  vector<t_enum_value*>::iterator val_iter;
+  for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) {
+    f_out_ << '|' << (*val_iter)->get_name();
+    f_out_ << " = ";
+    f_out_ << (*val_iter)->get_value();
+  }
+
+  f_out_ << "\"];" << endl;
+}
+
+void t_gv_generator::generate_const (t_const* tconst) {
+  string name = tconst->get_name();
+
+  f_out_ << "node [fillcolor=aliceblue];" << endl;
+  f_out_ << "const_" << name << " [label=\"";
+
+  f_out_ << escape_string(name);
+  f_out_ << " = ";
+  print_const_value(tconst->get_value());
+  f_out_ << " :: ";
+  print_type(tconst->get_type(), "const_" + name);
+
+  f_out_ << "\"];" << endl;
+}
+
+void t_gv_generator::generate_struct  (t_struct*   tstruct) {
+  string name = tstruct->get_name();
+
+  if (tstruct->is_xception()) {
+    f_out_ << "node [fillcolor=lightpink];" << endl;
+    f_out_ << "type_" << name << " [label=\"";
+    f_out_ << "exception " << escape_string(name);
+  } else if (tstruct->is_union()) {
+    f_out_ << "node [fillcolor=lightcyan];" << endl;
+    f_out_ << "type_" << name << " [label=\"";
+    f_out_ << "union " << escape_string(name);
+  } else {
+    f_out_ << "node [fillcolor=beige];" << endl;
+    f_out_ << "type_" << name << " [label=\"";
+    f_out_ << "struct " << escape_string(name);
+  }
+
+  vector<t_field*> members = tstruct->get_members();
+  vector<t_field*>::iterator mem_iter = members.begin();
+  for ( ; mem_iter != members.end(); mem_iter++) {
+    string field_name = (*mem_iter)->get_name();
+
+    // print port (anchor reference)
+    f_out_ << "|<field_" << field_name << '>';
+
+    // field name :: field type
+    f_out_ << (*mem_iter)->get_name();
+    f_out_ << " :: ";
+    print_type((*mem_iter)->get_type(),
+               "type_" + name + ":field_" + field_name);
+  }
+
+  f_out_ << "\"];" << endl;
+}
+
+void t_gv_generator::print_type(t_type* ttype, string struct_field_ref) {
+  if (ttype->is_container()) {
+    if (ttype->is_list()) {
+      f_out_ << "list\\<";
+      print_type(((t_list*)ttype)->get_elem_type(), struct_field_ref);
+      f_out_ << "\\>";
+    } else if (ttype->is_set()) {
+      f_out_ << "set\\<";
+      print_type(((t_set*)ttype)->get_elem_type(), struct_field_ref);
+      f_out_ << "\\>";
+    } else if (ttype->is_map()) {
+      f_out_ << "map\\<";
+      print_type(((t_map*)ttype)->get_key_type(), struct_field_ref);
+      f_out_ << ", ";
+      print_type(((t_map*)ttype)->get_val_type(), struct_field_ref);
+      f_out_ << "\\>";
+    }
+  } else if (ttype->is_base_type()) {
+    f_out_ << (((t_base_type*)ttype)->is_binary() ? "binary" : ttype->get_name());
+  } else {
+    f_out_ << ttype->get_name();
+    edges.push_back(struct_field_ref + " -> type_" + ttype->get_name());
+  }
+}
+
+/**
+ * Prints out an string representation of the provided constant value
+ */
+void t_gv_generator::print_const_value(t_const_value* tvalue) {
+  bool first = true;
+  switch (tvalue->get_type()) {
+    case t_const_value::CV_INTEGER:
+      f_out_ << tvalue->get_integer();
+      break;
+    case t_const_value::CV_DOUBLE:
+      f_out_ << tvalue->get_double();
+      break;
+    case t_const_value::CV_STRING:
+      f_out_ << "\\\"" <<  get_escaped_string(tvalue) << "\\\"";
+      break;
+    case t_const_value::CV_MAP:
+      {
+        f_out_ << "\\{ ";
+        map<t_const_value*, t_const_value*> map_elems = tvalue->get_map();
+        map<t_const_value*, t_const_value*>::iterator map_iter;
+        for (map_iter = map_elems.begin(); map_iter != map_elems.end(); map_iter++) {
+          if (!first) {
+            f_out_ << ", ";
+          }
+          first = false;
+          print_const_value(map_iter->first);
+          f_out_ << " = ";
+          print_const_value(map_iter->second);
+        }
+        f_out_ << " \\}";
+      }
+      break;
+    case t_const_value::CV_LIST:
+      {
+        f_out_ << "\\{ ";
+        vector<t_const_value*> list_elems = tvalue->get_list();;
+        vector<t_const_value*>::iterator list_iter;
+        for (list_iter = list_elems.begin(); list_iter != list_elems.end(); list_iter++) {
+          if (!first) {
+            f_out_ << ", ";
+          }
+          first = false;
+          print_const_value(*list_iter);
+        }
+        f_out_ << " \\}";
+      }
+      break;
+    default:
+      f_out_ << "UNKNOWN";
+      break;
+  }
+}
+
+void t_gv_generator::generate_service (t_service*  tservice) {
+  string service_name = get_service_name(tservice);
+  f_out_ << "subgraph cluster_" << service_name << " {" << endl;
+  f_out_ << "node [fillcolor=bisque];" << endl;
+  f_out_ << "style=dashed;" << endl;
+  f_out_ << "label = \"" << escape_string(service_name) << " service\";" << endl;
+
+  // TODO: service extends
+
+  vector<t_function*> functions = tservice->get_functions();
+  vector<t_function*>::iterator fn_iter = functions.begin();
+  for ( ; fn_iter != functions.end(); fn_iter++) {
+    string fn_name = (*fn_iter)->get_name();
+
+    f_out_ << "function_" << fn_name;
+    f_out_ << "[label=\"<return_type>function " << escape_string(fn_name);
+    f_out_ << " :: ";
+    print_type((*fn_iter)->get_returntype(), "function_" + fn_name + ":return_type");
+
+    vector<t_field*> args = (*fn_iter)->get_arglist()->get_members();
+    vector<t_field*>::iterator arg_iter = args.begin();
+    for ( ; arg_iter != args.end(); arg_iter++) {
+      f_out_ << "|<param_" << (*arg_iter)->get_name() << ">";
+      f_out_ << (*arg_iter)->get_name();
+      if ((*arg_iter)->get_value() != NULL) {
+        f_out_ << " = ";
+        print_const_value((*arg_iter)->get_value());
+      }
+      f_out_ << " :: ";
+      print_type((*arg_iter)->get_type(),
+                 "function_" + fn_name + ":param_" + (*arg_iter)->get_name());
+    }
+    // TODO: throws exceptions
+    f_out_ << "\"];" << endl;
+  }
+
+  f_out_ << " }";
+}
+
+THRIFT_REGISTER_GENERATOR(gv, "Graphviz", "")
+