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, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 #include <ctype.h>
20 #ifndef _WIN32
21 #include <sys/time.h>
22 #endif
23 #include <iostream>
24 #include <fstream>
25 #include <map>
26 #include <set>
27 
28 #include <boost/algorithm/string.hpp>
29 #include <boost/lexical_cast.hpp>
30 #include <boost/program_options.hpp>
31 
32 #include <boost/random/mersenne_twister.hpp>
33 #include <boost/random/uniform_int.hpp>
34 #include <boost/random/variate_generator.hpp>
35 
36 #include "Compiler.hh"
37 #include "ValidSchema.hh"
38 #include "NodeImpl.hh"
39 
40 using std::ostream;
41 using std::ifstream;
42 using std::ofstream;
43 using std::map;
44 using std::set;
45 using std::string;
46 using std::vector;
47 using internal_avro::NodePtr;
48 using internal_avro::resolveSymbol;
49 
50 using boost::lexical_cast;
51 
52 using internal_avro::ValidSchema;
53 using internal_avro::compileJsonSchema;
54 
55 struct PendingSetterGetter {
56   string structName;
57   string type;
58   string name;
59   size_t idx;
60 
PendingSetterGetterPendingSetterGetter61   PendingSetterGetter(const string& sn, const string& t, const string& n,
62                       size_t i)
63       : structName(sn), type(t), name(n), idx(i) {}
64 };
65 
66 struct PendingConstructor {
67   string structName;
68   string memberName;
69   bool initMember;
PendingConstructorPendingConstructor70   PendingConstructor(const string& sn, const string& n, bool im)
71       : structName(sn), memberName(n), initMember(im) {}
72 };
73 
74 class CodeGen {
75   size_t unionNumber_;
76   std::ostream& os_;
77   bool inNamespace_;
78   const std::string ns_;
79   const std::string schemaFile_;
80   const std::string headerFile_;
81   const std::string includePrefix_;
82   const bool noUnion_;
83   const std::string guardString_;
84   boost::mt19937 random_;
85 
86   vector<PendingSetterGetter> pendingGettersAndSetters;
87   vector<PendingConstructor> pendingConstructors;
88 
89   map<NodePtr, string> done;
90   set<NodePtr> doing;
91 
92   std::string guard();
93   std::string fullname(const string& name) const;
94   std::string generateEnumType(const NodePtr& n);
95   std::string cppTypeOf(const NodePtr& n);
96   std::string generateRecordType(const NodePtr& n);
97   std::string unionName();
98   std::string generateUnionType(const NodePtr& n);
99   std::string generateType(const NodePtr& n);
100   std::string generateDeclaration(const NodePtr& n);
101   std::string doGenerateType(const NodePtr& n);
102   void generateEnumTraits(const NodePtr& n);
103   void generateTraits(const NodePtr& n);
104   void generateRecordTraits(const NodePtr& n);
105   void generateUnionTraits(const NodePtr& n);
106   void emitCopyright();
107 
108  public:
CodeGen(std::ostream & os,const std::string & ns,const std::string & schemaFile,const std::string & headerFile,const std::string & guardString,const std::string & includePrefix,bool noUnion)109   CodeGen(std::ostream& os, const std::string& ns,
110           const std::string& schemaFile, const std::string& headerFile,
111           const std::string& guardString, const std::string& includePrefix,
112           bool noUnion)
113       : unionNumber_(0),
114         os_(os),
115         inNamespace_(false),
116         ns_(ns),
117         schemaFile_(schemaFile),
118         headerFile_(headerFile),
119         includePrefix_(includePrefix),
120         noUnion_(noUnion),
121         guardString_(guardString),
122         random_(static_cast<uint32_t>(::time(0))) {}
123   void generate(const ValidSchema& schema);
124 };
125 
decorate(const internal_avro::Name & name)126 static string decorate(const internal_avro::Name& name) {
127   return name.simpleName();
128 }
129 
fullname(const string & name) const130 string CodeGen::fullname(const string& name) const {
131   return ns_.empty() ? name : (ns_ + "::" + name);
132 }
133 
generateEnumType(const NodePtr & n)134 string CodeGen::generateEnumType(const NodePtr& n) {
135   string s = decorate(n->name());
136   os_ << "enum " << s << " {\n";
137   size_t c = n->names();
138   for (size_t i = 0; i < c; ++i) {
139     os_ << "    " << n->nameAt(i) << ",\n";
140   }
141   os_ << "};\n\n";
142   return s;
143 }
144 
cppTypeOf(const NodePtr & n)145 string CodeGen::cppTypeOf(const NodePtr& n) {
146   switch (n->type()) {
147     case internal_avro::AVRO_STRING:
148       return "std::string";
149     case internal_avro::AVRO_BYTES:
150       return "std::vector<uint8_t>";
151     case internal_avro::AVRO_INT:
152       return "int32_t";
153     case internal_avro::AVRO_LONG:
154       return "int64_t";
155     case internal_avro::AVRO_FLOAT:
156       return "float";
157     case internal_avro::AVRO_DOUBLE:
158       return "double";
159     case internal_avro::AVRO_BOOL:
160       return "bool";
161     case internal_avro::AVRO_RECORD:
162     case internal_avro::AVRO_ENUM: {
163       string nm = decorate(n->name());
164       return inNamespace_ ? nm : fullname(nm);
165     }
166     case internal_avro::AVRO_ARRAY:
167       return "std::vector<" + cppTypeOf(n->leafAt(0)) + " >";
168     case internal_avro::AVRO_MAP:
169       return "std::map<std::string, " + cppTypeOf(n->leafAt(1)) + " >";
170     case internal_avro::AVRO_FIXED:
171       return "boost::array<uint8_t, " + lexical_cast<string>(n->fixedSize()) +
172              ">";
173     case internal_avro::AVRO_SYMBOLIC:
174       return cppTypeOf(resolveSymbol(n));
175     case internal_avro::AVRO_UNION:
176       return fullname(done[n]);
177     default:
178       return "$Undefined$";
179   }
180 }
181 
cppNameOf(const NodePtr & n)182 static string cppNameOf(const NodePtr& n) {
183   switch (n->type()) {
184     case internal_avro::AVRO_NULL:
185       return "null";
186     case internal_avro::AVRO_STRING:
187       return "string";
188     case internal_avro::AVRO_BYTES:
189       return "bytes";
190     case internal_avro::AVRO_INT:
191       return "int";
192     case internal_avro::AVRO_LONG:
193       return "long";
194     case internal_avro::AVRO_FLOAT:
195       return "float";
196     case internal_avro::AVRO_DOUBLE:
197       return "double";
198     case internal_avro::AVRO_BOOL:
199       return "bool";
200     case internal_avro::AVRO_RECORD:
201     case internal_avro::AVRO_ENUM:
202     case internal_avro::AVRO_FIXED:
203       return decorate(n->name());
204     case internal_avro::AVRO_ARRAY:
205       return "array";
206     case internal_avro::AVRO_MAP:
207       return "map";
208     case internal_avro::AVRO_SYMBOLIC:
209       return cppNameOf(resolveSymbol(n));
210     default:
211       return "$Undefined$";
212   }
213 }
214 
generateRecordType(const NodePtr & n)215 string CodeGen::generateRecordType(const NodePtr& n) {
216   size_t c = n->leaves();
217   vector<string> types;
218   for (size_t i = 0; i < c; ++i) {
219     types.push_back(generateType(n->leafAt(i)));
220   }
221 
222   map<NodePtr, string>::const_iterator it = done.find(n);
223   if (it != done.end()) {
224     return it->second;
225   }
226 
227   os_ << "struct " << decorate(n->name()) << " {\n";
228   if (!noUnion_) {
229     for (size_t i = 0; i < c; ++i) {
230       if (n->leafAt(i)->type() == internal_avro::AVRO_UNION) {
231         os_ << "    typedef " << types[i] << ' ' << n->nameAt(i) << "_t;\n";
232       }
233     }
234   }
235   for (size_t i = 0; i < c; ++i) {
236     if (!noUnion_ && n->leafAt(i)->type() == internal_avro::AVRO_UNION) {
237       os_ << "    " << n->nameAt(i) << "_t";
238     } else {
239       os_ << "    " << types[i];
240     }
241     os_ << ' ' << n->nameAt(i) << ";\n";
242   }
243   os_ << "};\n\n";
244   return decorate(n->name());
245 }
246 
makeCanonical(string & s,bool foldCase)247 void makeCanonical(string& s, bool foldCase) {
248   for (string::iterator it = s.begin(); it != s.end(); ++it) {
249     if (isalpha(*it)) {
250       if (foldCase) {
251         *it = toupper(*it);
252       }
253     } else if (!isdigit(*it)) {
254       *it = '_';
255     }
256   }
257 }
258 
unionName()259 string CodeGen::unionName() {
260   string s = schemaFile_;
261   string::size_type n = s.find_last_of("/\\");
262   if (n != string::npos) {
263     s = s.substr(n);
264   }
265   makeCanonical(s, false);
266 
267   return s + "_Union__" + boost::lexical_cast<string>(unionNumber_++) + "__";
268 }
269 
generateGetterAndSetter(ostream & os,const string & structName,const string & type,const string & name,size_t idx)270 static void generateGetterAndSetter(ostream& os, const string& structName,
271                                     const string& type, const string& name,
272                                     size_t idx) {
273   string sn = " " + structName + "::";
274 
275   os << "inline\n";
276 
277   os << type << sn << "get_" << name << "() const {\n"
278      << "    if (idx_ != " << idx << ") {\n"
279      << "        throw internal_avro::Exception(\"Invalid type for "
280      << "union\");\n"
281      << "    }\n"
282      << "    return boost::any_cast<" << type << " >(value_);\n"
283      << "}\n\n";
284 
285   os << "inline\n"
286      << "void" << sn << "set_" << name << "(const " << type << "& v) {\n"
287      << "    idx_ = " << idx << ";\n"
288      << "    value_ = v;\n"
289      << "}\n\n";
290 }
291 
generateConstructor(ostream & os,const string & structName,bool initMember,const string & type)292 static void generateConstructor(ostream& os, const string& structName,
293                                 bool initMember, const string& type) {
294   os << "inline " << structName << "::" << structName << "() : idx_(0)";
295   if (initMember) {
296     os << ", value_(" << type << "())";
297   }
298   os << " { }\n";
299 }
300 
301 /**
302  * Generates a type for union and emits the code.
303  * Since unions can encounter names that are not fully defined yet,
304  * such names must be declared and the inline functions deferred until all
305  * types are fully defined.
306  */
generateUnionType(const NodePtr & n)307 string CodeGen::generateUnionType(const NodePtr& n) {
308   size_t c = n->leaves();
309   vector<string> types;
310   vector<string> names;
311 
312   set<NodePtr>::const_iterator it = doing.find(n);
313   if (it != doing.end()) {
314     for (size_t i = 0; i < c; ++i) {
315       const NodePtr& nn = n->leafAt(i);
316       types.push_back(generateDeclaration(nn));
317       names.push_back(cppNameOf(nn));
318     }
319   } else {
320     doing.insert(n);
321     for (size_t i = 0; i < c; ++i) {
322       const NodePtr& nn = n->leafAt(i);
323       types.push_back(generateType(nn));
324       names.push_back(cppNameOf(nn));
325     }
326     doing.erase(n);
327   }
328   if (done.find(n) != done.end()) {
329     return done[n];
330   }
331 
332   const string result = unionName();
333 
334   os_ << "struct " << result << " {\n"
335       << "private:\n"
336       << "    size_t idx_;\n"
337       << "    boost::any value_;\n"
338       << "public:\n"
339       << "    size_t idx() const { return idx_; }\n";
340 
341   for (size_t i = 0; i < c; ++i) {
342     const NodePtr& nn = n->leafAt(i);
343     if (nn->type() == internal_avro::AVRO_NULL) {
344       os_ << "    bool is_null() const {\n"
345           << "        return (idx_ == " << i << ");\n"
346           << "    }\n"
347           << "    void set_null() {\n"
348           << "        idx_ = " << i << ";\n"
349           << "        value_ = boost::any();\n"
350           << "    }\n";
351     } else {
352       const string& type = types[i];
353       const string& name = names[i];
354       os_ << "    " << type << " get_" << name << "() const;\n"
355                                                   "    void set_" << name
356           << "(const " << type << "& v);\n";
357       pendingGettersAndSetters.push_back(
358           PendingSetterGetter(result, type, name, i));
359     }
360   }
361 
362   os_ << "    " << result << "();\n";
363   pendingConstructors.push_back(PendingConstructor(
364       result, types[0], n->leafAt(0)->type() != internal_avro::AVRO_NULL));
365   os_ << "};\n\n";
366 
367   return result;
368 }
369 
370 /**
371  * Returns the type for the given schema node and emits code to os.
372  */
generateType(const NodePtr & n)373 string CodeGen::generateType(const NodePtr& n) {
374   NodePtr nn =
375       (n->type() == internal_avro::AVRO_SYMBOLIC) ? resolveSymbol(n) : n;
376 
377   map<NodePtr, string>::const_iterator it = done.find(nn);
378   if (it != done.end()) {
379     return it->second;
380   }
381   string result = doGenerateType(nn);
382   done[nn] = result;
383   return result;
384 }
385 
doGenerateType(const NodePtr & n)386 string CodeGen::doGenerateType(const NodePtr& n) {
387   switch (n->type()) {
388     case internal_avro::AVRO_STRING:
389     case internal_avro::AVRO_BYTES:
390     case internal_avro::AVRO_INT:
391     case internal_avro::AVRO_LONG:
392     case internal_avro::AVRO_FLOAT:
393     case internal_avro::AVRO_DOUBLE:
394     case internal_avro::AVRO_BOOL:
395     case internal_avro::AVRO_NULL:
396     case internal_avro::AVRO_FIXED:
397       return cppTypeOf(n);
398     case internal_avro::AVRO_ARRAY:
399       return "std::vector<" + generateType(n->leafAt(0)) + " >";
400     case internal_avro::AVRO_MAP:
401       return "std::map<std::string, " + generateType(n->leafAt(1)) + " >";
402     case internal_avro::AVRO_RECORD:
403       return generateRecordType(n);
404     case internal_avro::AVRO_ENUM:
405       return generateEnumType(n);
406     case internal_avro::AVRO_UNION:
407       return generateUnionType(n);
408     default:
409       break;
410   }
411   return "$Undefuned$";
412 }
413 
generateDeclaration(const NodePtr & n)414 string CodeGen::generateDeclaration(const NodePtr& n) {
415   NodePtr nn =
416       (n->type() == internal_avro::AVRO_SYMBOLIC) ? resolveSymbol(n) : n;
417   switch (nn->type()) {
418     case internal_avro::AVRO_STRING:
419     case internal_avro::AVRO_BYTES:
420     case internal_avro::AVRO_INT:
421     case internal_avro::AVRO_LONG:
422     case internal_avro::AVRO_FLOAT:
423     case internal_avro::AVRO_DOUBLE:
424     case internal_avro::AVRO_BOOL:
425     case internal_avro::AVRO_NULL:
426     case internal_avro::AVRO_FIXED:
427       return cppTypeOf(nn);
428     case internal_avro::AVRO_ARRAY:
429       return "std::vector<" + generateDeclaration(nn->leafAt(0)) + " >";
430     case internal_avro::AVRO_MAP:
431       return "std::map<std::string, " + generateDeclaration(nn->leafAt(1)) +
432              " >";
433     case internal_avro::AVRO_RECORD:
434       os_ << "struct " << cppTypeOf(nn) << ";\n";
435       return cppTypeOf(nn);
436     case internal_avro::AVRO_ENUM:
437       return generateEnumType(nn);
438     case internal_avro::AVRO_UNION:
439       // FIXME: When can this happen?
440       return generateUnionType(nn);
441     default:
442       break;
443   }
444   return "$Undefuned$";
445 }
446 
generateEnumTraits(const NodePtr & n)447 void CodeGen::generateEnumTraits(const NodePtr& n) {
448   string fn = fullname(decorate(n->name()));
449   os_ << "template<> struct codec_traits<" << fn << "> {\n"
450       << "    static void encode(Encoder& e, " << fn << " v) {\n"
451       << "        e.encodeEnum(v);\n"
452       << "    }\n"
453       << "    static void decode(Decoder& d, " << fn << "& v) {\n"
454       << "        v = static_cast<" << fn << ">(d.decodeEnum());\n"
455       << "    }\n"
456       << "};\n\n";
457 }
458 
generateRecordTraits(const NodePtr & n)459 void CodeGen::generateRecordTraits(const NodePtr& n) {
460   size_t c = n->leaves();
461   for (size_t i = 0; i < c; ++i) {
462     generateTraits(n->leafAt(i));
463   }
464 
465   string fn = fullname(decorate(n->name()));
466   os_ << "template<> struct codec_traits<" << fn << "> {\n"
467       << "    static void encode(Encoder& e, const " << fn << "& v) {\n";
468 
469   for (size_t i = 0; i < c; ++i) {
470     os_ << "        internal_avro::encode(e, v." << n->nameAt(i) << ");\n";
471   }
472 
473   os_ << "    }\n"
474       << "    static void decode(Decoder& d, " << fn << "& v) {\n";
475 
476   for (size_t i = 0; i < c; ++i) {
477     os_ << "        internal_avro::decode(d, v." << n->nameAt(i) << ");\n";
478   }
479 
480   os_ << "    }\n"
481       << "};\n\n";
482 }
483 
generateUnionTraits(const NodePtr & n)484 void CodeGen::generateUnionTraits(const NodePtr& n) {
485   size_t c = n->leaves();
486 
487   for (size_t i = 0; i < c; ++i) {
488     const NodePtr& nn = n->leafAt(i);
489     generateTraits(nn);
490   }
491 
492   string name = done[n];
493   string fn = fullname(name);
494 
495   os_ << "template<> struct codec_traits<" << fn << "> {\n"
496       << "    static void encode(Encoder& e, " << fn << " v) {\n"
497       << "        e.encodeUnionIndex(v.idx());\n"
498       << "        switch (v.idx()) {\n";
499 
500   for (size_t i = 0; i < c; ++i) {
501     const NodePtr& nn = n->leafAt(i);
502     os_ << "        case " << i << ":\n";
503     if (nn->type() == internal_avro::AVRO_NULL) {
504       os_ << "            e.encodeNull();\n";
505     } else {
506       os_ << "            internal_avro::encode(e, v.get_" << cppNameOf(nn)
507           << "());\n";
508     }
509     os_ << "            break;\n";
510   }
511 
512   os_ << "        }\n"
513       << "    }\n"
514       << "    static void decode(Decoder& d, " << fn << "& v) {\n"
515       << "        size_t n = d.decodeUnionIndex();\n"
516       << "        if (n >= " << c << ") { throw internal_avro::Exception(\""
517                                      "Union index too big\"); }\n"
518       << "        switch (n) {\n";
519 
520   for (size_t i = 0; i < c; ++i) {
521     const NodePtr& nn = n->leafAt(i);
522     os_ << "        case " << i << ":\n";
523     if (nn->type() == internal_avro::AVRO_NULL) {
524       os_ << "            d.decodeNull();\n"
525           << "            v.set_null();\n";
526     } else {
527       os_ << "            {\n"
528           << "                " << cppTypeOf(nn) << " vv;\n"
529           << "                internal_avro::decode(d, vv);\n"
530           << "                v.set_" << cppNameOf(nn) << "(vv);\n"
531           << "            }\n";
532     }
533     os_ << "            break;\n";
534   }
535   os_ << "        }\n"
536       << "    }\n"
537       << "};\n\n";
538 }
539 
generateTraits(const NodePtr & n)540 void CodeGen::generateTraits(const NodePtr& n) {
541   switch (n->type()) {
542     case internal_avro::AVRO_STRING:
543     case internal_avro::AVRO_BYTES:
544     case internal_avro::AVRO_INT:
545     case internal_avro::AVRO_LONG:
546     case internal_avro::AVRO_FLOAT:
547     case internal_avro::AVRO_DOUBLE:
548     case internal_avro::AVRO_BOOL:
549     case internal_avro::AVRO_NULL:
550       break;
551     case internal_avro::AVRO_RECORD:
552       generateRecordTraits(n);
553       break;
554     case internal_avro::AVRO_ENUM:
555       generateEnumTraits(n);
556       break;
557     case internal_avro::AVRO_ARRAY:
558     case internal_avro::AVRO_MAP:
559       generateTraits(n->leafAt(n->type() == internal_avro::AVRO_ARRAY ? 0 : 1));
560       break;
561     case internal_avro::AVRO_UNION:
562       generateUnionTraits(n);
563       break;
564     case internal_avro::AVRO_FIXED:
565       break;
566     default:
567       break;
568   }
569 }
570 
emitCopyright()571 void CodeGen::emitCopyright() {
572   os_ << "/**\n"
573          " * Licensed to the Apache Software Foundation (ASF) under one\n"
574          " * or more contributor license agreements.  See the NOTICE file\n"
575          " * distributed with this work for additional information\n"
576          " * regarding copyright ownership.  The ASF licenses this file\n"
577          " * to you under the Apache License, Version 2.0 (the\n"
578          " * \"License\"); you may not use this file except in compliance\n"
579          " * with the License.  You may obtain a copy of the License at\n"
580          " *\n"
581          " *     http://www.apache.org/licenses/LICENSE-2.0\n"
582          " *\n"
583          " * Unless required by applicable law or agreed to in writing, "
584          "software\n"
585          " * distributed under the License is distributed on an "
586          "\"AS IS\" BASIS,\n"
587          " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express "
588          "or implied.\n"
589          " * See the License for the specific language governing "
590          "permissions and\n"
591          " * limitations under the License.\n"
592          " */\n\n\n";
593 }
594 
guard()595 string CodeGen::guard() {
596   string h = headerFile_;
597   makeCanonical(h, true);
598   return h + "_" + lexical_cast<string>(random_()) + "__H_";
599 }
600 
generate(const ValidSchema & schema)601 void CodeGen::generate(const ValidSchema& schema) {
602   emitCopyright();
603 
604   string h = guardString_.empty() ? guard() : guardString_;
605 
606   os_ << "#ifndef " << h << "\n";
607   os_ << "#define " << h << "\n\n\n";
608 
609   os_ << "#include \"boost/any.hpp\"\n"
610       << "#include \"" << includePrefix_ << "Specific.hh\"\n"
611       << "#include \"" << includePrefix_ << "Encoder.hh\"\n"
612       << "#include \"" << includePrefix_ << "Decoder.hh\"\n"
613       << "\n";
614 
615   if (!ns_.empty()) {
616     os_ << "namespace " << ns_ << " {\n";
617     inNamespace_ = true;
618   }
619 
620   const NodePtr& root = schema.root();
621   generateType(root);
622 
623   for (vector<PendingSetterGetter>::const_iterator it =
624            pendingGettersAndSetters.begin();
625        it != pendingGettersAndSetters.end(); ++it) {
626     generateGetterAndSetter(os_, it->structName, it->type, it->name, it->idx);
627   }
628 
629   for (vector<PendingConstructor>::const_iterator it =
630            pendingConstructors.begin();
631        it != pendingConstructors.end(); ++it) {
632     generateConstructor(os_, it->structName, it->initMember, it->memberName);
633   }
634 
635   if (!ns_.empty()) {
636     inNamespace_ = false;
637     os_ << "}\n";
638   }
639 
640   os_ << "namespace internal_avro {\n";
641 
642   unionNumber_ = 0;
643 
644   generateTraits(root);
645 
646   os_ << "}\n";
647 
648   os_ << "#endif\n";
649   os_.flush();
650 }
651 
652 namespace po = boost::program_options;
653 
654 static const string NS("namespace");
655 static const string OUT("output");
656 static const string IN("input");
657 static const string INCLUDE_PREFIX("include-prefix");
658 static const string NO_UNION_TYPEDEF("no-union-typedef");
659 
readGuard(const string & filename)660 static string readGuard(const string& filename) {
661   std::ifstream ifs(filename.c_str());
662   string buf;
663   string candidate;
664   while (std::getline(ifs, buf)) {
665     boost::algorithm::trim(buf);
666     if (candidate.empty()) {
667       if (boost::algorithm::starts_with(buf, "#ifndef ")) {
668         candidate = buf.substr(8);
669       }
670     } else if (boost::algorithm::starts_with(buf, "#define ")) {
671       if (candidate == buf.substr(8)) {
672         break;
673       }
674     } else {
675       candidate.erase();
676     }
677   }
678   return candidate;
679 }
680 
main(int argc,char ** argv)681 int main(int argc, char** argv) {
682   po::options_description desc("Allowed options");
683   desc.add_options()("help,h", "produce help message")(
684       "include-prefix,p", po::value<string>()->default_value("internal_avro"),
685       "prefix for include headers, - for none, default: internal_avro")(
686       "no-union-typedef,U", "do not generate typedefs for unions in records")(
687       "namespace,n", po::value<string>(), "set namespace for generated code")(
688       "input,i", po::value<string>(), "input file")(
689       "output,o", po::value<string>(), "output file to generate");
690 
691   po::variables_map vm;
692   po::store(po::parse_command_line(argc, argv, desc), vm);
693   po::notify(vm);
694 
695   if (vm.count("help") || vm.count(IN) == 0 || vm.count(OUT) == 0) {
696     std::cout << desc << std::endl;
697     return 1;
698   }
699 
700   string ns = vm.count(NS) > 0 ? vm[NS].as<string>() : string();
701   string outf = vm.count(OUT) > 0 ? vm[OUT].as<string>() : string();
702   string inf = vm.count(IN) > 0 ? vm[IN].as<string>() : string();
703   string incPrefix = vm[INCLUDE_PREFIX].as<string>();
704   bool noUnion = vm.count(NO_UNION_TYPEDEF) != 0;
705   if (incPrefix == "-") {
706     incPrefix.clear();
707   } else if (*incPrefix.rbegin() != '/') {
708     incPrefix += "/";
709   }
710 
711   try {
712     ValidSchema schema;
713 
714     if (!inf.empty()) {
715       ifstream in(inf.c_str());
716       compileJsonSchema(in, schema);
717     } else {
718       compileJsonSchema(std::cin, schema);
719     }
720 
721     if (!outf.empty()) {
722       string g = readGuard(outf);
723       ofstream out(outf.c_str());
724       CodeGen(out, ns, inf, outf, g, incPrefix, noUnion).generate(schema);
725     } else {
726       CodeGen(std::cout, ns, inf, outf, "", incPrefix, noUnion)
727           .generate(schema);
728     }
729     return 0;
730   }
731   catch (std::exception& e) {
732     std::cerr << "Failed to parse or compile schema: " << e.what() << std::endl;
733     return 1;
734   }
735 }
736