3 class Error(Exception):
4 def __init__(self, msg):
5 Exception.__init__(self)
8 def getMember(json, name, validTypes, description, default=None):
11 if len(validTypes) and type(member) not in validTypes:
12 raise Error("%s: type mismatch for '%s' member"
13 % (description, name))
17 def mustGetMember(json, name, expectedType, description):
18 member = getMember(json, name, expectedType, description)
20 raise Error("%s: missing '%s' member" % (description, name))
24 def __init__(self, name, comment, tables):
26 self.comment = comment
31 name = mustGetMember(json, 'name', [unicode], 'database')
32 comment = getMember(json, 'comment', [unicode], 'database')
33 tablesJson = mustGetMember(json, 'tables', [dict], 'database')
35 for tableName, tableJson in tablesJson.iteritems():
36 tables[tableName] = TableSchema.fromJson(tableJson,
37 "%s table" % tableName)
38 return DbSchema(name, comment, tables)
40 class IdlSchema(DbSchema):
41 def __init__(self, name, comment, tables, idlPrefix, idlHeader):
42 DbSchema.__init__(self, name, comment, tables)
43 self.idlPrefix = idlPrefix
44 self.idlHeader = idlHeader
48 schema = DbSchema.fromJson(json)
49 idlPrefix = mustGetMember(json, 'idlPrefix', [unicode], 'database')
50 idlHeader = mustGetMember(json, 'idlHeader', [unicode], 'database')
51 return IdlSchema(schema.name, schema.comment, schema.tables,
55 def __init__(self, comment, columns):
56 self.comment = comment
57 self.columns = columns
60 def fromJson(json, description):
61 comment = getMember(json, 'comment', [unicode], description)
62 columnsJson = mustGetMember(json, 'columns', [dict], description)
64 for name, json in columnsJson.iteritems():
65 columns[name] = ColumnSchema.fromJson(
66 json, "column %s in %s" % (name, description))
67 return TableSchema(comment, columns)
70 def __init__(self, comment, type, persistent):
71 self.comment = comment
73 self.persistent = persistent
76 def fromJson(json, description):
77 comment = getMember(json, 'comment', [unicode], description)
78 type = Type.fromJson(mustGetMember(json, 'type', [dict, unicode],
80 'type of %s' % description)
81 ephemeral = getMember(json, 'ephemeral', [bool], description)
82 persistent = ephemeral != True
83 return ColumnSchema(comment, type, persistent)
85 def escapeCString(src):
106 dst += '\\%03o' % ord(c)
111 def returnUnchanged(x):
116 uuidRE = re.compile("^(%s{8})-(%s{4})-(%s{4})-(%s{4})-(%s{4})(%s{8})$"
117 % (x, x, x, x, x, x))
119 def __init__(self, value):
124 if not uuidRE.match(s):
125 raise Error("%s is not a valid UUID" % s)
130 if UUID.isValidJson(json):
133 raise Error("%s is not valid JSON for a UUID" % json)
136 def isValidJson(json):
137 return len(json) == 2 and json[0] == "uuid" and uuidRE.match(json[1])
140 return ["uuid", self.value]
142 def cInitUUID(self, var):
143 m = re.match(self.value)
144 return ["%s.parts[0] = 0x%s;" % (var, m.group(1)),
145 "%s.parts[1] = 0x%s%s;" % (var, m.group(2), m.group(3)),
146 "%s.parts[2] = 0x%s%s;" % (var, m.group(4), m.group(5)),
147 "%s.parts[3] = 0x%s;" % (var, m.group(6))]
150 def __init__(self, type, value):
155 def fromJson(type_, json):
156 if ((type_ == 'integer' and type(json) in [int, long])
157 or (type_ == 'real' and type(json) in [int, long, float])
158 or (type_ == 'boolean' and json in [True, False])
159 or (type_ == 'string' and type(json) in [str, unicode])):
160 return Atom(type_, json)
161 elif type_ == 'uuid':
162 return UUID.fromJson(json)
164 raise Error("%s is not valid JSON for type %s" % (json, type_))
167 if self.type == 'uuid':
168 return self.value.toString()
172 def cInitAtom(self, var):
173 if self.type == 'integer':
174 return ['%s.integer = %d;' % (var, self.value)]
175 elif self.type == 'real':
176 return ['%s.real = %.15g;' % (var, self.value)]
177 elif self.type == 'boolean':
179 return ['%s.boolean = true;']
181 return ['%s.boolean = false;']
182 elif self.type == 'string':
183 return ['%s.string = xstrdup("%s");'
184 % (var, escapeCString(self.value))]
185 elif self.type == 'uuid':
186 return self.value.cInitUUID(var)
188 def toEnglish(self, escapeLiteral=returnUnchanged):
189 if self.type == 'integer':
190 return '%d' % self.value
191 elif self.type == 'real':
192 return '%.15g' % self.value
193 elif self.type == 'boolean':
198 elif self.type == 'string':
199 return escapeLiteral(self.value)
200 elif self.type == 'uuid':
201 return self.value.value
204 def __init__(self, type,
207 minInteger=None, maxInteger=None,
208 minReal=None, maxReal=None,
209 minLength=None, maxLength=None):
212 self.refTable = refTable
213 self.minInteger = minInteger
214 self.maxInteger = maxInteger
215 self.minReal = minReal
216 self.maxReal = maxReal
217 self.minLength = minLength
218 self.maxLength = maxLength
221 def fromJson(json, description):
222 if type(json) == unicode:
223 return BaseType(json)
225 atomicType = mustGetMember(json, 'type', [unicode], description)
226 enum = getMember(json, 'enum', [], description)
228 enumType = Type(atomicType, None, 0, 'unlimited')
229 enum = Datum.fromJson(enumType, enum)
230 refTable = getMember(json, 'refTable', [unicode], description)
231 minInteger = getMember(json, 'minInteger', [int, long], description)
232 maxInteger = getMember(json, 'maxInteger', [int, long], description)
233 minReal = getMember(json, 'minReal', [int, long, float], description)
234 maxReal = getMember(json, 'maxReal', [int, long, float], description)
235 minLength = getMember(json, 'minLength', [int], description)
236 maxLength = getMember(json, 'minLength', [int], description)
237 return BaseType(atomicType, enum, refTable, minInteger, maxInteger, minReal, maxReal, minLength, maxLength)
239 def toEnglish(self, escapeLiteral=returnUnchanged):
240 if self.type == 'uuid' and self.refTable:
241 return escapeLiteral(self.refTable)
245 def constraintsToEnglish(self, escapeLiteral=returnUnchanged):
247 literals = [value.toEnglish(escapeLiteral)
248 for value in self.enum.values]
249 if len(literals) == 2:
250 return 'either %s or %s' % (literals[0], literals[1])
252 return 'one of %s, %s, or %s' % (literals[0],
253 ', '.join(literals[1:-1]),
255 elif self.minInteger != None and self.maxInteger != None:
256 return 'in range [%d,%d]' % (self.minInteger, self.maxInteger)
257 elif self.minInteger != None:
258 return 'at least %d' % self.minInteger
259 elif self.maxInteger != None:
260 return 'at most %d' % self.maxInteger
261 elif self.minReal != None and self.maxReal != None:
262 return 'in range [%g, %g]' % (self.minReal, self.maxReal)
263 elif self.minReal != None:
264 return 'at least %g' % self.minReal
265 elif self.maxReal != None:
266 return 'at most %g' % self.maxReal
267 elif self.minLength != None and self.maxLength != None:
268 if self.minLength == self.maxLength:
269 return 'exactly %d characters long' % (self.minLength)
271 return 'between %d and %d characters long' % (self.minLength, self.maxLength)
272 elif self.minLength != None:
273 return 'at least %d characters long' % self.minLength
274 elif self.maxLength != None:
275 return 'at most %d characters long' % self.maxLength
279 def toCType(self, prefix):
281 return "struct %s%s *" % (prefix, self.refTable.lower())
283 return {'integer': 'int64_t ',
285 'uuid': 'struct uuid ',
287 'string': 'char *'}[self.type]
289 def copyCValue(self, dst, src):
290 args = {'dst': dst, 'src': src}
292 return ("%(dst)s = %(src)s->header_.uuid;") % args
293 elif self.type == 'string':
294 return "%(dst)s = xstrdup(%(src)s);" % args
296 return "%(dst)s = %(src)s;" % args
298 def initCDefault(self, var, isOptional):
300 return "%s = NULL;" % var
301 elif self.type == 'string' and not isOptional:
302 return "%s = \"\";" % var
304 return {'integer': '%s = 0;',
306 'uuid': 'uuid_zero(&%s);',
307 'boolean': '%s = false;',
308 'string': '%s = NULL;'}[self.type] % var
310 def cInitBaseType(self, indent, var):
312 stmts.append('ovsdb_base_type_init(&%s, OVSDB_TYPE_%s);' % (
313 var, self.type.upper()),)
315 stmts.append("%s.enum_ = xmalloc(sizeof *%s.enum_);"
317 stmts += self.enum.cInitDatum("%s.enum_" % var)
318 if self.type == 'integer':
319 if self.minInteger != None:
320 stmts.append('%s.u.integer.min = %d;' % (var, self.minInteger))
321 if self.maxInteger != None:
322 stmts.append('%s.u.integer.max = %d;' % (var, self.maxInteger))
323 elif self.type == 'real':
324 if self.minReal != None:
325 stmts.append('%s.u.real.min = %d;' % (var, self.minReal))
326 if self.maxReal != None:
327 stmts.append('%s.u.real.max = %d;' % (var, self.maxReal))
328 elif self.type == 'string':
329 if self.minLength != None:
330 stmts.append('%s.u.string.minLen = %d;' % (var, self.minLength))
331 if self.maxLength != None:
332 stmts.append('%s.u.string.maxLen = %d;' % (var, self.maxLength))
333 elif self.type == 'uuid':
334 if self.refTable != None:
335 stmts.append('%s.u.uuid.refTableName = "%s";' % (var, escapeCString(self.refTable)))
336 return '\n'.join([indent + stmt for stmt in stmts])
339 def __init__(self, key, value=None, min=1, max=1):
346 def fromJson(json, description):
347 if type(json) == unicode:
348 return Type(BaseType(json))
350 keyJson = mustGetMember(json, 'key', [dict, unicode], description)
351 key = BaseType.fromJson(keyJson, 'key in %s' % description)
353 valueJson = getMember(json, 'value', [dict, unicode], description)
355 value = BaseType.fromJson(valueJson,
356 'value in %s' % description)
360 min = getMember(json, 'min', [int], description, 1)
361 max = getMember(json, 'max', [int, unicode], description, 1)
362 return Type(key, value, min, max)
365 return self.min == 1 and self.max == 1 and not self.value
367 def isOptional(self):
368 return self.min == 0 and self.max == 1
370 def isOptionalPointer(self):
371 return (self.min == 0 and self.max == 1 and not self.value
372 and (self.key.type == 'string' or self.key.refTable))
374 def toEnglish(self, escapeLiteral=returnUnchanged):
375 keyName = self.key.toEnglish(escapeLiteral)
377 valueName = self.value.toEnglish(escapeLiteral)
381 elif self.isOptional():
383 return "optional %s-%s pair" % (keyName, valueName)
385 return "optional %s" % keyName
387 if self.max == "unlimited":
389 quantity = "%d or more " % self.min
393 quantity = "%d to %d " % (self.min, self.max)
395 quantity = "up to %d " % self.max
398 return "map of %s%s-%s pairs" % (quantity, keyName, valueName)
400 if keyName.endswith('s'):
401 plural = keyName + "es"
403 plural = keyName + "s"
404 return "set of %s%s" % (quantity, plural)
406 def constraintsToEnglish(self, escapeLiteral=returnUnchanged):
410 keyConstraints = self.key.constraintsToEnglish(escapeLiteral)
413 constraints += ['key ' + keyConstraints]
415 constraints += [keyConstraints]
418 valueConstraints = self.value.constraintsToEnglish(escapeLiteral)
420 constraints += ['value ' + valueConstraints]
422 return ', '.join(constraints)
424 def cDeclComment(self):
425 if self.min == 1 and self.max == 1 and self.key.type == "string":
426 return "\t/* Always nonnull. */"
430 def cInitType(self, indent, var):
431 initKey = self.key.cInitBaseType(indent, "%s.key" % var)
433 initValue = self.value.cInitBaseType(indent, "%s.value" % var)
435 initValue = ('%sovsdb_base_type_init(&%s.value, '
436 'OVSDB_TYPE_VOID);' % (indent, var))
437 initMin = "%s%s.n_min = %s;" % (indent, var, self.min)
438 if self.max == "unlimited":
442 initMax = "%s%s.n_max = %s;" % (indent, var, max)
443 return "\n".join((initKey, initValue, initMin, initMax))
446 def __init__(self, type, values):
451 def fromJson(type_, json):
453 if len(json) == 2 and json[0] == "set":
455 for atomJson in json[1]:
456 values += [Atom.fromJson(type_.key, atomJson)]
458 values = [Atom.fromJson(type_.key, json)]
460 if len(json) != 2 or json[0] != "map":
461 raise Error("%s is not valid JSON for a map" % json)
463 for pairJson in json[1]:
464 values += [(Atom.fromJson(type_.key, pairJson[0]),
465 Atom.fromJson(type_.value, pairJson[1]))]
466 return Datum(type_, values)
468 def cInitDatum(self, var):
469 if len(self.values) == 0:
470 return ["ovsdb_datum_init_empty(%s);" % var]
472 s = ["%s->n = %d;" % (var, len(self.values))]
473 s += ["%s->keys = xmalloc(%d * sizeof *%s->keys);"
474 % (var, len(self.values), var)]
476 for i in range(len(self.values)):
480 s += key.cInitAtom("%s->keys[%d]" % (var, i))
483 s += ["%s->values = xmalloc(%d * sizeof *%s->values);"
484 % (var, len(self.values), var)]
485 for i in range(len(self.values)):
486 value = self.values[i][1]
487 s += key.cInitAtom("%s->values[%d]" % (var, i))
489 s += ["%s->values = NULL;" % var]
491 if len(self.values) > 1:
492 s += ["ovsdb_datum_sort_assert(%s, OVSDB_TYPE_%s);"
493 % (var, self.type.key.upper())]