Various improvements for tests.

- Sorting serialization and deserialization functions to reduce random
  noise when regenerating code.
- IRI methods can now short-cut to a URI equivalent value.
- Kind constructors.
- Extended types now can also be attached to properties (bleh). Blows up
  the API size, really quickly.
- Minor bugfixes.
This commit is contained in:
Cory Slep 2019-02-04 22:06:50 +01:00
parent 161ebc62e3
commit 0f6ba298b3
9 changed files with 388 additions and 135 deletions

View file

@ -8,6 +8,7 @@ import (
"github.com/go-fed/activity/astool/gen"
"github.com/go-fed/activity/astool/rdf"
"net/url"
"sort"
"strings"
"unicode"
)
@ -57,6 +58,7 @@ func (v vocabulary) allTypeArray() []*gen.TypeGenerator {
typeArray = append(typeArray, ref.typeArray()...)
}
typeArray = append(typeArray, v.typeArray()...)
sort.Sort(sortableTypeGenerator(typeArray))
return typeArray
}
@ -68,6 +70,7 @@ func (v vocabulary) allPropArray() []*gen.PropertyGenerator {
propArray = append(propArray, ref.propArray()...)
}
propArray = append(propArray, v.propArray()...)
sort.Sort(sortablePropertyGenerator(propArray))
return propArray
}
@ -79,6 +82,7 @@ func (v vocabulary) allFuncPropArray() []*gen.FunctionalPropertyGenerator {
funcPropArray = append(funcPropArray, ref.funcPropArray()...)
}
funcPropArray = append(funcPropArray, v.funcPropArray()...)
sort.Sort(sortableFuncPropertyGenerator(funcPropArray))
return funcPropArray
}
@ -90,6 +94,7 @@ func (v vocabulary) allNonFuncPropArray() []*gen.NonFunctionalPropertyGenerator
nonFuncPropArray = append(nonFuncPropArray, ref.nonFuncPropArray()...)
}
nonFuncPropArray = append(nonFuncPropArray, v.nonFuncPropArray()...)
sort.Sort(sortableNonFuncPropertyGenerator(nonFuncPropArray))
return nonFuncPropArray
}
@ -99,6 +104,7 @@ func (v vocabulary) typeArray() []*gen.TypeGenerator {
for _, t := range v.Types {
tg = append(tg, t)
}
sort.Sort(sortableTypeGenerator(tg))
return tg
}
@ -111,6 +117,7 @@ func (v vocabulary) propArray() []*gen.PropertyGenerator {
for _, f := range v.NFProps {
fp = append(fp, &f.PropertyGenerator)
}
sort.Sort(sortablePropertyGenerator(fp))
return fp
}
@ -120,6 +127,7 @@ func (v vocabulary) funcPropArray() []*gen.FunctionalPropertyGenerator {
for _, f := range v.FProps {
fp = append(fp, f)
}
sort.Sort(sortableFuncPropertyGenerator(fp))
return fp
}
@ -129,6 +137,7 @@ func (v vocabulary) nonFuncPropArray() []*gen.NonFunctionalPropertyGenerator {
for _, nf := range v.NFProps {
nfp = append(nfp, nf)
}
sort.Sort(sortableNonFuncPropertyGenerator(nfp))
return nfp
}
@ -705,50 +714,21 @@ func (c Converter) convertNonFunctionalProperty(p rdf.VocabularyProperty,
}
// convertValue turns a rdf.VocabularyValue into a Kind.
//
// TODO: Turn this into a Kind constructor in gen?
func (c Converter) convertValue(v rdf.VocabularyValue) (k *gen.Kind) {
func (c Converter) convertValue(v rdf.VocabularyValue) *gen.Kind {
s := v.SerializeFn.CloneToPackage(c.vocabValuePackage(v).Path())
d := v.DeserializeFn.CloneToPackage(c.vocabValuePackage(v).Path())
l := v.LessFn.CloneToPackage(c.vocabValuePackage(v).Path())
k = &gen.Kind{
// Name must use toIdentifier for vocabValuePackage and
// valuePackage to be the same.
Name: toIdentifier(v),
ConcreteKind: v.DefinitionType,
Nilable: v.IsNilable,
IsURI: v.IsURI,
SerializeFn: s.QualifiedName(),
DeserializeFn: d.QualifiedName(),
LessFn: l.QualifiedName(),
SerializeDef: s,
DeserializeDef: d,
LessDef: l,
}
return
}
// convertTypeToKind turns a rdf.VocabularyType into a Kind.
//
// TODO: Turn this into a Kind constructor in gen?
func (c Converter) convertTypeToKind(v rdf.VocabularyType) (k *gen.Kind, e error) {
k = &gen.Kind{
// Name must use toIdentifier for vocabValuePackage and
// valuePackage to be the same.
Name: toIdentifier(v),
Nilable: true,
IsURI: false,
// Instead of populating:
// - ConcreteKind
// - SerializeFn
// - DeserializeFn
// - LessFn
//
// The TypeGenerator is responsible for calling SetKindFns on
// the properties, to property wire a Property's Kind back to
// the Type's implementation.
}
return
// Name must use toIdentifier for vocabValuePackage and valuePackage to
// be the same.
id := toIdentifier(v)
return gen.NewKindForValue(id.LowerName,
id.CamelName,
v.DefinitionType,
v.IsNilable,
v.IsURI,
s,
d,
l)
}
// convertTypeToName makes a Titled version of the VocabularyType's name.
@ -771,11 +751,8 @@ func (c Converter) propertyKinds(v rdf.VocabularyProperty,
e = fmt.Errorf("cannot find own kind with name %q", r.Name)
return
} else {
var kt *gen.Kind
kt, e = c.convertTypeToKind(t)
if e != nil {
return
}
id := toIdentifier(t)
kt := gen.NewKindForType(id.LowerName, id.CamelName)
k = append(k, *kt)
}
} else {
@ -800,11 +777,8 @@ func (c Converter) propertyKinds(v rdf.VocabularyProperty,
e = fmt.Errorf("cannot find kind with name %q in %s", r.Name, url)
return
} else {
var kt *gen.Kind
kt, e = c.convertTypeToKind(t)
if e != nil {
return
}
id := toIdentifier(t)
kt := gen.NewKindForType(id.LowerName, id.CamelName)
k = append(k, *kt)
}
} else {

84
astool/convert/sort.go Normal file
View file

@ -0,0 +1,84 @@
package convert
import (
"github.com/go-fed/activity/astool/gen"
)
// sortableTypeGenerator is a TypeGenerator slice sorted by TypeName.
type sortableTypeGenerator []*gen.TypeGenerator
// Len is the length of this slice.
func (s sortableTypeGenerator) Len() int {
return len(s)
}
// Less returns true if the TypeName at one index is less than one at another
// index.
func (s sortableTypeGenerator) Less(i, j int) bool {
return s[i].TypeName() < s[j].TypeName()
}
// Swap elements at indicated indices.
func (s sortableTypeGenerator) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// sortableFuncPropertyGenerator is a FunctionalPropertyGenerator slice sorted
// by PropertyName.
type sortableFuncPropertyGenerator []*gen.FunctionalPropertyGenerator
// Len is the length of this slice.
func (s sortableFuncPropertyGenerator) Len() int {
return len(s)
}
// Less returns true if the PropertyName at one index is less than one at
// another index.
func (s sortableFuncPropertyGenerator) Less(i, j int) bool {
return s[i].PropertyName() < s[j].PropertyName()
}
// Swap elements at indicated indices.
func (s sortableFuncPropertyGenerator) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// sortableNonFuncPropertyGenerator is a NonFunctionalPropertyGenerator slice
// sorted by PropertyName.
type sortableNonFuncPropertyGenerator []*gen.NonFunctionalPropertyGenerator
// Len is the length of this slice.
func (s sortableNonFuncPropertyGenerator) Len() int {
return len(s)
}
// Less returns true if the PropertyName at one index is less than one at
// another index.
func (s sortableNonFuncPropertyGenerator) Less(i, j int) bool {
return s[i].PropertyName() < s[j].PropertyName()
}
// Swap elements at indicated indices.
func (s sortableNonFuncPropertyGenerator) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// sortablePropertyGenerator is a PropertyGenerator slice sorted by
// PropertyName.
type sortablePropertyGenerator []*gen.PropertyGenerator
// Len is the length of this slice.
func (s sortablePropertyGenerator) Len() int {
return len(s)
}
// Less returns true if the PropertyName at one index is less than one at
// another index.
func (s sortablePropertyGenerator) Less(i, j int) bool {
return s[i].PropertyName() < s[j].PropertyName()
}
// Swap elements at indicated indices.
func (s sortablePropertyGenerator) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}

