Subversion Repositories SmartDukaan

Rev

Rev 30 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
30 ashish 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 <string>
21
#include <fstream>
22
#include <iostream>
23
#include <vector>
24
#include <map>
25
 
26
#include <stdlib.h>
27
#include <sys/stat.h>
28
#include <sstream>
29
#include "t_generator.h"
30
#include "platform.h"
31
using namespace std;
32
 
33
 
34
/**
35
 * HTML code generator
36
 *
37
 * mostly copy/pasting/tweaking from mcslee's work.
38
 */
39
class t_html_generator : public t_generator {
40
 public:
41
  t_html_generator(
42
      t_program* program,
43
      const std::map<std::string, std::string>& parsed_options,
44
      const std::string& option_string)
45
    : t_generator(program)
46
  {
47
    out_dir_base_ = "gen-html";
48
    escape_.clear();
49
    escape_['&']  = "&amp;";
50
    escape_['<']  = "&lt;";
51
    escape_['>']  = "&gt;";
52
    escape_['"']  = "&quot;";
53
    escape_['\''] = "&apos;";
54
  }
55
 
56
  void generate_program();
57
  void generate_program_toc();
58
  void generate_program_toc_row(t_program* tprog);
59
  void generate_program_toc_rows(t_program* tprog,
60
				 std::vector<t_program*>& finished);
61
  void generate_index();
62
  void generate_css();
63
 
64
  /**
65
   * Program-level generation functions
66
   */
67
 
68
  void generate_typedef (t_typedef*  ttypedef);
69
  void generate_enum    (t_enum*     tenum);
70
  void generate_const   (t_const*    tconst);
71
  void generate_struct  (t_struct*   tstruct);
72
  void generate_service (t_service*  tservice);
73
  void generate_xception(t_struct*   txception);
74
 
75
  void print_doc        (t_doc* tdoc);
76
  int  print_type       (t_type* ttype);
77
  void print_const_value(t_const_value* tvalue);
78
 
79
  std::ofstream f_out_;
80
};
81
 
82
/**
83
 * Emits the Table of Contents links at the top of the module's page
84
 */
85
void t_html_generator::generate_program_toc() {
86
  f_out_ << "<table><tr><th>Module</th><th>Services</th>"
87
	 << "<th>Data types</th><th>Constants</th></tr>" << endl;
88
  generate_program_toc_row(program_);
89
  f_out_ << "</table>" << endl;
90
}
91
 
92
 
93
/**
94
 * Recurses through from the provided program and generates a ToC row
95
 * for each discovered program exactly once by maintaining the list of
96
 * completed rows in 'finished'
97
 */
98
void t_html_generator::generate_program_toc_rows(t_program* tprog,
99
				 std::vector<t_program*>& finished) {
100
  for (vector<t_program*>::iterator iter = finished.begin();
101
       iter != finished.end(); iter++) {
102
    if (tprog->get_path() == (*iter)->get_path()) {
103
      return;
104
    }
105
  }
106
  finished.push_back(tprog);
107
  generate_program_toc_row(tprog);
108
  vector<t_program*> includes = tprog->get_includes();
109
  for (vector<t_program*>::iterator iter = includes.begin();
110
       iter != includes.end(); iter++) {
111
    generate_program_toc_rows(*iter, finished);
112
  }
113
}
114
 
115
/**
116
 * Emits the Table of Contents links at the top of the module's page
117
 */
118
void t_html_generator::generate_program_toc_row(t_program* tprog) {
119
  string fname = tprog->get_name() + ".html";
120
  f_out_ << "<tr>" << endl << "<td>" << tprog->get_name() << "</td><td>";
121
  if (!tprog->get_services().empty()) {
122
    vector<t_service*> services = tprog->get_services();
123
    vector<t_service*>::iterator sv_iter;
124
    for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) {
125
      string name = get_service_name(*sv_iter);
126
      f_out_ << "<a href=\"" << fname << "#Svc_" << name << "\">" << name
127
	     << "</a><br/>" << endl;
128
      f_out_ << "<ul>" << endl;
129
      map<string,string> fn_html;
130
      vector<t_function*> functions = (*sv_iter)->get_functions();
131
      vector<t_function*>::iterator fn_iter;
132
      for (fn_iter = functions.begin(); fn_iter != functions.end(); ++fn_iter) {
133
	string fn_name = (*fn_iter)->get_name();
134
	string html = "<li><a href=\"" + fname + "#Fn_" + name + "_" +
135
	  fn_name + "\">" + fn_name + "</a></li>";
136
	fn_html.insert(pair<string,string>(fn_name, html));
137
      }
138
      for (map<string,string>::iterator html_iter = fn_html.begin();
139
	   html_iter != fn_html.end(); html_iter++) {
140
	f_out_ << html_iter->second << endl;
141
      }
142
      f_out_ << "</ul>" << endl;
143
    }
144
  }
