1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 #include <fstream>
21 #include <iostream>
22 #include <sstream>
23 #include <limits>
24 
25 #include <stdlib.h>
26 #include <sys/stat.h>
27 #include <sstream>
28 
29 #include "thrift/platform.h"
30 #include "thrift/generate/t_generator.h"
31 
32 using std::map;
33 using std::ofstream;
34 using std::ostream;
35 using std::ostringstream;
36 using std::string;
37 using std::stringstream;
38 using std::vector;
39 using std::stack;
40 using std::set;
41 
42 static const string endl = "\n";
43 static const string quot = "\"";
44 
45 static const string default_ns_prefix = "http://thrift.apache.org/xml/ns/";
46 
47 /**
48  * This generator creates an XML model of the parsed IDL tree, and is designed
49  * to make it easy to use this file as the input for other template engines,
50  * such as XSLT.  To this end, the generated XML is slightly more verbose than
51  * you might expect... for example, references to "id" types (such as structs,
52  * unions, etc) always specify the name of the IDL document, even if the type
53  * is defined in the same document as the reference.
54  */
55 class t_xml_generator : public t_generator {
56 public:
t_xml_generator(t_program * program,const std::map<std::string,std::string> & parsed_options,const std::string & option_string)57   t_xml_generator( t_program* program,
58                    const std::map<std::string, std::string>& parsed_options,
59                    const std::string& option_string)
60     : t_generator(program) {
61     (void)option_string;
62     std::map<std::string, std::string>::const_iterator iter;
63 
64     should_merge_includes_ = false;
65     should_use_default_ns_ = true;
66     should_use_namespaces_ = true;
67     for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
68       if( iter->first.compare("merge") == 0) {
69         should_merge_includes_ = true;
70       } else if( iter->first.compare("no_default_ns") == 0) {
71         should_use_default_ns_ = false;
72       } else if( iter->first.compare("no_namespaces") == 0) {
73         should_use_namespaces_ = false;
74       } else {
75         throw "unknown option xml:" + iter->first;
76       }
77     }
78 
79     out_dir_base_ = "gen-xml";
80   }
81 
82   ~t_xml_generator() override = default;
83 
84   void init_generator() override;
85   void close_generator() override;
86   std::string display_name() const override;
87 
88   void generate_program() override;
89 
90   void iterate_program(t_program* program);
91   void generate_typedef(t_typedef* ttypedef) override;
92   void generate_enum(t_enum* tenum) override;
93   void generate_function(t_function* tfunc);
94   void generate_field(t_field* field);
95 
96   void generate_service(t_service* tservice) override;
97   void generate_struct(t_struct* tstruct) override;
98 
99   void generate_annotations(std::map<std::string, std::vector<std::string>> annotations);
100 
101 private:
102   bool should_merge_includes_;
103   bool should_use_default_ns_;
104   bool should_use_namespaces_;
105 
106   ofstream_with_content_based_conditional_update f_xml_;
107 
108   std::set<string> programs_;
109   std::stack<string> elements_;
110   bool top_element_is_empty;
111   bool top_element_is_open;
112 
113   string target_namespace(t_program* program);
114   void write_element_start(const string name);
115   void close_top_element();
116   void write_element_end();
117   void write_attribute(string key, string val);
118   void write_int_attribute(string key, int val);
119   string escape_xml_string(const string& input);
120 
121   void write_xml_comment(string msg);
122 
123   void write_type(t_type* ttype);
124   void write_doc(t_doc* tdoc);
125 
126   template <typename T>
number_to_string(T t)127   string number_to_string(T t) {
128     std::ostringstream out;
129     out.imbue(std::locale::classic());
130     out.precision(std::numeric_limits<T>::digits10);
131     out << t;
132     return out.str();
133   }
134 
135   template <typename T>
write_number(T n)136   void write_number(T n) {
137     f_xml_ << number_to_string(n);
138   }
139 
140   template <typename T>
write_element_number(string name,T n)141   void write_element_number(string name, T n) {
142     write_element_string(name, number_to_string(n));
143   }
144 
145   string get_type_name(t_type* ttype);
146 
147   void generate_constant(t_const* con);
148 
149   void write_element_string(string name, string value);
150   void write_value(t_type* tvalue);
151   void write_const_value(t_const_value* value);
xml_autogen_comment()152   virtual std::string xml_autogen_comment() {
153     return std::string("\n") + " * Autogenerated by Thrift Compiler (" + THRIFT_VERSION + ")\n"
154            + " *\n" + " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n";
155   }
156 };
157 
init_generator()158 void t_xml_generator::init_generator() {
159   MKDIR(get_out_dir().c_str());
160 
161   string f_xml_name = get_out_dir() + program_->get_name() + ".xml";
162   f_xml_.open(f_xml_name.c_str());
163 
164   top_element_is_open = false;
165 }
166 
target_namespace(t_program * program)167 string t_xml_generator::target_namespace(t_program* program) {
168   std::map<std::string, std::string> map;
169   std::map<std::string, std::string>::iterator iter;
170   std::map<std::string, std::vector<std::string>> annotations;
171   std::map<std::string, std::vector<std::string>>::iterator annotations_iter;
172   annotations = program->get_namespace_annotations("xml");
173   if ((annotations_iter = annotations.find("targetNamespace")) != annotations.end()) {
174     if (!annotations_iter->second.empty()) {
175       return annotations_iter->second.back();
176     }
177   }
178   map = program->get_namespaces();
179   if ((iter = map.find("xml")) != map.end()) {
180     return default_ns_prefix + iter->second;
181   }
182   annotations = program->get_namespace_annotations("*");
183   if ((annotations_iter = annotations.find("xml.targetNamespace")) != annotations.end()) {
184     if (!annotations_iter->second.empty()) {
185       return annotations_iter->second.back();
186     }
187   }
188   map = program->get_namespaces();
189   if ((iter = map.find("*")) != map.end()) {
190     return default_ns_prefix + iter->second;
191   }
192   return default_ns_prefix + program->get_name();
193 }
194 
write_xml_comment(string msg)195 void t_xml_generator::write_xml_comment(string msg) {
196   close_top_element();
197   // TODO: indent any EOLs that may occur with msg
198   // TODO: proper msg escaping needed?
199   f_xml_ << indent() << "<!-- " << msg << " -->"  << endl;
200   top_element_is_empty = false;
201 }
202 
close_top_element()203 void t_xml_generator::close_top_element() {
204   if( top_element_is_open) {
205     top_element_is_open = false;
206     if (elements_.size() > 0 && top_element_is_empty) {
207       f_xml_ << ">" << endl;
208     }
209   }
210 }
211 
write_element_start(string name)212 void t_xml_generator::write_element_start(string name) {
213   if (should_use_namespaces_ && !should_use_default_ns_) {
214     name = "idl:" + name;
215   }
216   close_top_element();
217   f_xml_ << indent() << "<" << name;
218   elements_.push(name);
219   top_element_is_empty = true;
220   top_element_is_open = true;
221   indent_up();
222 }
223 
write_element_end()224 void t_xml_generator::write_element_end() {
225   indent_down();
226   if (top_element_is_empty && top_element_is_open) {
227     f_xml_ << " />" << endl;
228   } else {
229     f_xml_ << indent() << "</" << elements_.top() << ">" << endl;
230   }
231   top_element_is_empty = false;
232   elements_.pop();
233 }
234 
write_attribute(string key,string val)235 void t_xml_generator::write_attribute(string key, string val) {
236   f_xml_ << " " << key << "=\"" << escape_xml_string(val) << "\"";
237 }
238 
write_int_attribute(string key,int val)239 void t_xml_generator::write_int_attribute(string key, int val) {
240   write_attribute(key, number_to_string(val));
241 }
242 
write_element_string(string name,string val)243 void t_xml_generator::write_element_string(string name, string val) {
244   if (should_use_namespaces_ && !should_use_default_ns_) {
245     name = "idl:" + name;
246   }
247   close_top_element();
248   top_element_is_empty = false;
249   f_xml_ << indent()
250     << "<" << name << ">" << escape_xml_string(val) << "</" << name << ">"
251     << endl;
252 }
253 
escape_xml_string(const string & input)254 string t_xml_generator::escape_xml_string(const string& input) {
255   std::ostringstream ss;
256   for (char iter : input) {
257     switch (iter) {
258     case '&':
259       ss << "&amp;";
260       break;
261     case '"':
262       ss << "&quot;";
263       break;
264     case '\'':
265       ss << "&apos;";
266       break;
267     case '<':
268       ss << "&lt;";
269       break;
270     case '>':
271       ss << "&gt;";
272       break;
273     default:
274       ss << iter;
275       break;
276     }
277   }
278   return ss.str();
279 }
280 
close_generator()281 void t_xml_generator::close_generator() {
282   f_xml_.close();
283 }
284 
generate_program()285 void t_xml_generator::generate_program() {
286 
287   init_generator();
288 
289   write_element_start("idl");
290   if (should_use_namespaces_) {
291     if (should_use_default_ns_) {
292       write_attribute("xmlns", "http://thrift.apache.org/xml/idl");
293     }
294     write_attribute("xmlns:idl", "http://thrift.apache.org/xml/idl");
295   }
296 
297   write_xml_comment( xml_autogen_comment());
298 
299   iterate_program(program_);
300 
301   write_element_end();
302 
303   close_generator();
304 
305 }
306 
iterate_program(t_program * program)307 void t_xml_generator::iterate_program(t_program* program) {
308 
309   write_element_start("document");
310   write_attribute("name", program->get_name());
311   if (should_use_namespaces_) {
312     const string targetNamespace = target_namespace(program);
313     write_attribute("targetNamespace", targetNamespace);
314     write_attribute("xmlns:" + program->get_name(), targetNamespace);
315   }
316   write_doc(program);
317 
318   const vector<t_program*> includes = program->get_includes();
319   vector<t_program*>::const_iterator inc_it;
320   for (inc_it = includes.begin(); inc_it != includes.end(); ++inc_it) {
321     write_element_start("include");
322     write_attribute("name", (*inc_it)->get_name());
323     write_element_end();
324   }
325 
326   const map<string, string>& namespaces = program->get_namespaces();
327   map<string, string>::const_iterator ns_it;
328   for (ns_it = namespaces.begin(); ns_it != namespaces.end(); ++ns_it) {
329     write_element_start("namespace");
330     write_attribute("name", ns_it->first);
331     write_attribute("value", ns_it->second);
332     generate_annotations(program->get_namespace_annotations(ns_it->first));
333     write_element_end();
334   }
335 
336   // TODO: can constants have annotations?
337   vector<t_const*> consts = program->get_consts();
338   vector<t_const*>::iterator c_iter;
339   for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
340     generate_constant(*c_iter);
341   }
342 
343   vector<t_typedef*> typedefs = program->get_typedefs();
344   vector<t_typedef*>::iterator td_iter;
345   for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) {
346     generate_typedef(*td_iter);
347   }
348 
349   vector<t_enum*> enums = program->get_enums();
350   vector<t_enum*>::iterator en_iter;
351   for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) {
352     generate_enum(*en_iter);
353   }
354 
355   vector<t_struct*> objects = program->get_objects();
356   vector<t_struct*>::iterator o_iter;
357   for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) {
358     if ((*o_iter)->is_xception()) {
359       generate_xception(*o_iter);
360     } else {
361       generate_struct(*o_iter);
362     }
363   }
364 
365   vector<t_service*> services = program->get_services();
366   vector<t_service*>::iterator sv_iter;
367   for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) {
368     generate_service(*sv_iter);
369   }
370 
371   write_element_end();
372 
373   if (should_merge_includes_) {
374     programs_.insert(program->get_name());
375     const vector<t_program*> programs = program->get_includes();
376     vector<t_program*>::const_iterator prog_it;
377     for (prog_it = programs.begin(); prog_it != programs.end(); ++prog_it) {
378       if (!programs_.count((*prog_it)->get_name())) {
379         iterate_program(*prog_it);
380       }
381     }
382   }
383 
384 }
385 
generate_typedef(t_typedef * ttypedef)386 void t_xml_generator::generate_typedef(t_typedef* ttypedef) {
387   write_element_start("typedef");
388   write_attribute("name", ttypedef->get_name());
389   write_doc(ttypedef);
390   write_type(ttypedef->get_true_type());
391   generate_annotations(ttypedef->annotations_);
392   write_element_end();
393   return;
394 }
395 
write_type(t_type * ttype)396 void t_xml_generator::write_type(t_type* ttype) {
397   const string type = get_type_name(ttype);
398   write_attribute("type", type);
399   if (type == "id") {
400     write_attribute("type-module", ttype->get_program()->get_name());
401     write_attribute("type-id", ttype->get_name());
402   } else if (type == "list") {
403     t_type* etype = ((t_list*)ttype)->get_elem_type();
404     write_element_start("elemType");
405     write_type(etype);
406     write_element_end();
407   } else if (type == "set") {
408     t_type* etype = ((t_set*)ttype)->get_elem_type();
409     write_element_start("elemType");
410     write_type(etype);
411     write_element_end();
412   } else if (type == "map") {
413     t_type* ktype = ((t_map*)ttype)->get_key_type();
414     write_element_start("keyType");
415     write_type(ktype);
416     write_element_end();
417     t_type* vtype = ((t_map*)ttype)->get_val_type();
418     write_element_start("valueType");
419     write_type(vtype);
420     write_element_end();
421   }
422 }
423 
write_doc(t_doc * tdoc)424 void t_xml_generator::write_doc(t_doc* tdoc) {
425   if (tdoc->has_doc()) {
426     string doc = tdoc->get_doc();
427     // for some reason there always seems to be a trailing newline on doc
428     // comments; loop below naively tries to strip off trailing cr/lf
429     int n = 0;
430     for (string::reverse_iterator i = doc.rbegin(); i != doc.rend(); i++,n++) {
431       if (*i != '\n' || *i == '\r') {
432         if (n > 0) {
433           doc.erase(doc.length() - n);
434         }
435         break;
436       }
437     }
438     write_attribute("doc", doc);
439   }
440 }
441 
generate_annotations(std::map<std::string,std::vector<std::string>> annotations)442 void t_xml_generator::generate_annotations(
443     std::map<std::string, std::vector<std::string>> annotations) {
444   std::map<std::string, std::vector<std::string>>::iterator iter;
445   for (iter = annotations.begin(); iter != annotations.end(); ++iter) {
446     for (auto& annotations_value : iter->second) {
447       write_element_start("annotation");
448       write_attribute("key", iter->first);
449       write_attribute("value", annotations_value);
450       write_element_end();
451     }
452   }
453 }
454 
generate_constant(t_const * con)455 void t_xml_generator::generate_constant(t_const* con) {
456   write_element_start("const");
457   write_attribute("name", con->get_name());
458   write_doc(con);
459   write_type(con->get_type());
460   write_const_value(con->get_value());
461   write_element_end();
462 }
463 
write_const_value(t_const_value * value)464 void t_xml_generator::write_const_value(t_const_value* value) {
465 
466   switch (value->get_type()) {
467 
468   case t_const_value::CV_IDENTIFIER:
469   case t_const_value::CV_INTEGER:
470     write_element_number("int", value->get_integer());
471     break;
472 
473   case t_const_value::CV_DOUBLE:
474     write_element_number("double", value->get_double());
475     break;
476 
477   case t_const_value::CV_STRING:
478     write_element_string("string", value->get_string());
479     break;
480 
481   case t_const_value::CV_LIST: {
482     write_element_start("list");
483     std::vector<t_const_value*> list = value->get_list();
484     std::vector<t_const_value*>::iterator lit;
485     for (lit = list.begin(); lit != list.end(); ++lit) {
486       write_element_start("entry");
487       write_const_value(*lit);
488       write_element_end();
489     }
490     write_element_end();
491     break;
492   }
493 
494   case t_const_value::CV_MAP: {
495     write_element_start("map");
496     std::map<t_const_value*, t_const_value*, t_const_value::value_compare> map = value->get_map();
497     std::map<t_const_value*, t_const_value*, t_const_value::value_compare>::iterator mit;
498     for (mit = map.begin(); mit != map.end(); ++mit) {
499       write_element_start("entry");
500       write_element_start("key");
501       write_const_value(mit->first);
502       write_element_end();
503       write_element_start("value");
504       write_const_value(mit->second);
505       write_element_end();
506       write_element_end();
507     }
508     write_element_end();
509     break;
510   }
511 
512   default:
513     indent_up();
514     f_xml_ << indent() << "<null />" << endl;
515     indent_down();
516     break;
517   }
518 
519 }
520 
generate_enum(t_enum * tenum)521 void t_xml_generator::generate_enum(t_enum* tenum) {
522 
523   write_element_start("enum");
524   write_attribute("name", tenum->get_name());
525   write_doc(tenum);
526 
527   vector<t_enum_value*> values = tenum->get_constants();
528   vector<t_enum_value*>::iterator val_iter;
529   for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) {
530     t_enum_value* val = (*val_iter);
531     write_element_start("member");
532     write_attribute("name", val->get_name());
533     write_int_attribute("value", val->get_value());
534     write_doc(val);
535     generate_annotations(val->annotations_);
536     write_element_end();
537   }
538 
539   generate_annotations(tenum->annotations_);
540 
541   write_element_end();
542 
543 }
544 
generate_struct(t_struct * tstruct)545 void t_xml_generator::generate_struct(t_struct* tstruct) {
546 
547   string tagname = "struct";
548   if (tstruct->is_union()) {
549     tagname = "union";
550   } else if (tstruct->is_xception()) {
551     tagname = "exception";
552   }
553 
554   write_element_start(tagname);
555   write_attribute("name", tstruct->get_name());
556   write_doc(tstruct);
557   vector<t_field*> members = tstruct->get_members();
558   vector<t_field*>::iterator mem_iter;
559   for (mem_iter = members.begin(); mem_iter != members.end(); mem_iter++) {
560     write_element_start("field");
561     generate_field(*mem_iter);
562     write_element_end();
563   }
564 
565   generate_annotations(tstruct->annotations_);
566 
567   write_element_end();
568 
569 }
570 
generate_field(t_field * field)571 void t_xml_generator::generate_field(t_field* field) {
572   write_attribute("name", field->get_name());
573   write_int_attribute("field-id", field->get_key());
574   write_doc(field);
575   string requiredness;
576   switch (field->get_req()) {
577   case t_field::T_REQUIRED:
578     requiredness = "required";
579     break;
580   case t_field::T_OPTIONAL:
581     requiredness = "optional";
582     break;
583   default:
584     requiredness = "";
585     break;
586   }
587   if (requiredness != "") {
588     write_attribute("required", requiredness);
589   }
590   write_type(field->get_type());
591   if (field->get_value()) {
592     write_element_start("default");
593     write_const_value(field->get_value());
594     write_element_end();
595   }
596   generate_annotations(field->annotations_);
597 }
598 
generate_service(t_service * tservice)599 void t_xml_generator::generate_service(t_service* tservice) {
600 
601   write_element_start("service");
602   write_attribute("name", tservice->get_name());
603 
604   if (should_use_namespaces_) {
605     string prog_ns = target_namespace(tservice->get_program());
606     if (*prog_ns.rbegin() != '/') {
607       prog_ns.push_back('/');
608     }
609     const string tns = prog_ns + tservice->get_name();
610     write_attribute("targetNamespace", tns);
611     write_attribute("xmlns:tns", tns);
612   }
613 
614   if (tservice->get_extends()) {
615     const t_service* extends = tservice->get_extends();
616     write_attribute("parent-module", extends->get_program()->get_name());
617     write_attribute("parent-id", extends->get_name());
618   }
619 
620   write_doc(tservice);
621 
622   vector<t_function*> functions = tservice->get_functions();
623   vector<t_function*>::iterator fn_iter = functions.begin();
624   for (; fn_iter != functions.end(); fn_iter++) {
625     generate_function(*fn_iter);
626   }
627 
628   generate_annotations(tservice->annotations_);
629 
630   write_element_end();
631 
632 }
633 
generate_function(t_function * tfunc)634 void t_xml_generator::generate_function(t_function* tfunc) {
635 
636   write_element_start("method");
637 
638   write_attribute("name", tfunc->get_name());
639   if (tfunc->is_oneway()) {
640     write_attribute("oneway", "true");
641   }
642 
643   write_doc(tfunc);
644 
645   write_element_start("returns");
646   write_type(tfunc->get_returntype());
647   write_element_end();
648 
649   vector<t_field*> members = tfunc->get_arglist()->get_members();
650   vector<t_field*>::iterator mem_iter = members.begin();
651   for (; mem_iter != members.end(); mem_iter++) {
652     write_element_start("arg");
653     generate_field(*mem_iter);
654     write_element_end();
655   }
656 
657   vector<t_field*> excepts = tfunc->get_xceptions()->get_members();
658   vector<t_field*>::iterator ex_iter = excepts.begin();
659   for (; ex_iter != excepts.end(); ex_iter++) {
660     write_element_start("throws");
661     generate_field(*ex_iter);
662     write_element_end();
663   }
664 
665   generate_annotations(tfunc->annotations_);
666 
667   write_element_end();
668 
669 }
670 
get_type_name(t_type * ttype)671 string t_xml_generator::get_type_name(t_type* ttype) {
672   if (ttype->is_list()) {
673     return "list";
674   }
675   if (ttype->is_set()) {
676     return "set";
677   }
678   if (ttype->is_map()) {
679     return "map";
680   }
681   if ((ttype->is_enum()    )||
682       (ttype->is_struct()  )||
683       (ttype->is_typedef() )||
684       (ttype->is_xception())){
685     return "id";
686   }
687   if (ttype->is_base_type()) {
688     t_base_type* tbasetype = (t_base_type*)ttype;
689     if (tbasetype->is_binary() ) {
690       return "binary";
691     }
692     return t_base_type::t_base_name(tbasetype->get_base());
693   }
694   return "(unknown)";
695 }
696 
display_name() const697 std::string t_xml_generator::display_name() const {
698   return "XML";
699 }
700 
701 
702 THRIFT_REGISTER_GENERATOR(
703   xml,
704   "XML",
705   "    merge:           Generate output with included files merged\n"
706   "    no_default_ns:   Omit default xmlns and add idl: prefix to all elements\n"
707   "    no_namespaces:   Do not add namespace definitions to the XML model\n")
708