Properly handle natural language map.

- Deserialization now happens correctly into the rdf:langString
  property.
- Kluges to make sure the member is being set and referred to in the
  generation code.
- No longer generate special member for natural language map nor a
  special Is method. Use the rdf:langString generated proeprties.
- Keep the Set/Get special language members for handling individual
  languages.
This commit is contained in:
Cory Slep 2019-02-09 13:22:20 +01:00
parent aca6f4e857
commit 8a7ec8c3a9
6 changed files with 169 additions and 72 deletions

View file

@ -670,7 +670,7 @@ func (c *Converter) convertFunctionalProperty(p rdf.VocabularyProperty,
if len(examples) > 0 {
comment = fmt.Sprintf("%s\n\n%s", comment, strings.Join(examples, "\n\n"))
}
fp = gen.NewFunctionalPropertyGenerator(
fp, e = gen.NewFunctionalPropertyGenerator(
v.GetName(),
v.URI,
vocabNameToAlias(v.GetName()),
@ -679,6 +679,9 @@ func (c *Converter) convertFunctionalProperty(p rdf.VocabularyProperty,
comment,
k,
p.NaturalLanguageMap)
if e != nil {
return
}
e = backPopulateProperty(v.Registry, p, genRefs, fp)
if e != nil {
return
@ -712,7 +715,7 @@ func (c *Converter) convertNonFunctionalProperty(p rdf.VocabularyProperty,
if len(examples) > 0 {
comment = fmt.Sprintf("%s\n\n%s", comment, strings.Join(examples, "\n\n"))
}
nfp = gen.NewNonFunctionalPropertyGenerator(
nfp, e = gen.NewNonFunctionalPropertyGenerator(
v.GetName(),
v.URI,
vocabNameToAlias(v.GetName()),
@ -721,6 +724,9 @@ func (c *Converter) convertNonFunctionalProperty(p rdf.VocabularyProperty,
comment,
k,
p.NaturalLanguageMap)
if e != nil {
return
}
e = backPopulateProperty(v.Registry, p, genRefs, nfp)
if e != nil {
return

View file

@ -34,7 +34,20 @@ func NewFunctionalPropertyGenerator(vocabName string,
name Identifier,
comment string,
kinds []Kind,
hasNaturalLanguageMap bool) *FunctionalPropertyGenerator {
hasNaturalLanguageMap bool) (*FunctionalPropertyGenerator, error) {
// Ensure that the natural language map has the langString kind.
if hasNaturalLanguageMap {
found := false
for _, k := range kinds {
if k.Name.LowerName == "langString" {
found = true
break
}
}
if !found {
return nil, fmt.Errorf("Property has natural language map, but not an rdf:langString kind")
}
}
return &FunctionalPropertyGenerator{
PropertyGenerator: PropertyGenerator{
vocabName: vocabName,
@ -46,7 +59,7 @@ func NewFunctionalPropertyGenerator(vocabName string,
comment: comment,
kinds: kinds,
},
}
}, nil
}
// InterfaceDefinition creates an interface definition in the provided package.
@ -156,25 +169,6 @@ func (p *FunctionalPropertyGenerator) funcs() []*codegen.Method {
),
}
if p.hasNaturalLanguageMap {
// IsLanguageMap Method
methods = append(methods,
codegen.NewCommentedValueMethod(
p.GetPrivatePackage().Path(),
isLanguageMapMethod,
p.StructName(),
/*params=*/ nil,
[]jen.Code{jen.Bool()},
[]jen.Code{
jen.Return(jen.Id(codegen.This()).Dot(langMapMember).Op("!=").Nil()),
},
fmt.Sprintf(
"%s determines if this property is represented by a natural language map. When true, use %s, %s, and %s methods to access and mutate the natural language map. The %s method can be used to clear the natural language map. Note that this method is only used for natural language representations, and does not determine the presence nor absence of other values for this property.",
isLanguageMapMethod,
hasLanguageMethod,
getLanguageMethod,
setLanguageMethod,
p.clearMethodName(),
)))
// HasLanguage Method
methods = append(methods,
codegen.NewCommentedValueMethod(
@ -366,6 +360,20 @@ func (p *FunctionalPropertyGenerator) serializationFuncs() (*codegen.Method, *co
typeDeserializeFns = typeDeserializeFns.Add(tmp)
}
}
mapProperty := jen.Empty()
if p.hasNaturalLanguageMap {
mapProperty = jen.If(
jen.Id("!ok"),
).Block(
jen.Commentf("Attempt to find the map instead."),
jen.List(
jen.Id("i"),
jen.Id("ok"),
).Op("=").Id("m").Index(
jen.Id("propName").Op("+").Lit("Map"),
),
)
}
var deserialize *codegen.Function
if p.asIterator {
deserialize = codegen.NewCommentedFunction(
@ -384,13 +392,7 @@ func (p *FunctionalPropertyGenerator) serializationFuncs() (*codegen.Method, *co
).Block(
jen.Id("alias").Op("=").Id("a"),
),
p.wrapDeserializeCode(valueDeserializeFns, typeDeserializeFns).Line().Return(
jen.Nil(),
jen.Qual("fmt", "Errorf").Call(
jen.Lit("could not deserialize %q property"),
jen.Lit(p.PropertyName()),
),
),
p.wrapDeserializeCode(valueDeserializeFns, typeDeserializeFns),
},
fmt.Sprintf("%s creates an iterator from an element that has been unmarshalled from a text or binary format.", p.DeserializeFnName()))
} else {
@ -421,15 +423,14 @@ func (p *FunctionalPropertyGenerator) serializationFuncs() (*codegen.Method, *co
jen.Lit(p.PropertyName()),
),
),
jen.If(
jen.List(
jen.Id("i"),
jen.Id("ok"),
).Op(":=").Id("m").Index(
jen.Id("propName"),
),
jen.List(
jen.Id("i"),
jen.Id("ok"),
).Block(
).Op(":=").Id("m").Index(
jen.Id("propName"),
),
mapProperty,
jen.If(jen.Id("ok")).Block(
p.wrapDeserializeCode(valueDeserializeFns, typeDeserializeFns),
),
jen.Return(
@ -471,9 +472,6 @@ func (p *FunctionalPropertyGenerator) singleTypeDef() *codegen.Struct {
}
// TODO: Normalize alias of values when setting on this property.
kindMembers = append(kindMembers, jen.Id(aliasMember).String())
if p.hasNaturalLanguageMap {
kindMembers = append(kindMembers, jen.Id(langMapMember).Map(jen.String()).String())
}
if p.asIterator {
kindMembers = append(kindMembers, jen.Id(myIndexMemberName).Int())
kindMembers = append(kindMembers, jen.Id(parentMemberName).Qual(p.GetPublicPackage().Path(), p.parentTypeInterfaceName()))
@ -487,6 +485,7 @@ func (p *FunctionalPropertyGenerator) singleTypeDef() *codegen.Struct {
methods = append(methods, p.singleTypeFuncs()...)
methods = append(methods, p.funcs()...)
methods = append(methods, p.commonMethods()...)
methods = append(methods, p.nameMethod())
return codegen.NewStruct(comment,
p.StructName(),
methods,
@ -719,9 +718,6 @@ func (p *FunctionalPropertyGenerator) multiTypeDef() *codegen.Struct {
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())
}
explanation := "At most, one type of value can be present, or none at all. Setting a value will clear the other types of values so that only one of the 'Is' methods will return true. It is possible to clear all values, so that this property is empty."
comment := fmt.Sprintf(
"%s is the functional property %q. It is permitted to be one of multiple value types. %s",
@ -747,6 +743,7 @@ func (p *FunctionalPropertyGenerator) multiTypeDef() *codegen.Struct {
methods = append(methods, p.multiTypeFuncs()...)
methods = append(methods, p.funcs()...)
methods = append(methods, p.commonMethods()...)
methods = append(methods, p.nameMethod())
return codegen.NewStruct(comment,
p.StructName(),
methods,
@ -1150,3 +1147,34 @@ func (p *FunctionalPropertyGenerator) hasValueKind() bool {
}
return false
}
// nameMethod returns the Name method for this functional property.
func (p *FunctionalPropertyGenerator) nameMethod() *codegen.Method{
nameImpl := jen.Return(
jen.Lit(p.PropertyName()),
)
if p.hasNaturalLanguageMap {
nameImpl = jen.If(
jen.Id(codegen.This()).Dot(isLanguageMapMethod).Call(),
).Block(
jen.Return(
jen.Lit(p.PropertyName() + "Map"),
),
).Else().Block(
jen.Return(
jen.Lit(p.PropertyName()),
),
)
}
return codegen.NewCommentedValueMethod(
p.GetPrivatePackage().Path(),
nameMethod,
p.StructName(),
/*params=*/ nil,
[]jen.Code{jen.String()},
[]jen.Code{
nameImpl,
},
fmt.Sprintf("%s returns the name of this property: %q.", nameMethod, p.PropertyName()),
)
}

View file

@ -35,7 +35,20 @@ func NewNonFunctionalPropertyGenerator(vocabName string,
name Identifier,
comment string,
kinds []Kind,
hasNaturalLanguageMap bool) *NonFunctionalPropertyGenerator {
hasNaturalLanguageMap bool) (*NonFunctionalPropertyGenerator, error) {
// Ensure that the natural language map has the langString kind.
if hasNaturalLanguageMap {
found := false
for _, k := range kinds {
if k.Name.LowerName == "langString" {
found = true
break
}
}
if !found {
return nil, fmt.Errorf("Property has natural language map, but not an rdf:langString kind")
}
}
return &NonFunctionalPropertyGenerator{
PropertyGenerator: PropertyGenerator{
vocabName: vocabName,
@ -47,7 +60,7 @@ func NewNonFunctionalPropertyGenerator(vocabName string,
comment: comment,
kinds: kinds,
},
}
}, nil
}
// InterfaceDefinitions creates interface definitions in the provided package.
@ -539,6 +552,7 @@ func (p *NonFunctionalPropertyGenerator) funcs() []*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)))
methods = append(methods, p.commonMethods()...)
methods = append(methods, p.nameMethod())
return methods
}
@ -621,6 +635,20 @@ func (p *NonFunctionalPropertyGenerator) serializationFuncs() (*codegen.Method,
),
)
}
mapProperty := jen.Empty()
if p.hasNaturalLanguageMap {
mapProperty = jen.If(
jen.Id("!ok"),
).Block(
jen.Commentf("Attempt to find the map instead."),
jen.List(
jen.Id("i"),
jen.Id("ok"),
).Op("=").Id("m").Index(
jen.Id("propName").Op("+").Lit("Map"),
),
)
}
deserialize := codegen.NewCommentedFunction(
p.GetPrivatePackage().Path(),
p.DeserializeFnName(),
@ -647,13 +675,14 @@ func (p *NonFunctionalPropertyGenerator) serializationFuncs() (*codegen.Method,
jen.Lit(p.PropertyName()),
),
),
jen.List(
jen.Id("i"),
jen.Id("ok"),
).Op(":=").Id("m").Index(
jen.Id("propName"),
),
mapProperty,
jen.If(
jen.List(
jen.Id("i"),
jen.Id("ok"),
).Op(":=").Id("m").Index(
jen.Id("propName"),
),
jen.Id("ok"),
).Block(
jen.Id(codegen.This()).Op(":=").Op("&").Id(p.StructName()).Values(
@ -717,3 +746,36 @@ func (p *NonFunctionalPropertyGenerator) thisIRI() *jen.Statement {
}
return nil
}
// nameMethod returns the Name method for this non-functional property.
func (p *NonFunctionalPropertyGenerator) nameMethod() *codegen.Method{
nameImpl := jen.Return(
jen.Lit(p.PropertyName()),
)
if p.hasNaturalLanguageMap {
nameImpl = jen.If(
jen.Id(codegen.This()).Dot(lenMethod).Call().Op("==").Lit(1).Op(
"&&",
).Id(codegen.This()).Dot(atMethodName).Call(jen.Lit(0)).Dot(isLanguageMapMethod).Call(),
).Block(
jen.Return(
jen.Lit(p.PropertyName() + "Map"),
),
).Else().Block(
jen.Return(
jen.Lit(p.PropertyName()),
),
)
}
return codegen.NewCommentedValueMethod(
p.GetPrivatePackage().Path(),
nameMethod,
p.StructName(),
/*params=*/ nil,
[]jen.Code{jen.String()},
[]jen.Code{
nameImpl,
},
fmt.Sprintf("%s returns the name of this property: %q.", nameMethod, p.PropertyName()),
)
}

View file

@ -32,7 +32,6 @@ const (
nameMethod = "Name"
serializeIteratorMethod = "serialize"
deserializeIteratorMethod = "deserialize"
isLanguageMapMethod = "IsLanguageMap"
hasLanguageMethod = "HasLanguage"
getLanguageMethod = "GetLanguage"
setLanguageMethod = "SetLanguage"
@ -45,7 +44,10 @@ const (
contextMethod = "JSONLDContext"
// Member names for generated code
unknownMemberName = "unknown"
langMapMember = "langMap"
// Reference to the rdf:langString member! Kludge: both of these must be
// kept in sync with the generated code.
langMapMember = "langStringMember"
isLanguageMapMethod = "IsLangString"
// Kind Index constants
iriKindIndex = -2
noneOrUnknownKindIndex = -1
@ -202,6 +204,12 @@ type PropertyGenerator struct {
asIterator bool
}
// HasNaturalLanguageMap returns whether this property has a natural language
// map.
func (p *PropertyGenerator) HasNaturalLanguageMap() bool {
return p.hasNaturalLanguageMap
}
// VocabName returns this property's vocabulary name.
func (p *PropertyGenerator) VocabName() string {
return p.vocabName
@ -369,23 +377,7 @@ func (p *PropertyGenerator) clearMethodName() string {
}
// commonMethods returns methods common to every property.
func (p *PropertyGenerator) commonMethods() []*codegen.Method {
// Name method
m := []*codegen.Method{
codegen.NewCommentedValueMethod(
p.GetPrivatePackage().Path(),
nameMethod,
p.StructName(),
/*params=*/ nil,
[]jen.Code{jen.String()},
[]jen.Code{
jen.Return(
jen.Lit(p.PropertyName()),
),
},
fmt.Sprintf("%s returns the name of this property: %q.", nameMethod, p.PropertyName()),
),
}
func (p *PropertyGenerator) commonMethods() (m []*codegen.Method) {
if p.asIterator {
// Next & Prev methods
m = append(m, codegen.NewCommentedValueMethod(

View file

@ -82,6 +82,7 @@ type Property interface {
InterfaceName() string
SetKindFns(docName, idName string, kind *jen.Statement, deser *codegen.Method) error
DeserializeFnName() string
HasNaturalLanguageMap() bool
}
// TypeGenerator represents an ActivityStream type definition to generate in Go.
@ -780,6 +781,13 @@ func (t *TypeGenerator) deserializationFn() (deser *codegen.Function) {
).Block(
jen.Continue(),
)
if prop.HasNaturalLanguageMap() {
knownProps = knownProps.Else().If(
jen.Id("k").Op("==").Lit(prop.PropertyName() + "Map"),
).Block(
jen.Continue(),
)
}
}
knownProps = knownProps.Commentf("End: Code that ensures a property name is unknown").Line()
unknownCode := jen.Commentf("Begin: Unknown deserialization").Line().For(

View file

@ -158,10 +158,11 @@ func (l *langstring) Exit(key string, ctx *ParsingContext) (bool, error) {
// 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 _, ref := range p.Range {
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
}
}