145
  f_out_ << "</td>" << endl << "<td>";
146
  map<string,string> data_types;
147
  if (!tprog->get_enums().empty()) {
148
    vector<t_enum*> enums = tprog->get_enums();
149
    vector<t_enum*>::iterator en_iter;
150
    for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) {
151
      string name = (*en_iter)->get_name();
152
      // f_out_ << "<a href=\"" << fname << "#Enum_" << name << "\">" << name
153
      // <<  "</a><br/>" << endl;
154
      string html = "<a href=\"" + fname + "#Enum_" + name + "\">" + name +
155
	"</a>";
156
      data_types.insert(pair<string,string>(name, html));
157
    }
158
  }
159
  if (!tprog->get_typedefs().empty()) {
160
    vector<t_typedef*> typedefs = tprog->get_typedefs();
161
    vector<t_typedef*>::iterator td_iter;
162
    for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) {
163
      string name = (*td_iter)->get_symbolic();
164
      // f_out_ << "<a href=\"" << fname << "#Typedef_" << name << "\">" << name
165
      // << "</a><br/>" << endl;
166
      string html = "<a href=\"" + fname + "#Typedef_" + name + "\">" + name +
167
	"</a>";
168
      data_types.insert(pair<string,string>(name, html));
169
    }
170
  }
171
  if (!tprog->get_objects().empty()) {
172
    vector<t_struct*> objects = tprog->get_objects();
173
    vector<t_struct*>::iterator o_iter;
174
    for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) {
175
      string name = (*o_iter)->get_name();
176
      //f_out_ << "<a href=\"" << fname << "#Struct_" << name << "\">" << name
177
      //<< "</a><br/>" << endl;
178
      string html = "<a href=\"" + fname + "#Struct_" + name + "\">" + name +
179
	"</a>";
180
      data_types.insert(pair<string,string>(name, html));
181
    }
182
  }
183
  for (map<string,string>::iterator dt_iter = data_types.begin();
184
       dt_iter != data_types.end(); dt_iter++) {
185
    f_out_ << dt_iter->second << "<br/>" << endl;
186
  }
187
  f_out_ << "</td>" << endl << "<td><code>";
188
  if (!tprog->get_consts().empty()) {
189
    map<string,string> const_html;
190
    vector<t_const*> consts = tprog->get_consts();
191
    vector<t_const*>::iterator con_iter;
192
    for (con_iter = consts.begin(); con_iter != consts.end(); ++con_iter) {
193
      string name = (*con_iter)->get_name();
194
      string html ="<a href=\"" + fname + "#Const_" + name +
195
	"\">" + name + "</a>";
196
      const_html.insert(pair<string,string>(name, html));
197
    }
198
    for (map<string,string>::iterator con_iter = const_html.begin();
199
	 con_iter != const_html.end(); con_iter++) {
200
      f_out_ << con_iter->second << "<br/>" << endl;
201
    }
202
  }
203
  f_out_ << "</code></td>" << endl << "</tr>";
204
}
205
 
206
/**
207
 * Prepares for file generation by opening up the necessary file output
208
 * stream.
209
 */
