1 """
2 Writing annotations in RDs.
3
4 This module provides the glue between annotations (typically in SIL)
5 and the rest of the RDs. It provides the ResAnnotation struct, which
6 contains the SIL, and the makeAttributeAnnotation function at is a factory
7 for attribute annotations.
8 """
9
10
11
12
13
14
15
16 import functools
17 import itertools
18
19 from gavo import base
20 from gavo.dm import annotations
21 from gavo.dm import common
22 from gavo.dm import sil
23
24
26 """DM annotation copied and adapted to a new table.
27
28 This is a stand-in for DataModelRoles in tables not parsed from
29 XMLs. Their DM structure is defined through references the columns and
30 params make to the annotations their originals had.
31
32 These have no attributes but just arrange for the new annotations
33 to be generated.
34 """
35 name_ = "_synthesizedRoles"
36
38
39
40
41 if self.parent.annotations:
42 return
43
44 annotationMap = {}
45
46
47
48
49 for item in itertools.chain(self.parent.params, self.parent.columns):
50 if item.parent!=self.parent:
51
52
53
54
55 continue
56
57 if isinstance(item.dmRoles, list):
58
59
60 continue
61
62 for annotation in item.dmRoles.oldRoles:
63 annotationMap[annotation()] = item
64 item.dmRoles = []
65
66
67 oldInstances = set(ann.instance() for ann in annotationMap)
68
69
70
71 newInstances = []
72 for oldInstance in oldInstances:
73 newInstances.append(
74 oldInstance.copyWithAnnotationMap(
75 annotationMap, self.parent, None))
76 self.parent.annotations = newInstances
77
81
82
84 """an annotation of a table in terms of data models.
85
86 The content of this element is a Simple Instance Language clause.
87 """
88
89
90
91
92
93
94
95
96
97
98 name_ = "dm"
99
100 _sil = base.DataContent(description="SIL (simple instance language)"
101 " annotation.", copyable=True)
102
105
116 self._buildAnnotation = _buildAnnotation
117
118 ctx.addExitFunc(lambda rd, ctx: self._buildAnnotation())
119 self._completeElementNext(DataModelRoles, ctx)
120
122 """returns a parsed version of the embedded annotation.
123
124 Do not call this while the RD is still being built, as dm
125 elements may contain forward references, and these might
126 not yet be available during the parse.
127 """
128 self._buildAnnotation()
129 return self._parsedAnnotation
130
131 - def copy(self, newParent, ctx):
136
137
139 """an attribute allowing data model annotation using SIL.
140
141 It will also give an annotations attribute on the instance, and a
142 getAnnotationsOfType method letting you pull out a specific annotation.
143
144 For situation where an existing annotation should be copied from
145 annotations coming in through columns and/or params, the attribute
146 also gives an updateAnnotationFromChildren method. It will do
147 nothing if some annotation already exists.
148 """
155
157 def iterAnnotationsOfType(instance, typeName):
158 """returns the first annotation of the type passed.
159 """
160 for ann in instance.annotations:
161 if ann.type==typeName:
162 yield ann
163
164 yield ("iterAnnotationsOfType", iterAnnotationsOfType)
165
166 def updateAnnotationFromChildren(instance):
167 roleSynthesizer = SynthesizedRoles(instance)
168 roleSynthesizer.synthesizeAnnotations(None, None)
169 yield ("updateAnnotationFromChildren", updateAnnotationFromChildren)
170
171
173 """returns a typed annotation for attValue within container.
174
175 When attValue is a literal, this is largely trivial. If it's a reference,
176 this figures out what it points to and creates an annotation of
177 the appropriate type (e.g., ColumnAnnotation, ParamAnnotation, etc).
178
179 container in current DaCHS should be a TableDef or something similar;
180 this function expects at least a getByName function and an rd attribute.
181
182 instance is the root of the current annotation. Complex objects should
183 keep a (weak) reference to that. We don't have parent links in
184 our dm trees, and without a reference to the root there's no
185 way we can go "up".
186
187 This is usually used as a callback from within sil.getAnnotation and
188 expects Atom and Reference instances as used there.
189 """
190 if isinstance(attValue, sil.Atom):
191 return common.AtomicAnnotation(attName, attValue, instance=instance)
192
193 elif isinstance(attValue, sil.Reference):
194
195
196 try:
197 res = container.getByName(attValue)
198 except base.NotFoundError:
199 if container.rd:
200 res = base.resolveId(container.rd, attValue, instance=container)
201 else:
202 raise
203
204 if not hasattr(res, "getAnnotation"):
205 raise base.StructureError("Element %s cannot be referenced"
206 " within a data model."%repr(res))
207
208 return res.getAnnotation(attName, container, instance)
209
210 else:
211 assert False
212
213
215 """wraps makeAttributeAnnotationMaker such that names are resolved
216 within container.
217 """
218 return functools.partial(makeAttributeAnnotation, container)
219