1 """
2 Parsing VO-DML files and validating against the rules obtained in this way.
3
4 Validation is something we expect to do only fairly rarely, so none of
5 this code is expected to be efficient.
6 """
7
8
9
10
11
12
13
14 from gavo import base
15 from gavo import utils
16 from gavo.utils import ElementTree
17 from gavo.votable import V
18
19
20 KNOWN_MODELS = {
21
22
23 "NDcube": ("CubeDM-1.0.vo-dml.xml",
24 "http://www.ivoa.net/dm/CubeDM-1.0.vo-dml.xml"),
25 "ds": ("DatasetMetadata-1.0.vo-dml.xml",
26 "http://www.ivoa.net/dm/DatasetMetadata-1.0.vo-dml.xml"),
27 "ivoa": ("IVOA.vo-dml.xml", "http://www.ivoa.net/dm/ivoa.vo-dml.xml"),
28 "vo-dml": ("VO-DML.vo-dml.xml", "http://www.ivoa.net/dm/VO-DML.vo-dml.xml"),
29 "dachstoy": ("dachstoy.vo-dml.xml", "http://docs.g-vo.org/dachstoy"),
30 "stc2": ("STC.vo-dml.xml", "http://www.ivoa.net/dm/STC.vo-dml.xml"),
31 "geojson": ("geojson.vo-dml.xml", "http://docs.g-vo.org/geojson"),
32 }
36 """returns an open file for the VO-DML file corresponding to prefix.
37
38 This will raise a NotFoundError for an unknown prefix.
39 """
40 try:
41 fName, _ = KNOWN_MODELS[prefix]
42 except KeyError:
43 raise base.NotFoundError(prefix, "VO-DML file for prefix",
44 "data models known to DaCHS", hint="This can happen if there"
45 " are new data models around or if data providers have defined"
46 " custom data models. If this error was fatal during VOTable"
47 " processing, please report it as an error; bad data model"
48 " annotation should not be fatal in DaCHS.")
49 return base.openDistFile("dm/"+fName)
50
53 """a vo-dml model.
54
55 These are usually constructed using the fromPrefix constructor,
56 which uses a built-in mapping from well-known prefix to VO-DML file
57 to populate the model.
58 """
59
60
61
62
63
64 _modelsReadFromFile = {}
65
67 self.prefix = prefix
68 self.title = self.version = None
69 self.version = self.url = None
70 self.description = None
71 self.dmlTree = dmlTree
72 self.__idIndex = None
73 if self.dmlTree:
74 self._getModelMeta()
75
76 @classmethod
98
99 @classmethod
100 - def fromFile(cls, src, srcURL="http //not.given/invalid"):
101 """returns a VO-DML model from src.
102
103 src can either be a file name (interpreted relative to the root
104 of DaCHS' VO-DML repository) or an open file (which will be closed
105 as a side effect of this function).
106
107 This is intended for documents using non-standard models with custom
108 prefixes (i.e., not known to DaCHS).
109 """
110 if hasattr(src, "read"):
111 inF = src
112 else:
113 inF = openModelFile(src)
114
115 try:
116 tree = ElementTree.parse(inF)
117 prefix = tree.find("name").text
118 res = cls(prefix, tree)
119 res.url = srcURL
120
121 if prefix not in KNOWN_MODELS:
122 cls._modelsReadFromFile[prefix] = res
123 return res
124 finally:
125 inF.close()
126
127 @property
129 """returns a dictionary mapping vodmlids to elementtree objects.
130 """
131 if self.__idIndex is None:
132 self.__idIndex = self._createIndex()
133 return self.__idIndex
134
136 """returns a dictionary mapping vodml-ids to elementtree objects.
137
138 Use the idIndex property rather than this function, as the former will
139 cache the dicts.
140 """
141 res = {}
142 for element in self.dmlTree.getroot().iter():
143 id = element.find("vodml-id")
144 if id is not None:
145 res[id.text] = element
146 return res
147
164
166 """returns an etree Element pointed to by the VO-DML id
167
168 This is a helper for getByVODMLId and works by sucessively
169 trying shorter pieces of id.
170
171 This returns None on a failure rather than raising an exception
172 (because it's really a helper for getByVODMLId).
173 """
174 parts = id.split(".")
175 for splitPoint in range(len(parts)-1, 0, -1):
176 newId = ".".join(parts[:splitPoint])
177 if newId in self.idIndex:
178
179
180 att = self.idIndex[newId]
181 thisType = resolveVODMLId(
182 att.find("datatype").find("vodml-ref").text)
183
184 for attName in parts[splitPoint:]:
185 att = getAttributeDefinition(thisType, attName)
186 thisType = resolveVODMLId(
187 att.find("datatype").find("vodml-ref").text)
188 return att
189
190
192 """returns the element with vodmlId.
193
194 This raises a NotFoundError for elements that are not present.
195
196 This can be used with or without the prefix. The prefix is not
197 validated, though.
198 """
199 if ":" in vodmlId:
200 vodmlId = vodmlId.split(":", 1)[1]
201
202
203
204 if vodmlId in self.idIndex:
205 return self.idIndex[vodmlId]
206
207 res = self._resolveNonLocalVODMLId(vodmlId)
208 if res:
209 return res
210 else:
211 raise base.NotFoundError(vodmlId, "data model element",
212 self.prefix+" data model")
213
225
226 - def getVOT(self, ctx, instance):
227 """returns xmlstan for a VOTable declaration of this DM.
228 """
229 return V.MODEL[
230 V.NAME(version=self.version)[self.prefix],
231 V.URL[self.url]]
232
236 """returns a vodml.Model instance for as well-known VODML prefix.
237
238 This caches models for prefixes and thus should usually be used
239 from user code.
240
241 Note that this currently will currently return some stand-in shim
242 for unknown prefixes. That behaviour will change to become a
243 NotFoundError exception when there's actually useful data models.
244 """
245 try:
246 return Model.fromPrefix(prefix)
247 except base.NotFoundError:
248 res = Model(prefix, ElementTree.fromstring(
249 """<junk><title>DaCHS standin model</title>
250 <description>This is used by DaCHS during the old west
251 days of VO DM development. Any annotation using this will
252 not be interoperable.</description>
253 <version>invalid</version></junk>"""))
254 res.url = "urn:dachsjunk:not-model:"+prefix
255 return res
256
258 """returns the attribute definition for attName in typeDef as an etree.
259
260 This raises a NotFoundError if the attribute is not found.
261 """
262 for attribute in typeDef.findall("attribute"):
263 if attribute.find("name").text==attName:
264 return attribute
265 raise base.NotFoundError(attName, "Attribute",
266 "VO-DML type "+typeDef.find("name").text)
267
270 """returns an etree element corresponding to the prefixed vodmlId.
271
272 Of course, this only works if vodmlId has a well-known prefix.
273 """
274 prefix, id = vodmlId.split(":", 1)
275 return getModelForPrefix(prefix).getByVODMLId(id)
276