From 94569ca5494a55188795f6b23fff46993fca3973 Mon Sep 17 00:00:00 2001 From: Cory Slep Date: Thu, 29 Nov 2018 22:53:48 +0100 Subject: [PATCH] Setup applying RDF node understanding. Next, the actual nodes need to be created in order to construct the proper intermediate form and translate the parsed data into a meaningful structure that can be used to generate code. Ideally, this could also potentially allow code generation in other languages too. And different ways to read in ActivityStreams specifications and extensions. But that would be way off in the future. --- tools/exp/rdf/data.go | 87 +++++++++++++++++++++++++++++++- tools/exp/rdf/parse.go | 109 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 192 insertions(+), 4 deletions(-) diff --git a/tools/exp/rdf/data.go b/tools/exp/rdf/data.go index cdf1af3..91244df 100644 --- a/tools/exp/rdf/data.go +++ b/tools/exp/rdf/data.go @@ -33,18 +33,53 @@ type VocabularyValue struct { Zero string } +func (v *VocabularyValue) SetName(s string) { + v.Name = s +} + +func (v *VocabularyValue) SetURI(s string) error { + var e error + v.URI, e = url.Parse(s) + return e +} + +var ( + _ NameSetter = &VocabularyValue{} + _ URISetter = &VocabularyValue{} +) + // VocabularyType represents a single ActivityStream type in a vocabulary. type VocabularyType struct { Name string URI *url.URL Notes string DisjointWith []VocabularyReference - Extends []VocabularyReference + Extends []VocabularyReference // TODO: Object improperly extends Link Properties []VocabularyReference // TODO: Check for duplication WithoutProperties []VocabularyReference // TODO: Missing for IntransitiveActivity Examples []VocabularyExample } +func (v *VocabularyType) SetName(s string) { + v.Name = s +} + +func (v *VocabularyType) SetURI(s string) error { + var e error + v.URI, e = url.Parse(s) + return e +} + +func (v *VocabularyType) SetNotes(s string) { + v.Notes = s +} + +var ( + _ NameSetter = &VocabularyType{} + _ URISetter = &VocabularyType{} + _ NotesSetter = &VocabularyType{} +) + // VocabularyProperty represents a single ActivityStream property type in a // vocabulary. type VocabularyProperty struct { @@ -59,6 +94,26 @@ type VocabularyProperty struct { NaturalLanguageMap bool } +func (v *VocabularyProperty) SetName(s string) { + v.Name = s +} + +func (v *VocabularyProperty) SetURI(s string) error { + var e error + v.URI, e = url.Parse(s) + return e +} + +func (v *VocabularyProperty) SetNotes(s string) { + v.Notes = s +} + +var ( + _ NameSetter = &VocabularyProperty{} + _ URISetter = &VocabularyProperty{} + _ NotesSetter = &VocabularyProperty{} +) + // VocabularyExample documents an Example for an ActivityStream type or property // in the vocabulary. type VocabularyExample struct { @@ -67,6 +122,21 @@ type VocabularyExample struct { Example map[string]interface{} } +func (v *VocabularyExample) SetName(s string) { + v.Name = s +} + +func (v *VocabularyExample) SetURI(s string) error { + var e error + v.URI, e = url.Parse(s) + return e +} + +var ( + _ NameSetter = &VocabularyExample{} + _ URISetter = &VocabularyExample{} +) + // VocabularyReference refers to another Vocabulary reference, either a // VocabularyType, VocabularyValue, or a VocabularyProperty. It may refer to // another Vocabulary's type or property entirely. @@ -75,3 +145,18 @@ type VocabularyReference struct { URI *url.URL Vocab string // If present, must match key in ParsedVocabulary.References } + +func (v *VocabularyReference) SetName(s string) { + v.Name = s +} + +func (v *VocabularyReference) SetURI(s string) error { + var e error + v.URI, e = url.Parse(s) + return e +} + +var ( + _ NameSetter = &VocabularyReference{} + _ URISetter = &VocabularyReference{} +) diff --git a/tools/exp/rdf/parse.go b/tools/exp/rdf/parse.go index 3acad09..f8cc6c6 100644 --- a/tools/exp/rdf/parse.go +++ b/tools/exp/rdf/parse.go @@ -15,23 +15,126 @@ type JSONLD map[string]interface{} // ParsingContext contains the results of the parsing as well as scratch space // required for RDFNodes to be able to statefully apply changes. type ParsingContext struct { - Result ParsedVocabulary + Result *ParsedVocabulary + Current interface{} +} + +type NameSetter interface { + SetName(string) +} + +type URISetter interface { + SetURI(string) error +} + +type NotesSetter interface { + SetNotes(string) } // RDFNode is able to operate on a specific key if it applies towards its // ontology (determined at creation time). It applies the value in its own // specific implementation on the context. type RDFNode interface { - Apply(key string, value interface{}, ctx ParsedVocabulary) (bool, error) + Enter(key string, ctx *ParsingContext) (bool, error) + Exit(key string, ctx *ParsingContext) (bool, error) + Apply(key string, value interface{}, ctx *ParsingContext) (bool, error) } // ParseVocabulary parses the specified input as an ActivityStreams context that // specifies a Core, Extended, or Extension vocabulary. func ParseVocabulary(registry *RDFRegistry, input JSONLD) (vocabulary *ParsedVocabulary, err error) { - _, err = parseJSONLDContext(registry, input) + var nodes []RDFNode + nodes, err = parseJSONLDContext(registry, input) + if err != nil { + return + } + vocabulary = &ParsedVocabulary{} + ctx := &ParsingContext{ + Result: vocabulary, + } + err = apply(nodes, input, ctx) return } +// apply takes a specification input to populate the ParsingContext, based on +// the capabilities of the RDFNodes created from ontologies. +func apply(nodes []RDFNode, input JSONLD, ctx *ParsingContext) error { + for k, v := range input { + // Skip the context as it has already been parsed to create the + // nodes. + if k == JSON_LD_CONTEXT { + continue + } + if mapValue, ok := v.(map[string]interface{}); ok { + if err := enterFirstNode(nodes, k, ctx); err != nil { + return err + } else if err = apply(nodes, mapValue, ctx); err != nil { + return err + } else if err = exitFirstNode(nodes, k, ctx); err != nil { + return err + } + } else if arrValue, ok := v.([]interface{}); ok { + for _, val := range arrValue { + // First, enter for this key + if err := enterFirstNode(nodes, k, ctx); err != nil { + return err + } + // Recur or handle the value as necessary. + if mapValue, ok := val.(map[string]interface{}); ok { + if err := apply(nodes, mapValue, ctx); err != nil { + return err + } + } else if err := applyFirstNode(nodes, k, val, ctx); err != nil { + return err + } + // Finally, exit for this key + if err := exitFirstNode(nodes, k, ctx); err != nil { + return err + } + } + } else if err := applyFirstNode(nodes, k, v, ctx); err != nil { + return err + } + } + return nil +} + +// enterFirstNode will Enter the first RDFNode that returns true or an error. +func enterFirstNode(nodes []RDFNode, key string, ctx *ParsingContext) error { + for _, node := range nodes { + if applied, err := node.Enter(key, ctx); applied { + return nil + } else if err != nil { + return err + } + } + return nil +} + +// exitFirstNode will Exit the first RDFNode that returns true or an error. +func exitFirstNode(nodes []RDFNode, key string, ctx *ParsingContext) error { + for _, node := range nodes { + if applied, err := node.Exit(key, ctx); applied { + return nil + } else if err != nil { + return err + } + } + return nil +} + +// applyFirstNode will Apply the first RDFNode that returns true or an error. +func applyFirstNode(nodes []RDFNode, key string, value interface{}, ctx *ParsingContext) error { + for _, node := range nodes { + if applied, err := node.Apply(key, value, ctx); applied { + return nil + } else if err != nil { + return err + } + } + return nil +} + // parseJSONLDContext implements a super basic JSON-LD @context parsing // algorithm in order to build a set of nodes which will be able to parse the // rest of the document.