shacl2code 0.0.5__tar.gz → 0.0.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {shacl2code-0.0.5 → shacl2code-0.0.7}/PKG-INFO +4 -3
- {shacl2code-0.0.5 → shacl2code-0.0.7}/pyproject.toml +5 -4
- {shacl2code-0.0.5 → shacl2code-0.0.7}/src/shacl2code/context.py +60 -21
- {shacl2code-0.0.5 → shacl2code-0.0.7}/src/shacl2code/lang/common.py +28 -2
- shacl2code-0.0.7/src/shacl2code/lang/jsonschema.py +51 -0
- shacl2code-0.0.7/src/shacl2code/lang/python.py +53 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/src/shacl2code/lang/templates/jsonschema.j2 +111 -39
- shacl2code-0.0.7/src/shacl2code/lang/templates/python.j2 +1614 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/src/shacl2code/main.py +29 -14
- shacl2code-0.0.7/src/shacl2code/model.py +294 -0
- shacl2code-0.0.7/src/shacl2code/version.py +1 -0
- shacl2code-0.0.7/tests/conftest.py +63 -0
- shacl2code-0.0.7/tests/data/bad-node-kind.ttl +12 -0
- shacl2code-0.0.7/tests/data/bad-pattern-class.ttl +21 -0
- shacl2code-0.0.7/tests/data/bad-pattern-integer.ttl +21 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/tests/data/bad-reference.jsonld +0 -3
- shacl2code-0.0.7/tests/data/missing-range.ttl +18 -0
- shacl2code-0.0.7/tests/data/model/test-context.json +119 -0
- shacl2code-0.0.7/tests/data/model/test.ttl +398 -0
- shacl2code-0.0.7/tests/data/python/links.json +93 -0
- shacl2code-0.0.7/tests/data/python/roundtrip.json +75 -0
- shacl2code-0.0.7/tests/expect/jsonschema/test-context.json +794 -0
- shacl2code-0.0.7/tests/expect/jsonschema/test.json +790 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/tests/expect/make_expect.py +1 -1
- shacl2code-0.0.7/tests/expect/python/test-context.py +1957 -0
- shacl2code-0.0.7/tests/expect/python/test.py +1889 -0
- shacl2code-0.0.7/tests/expect/raw/test-context.txt +32 -0
- shacl2code-0.0.7/tests/expect/raw/test.txt +17 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/tests/test_common_jinja.py +7 -7
- shacl2code-0.0.7/tests/test_common_prefix.py +30 -0
- shacl2code-0.0.7/tests/test_context.py +144 -0
- shacl2code-0.0.7/tests/test_jsonschema.py +785 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/tests/test_model_source.py +125 -42
- shacl2code-0.0.7/tests/test_python.py +1327 -0
- shacl2code-0.0.5/conftest.py +0 -14
- shacl2code-0.0.5/src/shacl2code/lang/jsonschema.py +0 -30
- shacl2code-0.0.5/src/shacl2code/lang/python.py +0 -15
- shacl2code-0.0.5/src/shacl2code/lang/templates/python.j2 +0 -952
- shacl2code-0.0.5/src/shacl2code/model.py +0 -234
- shacl2code-0.0.5/src/shacl2code/version.py +0 -1
- shacl2code-0.0.5/tests/data/jsonschema/compact-array.json +0 -11
- shacl2code-0.0.5/tests/data/jsonschema/context-only.json +0 -3
- shacl2code-0.0.5/tests/data/jsonschema/graph-bad-context.json +0 -5
- shacl2code-0.0.5/tests/data/jsonschema/graph-empty.json +0 -6
- shacl2code-0.0.5/tests/data/jsonschema/graph-missing-context.json +0 -13
- shacl2code-0.0.5/tests/data/jsonschema/graph-unknown-field-nested.json +0 -15
- shacl2code-0.0.5/tests/data/jsonschema/graph-unknown-field.json +0 -15
- shacl2code-0.0.5/tests/data/jsonschema/graph-unknown-root-field.json +0 -15
- shacl2code-0.0.5/tests/data/jsonschema/graph.json +0 -14
- shacl2code-0.0.5/tests/data/jsonschema/root-bad-context.json +0 -10
- shacl2code-0.0.5/tests/data/jsonschema/root-missing-context.json +0 -9
- shacl2code-0.0.5/tests/data/jsonschema/root-unknown-field-nested.json +0 -11
- shacl2code-0.0.5/tests/data/jsonschema/root-unknown-field.json +0 -11
- shacl2code-0.0.5/tests/data/jsonschema/root-unknown-type.json +0 -19
- shacl2code-0.0.5/tests/data/jsonschema/root.json +0 -10
- shacl2code-0.0.5/tests/data/model/spdx3-context.json +0 -1
- shacl2code-0.0.5/tests/data/model/spdx3.jsonld +0 -7816
- shacl2code-0.0.5/tests/data/python/links.json +0 -40
- shacl2code-0.0.5/tests/data/python/roundtrip.json +0 -22
- shacl2code-0.0.5/tests/expect/jsonschema/spdx3-context.json +0 -3523
- shacl2code-0.0.5/tests/expect/jsonschema/spdx3.json +0 -3519
- shacl2code-0.0.5/tests/expect/python/spdx3-context.py +0 -5210
- shacl2code-0.0.5/tests/expect/python/spdx3.py +0 -4457
- shacl2code-0.0.5/tests/expect/raw/spdx3-context.txt +0 -156
- shacl2code-0.0.5/tests/expect/raw/spdx3.txt +0 -79
- shacl2code-0.0.5/tests/test_context.py +0 -72
- shacl2code-0.0.5/tests/test_jsonschema.py +0 -169
- shacl2code-0.0.5/tests/test_python.py +0 -357
- {shacl2code-0.0.5 → shacl2code-0.0.7}/.flake8 +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/.github/workflows/coverage-generate.yaml +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/.github/workflows/coverage-report.yaml +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/.github/workflows/publish.yaml +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/.github/workflows/test.yaml +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/.gitignore +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/LICENSE +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/README.md +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/src/shacl2code/__init__.py +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/src/shacl2code/__main__.py +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/src/shacl2code/lang/__init__.py +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/src/shacl2code/lang/jinja.py +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/src/shacl2code/lang/lang.py +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/src/shacl2code/urlcontext.py +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/tests/data/abort.j2 +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/tests/data/bad-id.j2 +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/tests/data/context-url.j2 +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/tests/data/context.j2 +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/tests/data/raw.j2 +0 -0
- {shacl2code-0.0.5 → shacl2code-0.0.7}/tests/test_cli.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: shacl2code
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.7
|
|
4
4
|
Summary: Convert SHACL model file to code bindings
|
|
5
5
|
Project-URL: Homepage, https://github.com/JPEWdev/shacl2code
|
|
6
6
|
Project-URL: Repository, https://github.com/JPEWdev/shacl2code.git
|
|
@@ -18,10 +18,11 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Topic :: Software Development :: Build Tools
|
|
20
20
|
Requires-Dist: jinja2>=3.1.2
|
|
21
|
-
Requires-Dist:
|
|
21
|
+
Requires-Dist: rdflib>=7.0.0
|
|
22
22
|
Provides-Extra: dev
|
|
23
23
|
Requires-Dist: flake8>=7.0.0; extra == 'dev'
|
|
24
24
|
Requires-Dist: jsonschema>=4.21.1; extra == 'dev'
|
|
25
|
+
Requires-Dist: pyshacl>=0.25.0; extra == 'dev'
|
|
25
26
|
Requires-Dist: pytest-cov>=4.1; extra == 'dev'
|
|
26
27
|
Requires-Dist: pytest-server-fixtures>=1.7; extra == 'dev'
|
|
27
28
|
Requires-Dist: pytest>=7.4; extra == 'dev'
|
|
@@ -4,7 +4,7 @@ description = "Convert SHACL model file to code bindings"
|
|
|
4
4
|
dynamic = ["version"]
|
|
5
5
|
dependencies = [
|
|
6
6
|
"jinja2 >= 3.1.2",
|
|
7
|
-
"
|
|
7
|
+
"rdflib >= 7.0.0",
|
|
8
8
|
]
|
|
9
9
|
required-python = ">= 3.8"
|
|
10
10
|
authors = [
|
|
@@ -26,11 +26,12 @@ classifiers = [
|
|
|
26
26
|
|
|
27
27
|
[project.optional-dependencies]
|
|
28
28
|
dev = [
|
|
29
|
-
"pytest >= 7.4",
|
|
30
|
-
"pytest-server-fixtures >= 1.7",
|
|
31
|
-
"pytest-cov >= 4.1",
|
|
32
29
|
"flake8 >= 7.0.0",
|
|
33
30
|
"jsonschema >= 4.21.1",
|
|
31
|
+
"pyshacl >= 0.25.0",
|
|
32
|
+
"pytest >= 7.4",
|
|
33
|
+
"pytest-cov >= 4.1",
|
|
34
|
+
"pytest-server-fixtures >= 1.7",
|
|
34
35
|
]
|
|
35
36
|
|
|
36
37
|
[project.urls]
|
|
@@ -40,30 +40,50 @@ class Context(object):
|
|
|
40
40
|
|
|
41
41
|
return contexts
|
|
42
42
|
|
|
43
|
-
def compact(self, _id
|
|
43
|
+
def compact(self, _id):
|
|
44
|
+
return self.__compact_contexts(_id)
|
|
45
|
+
|
|
46
|
+
def compact_vocab(self, _id, vocab=None):
|
|
44
47
|
with self.vocab_push(vocab):
|
|
45
48
|
if not self.__vocabs:
|
|
46
49
|
v = ""
|
|
47
50
|
else:
|
|
48
51
|
v = self.__vocabs[-1]
|
|
49
52
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
return self.__compact_contexts(_id, v, self.__get_vocab_contexts())
|
|
54
|
+
|
|
55
|
+
def __compact_contexts(self, _id, v="", apply_vocabs=False):
|
|
56
|
+
if v not in self.__compacted or _id not in self.__compacted[v]:
|
|
57
|
+
if apply_vocabs:
|
|
58
|
+
contexts = self.__get_vocab_contexts() + self.contexts
|
|
59
|
+
else:
|
|
60
|
+
contexts = self.contexts
|
|
61
|
+
|
|
62
|
+
self.__compacted.setdefault(v, {})[_id] = self.__compact(
|
|
63
|
+
_id,
|
|
64
|
+
contexts,
|
|
65
|
+
apply_vocabs,
|
|
66
|
+
)
|
|
67
|
+
return self.__compacted[v][_id]
|
|
68
|
+
|
|
69
|
+
def __compact(self, _id, contexts, apply_vocabs):
|
|
70
|
+
def remove_prefix(_id, value):
|
|
71
|
+
possible = set()
|
|
72
|
+
if _id.startswith(value):
|
|
73
|
+
tmp_id = _id[len(value) :]
|
|
74
|
+
possible.add(tmp_id)
|
|
75
|
+
possible |= collect_possible(tmp_id)
|
|
76
|
+
return possible
|
|
56
77
|
|
|
57
|
-
def __compact(self, _id, contexts):
|
|
58
78
|
def collect_possible(_id):
|
|
59
79
|
possible = set()
|
|
60
80
|
for ctx in contexts:
|
|
61
81
|
for name, value in ctx.items():
|
|
62
82
|
if name == "@vocab":
|
|
63
|
-
if
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
83
|
+
if apply_vocabs:
|
|
84
|
+
possible |= remove_prefix(_id, value)
|
|
85
|
+
elif name == "@base":
|
|
86
|
+
possible |= remove_prefix(_id, value)
|
|
67
87
|
else:
|
|
68
88
|
if isinstance(value, dict):
|
|
69
89
|
value = value["@id"]
|
|
@@ -90,14 +110,14 @@ class Context(object):
|
|
|
90
110
|
|
|
91
111
|
return possible[0]
|
|
92
112
|
|
|
93
|
-
def
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
else:
|
|
98
|
-
v = self.__vocabs[-1]
|
|
113
|
+
def is_relative(self, _id):
|
|
114
|
+
import re
|
|
115
|
+
|
|
116
|
+
return not re.match(r"[^:]+:", _id)
|
|
99
117
|
|
|
100
|
-
|
|
118
|
+
def __expand_contexts(self, _id, v="", apply_vocabs=False):
|
|
119
|
+
if v not in self.__expanded or _id not in self.__expanded[v]:
|
|
120
|
+
if apply_vocabs:
|
|
101
121
|
contexts = self.__get_vocab_contexts() + self.contexts
|
|
102
122
|
|
|
103
123
|
# Apply contexts
|
|
@@ -105,10 +125,29 @@ class Context(object):
|
|
|
105
125
|
for name, value in ctx.items():
|
|
106
126
|
if name == "@vocab":
|
|
107
127
|
_id = value + _id
|
|
128
|
+
else:
|
|
129
|
+
contexts = self.contexts
|
|
130
|
+
|
|
131
|
+
for ctx in contexts:
|
|
132
|
+
for name, value in ctx.items():
|
|
133
|
+
if name == "@base" and self.is_relative(_id):
|
|
134
|
+
_id = value + _id
|
|
135
|
+
|
|
136
|
+
self.__expanded.setdefault(v, {})[_id] = self.__expand(_id, contexts)
|
|
108
137
|
|
|
109
|
-
|
|
138
|
+
return self.__expanded[v][_id]
|
|
139
|
+
|
|
140
|
+
def expand(self, _id):
|
|
141
|
+
return self.__expand_contexts(_id)
|
|
142
|
+
|
|
143
|
+
def expand_vocab(self, _id, vocab=""):
|
|
144
|
+
with self.vocab_push(vocab):
|
|
145
|
+
if not self.__vocabs:
|
|
146
|
+
v = ""
|
|
147
|
+
else:
|
|
148
|
+
v = self.__vocabs[-1]
|
|
110
149
|
|
|
111
|
-
return self.
|
|
150
|
+
return self.__expand_contexts(_id, v, True)
|
|
112
151
|
|
|
113
152
|
def __expand(self, _id, contexts):
|
|
114
153
|
for ctx in contexts:
|
|
@@ -8,6 +8,8 @@ import os
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from contextlib import contextmanager
|
|
10
10
|
from jinja2 import Environment, FileSystemLoader, TemplateRuntimeError
|
|
11
|
+
from rdflib.namespace import SH
|
|
12
|
+
from ..model import SHACL2CODE
|
|
11
13
|
|
|
12
14
|
THIS_DIR = Path(__file__).parent
|
|
13
15
|
|
|
@@ -57,10 +59,26 @@ class BasicJinjaRender(object):
|
|
|
57
59
|
def get_additional_render_args(self):
|
|
58
60
|
return {}
|
|
59
61
|
|
|
62
|
+
def get_extra_env(self):
|
|
63
|
+
return {}
|
|
64
|
+
|
|
60
65
|
def output(self, model):
|
|
61
66
|
def abort_helper(msg):
|
|
62
67
|
raise TemplateRuntimeError(msg)
|
|
63
68
|
|
|
69
|
+
def get_all_derived(cls):
|
|
70
|
+
nonlocal classes
|
|
71
|
+
|
|
72
|
+
def _recurse(cls):
|
|
73
|
+
result = set(cls.derived_ids)
|
|
74
|
+
for r in cls.derived_ids:
|
|
75
|
+
result |= _recurse(classes.get(r))
|
|
76
|
+
return result
|
|
77
|
+
|
|
78
|
+
d = list(_recurse(cls))
|
|
79
|
+
d.sort()
|
|
80
|
+
return d
|
|
81
|
+
|
|
64
82
|
class ObjectList(object):
|
|
65
83
|
def __init__(self, objs):
|
|
66
84
|
self.__objs = objs
|
|
@@ -77,13 +95,21 @@ class BasicJinjaRender(object):
|
|
|
77
95
|
env = Environment(
|
|
78
96
|
loader=FileSystemLoader([self.__template.parent, THIS_DIR.parent])
|
|
79
97
|
)
|
|
98
|
+
for k, v in self.get_extra_env().items():
|
|
99
|
+
env.globals[k] = v
|
|
80
100
|
env.globals["abort"] = abort_helper
|
|
101
|
+
env.globals["get_all_derived"] = get_all_derived
|
|
102
|
+
env.globals["SHACL2CODE"] = SHACL2CODE
|
|
103
|
+
env.globals["SH"] = SH
|
|
81
104
|
template = env.get_template(self.__template.name)
|
|
82
105
|
|
|
106
|
+
classes = ObjectList(model.classes)
|
|
107
|
+
enums = ObjectList(model.enums)
|
|
108
|
+
|
|
83
109
|
render = template.render(
|
|
84
110
|
disclaimer=f"This file was automatically generated by {os.path.basename(sys.argv[0])}. DO NOT MANUALLY MODIFY IT",
|
|
85
|
-
enums=
|
|
86
|
-
classes=
|
|
111
|
+
enums=enums,
|
|
112
|
+
classes=classes,
|
|
87
113
|
context=model.context,
|
|
88
114
|
**self.get_additional_render_args(),
|
|
89
115
|
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2024 Joshua Watt
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
|
|
6
|
+
from .common import BasicJinjaRender
|
|
7
|
+
from .lang import language, TEMPLATE_DIR
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
import keyword
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def varname(name):
|
|
14
|
+
name = str(name).replace("@", "_")
|
|
15
|
+
name = re.sub(r"[^a-zA-Z0-9_]", "", name)
|
|
16
|
+
while keyword.iskeyword(name):
|
|
17
|
+
name = name + "_"
|
|
18
|
+
return name
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@language("jsonschema")
|
|
22
|
+
class JsonSchemaRender(BasicJinjaRender):
|
|
23
|
+
HELP = "JSON Schema"
|
|
24
|
+
|
|
25
|
+
def __init__(self, args):
|
|
26
|
+
super().__init__(args, TEMPLATE_DIR / "jsonschema.j2")
|
|
27
|
+
self.__render_args = {
|
|
28
|
+
"schema_title": args.title,
|
|
29
|
+
"schema_id": args.id,
|
|
30
|
+
"allow_elided_lists": args.allow_elided_lists,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def get_arguments(cls, parser):
|
|
35
|
+
super().get_arguments(parser)
|
|
36
|
+
|
|
37
|
+
parser.add_argument("--title", help="Schema title")
|
|
38
|
+
parser.add_argument("--id", help="Schema ID")
|
|
39
|
+
parser.add_argument(
|
|
40
|
+
"--allow-elided-lists",
|
|
41
|
+
action="store_true",
|
|
42
|
+
help="Allow lists to be elided if they only contain a single element",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def get_extra_env(self):
|
|
46
|
+
return {
|
|
47
|
+
"varname": varname,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
def get_additional_render_args(self):
|
|
51
|
+
return self.__render_args
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2024 Joshua Watt
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
|
|
6
|
+
from .common import BasicJinjaRender
|
|
7
|
+
from .lang import language, TEMPLATE_DIR
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
import keyword
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def varname(name):
|
|
14
|
+
# Any invalid characters at the beginning of the name are removed (except
|
|
15
|
+
# "@")
|
|
16
|
+
name = re.sub(r"^[^a-zA-Z0-9_@]*", "", name)
|
|
17
|
+
# Any other invalid characters are replaced with "_" (including "@")
|
|
18
|
+
name = re.sub(r"[^a-zA-Z0-9_]", "_", name)
|
|
19
|
+
# Consolidate runs of "_" to a single one
|
|
20
|
+
name = re.sub(r"__+", "_", name)
|
|
21
|
+
# Add a _ to anything that is a python keyword
|
|
22
|
+
while keyword.iskeyword(name):
|
|
23
|
+
name = name + "_"
|
|
24
|
+
return name
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@language("python")
|
|
28
|
+
class PythonRender(BasicJinjaRender):
|
|
29
|
+
HELP = "Python Language Bindings"
|
|
30
|
+
|
|
31
|
+
def __init__(self, args):
|
|
32
|
+
super().__init__(args, TEMPLATE_DIR / "python.j2")
|
|
33
|
+
self.__render_args = {
|
|
34
|
+
"elide_lists": args.elide_lists,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def get_arguments(cls, parser):
|
|
39
|
+
super().get_arguments(parser)
|
|
40
|
+
|
|
41
|
+
parser.add_argument(
|
|
42
|
+
"--elide-lists",
|
|
43
|
+
action="store_true",
|
|
44
|
+
help="Elide lists when writing documents if they only contain a single item",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
def get_extra_env(self):
|
|
48
|
+
return {
|
|
49
|
+
"varname": varname,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
def get_additional_render_args(self):
|
|
53
|
+
return self.__render_args
|
|
@@ -57,34 +57,56 @@
|
|
|
57
57
|
{#- Classes are divided into 2 parts. The properties are separated into a separate object #}
|
|
58
58
|
{#- so that a object can references the properties of its parent without needing the const #}
|
|
59
59
|
{#- @type tag #}
|
|
60
|
-
"{{ class.clsname }}": {
|
|
60
|
+
"{{ varname(class.clsname) }}": {
|
|
61
61
|
"allOf": [
|
|
62
62
|
{
|
|
63
|
-
"type": "object",
|
|
64
63
|
"properties": {
|
|
65
|
-
"{{
|
|
66
|
-
"{{ context.compact("@type") }}": { "const": "{{ context.
|
|
67
|
-
},
|
|
68
|
-
"required": ["{{
|
|
64
|
+
"{{ class.id_property or "@id" }}": { "$ref": "#/$defs/{{ class.node_kind.split("#")[-1] }}" },
|
|
65
|
+
"{{ context.compact("@type") }}": { "const": "{{ context.compact_vocab(class._id) }}" }
|
|
66
|
+
}{%- if class.node_kind == SH.IRI %},
|
|
67
|
+
"required": ["{{ class.id_property or "@id" }}"]
|
|
68
|
+
{%- endif %}
|
|
69
69
|
},
|
|
70
|
-
{ "$ref": "#/$defs/{{ class.clsname }}_props" }
|
|
70
|
+
{ "$ref": "#/$defs/{{ varname(class.clsname) }}_props" }
|
|
71
71
|
]
|
|
72
72
|
},
|
|
73
|
-
"{{ class.clsname }}
|
|
73
|
+
"{{ varname(class.clsname) }}_derived": {
|
|
74
|
+
{%- set ns = namespace(external_ref=False, local_ref=False, any_ref=False, json_refs=[]) %}
|
|
75
|
+
{%- for d in get_all_derived(class) + [class._id] %}
|
|
76
|
+
{%- set ns.json_refs = ns.json_refs + ["#/$defs/" + varname(classes.get(d).clsname)] %}
|
|
77
|
+
{%- endfor %}
|
|
78
|
+
{%- set ns.json_refs = ns.json_refs + ["#/$defs/BlankNodeOrIRI"] %}
|
|
79
|
+
|
|
80
|
+
{%- if ns.json_refs | length == 1 %}
|
|
81
|
+
"$ref": "{{ ns.json_refs[0] }}",
|
|
82
|
+
{%- elif ns.json_refs %}
|
|
83
|
+
"oneOf": [
|
|
84
|
+
{%- for r in ns.json_refs %}
|
|
85
|
+
{ "$ref": "{{ r }}" }{% if not loop.last %},{% endif %}
|
|
86
|
+
{%- endfor %}
|
|
87
|
+
],
|
|
88
|
+
{%- endif %}
|
|
89
|
+
"unevaluatedProperties": false
|
|
90
|
+
},
|
|
91
|
+
"{{ varname(class.clsname) }}_props": {
|
|
74
92
|
"allOf": [
|
|
93
|
+
{%- if class.parent_ids %}
|
|
75
94
|
{%- for parent in class.parent_ids %}
|
|
76
|
-
{ "$ref": "#/$defs/{{ classes.get(parent).clsname }}_props" },
|
|
95
|
+
{ "$ref": "#/$defs/{{ varname(classes.get(parent).clsname) }}_props" },
|
|
77
96
|
{%- endfor %}
|
|
97
|
+
{%- else %}
|
|
98
|
+
{ "$ref": "#/$defs/SHACLClass" },
|
|
99
|
+
{%- endif %}
|
|
78
100
|
{%- set required = [] %}
|
|
79
101
|
{
|
|
80
|
-
"type": "object",
|
|
81
102
|
"properties": {
|
|
82
103
|
{%- for prop in class.properties %}
|
|
83
104
|
{%- if not prop.min_count is none and prop.min_count > 0 %}
|
|
84
105
|
{{- required.append(prop.path) or "" }}
|
|
85
106
|
{%- endif %}
|
|
86
107
|
{%- set is_list = prop.max_count is none or prop.max_count != 1 %}
|
|
87
|
-
"{{ context.
|
|
108
|
+
"{{ context.compact_vocab(prop.path) }}": {
|
|
109
|
+
{%- set prop_ref = "#/$defs/prop_" + varname(class.clsname) + "_" + varname(prop.varname) %}
|
|
88
110
|
{%- if is_list %}
|
|
89
111
|
"oneOf": [
|
|
90
112
|
{
|
|
@@ -96,22 +118,23 @@
|
|
|
96
118
|
"minItems": {{ prop.min_count }},
|
|
97
119
|
{%- endif %}
|
|
98
120
|
"items": {
|
|
99
|
-
"$ref": "
|
|
121
|
+
"$ref": "{{ prop_ref }}"
|
|
100
122
|
}
|
|
101
|
-
},
|
|
123
|
+
}{% if allow_elided_lists %},
|
|
102
124
|
{
|
|
103
|
-
|
|
104
|
-
"$ref": "#/$defs/{{ prop.path | replace("/", "_") }}"
|
|
105
|
-
{%- if is_list %}
|
|
125
|
+
"$ref": "{{ prop_ref }}"
|
|
106
126
|
}
|
|
127
|
+
{%- endif %}
|
|
107
128
|
]
|
|
129
|
+
{%- else %}
|
|
130
|
+
"$ref": "{{ prop_ref }}"
|
|
108
131
|
{%- endif %}
|
|
109
132
|
}{% if not loop.last %},{% endif %}
|
|
110
133
|
{%- endfor %}
|
|
111
134
|
}{% if required %},
|
|
112
135
|
"required": [
|
|
113
136
|
{%- for r in required %}
|
|
114
|
-
"{{ context.
|
|
137
|
+
"{{ context.compact_vocab(r) }}"{% if not loop.last %},{% endif %}
|
|
115
138
|
{%- endfor %}
|
|
116
139
|
]
|
|
117
140
|
{%- endif %}
|
|
@@ -119,43 +142,68 @@
|
|
|
119
142
|
]
|
|
120
143
|
},
|
|
121
144
|
{%- for prop in class.properties %}
|
|
122
|
-
"{{
|
|
145
|
+
"prop_{{ varname(class.clsname) }}_{{ varname(prop.varname) }}": {
|
|
123
146
|
{%- if prop.class_id %}
|
|
124
|
-
"
|
|
125
|
-
{ "$ref": "#/$defs/idRef" },
|
|
126
|
-
{
|
|
127
|
-
"$ref": "#/$defs/{{ classes.get(prop.class_id).clsname }}",
|
|
128
|
-
"unevaluatedProperties": false
|
|
129
|
-
}
|
|
130
|
-
]
|
|
147
|
+
"$ref": "#/$defs/{{ varname(classes.get(prop.class_id).clsname) }}_derived"
|
|
131
148
|
{%- elif prop.enum_id %}
|
|
132
149
|
"enum": [
|
|
133
150
|
{%- for e in enums.get(prop.enum_id).values %}
|
|
134
|
-
"{{ context.
|
|
151
|
+
"{{ context.compact_vocab(e._id, prop.path) }}"{% if not loop.last %},{% endif %}
|
|
135
152
|
{%- endfor %}
|
|
136
153
|
]
|
|
137
154
|
{%- else %}
|
|
138
155
|
{%- if prop.datatype == "http://www.w3.org/2001/XMLSchema#string" %}
|
|
156
|
+
{%- if prop.pattern %}
|
|
157
|
+
"pattern": "{{ prop.pattern | replace("\\", "\\\\") }}",
|
|
158
|
+
{%- endif %}
|
|
139
159
|
"type": "string"
|
|
160
|
+
{%- elif prop.datatype == "http://www.w3.org/2001/XMLSchema#dateTime" %}
|
|
161
|
+
"type": "string",
|
|
162
|
+
"allOf": [
|
|
163
|
+
{
|
|
164
|
+
"pattern": "^[0-9]{4}-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-6][0-9]:[0-6][0-9](Z|[+-][0-9]{2}:[0-9]{2})?$"
|
|
165
|
+
}{%- if prop.pattern %},
|
|
166
|
+
{
|
|
167
|
+
"pattern": "{{ prop.pattern | replace("\\", "\\\\") }}"
|
|
168
|
+
}
|
|
169
|
+
{%- endif %}
|
|
170
|
+
]
|
|
171
|
+
{%- elif prop.datatype == "http://www.w3.org/2001/XMLSchema#dateTimeStamp" %}
|
|
172
|
+
"type": "string",
|
|
173
|
+
"allOf": [
|
|
174
|
+
{
|
|
175
|
+
"pattern": "^[0-9]{4}-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-6][0-9]:[0-6][0-9](Z|[+-][0-9]{2}:[0-9]{2})$"
|
|
176
|
+
}{%- if prop.pattern %},
|
|
177
|
+
{
|
|
178
|
+
"pattern": "{{ prop.pattern | replace("\\", "\\\\") }}"
|
|
179
|
+
}
|
|
180
|
+
{%- endif %}
|
|
181
|
+
]
|
|
140
182
|
{%- elif prop.datatype == "http://www.w3.org/2001/XMLSchema#anyURI" %}
|
|
183
|
+
{%- if prop.pattern %}
|
|
184
|
+
"pattern": "{{ prop.pattern | replace("\\", "\\\\") }}",
|
|
185
|
+
{%- endif %}
|
|
141
186
|
"$ref": "#/$defs/anyURI"
|
|
142
|
-
{%- elif prop.datatype == "https://spdx.org/rdf/v3/Core/DateTime" %}
|
|
143
|
-
"$ref": "#/$defs/DateTime"
|
|
144
|
-
{%- elif prop.datatype == "https://spdx.org/rdf/v3/Core/MediaType" %}
|
|
145
|
-
"$ref": "#/$defs/MediaType"
|
|
146
|
-
{%- elif prop.datatype == "https://spdx.org/rdf/v3/Core/SemVer" %}
|
|
147
|
-
"$ref": "#/$defs/SemVer"
|
|
148
|
-
{%- elif prop.datatype == "https://spdx.org/rdf/v3/Core/Extension" %}
|
|
149
|
-
"$ref": "#/$defs/Extension"
|
|
150
187
|
{%- elif prop.datatype == "http://www.w3.org/2001/XMLSchema#positiveInteger" %}
|
|
151
188
|
"type": "integer",
|
|
152
|
-
"minimum":
|
|
189
|
+
"minimum": 1
|
|
153
190
|
{%- elif prop.datatype == "http://www.w3.org/2001/XMLSchema#nonNegativeInteger" %}
|
|
191
|
+
"type": "integer",
|
|
192
|
+
"minimum": 0
|
|
193
|
+
{%- elif prop.datatype == "http://www.w3.org/2001/XMLSchema#integer" %}
|
|
154
194
|
"type": "integer"
|
|
155
195
|
{%- elif prop.datatype == "http://www.w3.org/2001/XMLSchema#boolean" %}
|
|
156
196
|
"type": "boolean"
|
|
157
197
|
{%- elif prop.datatype == "http://www.w3.org/2001/XMLSchema#decimal" %}
|
|
158
|
-
"
|
|
198
|
+
"oneOf": [
|
|
199
|
+
{
|
|
200
|
+
"type": "number"
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"type": "string",
|
|
204
|
+
"pattern": "^-?[0-9]+(\\.[0-9]*)?$"
|
|
205
|
+
}
|
|
206
|
+
]
|
|
159
207
|
{%- else %}
|
|
160
208
|
{{- abort("Unknown data type " + prop.datatype) }}
|
|
161
209
|
{%- endif %}
|
|
@@ -163,8 +211,19 @@
|
|
|
163
211
|
},
|
|
164
212
|
{%- endfor %}
|
|
165
213
|
{%- endfor %}
|
|
166
|
-
"
|
|
167
|
-
"type": "string"
|
|
214
|
+
"IRI": {
|
|
215
|
+
"type": "string",
|
|
216
|
+
"pattern": "^(?!_:).*"
|
|
217
|
+
},
|
|
218
|
+
"BlankNode": {
|
|
219
|
+
"type": "string",
|
|
220
|
+
"pattern": "^_:.*"
|
|
221
|
+
},
|
|
222
|
+
"BlankNodeOrIRI": {
|
|
223
|
+
"oneOf": [
|
|
224
|
+
{ "$ref": "#/$defs/IRI" },
|
|
225
|
+
{ "$ref": "#/$defs/BlankNode" }
|
|
226
|
+
]
|
|
168
227
|
},
|
|
169
228
|
"anyURI": {
|
|
170
229
|
"type": "string"
|
|
@@ -181,10 +240,23 @@
|
|
|
181
240
|
"Extension": {
|
|
182
241
|
"type": "string"
|
|
183
242
|
},
|
|
243
|
+
"SHACLClass": {
|
|
244
|
+
"type": "object",
|
|
245
|
+
"properties": {
|
|
246
|
+
"{{ context.compact("@type") }}": {
|
|
247
|
+
"enum": [
|
|
248
|
+
{%- for class in classes %}
|
|
249
|
+
"{{ context.compact_vocab(class._id) }}"{% if not loop.last %},{% endif %}
|
|
250
|
+
{%- endfor %}
|
|
251
|
+
]
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
"required": ["{{ context.compact("@type") }}"]
|
|
255
|
+
},
|
|
184
256
|
"AnyClass": {
|
|
185
257
|
"oneOf": [
|
|
186
258
|
{%- for class in classes %}
|
|
187
|
-
{ "$ref": "#/$defs/{{ class.clsname }}" }{% if not loop.last %},{% endif %}
|
|
259
|
+
{ "$ref": "#/$defs/{{ varname(class.clsname) }}" }{% if not loop.last %},{% endif %}
|
|
188
260
|
{%- endfor %}
|
|
189
261
|
]
|
|
190
262
|
}
|