KHealthCertificate

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

KDE's Doxygen guidelines are available online.