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