210
void t_html_generator::generate_program() {
211
  // Make output directory
212
  MKDIR(get_out_dir().c_str());
213
  string fname = get_out_dir() + program_->get_name() + ".html";
214
  f_out_.open(fname.c_str());
215
  f_out_ << "<html><head>" << endl;
216
  f_out_ << "<link href=\"style.css\" rel=\"stylesheet\" type=\"text/css\"/>"
217
	 << endl;
218
  f_out_ << "<title>Thrift module: " << program_->get_name()
219
	 << "</title></head><body>" << endl << "<h1>Thrift module: "
220
	 << program_->get_name() << "</h1>" << endl;
221
 
222
  print_doc(program_);
223
 
224
  generate_program_toc();
225
 
226
  if (!program_->get_consts().empty()) {
227
    f_out_ << "<hr/><h2 id=\"Constants\">Constants</h2>" << endl;
228
    vector<t_const*> consts = program_->get_consts();
229
    f_out_ << "<table>";
230
    f_out_ << "<tr><th>Constant</th><th>Type</th><th>Value</th></tr>" << endl;
231
    generate_consts(consts);
232
    f_out_ << "</table>";
233
  }
234
 
235
  if (!program_->get_enums().empty()) {
236
    f_out_ << "<hr/><h2 id=\"Enumerations\">Enumerations</h2>" << endl;
237
    // Generate enums
238
    vector<t_enum*> enums = program_->get_enums();
239
    vector<t_enum*>::iterator en_iter;
240
    for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) {
241
      generate_enum(*en_iter);
242
    }
243
  }
244
 
245
  if (!program_->get_typedefs().empty()) {
246
    f_out_ << "<hr/><h2 id=\"Typedefs\">Type declarations</h2>" << endl;
247
    // Generate typedefs
248
    vector<t_typedef*> typedefs = program_->get_typedefs();
249
    vector<t_typedef*>::iterator td_iter;
250
    for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) {
251
      generate_typedef(*td_iter);
252
    }
253
  }
254
 
255
  if (!program_->get_objects().empty()) {
256
    f_out_ << "<hr/><h2 id=\"Structs\">Data structures</h2>" << endl;
257
    // Generate structs and exceptions in declared order
258
    vector<t_struct*> objects = program_->get_objects();
259
    vector<t_struct*>::iterator o_iter;
260
    for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) {
261
      if ((*o_iter)->is_xception()) {
262
	generate_xception(*o_iter);
263
      } else {
264
	generate_struct(*o_iter);
265
      }
266
    }
267
  }
268
 
269
  if (!program_->get_services().empty()) {
270
    f_out_ << "<hr/><h2 id=\"Services\">Services</h2>" << endl;
271
    // Generate services
272
    vector<t_service*> services = program_->get_services();
273
    vector<t_service*>::iterator sv_iter;
274
    for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) {
275
      service_name_ = get_service_name(*sv_iter);
276
      generate_service(*sv_iter);
277
    }
278
  }
279
 
280
  f_out_ << "</body></html>" << endl;
281
  f_out_.close();
282
 
283
  generate_index();
284
  generate_css();
285
}
286
 
287
/**
288
 * Emits the index.html file for the recursive set of Thrift programs
289
 */
290
void t_html_generator::generate_index() {
291
  string index_fname = get_out_dir() + "index.html";
292
  f_out_.open(index_fname.c_str());
293
  f_out_ << "<html><head>" << endl;
294
  f_out_ << "<link href=\"style.css\" rel=\"stylesheet\" type=\"text/css\"/>"
295
	 << endl;
296
  f_out_ << "<title>All Thrift declarations</title></head><body>"
297
	 << endl << "<h1>All Thrift declarations</h1>" << endl;
298
  f_out_ << "<table><tr><th>Module</th><th>Services</th><th>Data types</th>"
299
	 << "<th>Constants</th></tr>" << endl;
300
  vector<t_program*> programs;
301
  generate_program_toc_rows(program_, programs);
302
  f_out_ << "</table>" << endl;
303
  f_out_ << "</body></html>" << endl;
304
  f_out_.close();
305
}
306
 
307
void t_html_generator::generate_css() {
308
  string css_fname = get_out_dir() + "style.css";
309
  f_out_.open(css_fname.c_str());
310
  f_out_ << "/* Auto-generated CSS for generated Thrift docs */" << endl;
311
  f_out_ <<
312
    "body { font-family: Tahoma, sans-serif; }" << endl;
313
  f_out_ <<
314
    "pre { background-color: #dddddd; padding: 6px; }" << endl;
315
  f_out_ <<
316
    "h3,h4 { padding-top: 0px; margin-top: 0px; }" << endl;
317
  f_out_ <<
318
    "div.definition { border: 1px solid gray; margin: 10px; padding: 10px; }" << endl;
319
  f_out_ <<
320
    "div.extends { margin: -0.5em 0 1em 5em }" << endl;
321
  f_out_ <<
322
    "table { border: 1px solid grey; border-collapse: collapse; }" << endl;
323
  f_out_ <<
324
    "td { border: 1px solid grey; padding: 1px 6px; vertical-align: top; }" << endl;
325
  f_out_ <<
326
    "th { border: 1px solid black; background-color: #bbbbbb;" << endl <<
327
    "     text-align: left; padding: 1px 6px; }" << endl;
328
  f_out_.close();
329
}
330
 
