| author | wenzelm | 
| Mon, 13 Feb 2023 10:49:33 +0100 | |
| changeset 77287 | d060545f01a2 | 
| 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: 
69902diff
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: 
69902diff
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: 
69900diff
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: 
69900diff
changeset | 36 | require(`object`.isEmpty || resource.isEmpty) | 
| 
9c697c7ad8d6
URIs should normally be "rdf:resource", not string body;
 wenzelm parents: 
69900diff
changeset | 37 | |
| 
9c697c7ad8d6
URIs should normally be "rdf:resource", not string body;
 wenzelm parents: 
69900diff
changeset | 38 | def property: XML.Elem = | 
| 
9c697c7ad8d6
URIs should normally be "rdf:resource", not string body;
 wenzelm parents: 
69900diff
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: 
69900diff
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: 
69868diff
changeset | 44 | XML.newline :: | 
| 
0af6b4a5a7d9
afford redundant whitespace for improved readability;
 wenzelm parents: 
69868diff
changeset | 45 |       args.zipWithIndex.groupBy({ case (t, _) => t.subject }).iterator.map(_._2).
 | 
| 
0af6b4a5a7d9
afford redundant whitespace for improved readability;
 wenzelm parents: 
69868diff
changeset | 46 | toList.sortBy(ps => ps.head._2).flatMap(ps => | 
| 
0af6b4a5a7d9
afford redundant whitespace for improved readability;
 wenzelm parents: 
69868diff
changeset | 47 | List( | 
| 
0af6b4a5a7d9
afford redundant whitespace for improved readability;
 wenzelm parents: 
69868diff
changeset | 48 | description(ps.head._1.subject, | 
| 
0af6b4a5a7d9
afford redundant whitespace for improved readability;
 wenzelm parents: 
69868diff
changeset | 49 |               XML.newline :: ps.flatMap({ case (t, _) => List(t.property, XML.newline) })),
 | 
| 
0af6b4a5a7d9
afford redundant whitespace for improved readability;
 wenzelm parents: 
69868diff
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: 
69982diff
changeset | 86 |     def title: String = dcterms("title")
 | 
| 
4ce5ce3a612b
more robust: avoid NPE due to odd problems with object initialization;
 wenzelm parents: 
69982diff
changeset | 87 |     def creator: String = dcterms("creator")
 | 
| 
4ce5ce3a612b
more robust: avoid NPE due to odd problems with object initialization;
 wenzelm parents: 
69982diff
changeset | 88 |     def contributor: String = dcterms("contributor")
 | 
| 
4ce5ce3a612b
more robust: avoid NPE due to odd problems with object initialization;
 wenzelm parents: 
69982diff
changeset | 89 |     def date: String = dcterms("date")
 | 
| 
4ce5ce3a612b
more robust: avoid NPE due to odd problems with object initialization;
 wenzelm parents: 
69982diff
changeset | 90 |     def license: String = dcterms("license")
 | 
| 
4ce5ce3a612b
more robust: avoid NPE due to odd problems with object initialization;
 wenzelm parents: 
69982diff
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: 
69982diff
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 | } |