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.