331
/**
332
 * If the provided documentable object has documentation attached, this
333
 * will emit it to the output stream in HTML format.
334
 */
335
void t_html_generator::print_doc(t_doc* tdoc) {
336
  if (tdoc->has_doc()) {
337
    string doc = tdoc->get_doc();
338
    size_t index;
339
    while ((index = doc.find_first_of("\r\n")) != string::npos) {
340
      if (index == 0) {
341
	f_out_ << "<p/>" << endl;
342
      } else {
343
	f_out_ << doc.substr(0, index) << endl;
344
      }
345
      if (index + 1 < doc.size() && doc.at(index) != doc.at(index + 1) &&
346
	  (doc.at(index + 1) == '\r' || doc.at(index + 1) == '\n')) {
347
	index++;
348
      }
349
      doc = doc.substr(index + 1);
350
    }
351
    f_out_ << doc << "<br/>";
352
  }
353
}
354
 
355
/**
356
 * Prints out the provided type in HTML
357
 */
358
int t_html_generator::print_type(t_type* ttype) {
359
  int len = 0;
360
  f_out_ << "<code>";
361
  if (ttype->is_container()) {
362
    if (ttype->is_list()) {
363
      f_out_ << "list&lt;";
364
      len = 6 + print_type(((t_list*)ttype)->get_elem_type());
365
      f_out_ << "&gt;";
366
    } else if (ttype->is_set()) {
367
      f_out_ << "set&lt;";
368
      len = 5 + print_type(((t_set*)ttype)->get_elem_type());
369
      f_out_ << "&gt;";
370
    } else if (ttype->is_map()) {
371
      f_out_ << "map&lt;";
372
      len = 5 + print_type(((t_map*)ttype)->get_key_type());
373
      f_out_ << ", ";
374
      len += print_type(((t_map*)ttype)->get_val_type());
375
      f_out_ << "&gt;";
376
    }
377
  } else if (ttype->is_base_type()) {
378
    f_out_ << ttype->get_name();
379
    len = ttype->get_name().size();
380
  } else {
381
    string prog_name = ttype->get_program()->get_name();
382
    string type_name = ttype->get_name();
383
    f_out_ << "<a href=\"" << prog_name << ".html#";
384
    if (ttype->is_typedef()) {
385
      f_out_ << "Typedef_";
386
    } else if (ttype->is_struct() || ttype->is_xception()) {
387
      f_out_ << "Struct_";
388
    } else if (ttype->is_enum()) {
389
      f_out_ << "Enum_";
390
    } else if (ttype->is_service()) {
391
      f_out_ << "Svc_";
392
    }
393
    f_out_ << type_name << "\">";
394
    len = type_name.size();
395
    if (ttype->get_program() != program_) {
396
      f_out_ << prog_name << ".";
397
      len += prog_name.size() + 1;
398
    }
399
    f_out_ << type_name << "</a>";
400
  }
401
  f_out_ << "</code>";
402
  return len;
403
}
404
 
405
/**
406
 * Prints out an HTML representation of the provided constant value
407
 */
