Classdesc 3.44
xsd_generate_base.h
1/*
2 @copyright Russell Standish 2000-2013
3 @author Russell Standish
4 This file is part of Classdesc
5
6 Open source licensed under the MIT license. See LICENSE for details.
7*/
8
9#ifndef CLASSDESC_XSD_GENERATE_BASE_H
10#define CLASSDESC_XSD_GENERATE_BASE_H
11#include "classdesc.h"
12#include <map>
13#include <set>
14#include <ostream>
15#include <sstream>
16#include <stdexcept>
17#include <stdarg.h>
18
19namespace classdesc
20{
21 class xsd_generate_t
22 {
23 std::map<string, string> xsdDefs;
24 std::map<string, std::set<string> > dependencies;
25
26 struct TypeBeingAddedTo
27 {
28 bool complete; //set to true if current type definition is complete
29 bool sequenceAdded;
30 string name, description, baseClass;
31 TypeBeingAddedTo(const string& name="", const string& d="", bool complete=false):
32 complete(complete), sequenceAdded(false), name(name), description(d) {}
33 };
34
35 std::vector<TypeBeingAddedTo> typeBeingaddedTo;
36 std::set<string> written; // record when a type is written
37
38 void outputType(std::ostream& o, const string& type)
39 {
40 if (!written.insert(type).second) return; //already done
41 // ensure dependencies are written
42 const std::set<string>& deps=dependencies[type];
43 for (std::set<string>::const_iterator d=deps.begin(); d!=deps.end(); ++d)
44 outputType(o, *d);
45 o<<xsdDefs[type];
46 }
47
48 public:
50 string rootName, rootType;
53
55 struct Optional
56 {
57 xsd_generate_t& x;
58 bool prev_opt;
59 Optional(xsd_generate_t& x, bool o): x(x), prev_opt(x.optional) {x.optional=o;}
60 ~Optional() {x.optional=prev_opt;}
61 };
62
63 xsd_generate_t(): optional(false) {}
64
66 void addMember(const string& name, const string& memberType)
67 {
68 if (!name.empty() &&
69 !typeBeingaddedTo.empty() && !typeBeingaddedTo.back().complete)
70 {
71 if (!typeBeingaddedTo.back().sequenceAdded)
72 xsdDefs[typeBeingaddedTo.back().name]+=" <xs:sequence>\n";
73 typeBeingaddedTo.back().sequenceAdded=true;
74 xsdDefs[typeBeingaddedTo.back().name]+=
75 " <xs:element name=\""+name+"\" type=\""
76 +memberType+(optional?"\" minOccurs=\"0":"")+"\"/>\n";
77 addDependency(typeBeingaddedTo.back().name, memberType);
78 }
79 }
80
82 void addBase(const string& base)
83 {
84 if (!typeBeingaddedTo.empty() && !typeBeingaddedTo.back().complete)
85 {
86 if (typeBeingaddedTo.back().baseClass.empty())
87 {
88 xsdDefs[typeBeingaddedTo.back().name]+=
89 " <xs:complexContent>\n"
90 " <xs:extension base=\""+base+"\">\n";
91 typeBeingaddedTo.back().baseClass=base;
92 }
93 else if (typeBeingaddedTo.back().baseClass!=base)
94 throw exception
95 ("Multiple inheritance not supported: "+typeBeingaddedTo.back().name);
96 }
97 }
98
100 void addDependency(const string& type, const string& dependency)
101 {
102 // dependencies only exist in target namespace
103 if (dependency.substr(0,4)=="tns:")
104 dependencies[type].insert(dependency.substr(4));
105 }
106
110 void openType(const string& type, const string& description)
111 {
112 typeBeingaddedTo.push_back
113 (TypeBeingAddedTo(type, description, xsdDefs.count(type)>0));
114 if (!typeBeingaddedTo.back().complete)
115 xsdDefs[type]=" <xs:complexType name=\""+type+"\">\n";
116 }
117
119 {
120 if (!typeBeingaddedTo.empty() && !typeBeingaddedTo.back().complete)
121 {
122 // allow schema to be extensible - either for polymorphic
123 // reasons, or for future extensibility
124 xsdDefs[typeBeingaddedTo.back().name]+=
125 " <xs:any minOccurs=\"0\" "
126 "maxOccurs=\"unbounded\" processContents=\"lax\"/>\n";
127 if (typeBeingaddedTo.back().sequenceAdded)
128 xsdDefs[typeBeingaddedTo.back().name]+=" </xs:sequence>\n";
129 if (!typeBeingaddedTo.back().baseClass.empty())
130 xsdDefs[typeBeingaddedTo.back().name]+=" </xs:extension>\n"
131 " </xs:complexContent>\n";
132 xsdDefs[typeBeingaddedTo.back().name]+=" </xs:complexType>\n";
133 }
134 typeBeingaddedTo.pop_back();
135 }
136
137 string currentDescription() const
138 {
139 if (!typeBeingaddedTo.empty())
140 return typeBeingaddedTo.back().description;
141 else
142 return "";
143 }
144
146 void defineType(const string& type, const string& def)
147 {
148 if (xsdDefs.count(type)==0)
149 xsdDefs[type]=def;
150 }
151
155 void output(std::ostream& o, const string& targetNS)
156 {
157 written.clear();
158 o<<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
159 o<<"<xs:schema targetNamespace=\"";
160 o<<targetNS;
161 o<<"\"\n xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n";
162 o<<" xmlns:tns=\""<<targetNS<<"\"\n";
163 o<<" elementFormDefault=\"qualified\" attributeFormDefault=\"unqualified\">\n";
164 o<<"\n";
165 for (std::map<string, string>::const_iterator i=xsdDefs.begin();
166 i!=xsdDefs.end(); ++i)
167 outputType(o, i->first);
168 o<<"\n";
169 o<<" <xs:element name=\""<<rootName<<"\" type=\""<<rootType<<"\"/>\n";
170 o<<"</xs:schema>\n";
171 }
172 };
173
174 template <class T> struct UnknownSchema: public exception
175 {
176 string msg;
177 UnknownSchema(): msg("unknown schema for "+typeName<T>()) {}
178 ~UnknownSchema() throw() {}
179 const char* what() const throw()
180 {return msg.c_str();}
181 };
182
183}
184
185namespace classdesc_access
186{
187 template <class T> struct access_xsd_generate
188 {
189 // by default, throw an exception
190 void operator()(classdesc::xsd_generate_t&,const classdesc::string&,T&)
192 };
193}
194
195
196namespace classdesc
197{
198 // replace all occurances of non-acceptable characters with _
199 inline string transformTypeName(string x)
200 {
201 for (string::size_type i=0; i<x.length(); ++i)
202 if (!isalnum(x[i]))
203 x[i]='_';
204 return x;
205 }
206
207 template <class T> string xsd_typeName()
208 {return "tns:"+transformTypeName(typeName<T>());}
209
210 template <class T>
211 struct EverythingElse: public
212 Not<
213 Or<
214 Or<is_fundamental<T>,is_container<T> >,
215 is_enum<T>
216 >
217 >{};
218
219 template <class T>
220 typename enable_if<EverythingElse<T>, void>::T
221 xsd_generate(xsd_generate_t& g, const string& d, const T& a)
222 {
223 // if this is a toplevel name, record it as the root element
224 if (!d.empty() && d.find('.')==string::npos)
225 {
226 g.rootName=d;
227 g.rootType=xsd_typeName<T>();
228 }
229
230 if (g.currentDescription()==d) // this is a base class, not a member
231 g.addBase(xsd_typeName<T>());
232 else
233 g.addMember(tail(d),xsd_typeName<T>());
234
235 g.openType(transformTypeName(typeName<T>()), d);
236 classdesc_access::access_xsd_generate<T>()(g,d,const_cast<T&>(a));
237 g.closeType();
238 }
239
240 template <class T>
241 typename enable_if<EverythingElse<T>, void>::T
242 processExtraClass(xsd_generate_t& g, const string& d, const T& a);
243
244 template <class T>
245 typename enable_if<Not<EverythingElse<T> >, void>::T
246 processExtraClass(xsd_generate_t& g, const string& d, const T& a)
247 {xsd_generate(g,"",a);}
248
249 template <>
250 inline void processExtraClass(xsd_generate_t& g, const string& d, const std::string& a)
251 {}
252
253 template <class T, class U>
254 void processExtraClass(xsd_generate_t& g, const string& d, const std::pair<T,U>& a)
255 {xsd_generate(g,"",a);}
256
257 template <> inline string xsd_typeName<bool>() {return "xs:boolean";}
258 template <> inline string xsd_typeName<char>() {return "xs:string";}
259 template <> inline string xsd_typeName<signed char>() {return "xs:string";}
260 template <> inline string xsd_typeName<short>() {return "xs:short";}
261 template <> inline string xsd_typeName<int>() {return "xs:int";}
262 template <> inline string xsd_typeName<long>() {return "xs:long";}
263 template <> inline string xsd_typeName<unsigned char>() {return "xs:string";}
264 template <> inline string xsd_typeName<unsigned short>() {return "xs:unsignedShort";}
265 template <> inline string xsd_typeName<unsigned int>() {return "xs:unsignedInt";}
266 template <> inline string xsd_typeName<unsigned long>() {return "xs:unsignedLong";}
267
268#ifdef HAVE_LONGLONG
269 template <> inline string xsd_typeName<long long>() {return "xs:long";}
270 template <> inline string xsd_typeName<unsigned long long>() {return "xs:unsignedLong";}
271#endif
272
273 template <> inline string xsd_typeName<float>() {return "xs:float";}
274 template <> inline string xsd_typeName<double>() {return "xs:double";}
275 // long double is not explicitly supported in XSD, so overflows may result
276 template <> inline string xsd_typeName<long double>() {return "xs:double";}
277 template <> inline string xsd_typeName<string>() {return "xs:string";}
278 template <> inline string xsd_typeName<std::wstring>() {return "xs:string";}
279
280 template <class T>
281 typename enable_if<is_fundamental<T>, void>::T
282 xsd_generate(xsd_generate_t& g, const string& d, const T& a)
283 {g.addMember(tail(d),xsd_typeName<T>());}
284
285 template <class T>
286 void xsd_generate(xsd_generate_t& g, const string& d, const std::basic_string<T>& a)
287 {g.addMember(tail(d),"xs:string");}
288
290 template <class T>
291 void xsd_generate
292 (xsd_generate_t& g, const string& d, is_array ia, T& a,
293 int dims, size_t ncopies, ...)
294 {
295 std::ostringstream type;
296 type << "__builtin_array_"+transformTypeName(typeName<T>())<<"_"<<ncopies;
297 va_list ap;
298 va_start(ap,ncopies);
299 for (int i=1; i<dims; i++)
300 {
301 //assume that 2 and higher D arrays dimensions are int
302 int dim=va_arg(ap,int);
303 ncopies*=dim;
304 type<<"_"<<dim;
305 }
306 va_end(ap);
307
308 std::ostringstream os;
309 os<<" <xs:complexType name=\""+type.str()+"\">\n";
310 os<<" <xs:sequence minOccurs=\""<<ncopies<<
311 "\" maxOccurs=\""<<ncopies<<"\">\n";
312 os<<" <xs:element name=\""<<typeName<T>()<<"\" type=\""<<
313 xsd_typeName<T>()<<"\"/>\n";
314 os<<" </xs:sequence>\n";
315 os<<" </xs:complexType>\n";
316
317 g.defineType(type.str(), os.str());
318 g.addMember(tail(d), "tns:"+type.str());
319 g.addDependency(type.str(), xsd_typeName<T>());
320 }
321
323 template <class E>
324 typename enable_if<is_enum<E>, void>:: T
325 xsd_generate(xsd_generate_t& g, const string& d, const E& arg)
326 {
327 // const_cast OK here, xsd_generate is a read-only operation
328 xsd_generate(g, d, Enum_handle<E>(const_cast<E&>(arg)));
329 }
330
331
332 template <class E>
333 void xsd_generate(xsd_generate_t& g, const string& d, const Enum_handle<E>& e)
334 {
335 string type=transformTypeName(typeName<E>());
336 std::ostringstream os;
337 os << " <xs:simpleType name=\""<<type<<"\">\n";
338 os << " <xs:restriction base=\"xs:string\">\n";
339 for (typename EnumKeys<E>::iterator i=enum_keysData<E>::keys.begin();
340 i!=enum_keysData<E>::keys.end(); ++i)
341 os << " <xs:enumeration value=\""<<i->second<<"\"/>\n";
342 os << " </xs:restriction>\n";
343 os << " </xs:simpleType>\n";
344 g.defineType(type, os.str());
345 g.addMember(tail(d), xsd_typeName<E>());
346 }
347
348 // container handling
349 template <class T>
350 typename enable_if<is_container<T>, void>::T
351 xsd_generate(xsd_generate_t& g, const string& d, const T& e)
352 {
353 std::ostringstream os;
354 // element name is given by the type name
355 string eName=typeName<typename T::value_type>().c_str();
356 eName=eName.substr(0,eName.find('<')); //trim off any template args
357 // strip leading namespace and qualifiers
358 const char *el=eName.c_str()+eName.length();
359 while (el!=eName.c_str() && *(el-1)!=' ' && *(el-1)!=':') el--;
360
361 string type=transformTypeName(typeName<T>());
362 os << " <xs:complexType name=\"" << type << "\">\n";
363 os << " <xs:sequence minOccurs=\"0\" maxOccurs=\"unbounded\">\n";
364 os << " <xs:element name=\""<<el<<
365 "\" type=\""<<xsd_typeName<typename T::value_type>()<<"\"/>\n";
366 os << " </xs:sequence>\n";
367 os << " </xs:complexType>\n";
368 g.addMember(tail(d), xsd_typeName<T>());
369 g.defineType(type, os.str());
370 g.addDependency(type, xsd_typeName<typename T::value_type>());
371 // ensure that the value type as a definition also
372 processExtraClass(g, d+transformTypeName(typeName<typename T::value_type>()), typename T::value_type());
373 }
374
375#if defined(__cplusplus) && __cplusplus>=201103L
376 template <class T, std::size_t N>
377 void xsd_generate(xsd_generate_t& g, const string& d, const std::array<T,N>& e)
378 {
379 std::ostringstream os;
380 // element name is given by the type name
381 string eName=typeName<T>().c_str();
382 eName=eName.substr(0,eName.find('<')); //trim off any template args
383 // strip leading namespace and qualifiers
384 const char *el=eName.c_str()+eName.length();
385 while (el!=eName.c_str() && *(el-1)!=' ' && *(el-1)!=':') el--;
386
387 string type=transformTypeName(typeName<std::array<T,N>>());
388 os << " <xs:complexType name=\"" << type << "\">\n";
389 os << " <xs:sequence minOccurs=\""<<N<<"\" maxOccurs=\""<<N<<"\">\n";
390 os << " <xs:element name=\""<<el<<
391 "\" type=\""<<xsd_typeName<T>()<<"\"/>\n";
392 os << " </xs:sequence>\n";
393 os << " </xs:complexType>\n";
394 g.addMember(tail(d), xsd_typeName<std::array<T,N>>());
395 g.defineType(type, os.str());
396 g.addDependency(type, xsd_typeName<T>());
397 // ensure that the value type as a definition also
398 processExtraClass(g, d+transformTypeName(typeName<T>()), T());
399 }
400#endif
401
402 // support for maps
403 template <class T, class U>
404 void xsd_generate(xsd_generate_t& g, const string& d, const std::pair<T,U>& a)
405 {
406 g.openType(transformTypeName(typeName<std::pair<T,U> >()), d);
407 xsd_generate(g,d+".first",a.first);
408 xsd_generate(g,d+".second",a.second);
409 g.closeType();
410 }
411
412 template <class T>
413 void xsd_generate(xsd_generate_t& g, const string& d, const Exclude<T>& a) {}
414
415 template <class T>
416 void//typename enable_if<Not<is_pointer<T> >,void>::T
417 xsd_generate(xsd_generate_t& g, const string& d, is_const_static, T a) {}
418
419 // with shared_ptrs, just write out the schema for the base
420 // class. Additional data may included in the XML file, but in
421 // general, XML does not support polymorphism, so this won't really
422 // work anyway.
423 template <class T>
424 void xsd_generate(xsd_generate_t& g, const string& d, const shared_ptr<T>& a)
425 {
426 xsd_generate_t::Optional o(g,true);
427 xsd_generate(g,d,*a);
428 }
429
430 template <class T>
431 void xsd_generate_onbase(xsd_generate_t& g, const string& d, T a)
432 {xsd_generate(g,d+basename<T>(),a);}
433
434 template <class T>
435 typename enable_if<EverythingElse<T>, void>::T
436 processExtraClass(xsd_generate_t& g, const string& d, const T& a)
437 {
438 g.openType(transformTypeName(typeName<T>()), d);
439 classdesc_access::access_xsd_generate<T>()(g,d,const_cast<T&>(a));
440 g.closeType();
441 }
442
443}
444
445#include "use_mbr_pointers.h"
446CLASSDESC_USE_OLDSTYLE_MEMBER_OBJECTS(xsd_generate)
447CLASSDESC_FUNCTION_NOP(xsd_generate)
448
449using classdesc::xsd_generate;
450using classdesc::xsd_generate_onbase;
451
452#endif
Definition classdesc.h:868
Definition classdesc.h:920
Definition classdesc.h:923
Definition xsd_generate_base.h:22
bool optional
set to true if next addMember refers to an optional element
Definition xsd_generate_base.h:52
void addMember(const string &name, const string &memberType)
add an attribute name with XSD type memberType
Definition xsd_generate_base.h:66
void defineType(const string &type, const string &def)
add a complete XSD definition for type
Definition xsd_generate_base.h:146
void addDependency(const string &type, const string &dependency)
add a dependency between type and dependency (XSD qualifed name)
Definition xsd_generate_base.h:100
void openType(const string &type, const string &description)
Definition xsd_generate_base.h:110
void addBase(const string &base)
add a base class to the current definition
Definition xsd_generate_base.h:82
void closeType()
complete type definition - matching last nested openType
Definition xsd_generate_base.h:118
string rootName
name of the root element, and its XSD type, in the Schema
Definition xsd_generate_base.h:50
void output(std::ostream &o, const string &targetNS)
Definition xsd_generate_base.h:155
Contains access_* structs, and nothing else. These structs are used to gain access to private members...
Definition classdesc_access.h:20
Contains definitions related to classdesc functionality.
string basename()
returns a valid identifier to append to the descriptor of a base class
Definition classdesc.h:1127
Definition xsd_generate_base.h:217
Definition classdesc.h:1012
Definition classdesc.h:405
Definition xsd_generate_base.h:175
controlled template specialisation: stolen from boost::enable_if.
Definition classdesc.h:282
base class for exceptions thrown by classdesc
Definition classdesc.h:546
RAII helper class to set optional to opt for current scope.
Definition xsd_generate_base.h:56
Definition xsd_generate_base.h:188