1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | import { NamedNode, Literal, Quad } from "rdf-js";
|
23 | import { DataFactory } from "./rdfjs";
|
24 | import { IriString, LocalNode, Iri } from "./interfaces";
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 | export const xmlSchemaTypes = {
|
31 | boolean: "http://www.w3.org/2001/XMLSchema#boolean",
|
32 | dateTime: "http://www.w3.org/2001/XMLSchema#dateTime",
|
33 | decimal: "http://www.w3.org/2001/XMLSchema#decimal",
|
34 | integer: "http://www.w3.org/2001/XMLSchema#integer",
|
35 | string: "http://www.w3.org/2001/XMLSchema#string",
|
36 | langString: "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString",
|
37 | } as const;
|
38 |
|
39 | export type XmlSchemaTypeIri = typeof xmlSchemaTypes[keyof typeof xmlSchemaTypes];
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 | export function serializeBoolean(value: boolean): string {
|
47 | return value ? "1" : "0";
|
48 | }
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 | export function deserializeBoolean(value: string): boolean | null {
|
55 | if (value === "1") {
|
56 | return true;
|
57 | } else if (value === "0") {
|
58 | return false;
|
59 | } else {
|
60 | return null;
|
61 | }
|
62 | }
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 | export function serializeDatetime(value: Date): string {
|
70 |
|
71 |
|
72 | const roundedDate = new Date(
|
73 | Date.UTC(
|
74 | value.getUTCFullYear(),
|
75 | value.getUTCMonth(),
|
76 | value.getUTCDate(),
|
77 | value.getUTCHours(),
|
78 | value.getUTCMinutes(),
|
79 | value.getUTCSeconds(),
|
80 | 0
|
81 | )
|
82 | );
|
83 |
|
84 | const rdflibStyleString = roundedDate.toISOString().replace(/\.000Z$/, "Z");
|
85 | return rdflibStyleString;
|
86 | }
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 | export function deserializeDatetime(literalString: string): Date | null {
|
93 | if (
|
94 | literalString === null ||
|
95 | literalString.length <= 17 ||
|
96 | literalString.indexOf("Z") === -1
|
97 | ) {
|
98 | return null;
|
99 | }
|
100 |
|
101 |
|
102 | const utcFullYear = parseInt(literalString.substring(0, 4), 10);
|
103 | const utcMonth = parseInt(literalString.substring(5, 7), 10) - 1;
|
104 | const utcDate = parseInt(literalString.substring(8, 10), 10);
|
105 | const utcHours = parseInt(literalString.substring(11, 13), 10);
|
106 | const utcMinutes = parseInt(literalString.substring(14, 16), 10);
|
107 | const utcSeconds = parseInt(
|
108 | literalString.substring(17, literalString.indexOf("Z")),
|
109 | 10
|
110 | );
|
111 | const date = new Date(0);
|
112 | date.setUTCFullYear(utcFullYear);
|
113 | date.setUTCMonth(utcMonth);
|
114 | date.setUTCDate(utcDate);
|
115 | date.setUTCHours(utcHours);
|
116 | date.setUTCMinutes(utcMinutes);
|
117 | date.setUTCSeconds(utcSeconds);
|
118 | return date;
|
119 | }
|
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 | export function serializeDecimal(value: number): string {
|
127 | return value.toString();
|
128 | }
|
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 | export function deserializeDecimal(literalString: string): number | null {
|
135 | const deserialized = Number.parseFloat(literalString);
|
136 | if (Number.isNaN(deserialized)) {
|
137 | return null;
|
138 | }
|
139 | return deserialized;
|
140 | }
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 | export function serializeInteger(value: number): string {
|
148 | return value.toString();
|
149 | }
|
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
155 | export function deserializeInteger(literalString: string): number | null {
|
156 | const deserialized = Number.parseInt(literalString, 10);
|
157 | if (Number.isNaN(deserialized)) {
|
158 | return null;
|
159 | }
|
160 | return deserialized;
|
161 | }
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 | export function normalizeLocale(locale: string): string {
|
168 | return locale.toLowerCase();
|
169 | }
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 |
|
176 | export function isNamedNode<T>(value: T | NamedNode): value is NamedNode {
|
177 | return (
|
178 | typeof value === "object" &&
|
179 | typeof (value as NamedNode).termType === "string" &&
|
180 | (value as NamedNode).termType === "NamedNode"
|
181 | );
|
182 | }
|
183 |
|
184 |
|
185 |
|
186 |
|
187 |
|
188 |
|
189 | export function isLiteral<T>(value: T | Literal): value is Literal {
|
190 | return (
|
191 | typeof value === "object" &&
|
192 | typeof (value as Literal).termType === "string" &&
|
193 | (value as Literal).termType === "Literal"
|
194 | );
|
195 | }
|
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 |
|
202 | export function isLocalNode<T>(value: T | LocalNode): value is LocalNode {
|
203 | return (
|
204 | typeof value === "object" &&
|
205 | typeof (value as LocalNode).termType === "string" &&
|
206 | (value as LocalNode).termType === "BlankNode" &&
|
207 | typeof (value as LocalNode).name === "string"
|
208 | );
|
209 | }
|
210 |
|
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 | export function getLocalNode(name: string): LocalNode {
|
219 | const localNode: LocalNode = Object.assign(DataFactory.blankNode(), {
|
220 | name: name,
|
221 | });
|
222 | return localNode;
|
223 | }
|
224 |
|
225 |
|
226 |
|
227 |
|
228 |
|
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 | export function asNamedNode(iri: Iri | IriString): NamedNode {
|
236 | if (isNamedNode(iri)) {
|
237 | return iri;
|
238 | }
|
239 |
|
240 |
|
241 |
|
242 |
|
243 | if (typeof URL !== "undefined") {
|
244 | new URL(iri);
|
245 | }
|
246 | return DataFactory.namedNode(iri);
|
247 | }
|
248 |
|
249 | interface IsEqualOptions {
|
250 | resourceIri?: IriString;
|
251 | }
|
252 |
|
253 |
|
254 |
|
255 |
|
256 |
|
257 | export function isEqual(
|
258 | node1: NamedNode | LocalNode,
|
259 | node2: NamedNode | LocalNode,
|
260 | options: IsEqualOptions = {}
|
261 | ): boolean {
|
262 | if (isNamedNode(node1) && isNamedNode(node2)) {
|
263 | return node1.equals(node2);
|
264 | }
|
265 | if (isLocalNode(node1) && isLocalNode(node2)) {
|
266 | return node1.name === node2.name;
|
267 | }
|
268 | if (typeof options.resourceIri === "undefined") {
|
269 |
|
270 |
|
271 | return false;
|
272 | }
|
273 | const namedNode1 = isNamedNode(node1)
|
274 | ? node1
|
275 | : resolveIriForLocalNode(node1, options.resourceIri);
|
276 | const namedNode2 = isNamedNode(node2)
|
277 | ? node2
|
278 | : resolveIriForLocalNode(node2, options.resourceIri);
|
279 | return namedNode1.equals(namedNode2);
|
280 | }
|
281 |
|
282 |
|
283 |
|
284 |
|
285 |
|
286 |
|
287 | export function resolveIriForLocalNodes(
|
288 | quad: Quad,
|
289 | resourceIri: IriString
|
290 | ): Quad {
|
291 | const subject = isLocalNode(quad.subject)
|
292 | ? resolveIriForLocalNode(quad.subject, resourceIri)
|
293 | : quad.subject;
|
294 | const object = isLocalNode(quad.object)
|
295 | ? resolveIriForLocalNode(quad.object, resourceIri)
|
296 | : quad.object;
|
297 | return {
|
298 | ...quad,
|
299 | subject: subject,
|
300 | object: object,
|
301 | };
|
302 | }
|
303 |
|
304 |
|
305 |
|
306 |
|
307 |
|
308 |
|
309 | export function resolveIriForLocalNode(
|
310 | localNode: LocalNode,
|
311 | resourceIri: IriString
|
312 | ): NamedNode {
|
313 | return DataFactory.namedNode(resolveLocalIri(localNode.name, resourceIri));
|
314 | }
|
315 |
|
316 |
|
317 |
|
318 |
|
319 |
|
320 |
|
321 | export function resolveLocalIri(
|
322 | name: string,
|
323 | resourceIri: IriString
|
324 | ): IriString {
|
325 |
|
326 | if (typeof URL === "undefined") {
|
327 | throw new Error(
|
328 | "The URL interface is not available, so an IRI cannot be determined."
|
329 | );
|
330 | }
|
331 | const thingIri = new URL(resourceIri);
|
332 | thingIri.hash = name;
|
333 | return thingIri.href;
|
334 | }
|