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 << "&";
260 break;
261 case '"':
262 ss << """;
263 break;
264 case '\'':
265 ss << "'";
266 break;
267 case '<':
268 ss << "<";
269 break;
270 case '>':
271 ss << ">";
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