| author | wenzelm | 
| Tue, 06 Jun 2023 11:07:49 +0200 | |
| changeset 78134 | a11ebc8c751a | 
| parent 75393 | 87ebf5a50283 | 
| permissions | -rw-r--r-- | 
| 69807 | 1  | 
/* Title: Pure/General/rdf.scala  | 
2  | 
Author: Makarius  | 
|
3  | 
||
4  | 
Support for RDF/XML representation, see also:  | 
|
5  | 
- https://www.w3.org/RDF  | 
|
6  | 
- https://www.w3.org/TR/rdf-primer  | 
|
7  | 
*/  | 
|
8  | 
||
9  | 
package isabelle  | 
|
10  | 
||
11  | 
||
| 75393 | 12  | 
object RDF {
 | 
| 69807 | 13  | 
/* document */  | 
14  | 
||
15  | 
  val rdf: XML.Namespace = XML.Namespace("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
 | 
|
| 
69916
 
3235ecdcd884
more meta data from "dcterms" (superset of "dc");
 
wenzelm 
parents: 
69902 
diff
changeset
 | 
16  | 
  val dcterms: XML.Namespace = XML.Namespace("dcterms", "http://purl.org/dc/terms/")
 | 
| 69807 | 17  | 
|
| 
69916
 
3235ecdcd884
more meta data from "dcterms" (superset of "dc");
 
wenzelm 
parents: 
69902 
diff
changeset
 | 
18  | 
val default_namespaces: List[XML.Namespace] = List(rdf, dcterms)  | 
| 69807 | 19  | 
|
20  | 
def document(body: XML.Body,  | 
|
21  | 
namespaces: List[XML.Namespace] = default_namespaces,  | 
|
| 75393 | 22  | 
attributes: XML.Attributes = Nil  | 
23  | 
  ): XML.Elem = {
 | 
|
| 69807 | 24  | 
    XML.Elem(Markup(rdf("RDF"), namespaces.map(_.attribute) ::: attributes), body)
 | 
25  | 
}  | 
|
26  | 
||
27  | 
||
28  | 
/* multiple triples vs. compact description */  | 
|
29  | 
||
| 
69902
 
9c697c7ad8d6
URIs should normally be "rdf:resource", not string body;
 
wenzelm 
parents: 
69900 
diff
changeset
 | 
30  | 
sealed case class Triple(  | 
| 75393 | 31  | 
subject: String,  | 
32  | 
predicate: String,  | 
|
33  | 
`object`: XML.Body = Nil,  | 
|
34  | 
resource: String = ""  | 
|
35  | 
  ) {
 | 
|
| 
69902
 
9c697c7ad8d6
URIs should normally be "rdf:resource", not string body;
 
wenzelm 
parents: 
69900 
diff
changeset
 | 
36  | 
require(`object`.isEmpty || resource.isEmpty)  | 
| 
 
9c697c7ad8d6
URIs should normally be "rdf:resource", not string body;
 
wenzelm 
parents: 
69900 
diff
changeset
 | 
37  | 
|
| 
 
9c697c7ad8d6
URIs should normally be "rdf:resource", not string body;
 
wenzelm 
parents: 
69900 
diff
changeset
 | 
38  | 
def property: XML.Elem =  | 
| 
 
9c697c7ad8d6
URIs should normally be "rdf:resource", not string body;
 
wenzelm 
parents: 
69900 
diff
changeset
 | 
39  | 
      if (resource.nonEmpty) XML.elem(Markup(predicate, List("rdf:resource" -> resource)))
 | 
| 
 
9c697c7ad8d6
URIs should normally be "rdf:resource", not string body;
 
wenzelm 
parents: 
69900 
diff
changeset
 | 
40  | 
else XML.elem(predicate, `object`)  | 
| 69807 | 41  | 
}  | 
42  | 
||
43  | 
def triples(args: List[Triple]): XML.Body =  | 
|
| 
69870
 
0af6b4a5a7d9
afford redundant whitespace for improved readability;
 
wenzelm 
parents: 
69868 
diff
changeset
 | 
44  | 
XML.newline ::  | 
| 
 
0af6b4a5a7d9
afford redundant whitespace for improved readability;
 
wenzelm 
parents: 
69868 
diff
changeset
 | 
45  | 
      args.zipWithIndex.groupBy({ case (t, _) => t.subject }).iterator.map(_._2).
 | 
| 
 
0af6b4a5a7d9
afford redundant whitespace for improved readability;
 
wenzelm 
parents: 
69868 
diff
changeset
 | 
46  | 
toList.sortBy(ps => ps.head._2).flatMap(ps =>  | 
| 
 
0af6b4a5a7d9
afford redundant whitespace for improved readability;
 
wenzelm 
parents: 
69868 
diff
changeset
 | 
47  | 
List(  | 
| 
 
0af6b4a5a7d9
afford redundant whitespace for improved readability;
 
wenzelm 
parents: 
69868 
diff
changeset
 | 
48  | 
description(ps.head._1.subject,  | 
| 
 
0af6b4a5a7d9
afford redundant whitespace for improved readability;
 
wenzelm 
parents: 
69868 
diff
changeset
 | 
49  | 
              XML.newline :: ps.flatMap({ case (t, _) => List(t.property, XML.newline) })),
 | 
| 
 
0af6b4a5a7d9
afford redundant whitespace for improved readability;
 
wenzelm 
parents: 
69868 
diff
changeset
 | 
50  | 
XML.newline))  | 
| 69807 | 51  | 
|
52  | 
def description(subject: String, body: XML.Body, attributes: XML.Attributes = Nil): XML.Elem =  | 
|
53  | 
    XML.Elem(Markup(rdf("Description"), (rdf("about"), subject) :: attributes), body)
 | 
|
| 69808 | 54  | 
|
55  | 
||
56  | 
/* collections */  | 
|
57  | 
||
58  | 
def collection(kind: String, items: List[XML.Body]): XML.Elem =  | 
|
59  | 
    XML.elem(kind, items.map(item => XML.elem(rdf("li"), item)))
 | 
|
60  | 
||
61  | 
  def bag(items: List[XML.Body]): XML.Elem = collection(rdf("Bag"), items)
 | 
|
62  | 
  def seq(items: List[XML.Body]): XML.Elem = collection(rdf("Seq"), items)
 | 
|
63  | 
  def alt(items: List[XML.Body]): XML.Elem = collection(rdf("Alt"), items)
 | 
|
| 69809 | 64  | 
|
65  | 
||
66  | 
/* datatypes */  | 
|
67  | 
||
68  | 
// see https://www.w3.org/TR/rdf11-concepts/#section-Datatypes  | 
|
69  | 
// see https://www.w3.org/TR/xmlschema-2/#built-in-datatypes  | 
|
70  | 
||
71  | 
def string(x: String): XML.Body = if (x.isEmpty) Nil else List(XML.Text(x))  | 
|
72  | 
def bool(x: Boolean): XML.Body = string(x.toString)  | 
|
73  | 
def int(x: Int): XML.Body = string(Value.Int(x))  | 
|
74  | 
def long(x: Long): XML.Body = string(Value.Long(x))  | 
|
75  | 
def double(x: Double): XML.Body = string(Value.Double(x))  | 
|
76  | 
def seconds(x: Time): XML.Body = double(x.seconds)  | 
|
| 69982 | 77  | 
|
78  | 
  val date_format: Date.Format = Date.Format("uuuu-MM-dd'T'HH:mm:ss.SSSxxx")
 | 
|
79  | 
def date_time_stamp(x: Date): XML.Body = string(date_format(x))  | 
|
| 69858 | 80  | 
|
81  | 
||
82  | 
/* predicates */  | 
|
83  | 
||
| 75393 | 84  | 
  object Property {
 | 
85  | 
// binary relation with plain value  | 
|
| 
69983
 
4ce5ce3a612b
more robust: avoid NPE due to odd problems with object initialization;
 
wenzelm 
parents: 
69982 
diff
changeset
 | 
86  | 
    def title: String = dcterms("title")
 | 
| 
 
4ce5ce3a612b
more robust: avoid NPE due to odd problems with object initialization;
 
wenzelm 
parents: 
69982 
diff
changeset
 | 
87  | 
    def creator: String = dcterms("creator")
 | 
| 
 
4ce5ce3a612b
more robust: avoid NPE due to odd problems with object initialization;
 
wenzelm 
parents: 
69982 
diff
changeset
 | 
88  | 
    def contributor: String = dcterms("contributor")
 | 
| 
 
4ce5ce3a612b
more robust: avoid NPE due to odd problems with object initialization;
 
wenzelm 
parents: 
69982 
diff
changeset
 | 
89  | 
    def date: String = dcterms("date")
 | 
| 
 
4ce5ce3a612b
more robust: avoid NPE due to odd problems with object initialization;
 
wenzelm 
parents: 
69982 
diff
changeset
 | 
90  | 
    def license: String = dcterms("license")
 | 
| 
 
4ce5ce3a612b
more robust: avoid NPE due to odd problems with object initialization;
 
wenzelm 
parents: 
69982 
diff
changeset
 | 
91  | 
    def description: String = dcterms("description")
 | 
| 69858 | 92  | 
}  | 
| 69900 | 93  | 
|
| 
69983
 
4ce5ce3a612b
more robust: avoid NPE due to odd problems with object initialization;
 
wenzelm 
parents: 
69982 
diff
changeset
 | 
94  | 
private lazy val meta_data_table =  | 
| 69900 | 95  | 
Map(  | 
96  | 
Markup.META_TITLE -> Property.title,  | 
|
97  | 
Markup.META_CREATOR -> Property.creator,  | 
|
98  | 
Markup.META_CONTRIBUTOR -> Property.contributor,  | 
|
99  | 
Markup.META_DATE -> Property.date,  | 
|
| 69982 | 100  | 
Markup.META_LICENSE -> Property.license,  | 
101  | 
Markup.META_DESCRIPTION -> Property.description)  | 
|
| 69900 | 102  | 
|
103  | 
def meta_data(props: Properties.T): Properties.T =  | 
|
104  | 
    props.flatMap({ case (a, b) => meta_data_table.get(a).map((_, b)) })
 | 
|
| 69807 | 105  | 
}  |