KHealthCertificate

rdf.cpp
1/*
2 * SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
3 * SPDX-License-Identifier: LGPL-2.0-or-later
4 */
5
6#include "rdf_p.h"
7
8#include <QBuffer>
9#include <QCryptographicHash>
10#include <QHash>
11#include <QMap>
12#include <QIODevice>
13
14bool Rdf::Term::operator<(const Rdf::Term& other) const
15{
16 if (type == other.type) {
17 if (value == other.value) {
18 return literalType < other.literalType;
19 }
20 return value < other.value;
21 }
22 return type < other.type;
23}
24
25bool Rdf::Term::operator==(const Rdf::Term& other) const
26{
27 return type == other.type && value == other.value && literalType == other.literalType;
28}
29
30bool Rdf::Quad::operator<(const Rdf::Quad &other) const
31{
32 if (subject == other.subject) {
33 if (predicate == other.predicate) {
34 return object < other.object;
35 }
36 return predicate < other.predicate;
37 }
38 return subject < other.subject;
39}
40
41// see https://json-ld.github.io/rdf-dataset-canonicalization/spec/#hash-first-degree-quads
42static QByteArray hashFirstDegreeQuads(const std::vector<Rdf::Quad> &quads, const QString &refBlankNode)
43{
44 const auto renameBlankNode = [&refBlankNode](Rdf::Term &term) {
45 if (term.type == Rdf::Term::BlankNode) {
46 if (term.value == refBlankNode) {
47 term.value = QStringLiteral("a");
48 } else {
49 term.value = QStringLiteral("z");
50 }
51 }
52 };
53
54 std::vector<Rdf::Quad> toHash;
55 for (auto quad : quads) {
56 renameBlankNode(quad.subject);
57 renameBlankNode(quad.predicate);
58 renameBlankNode(quad.object);
59 toHash.push_back(std::move(quad));
60 }
61
62 std::sort(toHash.begin(), toHash.end());
64}
65
66QByteArray Rdf::serialize(const std::vector<Rdf::Quad>& quads)
67{
68 QByteArray out;
69 QBuffer buffer(&out);
70 buffer.open(QIODevice::WriteOnly);
71 Rdf::serialize(&buffer, quads);
72 buffer.close();
73 return out;
74}
75
76void Rdf::normalize(std::vector<Rdf::Quad>& quads)
77{
78 // see https://json-ld.github.io/rdf-dataset-canonicalization/spec/#algorithm
79 QHash<QString, std::vector<Rdf::Quad>> blankNodeToQuadMap;
80 for (const auto &quad : quads) {
81 // ignores predicates and the same blank nodes used multiple times in a quad, as that doesn't happen for us
82 if (quad.subject.type == Rdf::Term::BlankNode) {
83 blankNodeToQuadMap[quad.subject.value].push_back(quad);
84 }
85 if (quad.object.type == Rdf::Term::BlankNode) {
86 blankNodeToQuadMap[quad.object.value].push_back(quad);
87 }
88 }
89
90 QMap<QByteArray, QString> hashToBlankNodeMap;
91 for (auto it = blankNodeToQuadMap.begin(); it != blankNodeToQuadMap.end(); ++it) {
92 hashToBlankNodeMap.insert(hashFirstDegreeQuads(it.value(), it.key()), it.key());
93 }
94
95 int c14nIdCounter = 0;
96 QHash<QString, QString> blankNodeC14nMap;
97 for (auto it = hashToBlankNodeMap.begin(); it != hashToBlankNodeMap.end(); ++it) {
98 blankNodeC14nMap.insert(it.value(), QLatin1String("c14n") + QString::number(c14nIdCounter++));
99 }
100
101 const auto translateBlankNode = [&blankNodeC14nMap](Rdf::Term &term) {
102 if (term.type == Rdf::Term::BlankNode) {
103 const auto it = blankNodeC14nMap.constFind(term.value);
104 if (it != blankNodeC14nMap.constEnd()) {
105 term.value = it.value();
106 }
107 }
108 };
109 for (auto &quad : quads) {
110 translateBlankNode(quad.subject);
111 translateBlankNode(quad.predicate);
112 translateBlankNode(quad.object);
113 }
114
115 std::sort(quads.begin(), quads.end());
116 quads.erase(std::unique(quads.begin(), quads.end(), [](const auto &lhs, const auto &rhs) {
117 return lhs.subject == rhs.subject && lhs.predicate == rhs.predicate && lhs.object == rhs.object;
118 }), quads.end());
119}
120
121void Rdf::serialize(QIODevice *out, const std::vector<Rdf::Quad> &quads)
122{
123 for (const auto &quad : quads) {
124 serialize(out, quad);
125 }
126}
127
128void Rdf::serialize(QIODevice *out, const Rdf::Quad &quad)
129{
130 serialize(out, quad.subject);
131 out->write(" ");
132 serialize(out, quad.predicate);
133 out->write(" ");
134 serialize(out, quad.object);
135 out->write(" .\n");
136}
137
138void Rdf::serialize(QIODevice* out, const Rdf::Term &term)
139{
140 switch (term.type) {
141 case Term::IRI:
142 out->write("<");
143 out->write(term.value.toUtf8());
144 out->write(">");
145 break;
146 case Term::BlankNode:
147 out->write("_:");
148 out->write(term.value.toUtf8());
149 break;
150 case Term::Literal:
151 out->write("\"");
152 out->write(term.value.toUtf8());
153 out->write("\"");
154 if (!term.literalType.isEmpty()) {
155 out->write("^^<");
156 out->write(term.literalType.toUtf8());
157 out->write(">");
158 }
159 break;
160 case Term::Undefined:
161 out->write(term.value.toUtf8());
162 break;
163 }
164}
Type type(const QSqlDatabase &db)
QByteArray toHex(char separator) const const
QByteArray hash(QByteArrayView data, Algorithm method)
iterator begin()
const_iterator constEnd() const const
const_iterator constFind(const Key &key) const const
iterator end()
iterator insert(const Key &key, const T &value)
qint64 write(const QByteArray &data)
iterator begin()
iterator end()
iterator insert(const Key &key, const T &value)
QString number(double n, char format, int precision)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 10 2024 11:48:34 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.