408
void t_html_generator::print_const_value(t_const_value* tvalue) {
409
  bool first = true;
410
  switch (tvalue->get_type()) {
411
  case t_const_value::CV_INTEGER:
412
    f_out_ << tvalue->get_integer();
413
    break;
414
  case t_const_value::CV_DOUBLE:
415
    f_out_ << tvalue->get_double();
416
    break;
417
  case t_const_value::CV_STRING:
418
    f_out_ << '"' << get_escaped_string(tvalue) << '"';
419
    break;
420
  case t_const_value::CV_MAP:
421
    {
422
      f_out_ << "{ ";
423
      map<t_const_value*, t_const_value*> map_elems = tvalue->get_map();
424
      map<t_const_value*, t_const_value*>::iterator map_iter;
425
      for (map_iter = map_elems.begin(); map_iter != map_elems.end();
426
	   map_iter++) {
427
	if (!first) {
428
	  f_out_ << ", ";
429
	}
430
	first = false;
431
	print_const_value(map_iter->first);
432
	f_out_ << " = ";
433
	print_const_value(map_iter->second);
434
      }
435
      f_out_ << " }";
436
    }
437
    break;
438
  case t_const_value::CV_LIST:
439
    {
440
      f_out_ << "{ ";
441
      vector<t_const_value*> list_elems = tvalue->get_list();;
442
      vector<t_const_value*>::iterator list_iter;
443
      for (list_iter = list_elems.begin(); list_iter != list_elems.end();
444
	   list_iter++) {
445
	if (!first) {
446
	  f_out_ << ", ";
447
	}
448
	first = false;
449
	print_const_value(*list_iter);
450
      }
451
      f_out_ << " }";
452
    }
453
    break;
454
  default:
455
    f_out_ << "UNKNOWN";
456
    break;
457
  }
458
}
459
 
460
/**
461
 * Generates a typedef.
462
 *
463
 * @param ttypedef The type definition
464
 */
465
void t_html_generator::generate_typedef(t_typedef* ttypedef) {
466
  string name = ttypedef->get_name();
467
  f_out_ << "<div class=\"definition\">";
468
  f_out_ << "<h3 id=\"Typedef_" << name << "\">Typedef: " << name
469
	 << "</h3>" << endl;
470
  f_out_ << "<p><strong>Base type:</strong>&nbsp;";
471
  print_type(ttypedef->get_type());
472
  f_out_ << "</p>" << endl;
473
  print_doc(ttypedef);
474
  f_out_ << "</div>" << endl;
475
}
476
 
477
/**
478
 * Generates code for an enumerated type.
479
 *
480
 * @param tenum The enumeration
481
 */
482
void t_html_generator::generate_enum(t_enum* tenum) {
483
  string name = tenum->get_name();
484
  f_out_ << "<div class=\"definition\">";
485
  f_out_ << "<h3 id=\"Enum_" << name << "\">Enumeration: " << name
486
	 << "</h3>" << endl;
487
  print_doc(tenum);
488
  vector<t_enum_value*> values = tenum->get_constants();
489
  vector<t_enum_value*>::iterator val_iter;
490
  f_out_ << "<br/><table>" << endl;
491
  for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) {
492
    f_out_ << "<tr><td><code>";
493
    f_out_ << (*val_iter)->get_name();
494
    f_out_ << "</code></td><td><code>";
495
    f_out_ << (*val_iter)->get_value();
496
    f_out_ << "</code></td></tr>" << endl;
497
  }
498
  f_out_ << "</table></div>" << endl;
499
}
500
 
501
/**
502
 * Generates a constant value
503
 */
504
void t_html_generator::generate_const(t_const* tconst) {
505
  string name = tconst->get_name();
506
  f_out_ << "<tr id=\"Const_" << name << "\"><td><code>" << name
507
	 << "</code></td><td><code>";
508
  print_type(tconst->get_type());
509
  f_out_ << "</code></td><td><code>";
510
  print_const_value(tconst->get_value());
511
  f_out_ << "</code></td></tr>";
512
  if (tconst->has_doc()) {
513
    f_out_ << "<tr><td colspan=\"3\"><blockquote>";
514
    print_doc(tconst);
515
    f_out_ << "</blockquote></td></tr>";
516
  }
517
}
518
 
519
/**
520
 * Generates a struct definition for a thrift data type.
521
 *
522
 * @param tstruct The struct definition
523
 */
