343 行
9.3 KiB
Go
343 行
9.3 KiB
Go
package rdf
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/dave/jennifer/jen"
|
|
"github.com/go-fed/activity/astool/codegen"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
rdfName = "RDF"
|
|
rdfSpec = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
langstringSpec = "langString"
|
|
propertySpec = "Property"
|
|
)
|
|
|
|
// SerializeValueFunction is a helper for creating a value's Serialize function.
|
|
func SerializeValueFunction(pkg, valueName string,
|
|
concreteType jen.Code,
|
|
impl []jen.Code) *codegen.Function {
|
|
name := fmt.Sprintf("Serialize%s", strings.Title(valueName))
|
|
return codegen.NewCommentedFunction(
|
|
pkg,
|
|
name,
|
|
[]jen.Code{jen.Id(codegen.This()).Add(concreteType)},
|
|
[]jen.Code{jen.Interface(), jen.Error()},
|
|
impl,
|
|
fmt.Sprintf("%s converts a %s value to an interface representation suitable for marshalling into a text or binary format.", name, valueName))
|
|
}
|
|
|
|
// DeserializeValueFunction is a helper for creating a value's Deserialize
|
|
// function.
|
|
func DeserializeValueFunction(pkg, valueName string,
|
|
concreteType jen.Code,
|
|
impl []jen.Code) *codegen.Function {
|
|
name := fmt.Sprintf("Deserialize%s", strings.Title(valueName))
|
|
return codegen.NewCommentedFunction(
|
|
pkg,
|
|
name,
|
|
[]jen.Code{jen.Id(codegen.This()).Interface()},
|
|
[]jen.Code{concreteType, jen.Error()},
|
|
impl,
|
|
fmt.Sprintf("%s creates %s value from an interface representation that has been unmarshalled from a text or binary format.", name, valueName))
|
|
}
|
|
|
|
// LessFunction is a helper for creating a value's Less function.
|
|
func LessFunction(pkg, valueName string,
|
|
concreteType jen.Code,
|
|
impl []jen.Code) *codegen.Function {
|
|
name := fmt.Sprintf("Less%s", strings.Title(valueName))
|
|
return codegen.NewCommentedFunction(
|
|
pkg,
|
|
name,
|
|
[]jen.Code{jen.List(jen.Id("lhs"), jen.Id("rhs")).Add(concreteType)},
|
|
[]jen.Code{jen.Bool()},
|
|
impl,
|
|
fmt.Sprintf("%s returns true if the left %s value is less than the right value.", name, valueName))
|
|
}
|
|
|
|
var _ Ontology = &RDFOntology{}
|
|
|
|
// RDFOntology is an Ontology for the RDF namespace.
|
|
type RDFOntology struct {
|
|
Package string
|
|
alias string
|
|
}
|
|
|
|
// SpecURI returns the RDF URI spec.
|
|
func (o *RDFOntology) SpecURI() string {
|
|
return rdfSpec
|
|
}
|
|
|
|
// Load loads the ontology with no alias set.
|
|
func (o *RDFOntology) Load() ([]RDFNode, error) {
|
|
return o.LoadAsAlias("")
|
|
}
|
|
|
|
// LoadAsAlias loads the ontology with an alias.
|
|
func (o *RDFOntology) LoadAsAlias(s string) ([]RDFNode, error) {
|
|
o.alias = s
|
|
return []RDFNode{
|
|
&AliasedDelegate{
|
|
Spec: rdfSpec,
|
|
Alias: s,
|
|
Name: langstringSpec,
|
|
Delegate: &langstring{pkg: o.Package, alias: o.alias},
|
|
},
|
|
&AliasedDelegate{
|
|
Spec: rdfSpec,
|
|
Alias: s,
|
|
Name: propertySpec,
|
|
Delegate: &property{},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// LoadSpecificAsAlias loads a specific RDFNode with the given alias.
|
|
func (o *RDFOntology) LoadSpecificAsAlias(alias, name string) ([]RDFNode, error) {
|
|
switch name {
|
|
case langstringSpec:
|
|
return []RDFNode{
|
|
&AliasedDelegate{
|
|
Spec: "",
|
|
Alias: "",
|
|
Name: alias,
|
|
Delegate: &langstring{pkg: o.Package, alias: o.alias},
|
|
},
|
|
}, nil
|
|
case propertySpec:
|
|
return []RDFNode{
|
|
&AliasedDelegate{
|
|
Spec: "",
|
|
Alias: "",
|
|
Name: alias,
|
|
Delegate: &property{},
|
|
},
|
|
}, nil
|
|
}
|
|
return nil, fmt.Errorf("rdf ontology cannot find %q to make alias %q", name, alias)
|
|
}
|
|
|
|
// LoadElement does nothing.
|
|
func (o *RDFOntology) LoadElement(name string, payload map[string]interface{}) ([]RDFNode, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
// GetByName returns a raw, unguarded node by name.
|
|
func (o *RDFOntology) GetByName(name string) (RDFNode, error) {
|
|
name = strings.TrimPrefix(name, o.SpecURI())
|
|
switch name {
|
|
case langstringSpec:
|
|
return &langstring{pkg: o.Package, alias: o.alias}, nil
|
|
case propertySpec:
|
|
return &property{}, nil
|
|
}
|
|
return nil, fmt.Errorf("rdf ontology could not find node for name %s", name)
|
|
}
|
|
|
|
var _ RDFNode = &langstring{}
|
|
|
|
// langstring is an RDF node representing the langstring value.
|
|
type langstring struct {
|
|
alias string
|
|
pkg string
|
|
}
|
|
|
|
// Enter returns an error.
|
|
func (l *langstring) Enter(key string, ctx *ParsingContext) (bool, error) {
|
|
return true, fmt.Errorf("rdf langstring cannot be entered")
|
|
}
|
|
|
|
// Exit returns an error.
|
|
func (l *langstring) Exit(key string, ctx *ParsingContext) (bool, error) {
|
|
return true, fmt.Errorf("rdf langstring cannot be exited")
|
|
}
|
|
|
|
// Apply sets the langstring value in the context as a referenced spec.
|
|
func (l *langstring) Apply(key string, value interface{}, ctx *ParsingContext) (bool, error) {
|
|
for k, p := range ctx.Result.Vocab.Properties {
|
|
for i, ref := range p.Range {
|
|
if ref.Name == langstringSpec && ref.Vocab == l.alias {
|
|
p.NaturalLanguageMap = true
|
|
ctx.Result.Vocab.Properties[k] = p
|
|
p.Range = append(p.Range[:i], p.Range[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
u, e := url.Parse(rdfSpec + langstringSpec)
|
|
if e != nil {
|
|
return true, e
|
|
}
|
|
var vocab *Vocabulary
|
|
vocab, e = ctx.GetResultReferenceWithDefaults(rdfSpec, rdfName)
|
|
if e != nil {
|
|
return true, e
|
|
}
|
|
e = vocab.SetValue(langstringSpec, &VocabularyValue{
|
|
Name: langstringSpec,
|
|
URI: u,
|
|
DefinitionType: jen.Map(jen.String()).String(),
|
|
Zero: "make(map[string]string)",
|
|
IsNilable: true,
|
|
SerializeFn: SerializeValueFunction(
|
|
l.pkg,
|
|
langstringSpec,
|
|
jen.Map(jen.String()).String(),
|
|
[]jen.Code{
|
|
jen.Return(
|
|
jen.Id(codegen.This()),
|
|
jen.Nil(),
|
|
),
|
|
}),
|
|
DeserializeFn: DeserializeValueFunction(
|
|
l.pkg,
|
|
langstringSpec,
|
|
jen.Map(jen.String()).String(),
|
|
[]jen.Code{
|
|
jen.If(
|
|
jen.List(
|
|
jen.Id("m"),
|
|
jen.Id("ok"),
|
|
).Op(":=").Id(codegen.This()).Assert(jen.Map(jen.String()).Interface()),
|
|
jen.Id("ok"),
|
|
).Block(
|
|
jen.Id("r").Op(":=").Make(jen.Map(jen.String()).String()),
|
|
jen.For(
|
|
jen.List(
|
|
jen.Id("k"),
|
|
jen.Id("v"),
|
|
).Op(":=").Range().Id("m"),
|
|
).Block(
|
|
jen.If(
|
|
jen.List(
|
|
jen.Id("s"),
|
|
jen.Id("ok"),
|
|
).Op(":=").Id("v").Assert(jen.String()),
|
|
jen.Id("ok"),
|
|
).Block(
|
|
jen.Id("r").Index(jen.Id("k")).Op("=").Id("s"),
|
|
).Else().Block(
|
|
jen.Return(
|
|
jen.Nil(),
|
|
jen.Qual("fmt", "Errorf").Call(
|
|
jen.Lit("value %v cannot be interpreted as a string for rdf:langString"),
|
|
jen.Id("v"),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
jen.Return(
|
|
jen.Id("r"),
|
|
jen.Nil(),
|
|
),
|
|
).Else().Block(
|
|
jen.Return(
|
|
jen.Nil(),
|
|
jen.Qual("fmt", "Errorf").Call(
|
|
jen.Lit("%v cannot be interpreted as a map[string]interface{} for rdf:langString"),
|
|
jen.Id(codegen.This()),
|
|
),
|
|
),
|
|
),
|
|
}),
|
|
LessFn: LessFunction(
|
|
l.pkg,
|
|
langstringSpec,
|
|
jen.Map(jen.String()).String(),
|
|
[]jen.Code{
|
|
jen.Var().Id("lk").Index().String(),
|
|
jen.Var().Id("rk").Index().String(),
|
|
jen.For(
|
|
jen.List(
|
|
jen.Id("k"),
|
|
).Op(":=").Range().Id("lhs"),
|
|
).Block(
|
|
jen.Id("lk").Op("=").Append(
|
|
jen.Id("lk"),
|
|
jen.Id("k"),
|
|
),
|
|
),
|
|
jen.For(
|
|
jen.List(
|
|
jen.Id("k"),
|
|
).Op(":=").Range().Id("rhs"),
|
|
).Block(
|
|
jen.Id("rk").Op("=").Append(
|
|
jen.Id("rk"),
|
|
jen.Id("k"),
|
|
),
|
|
),
|
|
jen.Qual("sort", "Sort").Call(
|
|
jen.Qual("sort", "StringSlice").Call(jen.Id("lk")),
|
|
),
|
|
jen.Qual("sort", "Sort").Call(
|
|
jen.Qual("sort", "StringSlice").Call(jen.Id("rk")),
|
|
),
|
|
jen.For(
|
|
jen.Id("i").Op(":=").Lit(0),
|
|
jen.Id("i").Op("<").Len(jen.Id("lk")).Op("&&").Id("i").Op("<").Len(jen.Id("rk")),
|
|
jen.Id("i").Op("++"),
|
|
).Block(
|
|
jen.If(
|
|
jen.Id("lk").Index(jen.Id("i")).Op("<").Id("rk").Index(jen.Id("i")),
|
|
).Block(
|
|
jen.Return(jen.True()),
|
|
).Else().If(
|
|
jen.Id("rk").Index(jen.Id("i")).Op("<").Id("lk").Index(jen.Id("i")),
|
|
).Block(
|
|
jen.Return(jen.False()),
|
|
).Else().If(
|
|
jen.Id("lhs").Index(jen.Id("lk").Index(jen.Id("i"))).Op("<").Id("rhs").Index(jen.Id("rk").Index(jen.Id("i"))),
|
|
).Block(
|
|
jen.Return(jen.True()),
|
|
).Else().If(
|
|
jen.Id("rhs").Index(jen.Id("rk").Index(jen.Id("i"))).Op("<").Id("lhs").Index(jen.Id("lk").Index(jen.Id("i"))),
|
|
).Block(
|
|
jen.Return(jen.False()),
|
|
),
|
|
),
|
|
jen.If(
|
|
jen.Len(jen.Id("lk")).Op("<").Len(jen.Id("rk")),
|
|
).Block(
|
|
jen.Return(jen.True()),
|
|
).Else().Block(
|
|
jen.Return(jen.False()),
|
|
),
|
|
}),
|
|
})
|
|
return true, e
|
|
}
|
|
|
|
var _ RDFNode = &property{}
|
|
|
|
// property is an RDFNode that sets a VocabularyProperty as the current.
|
|
type property struct{}
|
|
|
|
// Enter returns an error.
|
|
func (p *property) Enter(key string, ctx *ParsingContext) (bool, error) {
|
|
return true, fmt.Errorf("rdf property cannot be entered")
|
|
}
|
|
|
|
// Exit returns an error.
|
|
func (p *property) Exit(key string, ctx *ParsingContext) (bool, error) {
|
|
return true, fmt.Errorf("rdf property cannot be exited")
|
|
}
|
|
|
|
// Apply sets the current context to be a VocabularyProperty, if it is not
|
|
// already. If the context isn't reset, an error is returned due to another node
|
|
// not having cleaned up properly.
|
|
func (p *property) Apply(key string, value interface{}, ctx *ParsingContext) (bool, error) {
|
|
// Prepare a new VocabularyProperty in the context. If one already
|
|
// exists, skip.
|
|
if _, ok := ctx.Current.(*VocabularyProperty); ok {
|
|
return true, nil
|
|
} else if !ctx.IsReset() {
|
|
return true, fmt.Errorf("rdf property applied with non-reset ParsingContext")
|
|
}
|
|
ctx.Current = &VocabularyProperty{}
|
|
return true, nil
|
|
}
|