View file

@ -63,6 +63,9 @@ func (p *FunctionalPropertyGenerator) isSingleTypeDef() bool {
// Definition produces the Go Struct code definition, which can generate its Go
// implementations.
//
// The TypeGenerator apply must be called for all types before Definition is
// called.
func (p *FunctionalPropertyGenerator) Definition() *codegen.Struct {
p.cacheOnce.Do(func() {
if p.isSingleTypeDef() {
@ -91,7 +94,9 @@ func (p *FunctionalPropertyGenerator) clearNonLanguageMapMembers() []jen.Code {
func (p *FunctionalPropertyGenerator) singleTypeClearNonLanguageMapMembers() []jen.Code {
clearCode := []jen.Code{
jen.Id(codegen.This()).Dot(unknownMemberName).Op("=").Nil(),
jen.Id(codegen.This()).Dot(iriMember).Op("=").Nil(),
}
if !p.hasURIKind() {
clearCode = append(clearCode, jen.Id(codegen.This()).Dot(iriMember).Op("=").Nil())
}
if p.kinds[0].Nilable {
clearCode = append(clearCode, jen.Id(codegen.This()).Dot(p.memberName(0)).Op("=").Nil())
@ -113,7 +118,9 @@ func (p *FunctionalPropertyGenerator) multiTypeClearNonLanguageMapMembers() []je
}
}
clearLine = append(clearLine, jen.Id(codegen.This()).Dot(unknownMemberName).Op("=").Nil())
clearLine = append(clearLine, jen.Id(codegen.This()).Dot(iriMember).Op("=").Nil())
if !p.hasURIKind() {
clearLine = append(clearLine, jen.Id(codegen.This()).Dot(iriMember).Op("=").Nil())
}
return clearLine
}
@ -293,14 +300,16 @@ func (p *FunctionalPropertyGenerator) serializationFuncs() (*codegen.Method, *co
)
}
}
serializeFns = serializeFns.Else().If(
jen.Id(codegen.This()).Dot(isIRIMethod).Call(),
).Block(
jen.Return(
jen.Id(codegen.This()).Dot(iriMember).Dot("String").Call(),
jen.Nil(),
),
)
if !p.hasURIKind() {
serializeFns = serializeFns.Else().If(
jen.Id(codegen.This()).Dot(isIRIMethod).Call(),
).Block(
jen.Return(
jen.Id(codegen.This()).Dot(iriMember).Dot("String").Call(),
jen.Nil(),
),
)
}
serialize := codegen.NewCommentedValueMethod(
p.GetPrivatePackage().Path(),
p.serializeFnName(),
@ -308,7 +317,7 @@ func (p *FunctionalPropertyGenerator) serializationFuncs() (*codegen.Method, *co
/*params=*/ nil,
[]jen.Code{jen.Interface(), jen.Error()},
[]jen.Code{serializeFns, jen.Return(
jen.Id(codegen.This()).Dot(unknownMemberName),
jen.String().Parens(jen.Id(codegen.This()).Dot(unknownMemberName)),
jen.Nil(),
)},
fmt.Sprintf("%s converts this into an interface representation suitable for marshalling into a text or binary format. Applications should not need this function as most typical use cases serialize types instead of individual properties. It is exposed for alternatives to go-fed implementations to use.", p.serializeFnName()))
@ -457,7 +466,9 @@ func (p *FunctionalPropertyGenerator) singleTypeDef() *codegen.Struct {
}
}
kindMembers = append(kindMembers, p.unknownMemberDef())
kindMembers = append(kindMembers, p.iriMemberDef())
if !p.hasURIKind() {
kindMembers = append(kindMembers, p.iriMemberDef())
}
// TODO: Normalize alias of values when setting on this property.
kindMembers = append(kindMembers, jen.Id(aliasMember).String())
if p.hasNaturalLanguageMap {
@ -488,7 +499,10 @@ func (p *FunctionalPropertyGenerator) singleTypeDef() *codegen.Struct {
func (p *FunctionalPropertyGenerator) singleTypeFuncs() []*codegen.Method {
var methods []*codegen.Method
// HasAny Method
isLine := jen.Id(codegen.This()).Dot(p.isMethodName(0)).Call().Op("||").Id(codegen.This()).Dot(iriMember).Op("!=").Nil()
isLine := jen.Id(codegen.This()).Dot(p.isMethodName(0)).Call()
if !p.hasURIKind() {
isLine.Op("||").Id(codegen.This()).Dot(iriMember).Op("!=").Nil()
}
methods = append(methods, codegen.NewCommentedValueMethod(
p.GetPrivatePackage().Path(),
hasAnyMethod,
@ -536,7 +550,7 @@ func (p *FunctionalPropertyGenerator) singleTypeFuncs() []*codegen.Method {
p.StructName(),
/*params=*/ nil,
[]jen.Code{jen.Bool()},
[]jen.Code{jen.Return(jen.Id(codegen.This()).Dot(iriMember).Op("!=").Nil())},
[]jen.Code{jen.Return(p.thisIRI().Op("!=").Nil())},
fmt.Sprintf("%s returns true if this property is an IRI.", isIRIMethod),
))
// Get Method
@ -556,7 +570,7 @@ func (p *FunctionalPropertyGenerator) singleTypeFuncs() []*codegen.Method {
p.StructName(),
/*params=*/ nil,
[]jen.Code{jen.Op("*").Qual("net/url", "URL")},
[]jen.Code{jen.Return(jen.Id(codegen.This()).Dot(iriMember))},
[]jen.Code{jen.Return(p.thisIRI())},
fmt.Sprintf("%s returns the IRI of this property. When %s returns false, %s will return any arbitrary value.", getIRIMethod, isIRIMethod, getIRIMethod),
))
// Set Method
@ -605,7 +619,7 @@ func (p *FunctionalPropertyGenerator) singleTypeFuncs() []*codegen.Method {
/*ret=*/ nil,
[]jen.Code{
jen.Id(codegen.This()).Dot(p.clearMethodName()).Call(),
jen.Id(codegen.This()).Dot(iriMember).Op("=").Id("v"),
p.thisIRISetFn(),
},
fmt.Sprintf("%s sets the value of this property. Calling %s afterwards will return true.", setIRIMethod, isIRIMethod),
))
@ -632,6 +646,18 @@ func (p *FunctionalPropertyGenerator) singleTypeFuncs() []*codegen.Method {
))
// LessThan Method
lessCode := p.kinds[0].lessFnCode(jen.Id(codegen.This()).Dot(p.getFnName(0)).Call(), jen.Id("o").Dot(p.getFnName(0)).Call())
iriCmp := jen.Empty()
if !p.hasURIKind() {
iriCmp = iriCmp.Add(
jen.Commentf("LessThan comparison for if either or both are IRIs.").Line(),
jen.If(
jen.Id(codegen.This()).Dot(isIRIMethod).Call().Op("&&").Id("o").Dot(isIRIMethod).Call(),
).Block(
jen.Return(
jen.Id(codegen.This()).Dot(iriMember).Dot("String").Call().Op("<").Id("o").Dot(getIRIMethod).Call().Dot("String").Call(),
),
).Else())
}
methods = append(methods, codegen.NewCommentedValueMethod(
p.GetPrivatePackage().Path(),
compareLessMethod,
@ -639,14 +665,7 @@ func (p *FunctionalPropertyGenerator) singleTypeFuncs() []*codegen.Method {
[]jen.Code{jen.Id("o").Qual(p.GetPublicPackage().Path(), p.InterfaceName())},
[]jen.Code{jen.Bool()},
[]jen.Code{
jen.Commentf("LessThan comparison for if either or both are IRIs."),
jen.If(
jen.Id(codegen.This()).Dot(isIRIMethod).Call().Op("&&").Id("o").Dot(isIRIMethod).Call(),
).Block(
jen.Return(
jen.Id(codegen.This()).Dot(iriMember).Dot("String").Call().Op("<").Id("o").Dot(getIRIMethod).Call().Dot("String").Call(),
),
).Else().If(
iriCmp.If(
jen.Id(codegen.This()).Dot(isIRIMethod).Call(),
).Block(
jen.Commentf("IRIs are always less than other values, none, or unknowns"),
@ -696,7 +715,9 @@ func (p *FunctionalPropertyGenerator) multiTypeDef() *codegen.Struct {
}
}
kindMembers = append(kindMembers, p.unknownMemberDef())
kindMembers = append(kindMembers, p.iriMemberDef())
if !p.hasURIKind() {
kindMembers = append(kindMembers, p.iriMemberDef())
}
kindMembers = append(kindMembers, jen.Id(aliasMember).String())
if p.hasNaturalLanguageMap {
kindMembers = append(kindMembers, jen.Id(langMapMember).Map(jen.String()).String())
@ -738,11 +759,18 @@ func (p *FunctionalPropertyGenerator) multiTypeDef() *codegen.Struct {
func (p *FunctionalPropertyGenerator) multiTypeFuncs() []*codegen.Method {
var methods []*codegen.Method
// HasAny Method
isLine := make([]jen.Code, len(p.kinds)+1)
isLine := make([]jen.Code, 0, len(p.kinds)+1)
for i := range p.kinds {
isLine[i] = jen.Id(codegen.This()).Dot(p.isMethodName(i)).Call().Op("||")
or := jen.Empty()
if i < len(p.kinds)-1 {
or = jen.Op("||")
}
isLine = append(isLine, jen.Id(codegen.This()).Dot(p.isMethodName(i)).Call().Add(or))
}
if !p.hasURIKind() {
isLine[len(isLine)-1] = jen.Add(isLine[len(isLine)-1], jen.Op("||"))
isLine = append(isLine, jen.Id(codegen.This()).Dot(iriMember).Op("!=").Nil())
}
isLine[len(isLine)-1] = jen.Id(codegen.This()).Dot(iriMember).Op("!=").Nil()
hasAnyComment := fmt.Sprintf(
"%s returns true if any of the different values is set.", hasAnyMethod,
)
@ -828,7 +856,7 @@ func (p *FunctionalPropertyGenerator) multiTypeFuncs() []*codegen.Method {
p.StructName(),
/*params=*/ nil,
[]jen.Code{jen.Bool()},
[]jen.Code{jen.Return(jen.Id(codegen.This()).Dot(iriMember).Op("!=").Nil())},
[]jen.Code{jen.Return(p.thisIRI().Op("!=").Nil())},
fmt.Sprintf(
"%s returns true if this property is an IRI. When true, use %s and %s to access and set this property",
isIRIMethod,
@ -883,7 +911,7 @@ func (p *FunctionalPropertyGenerator) multiTypeFuncs() []*codegen.Method {
/*ret=*/ nil,
[]jen.Code{
jen.Id(codegen.This()).Dot(p.clearMethodName()).Call(),
jen.Id(codegen.This()).Dot(iriMember).Op("=").Id("v"),
p.thisIRISetFn(),
},
fmt.Sprintf("%s sets the value of this property. Calling %s afterwards returns true.", setIRIMethod, isIRIMethod),
))
@ -906,7 +934,7 @@ func (p *FunctionalPropertyGenerator) multiTypeFuncs() []*codegen.Method {
p.StructName(),
/*params=*/ nil,
[]jen.Code{jen.Op("*").Qual("net/url", "URL")},
[]jen.Code{jen.Return(jen.Id(codegen.This()).Dot(iriMember))},
[]jen.Code{jen.Return(p.thisIRI())},
fmt.Sprintf("%s returns the IRI of this property. When %s returns false, %s will return an arbitrary value.", getIRIMethod, isIRIMethod, getIRIMethod),
))
// LessThan Method
@ -925,14 +953,16 @@ func (p *FunctionalPropertyGenerator) multiTypeFuncs() []*codegen.Method {
).Block(
jen.Return(kind.lessFnCode(jen.Id(codegen.This()).Dot(p.getFnName(i)).Call(), jen.Id("o").Dot(p.getFnName(i)).Call()))))
}
lessCode.Add(
jen.Else().If(
jen.Id(codegen.This()).Dot(isIRIMethod).Call(),
).Block(
jen.Return(
jen.Id(codegen.This()).Dot(iriMember).Dot("String").Call().Op("<").Id("o").Dot(getIRIMethod).Call().Dot("String").Call(),
),
))
if !p.hasURIKind() {
lessCode.Add(
jen.Else().If(
jen.Id(codegen.This()).Dot(isIRIMethod).Call(),
).Block(
jen.Return(
jen.Id(codegen.This()).Dot(iriMember).Dot("String").Call().Op("<").Id("o").Dot(getIRIMethod).Call().Dot("String").Call(),
),
))
}
methods = append(methods, codegen.NewCommentedValueMethod(
p.GetPrivatePackage().Path(),
compareLessMethod,
@ -978,7 +1008,8 @@ func (p *FunctionalPropertyGenerator) wrapDeserializeCode(valueExisting, typeExi
jen.Err(),
).Op(":=").Qual("net/url", "Parse").Call(jen.Id("s")),
jen.Commentf("If error exists, don't error out -- skip this and treat as unknown string ([]byte) at worst"),
jen.If(jen.Err().Op("==").Nil()).Block(
jen.Commentf("Also, if no scheme exists, don't treat it as a URL -- net/url is greedy"),
jen.If(jen.Err().Op("==").Nil().Op("&&").Len(jen.Id("u").Dot("Scheme")).Op(">").Lit(0)).Block(
jen.Id(codegen.This()).Op(":=").Op("&").Id(p.StructName()).Values(
jen.Dict{
jen.Id(iriMember): jen.Id("u"),
@ -1009,16 +1040,16 @@ func (p *FunctionalPropertyGenerator) wrapDeserializeCode(valueExisting, typeExi
iriCode = iriCode.Add(
jen.If(
jen.List(
jen.Id("v"),
jen.Id("str"),
jen.Id("ok"),
).Op(":=").Id("i").Assert(
jen.Index().Byte(),
jen.String(),
),
jen.Id("ok"),
).Block(
jen.Id(codegen.This()).Op(":=").Op("&").Id(p.StructName()).Values(
jen.Dict{
jen.Id(unknownMemberName): jen.Id("v"),
jen.Id(unknownMemberName): jen.Index().Byte().Parens(jen.Id("str")),
jen.Id(aliasMember): jen.Id("alias"),
},
),
@ -1090,14 +1121,34 @@ func (p *FunctionalPropertyGenerator) contextMethod() *codegen.Method {
fmt.Sprintf("%s returns the JSONLD URIs required in the context string for this property and the specific values that are set. The value in the map is the alias used to import the property's value or values.", contextMethod))
}
// hasURIKind returns true if this property already has a Kind that is a URI.
func (p *FunctionalPropertyGenerator) hasURIKind() bool {
for _, k := range p.kinds {
if k.IsURI {
return true
// thisIRI returns the statement to access this IRI -- it may be an xsd:anyURI
// or another equivalent type.
func (p *FunctionalPropertyGenerator) thisIRI() *jen.Statement {
if !p.hasURIKind() {
return jen.Id(codegen.This()).Dot(iriMember)
} else {
for i, k := range p.kinds {
if k.IsURI {
return jen.Id(codegen.This()).Dot(p.memberName(i))
}
}
}
return false
return nil
}
// thisIRI returns the statement to access this IRI -- it may be an xsd:anyURI
// or another equivalent type.
func (p *FunctionalPropertyGenerator) thisIRISetFn() *jen.Statement {
if !p.hasURIKind() {
return jen.Id(codegen.This()).Dot(iriMember).Op("=").Id("v")
} else {
for i, k := range p.kinds {
if k.IsURI {
return jen.Id(codegen.This()).Dot(p.setFnName(i)).Call(jen.Id("v"))
}
}
}
return nil
}
// hasTypeKind returns true if this property has a Kind that is a type.

View file

@ -218,10 +218,14 @@ func (m *ManagerGenerator) createDeserializationMethod(deserName string, pubPkg,
jen.Error(),
).Block(
jen.List(
// Note: this "i" must be the same as the "i" in the deserialization definition.
jen.Id("i"),
jen.Err(),
).Op(":=").Qual(privPkg.Path(), deserName).Call(jen.Id("m"), jen.Id("aliasMap")),
jen.If(
jen.Id("i").Op("==").Nil(),
).Block(
jen.Return(jen.Nil(), jen.Err()),
),
jen.Return(jen.List(
jen.Id("i"),
jen.Err(),

View file

@ -62,6 +62,9 @@ func (p *NonFunctionalPropertyGenerator) InterfaceDefinitions(pkg Package) []*co
// Definitions produces the Go code definitions, which can generate their Go
// implementations. The struct is the iterator for various values of the
// property, which is defined by the type definition.
//
// The TypeGenerator apply must be called for all types before Definition is
// called.
func (p *NonFunctionalPropertyGenerator) Definitions() (*codegen.Struct, *codegen.Struct) {
p.cacheOnce.Do(func() {
var methods []*codegen.Method
@ -222,7 +225,7 @@ func (p *NonFunctionalPropertyGenerator) funcs() []*codegen.Method {
jen.Id(codegen.This()).Dot(propertiesName).Op("=").Append(
jen.Index().Op("*").Id(p.iteratorTypeName().CamelName).Values(
jen.Values(jen.Dict{
jen.Id(iriMember): jen.Id("v"),
p.thisIRI(): jen.Id("v"),
jen.Id(parentMemberName): jen.Id(codegen.This()),
jen.Id(myIndexMemberName): jen.Lit(0),
jen.Id(aliasMember): jen.Id(codegen.This()).Dot(aliasMember),
@ -253,7 +256,7 @@ func (p *NonFunctionalPropertyGenerator) funcs() []*codegen.Method {
jen.Id(codegen.This()).Dot(propertiesName),
jen.Op("&").Id(p.iteratorTypeName().CamelName).Values(
jen.Dict{
jen.Id(iriMember): jen.Id("v"),
p.thisIRI(): jen.Id("v"),
jen.Id(parentMemberName): jen.Id(codegen.This()),
jen.Id(myIndexMemberName): jen.Id(codegen.This()).Dot(lenMethod).Call(),
jen.Id(aliasMember): jen.Id(codegen.This()).Dot(aliasMember),
@ -273,7 +276,7 @@ func (p *NonFunctionalPropertyGenerator) funcs() []*codegen.Method {
jen.Parens(jen.Id(codegen.This()).Dot(propertiesName)).Index(jen.Id("idx")).Dot(parentMemberName).Op("=").Nil(),
jen.Parens(jen.Id(codegen.This()).Dot(propertiesName)).Index(jen.Id("idx")).Op("=").Op("&").Id(p.iteratorTypeName().CamelName).Values(
jen.Dict{
jen.Id(iriMember): jen.Id("v"),
p.thisIRI(): jen.Id("v"),
jen.Id(parentMemberName): jen.Id(codegen.This()),
jen.Id(myIndexMemberName): jen.Id("idx"),
jen.Id(aliasMember): jen.Id(codegen.This()).Dot(aliasMember),
@ -625,7 +628,6 @@ func (p *NonFunctionalPropertyGenerator) serializationFuncs() (*codegen.Method,
).Block(
jen.Id("alias").Op("=").Id("a"),
),
jen.Var().Id(codegen.This()).Op("*").Id(p.StructName()),
jen.Id("propName").Op(":=").Lit(p.PropertyName()),
jen.If(
jen.Len(jen.Id("alias")).Op(">").Lit(0),
@ -678,12 +680,31 @@ func (p *NonFunctionalPropertyGenerator) serializationFuncs() (*codegen.Method,
jen.Id("ele").Dot(parentMemberName).Op("=").Id(codegen.This()),
jen.Id("ele").Dot(myIndexMemberName).Op("=").Id("idx"),
),
jen.Return(
jen.Id(codegen.This()),
jen.Nil(),
),
),
jen.Return(
jen.Id(codegen.This()),
jen.Nil(),
jen.Nil(),
),
},
fmt.Sprintf("%s creates a %q property from an interface representation that has been unmarshalled from a text or binary format.", p.DeserializeFnName(), p.PropertyName()))
return serialize, deserialize
}
// thisIRI returns the member to access this IRI -- it may be an xsd:anyURI
// or another equivalent type.
func (p *NonFunctionalPropertyGenerator) thisIRI() *jen.Statement {
if !p.hasURIKind() {
return jen.Id(iriMember)
} else {
for i, k := range p.kinds {
if k.IsURI {
return jen.Id(p.memberName(i))
}
}
}
return nil
}

View file

@ -103,6 +103,51 @@ type Kind struct {
LessDef *codegen.Function
}
// NewKindForValue creates a Kind for a value type.
func NewKindForValue(docName, idName string,
defType *jen.Statement,
isNilable, isURI bool,
serializeFn, deserializeFn, lessFn *codegen.Function) *Kind {
return &Kind{
Name: Identifier{
LowerName: docName,
CamelName: idName,
},
ConcreteKind: defType,
Nilable: isNilable,
IsURI: isURI,
SerializeFn: serializeFn.QualifiedName(),
DeserializeFn: deserializeFn.QualifiedName(),
LessFn: lessFn.QualifiedName(),
SerializeDef: serializeFn,
DeserializeDef: deserializeFn,
LessDef: lessFn,
}
}
// NewKindForType creates a Kind for an ActivitySteams type.
func NewKindForType(docName, idName string) *Kind {
return &Kind{
// Name must use toIdentifier for vocabValuePackage and
// valuePackage to be the same.
Name: Identifier{
LowerName: docName,
CamelName: idName,
},
Nilable: true,
IsURI: false,
// Instead of populating:
// - ConcreteKind
// - DeserializeFn
// - SerializeFn (Not populated for types)
// - LessFn (Not populated for types)
//
// The TypeGenerator is responsible for calling SetKindFns on
// the properties, to property wire a Property's Kind back to
// the Type's implementation.
}
}
// lessFnCode creates the correct code calling this Kind's less function
// depending on whether the Kind is a value or a type.
func (k Kind) lessFnCode(this, other *jen.Statement) *jen.Statement {
@ -183,11 +228,11 @@ func (p *PropertyGenerator) GetPublicPackage() Package {
// The name parameter must match the LowerName of an Identifier.
//
// This feels very hacky.
func (p *PropertyGenerator) SetKindFns(name string, qualKind *jen.Statement, deser *codegen.Method) error {
func (p *PropertyGenerator) SetKindFns(docName, idName string, qualKind *jen.Statement, deser *codegen.Method) error {
for i, kind := range p.kinds {
if kind.Name.LowerName == name {
if kind.Name.LowerName == docName {
if kind.SerializeFn != nil || kind.DeserializeFn != nil || kind.LessFn != nil {
return fmt.Errorf("property kind already has serialization functions set for %q", name)
return fmt.Errorf("property kind already has serialization functions set for %q", docName)
}
kind.ConcreteKind = qualKind
kind.DeserializeFn = deser.On(managerInitName())
@ -196,7 +241,15 @@ func (p *PropertyGenerator) SetKindFns(name string, qualKind *jen.Statement, des
return nil
}
}
return fmt.Errorf("cannot find property kind %q", name)
// In the case of extended types applying themselves to their parents'
// range, they will be missing from the property's kinds list. Append a
// new kind to handle this use case.
k := NewKindForType(docName, idName)
k.ConcreteKind = qualKind
k.DeserializeFn = deser.On(managerInitName())
p.managerMethods = append(p.managerMethods, deser)
p.kinds = append(p.kinds, *k)
return nil
}
// getAllManagerMethods returns the list of manager methods used by this
@ -402,3 +455,13 @@ func (p *PropertyGenerator) constructorFn() *codegen.Function {
},
fmt.Sprintf("%s%s creates a new %s property.", constructorName, p.StructName(), p.PropertyName()))
}
// hasURIKind returns true if this property already has a Kind that is a URI.
func (p *PropertyGenerator) hasURIKind() bool {
for _, k := range p.kinds {
if k.IsURI {
return true
}
}
return false
}

View file

@ -908,6 +908,9 @@ func (r *ResolverGenerator) toAliasFunction() *codegen.Function {
jen.Id("m").Map(jen.String()).String(),
},
[]jen.Code{
jen.Id("m").Op("=").Make(
jen.Map(jen.String()).String(),
),
jen.Id("toHttpHttpsFn").Op(":=").Func().Parens(
jen.Id("s").String(),
).Parens(

View file

@ -72,7 +72,7 @@ type Property interface {
GetPublicPackage() Package
PropertyName() string
InterfaceName() string
SetKindFns(name string, kind *jen.Statement, deser *codegen.Method) error
SetKindFns(docName, idName string, kind *jen.Statement, deser *codegen.Method) error
DeserializeFnName() string
}
@ -177,17 +177,36 @@ func (t *TypeGenerator) AddRangeProperty(property Property) {
// implementation as if this type were a Kind.
//
// Prepares to use the manager for the Definition generation.
//
// Must be called before Definition is called on the properties.
func (t *TypeGenerator) apply(m *ManagerGenerator) error {
t.m = m
// Set up Kind functions
// Set up Kind functions for this type, on its range of properties as
// well as the range of properties of those it is extending from.
deser := m.getDeserializationMethodForType(t)
kind := jen.Qual(t.PublicPackage().Path(), t.InterfaceName())
for _, p := range t.rangeProperties {
if e := p.SetKindFns(t.TypeName(), kind, deser); e != nil {
return e
// Refursively-applying function.
var setKindsOnWhoseProps func(whichType *TypeGenerator) error
setKindsOnWhoseProps = func(whichType *TypeGenerator) error {
// Apply this TypeGenerator's kinds to whichType's range of
// properties.
for _, p := range whichType.rangeProperties {
// Kluge: convert.toIdentifier must match this!
if e := p.SetKindFns(t.TypeName(), strings.Title(t.TypeName()), kind, deser); e != nil {
return e
}
}
// Recursively apply this TypeGenerator's kinds to the parents
// of whichType.
for _, extendParent := range whichType.extends {
if e := setKindsOnWhoseProps(extendParent); e != nil {
return e
}
}
return nil
}
return nil
// Begin the recursing by applying to this type's range of properties.
return setKindsOnWhoseProps(t)
}
// VocabName returns this TypeGenerator's vocabulary name.
@ -564,17 +583,21 @@ func (t *TypeGenerator) serializationMethod() (ser *codegen.Method) {
serCode.Add(
jen.Commentf("Maybe serialize property %q", prop.PropertyName()).Line(),
jen.If(
jen.List(
jen.Id("i"),
jen.Err(),
).Op(":=").Id(codegen.This()).Dot(t.memberName(prop)).Dot(serializeMethod).Call(),
jen.Err().Op("!=").Nil(),
jen.Id(codegen.This()).Dot(t.memberName(prop)).Op("!=").Nil(),
).Block(
jen.Return(jen.Nil(), jen.Err()),
).Else().If(
jen.Id("i").Op("!=").Nil(),
).Block(
jen.Id("m").Index(jen.Id(codegen.This()).Dot(t.memberName(prop)).Dot(nameMethod).Call()).Op("=").Id("i"),
jen.If(
jen.List(
jen.Id("i"),
jen.Err(),
).Op(":=").Id(codegen.This()).Dot(t.memberName(prop)).Dot(serializeMethod).Call(),
jen.Err().Op("!=").Nil(),
).Block(
jen.Return(jen.Nil(), jen.Err()),
).Else().If(
jen.Id("i").Op("!=").Nil(),
).Block(
jen.Id("m").Index(jen.Id(codegen.This()).Dot(t.memberName(prop)).Dot(nameMethod).Call()).Op("=").Id("i"),
),
).Line())
}
serCode = serCode.Commentf("End: Serialize known properties").Line()
@ -600,7 +623,7 @@ func (t *TypeGenerator) serializationMethod() (ser *codegen.Method) {
serializeMethodName,
t.TypeName(),
/*params=*/ nil,
[]jen.Code{jen.Interface(), jen.Error()},
[]jen.Code{jen.Map(jen.String()).Interface(), jen.Error()},
[]jen.Code{
jen.Id("m").Op(":=").Make(
jen.Map(jen.String()).Interface(),
@ -620,22 +643,43 @@ func (t *TypeGenerator) lessMethod() (less *codegen.Method) {
lessCode = lessCode.Add(
jen.Commentf("Compare property %q", prop.PropertyName()).Line(),
jen.If(
jen.Id(codegen.This()).Dot(t.memberName(prop)).Dot(compareLessMethod).Call(
jen.List(
jen.Id("lhs"),
jen.Id("rhs"),
).Op(":=").List(
jen.Id(codegen.This()).Dot(t.memberName(prop)),
jen.Id("o").Dot(
fmt.Sprintf(getMethodFormat, t.memberName(prop)),
).Call(),
),
jen.Id("lhs").Op("!=").Nil().Op("&&").Id("rhs").Op("!=").Nil(),
).Block(
jen.If(
jen.Id("lhs").Dot(compareLessMethod).Call(
jen.Id("rhs"),
),
).Block(
jen.Return(jen.True()),
).Else().If(
jen.Id("rhs").Dot(compareLessMethod).Call(
jen.Id("lhs"),
),
).Block(
jen.Return(jen.False()),
),
).Else().If(
jen.Id("lhs").Op("==").Nil().Op("&&").Id("rhs").Op("!=").Nil(),
).Block(
jen.Commentf("Nil is less than anything else"),
jen.Return(jen.True()),
).Else().If(
jen.Id("o").Dot(
fmt.Sprintf(getMethodFormat, t.memberName(prop)),
).Call().Dot(compareLessMethod).Call(
jen.Id(codegen.This()).Dot(t.memberName(prop)),
),
jen.Id("rhs").Op("!=").Nil().Op("&&").Id("rhs").Op("==").Nil(),
).Block(
jen.Commentf("Anything else is greater than nil"),
jen.Return(jen.False()),
).Line())
),
jen.Commentf("Else: Both are nil"),
jen.Line())
}
lessCode = lessCode.Commentf("End: Compare known properties").Line()
unknownCode := jen.Commentf("Begin: Compare unknown properties (only by number of them)").Line().If(
@ -688,7 +732,9 @@ func (t *TypeGenerator) deserializationFn() (deser *codegen.Function) {
jen.Err().Op("!=").Nil(),
).Block(
jen.Return(jen.Nil(), jen.Err()),
).Else().Block(
).Else().If(
jen.Id("p").Op("!=").Nil(),
).Block(
jen.Id(codegen.This()).Dot(t.memberName(prop)).Op("=").Id("p"),
).Line())
}

View file

@ -252,6 +252,13 @@ func (a *anyURI) Apply(key string, value interface{}, ctx *rdf.ParsingContext) (
jen.Id(codegen.This()),
jen.Err(),
),
).Else().If(
jen.Len(jen.Id("u").Dot("Scheme")).Op("==").Lit(0),
).Block(
jen.Err().Op("=").Qual("fmt", "Errorf").Call(
jen.Lit("%v cannot be interpreted as a xsd:anyURI: no scheme"),
jen.Id(codegen.This()),
),
),
).Else().Block(
jen.Err().Op("=").Qual("fmt", "Errorf").Call(