524
void t_html_generator::generate_struct(t_struct* tstruct) {
525
  string name = tstruct->get_name();
526
  f_out_ << "<div class=\"definition\">";
527
  f_out_ << "<h3 id=\"Struct_" << name << "\">";
528
  if (tstruct->is_xception()) {
529
    f_out_ << "Exception: ";
530
  } else {
531
    f_out_ << "Struct: ";
532
  }
533
  f_out_ << name << "</h3>" << endl;
534
  vector<t_field*> members = tstruct->get_members();
535
  vector<t_field*>::iterator mem_iter = members.begin();
536
  f_out_ << "<table>";
537
  f_out_ << "<tr><th>Field</th><th>Type</th><th>Required</th><th>Default value</th></tr>"
538
	 << endl;
539
  for ( ; mem_iter != members.end(); mem_iter++) {
540
    f_out_ << "<tr><td>" << (*mem_iter)->get_name() << "</td><td>";
541
    print_type((*mem_iter)->get_type());
542
    f_out_ << "</td><td>";
543
    if ((*mem_iter)->get_req() != t_field::T_OPTIONAL) {
544
      f_out_ << "yes";
545
    } else {
546
      f_out_ << "no";
547
    }
548
    f_out_ << "</td><td>";
549
    t_const_value* default_val = (*mem_iter)->get_value();
550
    if (default_val != NULL) {
551
      print_const_value(default_val);
552
    }
553
    f_out_ << "</td></tr>" << endl;
554
  }
555
  f_out_ << "</table><br/>";
556
  print_doc(tstruct);
557
  f_out_ << "</div>";
558
}
559
 
560
/**
561
 * Exceptions are special structs
562
 *
563
 * @param tstruct The struct definition
564
 */
565
void t_html_generator::generate_xception(t_struct* txception) {
566
  generate_struct(txception);
567
}
568
 
569
/**
570
 * Generates the HTML block for a Thrift service.
571
 *
572
 * @param tservice The service definition
573
 */
574
void t_html_generator::generate_service(t_service* tservice) {
575
  f_out_ << "<h3 id=\"Svc_" << service_name_ << "\">Service: "
576
	 << service_name_ << "</h3>" << endl;
577
 
578
  if (tservice->get_extends()) {
579
    f_out_ << "<div class=\"extends\"><em>extends</em> ";
580
    print_type(tservice->get_extends());
581
    f_out_ << "</div>\n";
582
  }
583
  print_doc(tservice);
584
  vector<t_function*> functions = tservice->get_functions();
585
  vector<t_function*>::iterator fn_iter = functions.begin();
586
  for ( ; fn_iter != functions.end(); fn_iter++) {
587
    string fn_name = (*fn_iter)->get_name();
588
    f_out_ << "<div class=\"definition\">";
589
    f_out_ << "<h4 id=\"Fn_" << service_name_ << "_" << fn_name
590
	   << "\">Function: " << service_name_ << "." << fn_name
591
	   << "</h4>" << endl;
592
    f_out_ << "<pre>";
593
    int offset = print_type((*fn_iter)->get_returntype());
594
    bool first = true;
595
    f_out_ << " " << fn_name << "(";
596
    offset += fn_name.size() + 2;
597
    vector<t_field*> args = (*fn_iter)->get_arglist()->get_members();
598
    vector<t_field*>::iterator arg_iter = args.begin();
599
    if (arg_iter != args.end()) {
600
      for ( ; arg_iter != args.end(); arg_iter++) {
601
	if (!first) {
602
	  f_out_ << "," << endl;
603
	  for (int i = 0; i < offset; ++i) {
604
	    f_out_ << " ";
605
	  }
606
	}
607
	first = false;
608
	print_type((*arg_iter)->get_type());
609
	f_out_ << " " << (*arg_iter)->get_name();
610
	if ((*arg_iter)->get_value() != NULL) {
611
	  f_out_ << " = ";
612
	  print_const_value((*arg_iter)->get_value());
613
	}
614
      }
615
    }
616
    f_out_ << ")" << endl;
617
    first = true;
618
    vector<t_field*> excepts = (*fn_iter)->get_xceptions()->get_members();
619
    vector<t_field*>::iterator ex_iter = excepts.begin();
620
    if (ex_iter != excepts.end()) {
621
      f_out_ << "    throws ";
622
      for ( ; ex_iter != excepts.end(); ex_iter++) {
623
	if (!first) {
624
	  f_out_ << ", ";
625
	}
626
	first = false;
627
	print_type((*ex_iter)->get_type());
628
      }
629
      f_out_ << endl;
630
    }
631
    f_out_ << "</pre>";
632
    print_doc(*fn_iter);
633
    f_out_ << "</div>";
634
  }
635
}
636
 
637
THRIFT_REGISTER_GENERATOR(html, "HTML", "");