diff --git a/astool/codegen/interface.go b/astool/codegen/interface.go index 4611a34..8495469 100644 --- a/astool/codegen/interface.go +++ b/astool/codegen/interface.go @@ -32,6 +32,15 @@ type FunctionSignature struct { Comment string } +// Signature returns the uncommented raw signature. +func (f FunctionSignature) Signature() jen.Code { + sig := jen.Func().Params(f.Params...) + if len(f.Ret) > 0 { + sig.Params(f.Ret...) + } + return sig +} + // Interface manages and generates a Golang interface definition. type Interface struct { qual *jen.Statement diff --git a/astool/codegen/method.go b/astool/codegen/method.go index c2c3497..6c56953 100644 --- a/astool/codegen/method.go +++ b/astool/codegen/method.go @@ -100,6 +100,16 @@ func (m Function) QualifiedName() *jen.Statement { return m.qual.Clone() } +// ToFunctionSignature obtains this function's FunctionSignature. +func (m Function) ToFunctionSignature() FunctionSignature { + return FunctionSignature{ + Name: m.Name(), + Params: m.params, + Ret: m.ret, + Comment: m.comment, + } +} + // Method represents a method on a type, not a free function, for Go code to be // generated. type Method struct { diff --git a/astool/convert/convert.go b/astool/convert/convert.go index 49649d5..65bc185 100644 --- a/astool/convert/convert.go +++ b/astool/convert/convert.go @@ -218,17 +218,16 @@ const ( // implementations. Developers' applications should only rely on the interfaces, // which are used internally anyway. type Converter struct { - GenRoot *gen.PackageManager - PackagePolicy PackagePolicy + GenRoot *gen.PackageManager + PackagePolicy PackagePolicy + typeProperty *gen.PropertyGenerator + typePropertyVocabName string } // Convert turns a ParsedVocabulary into a set of code-generated files. -func (c Converter) Convert(p *rdf.ParsedVocabulary) (f []*File, e error) { +func (c *Converter) Convert(p *rdf.ParsedVocabulary) (f []*File, e error) { v := newVocabulary() done := make(map[string]bool) - // We keep a reference of the type property constructor for convenience - // when constructing types, to auto-populate the property. - var typePropertyConstructor *codegen.Function = nil // Step 1: Convert referenced specifications for i, vocabURI := range p.Order { refP := p.Clone() @@ -238,7 +237,7 @@ func (c Converter) Convert(p *rdf.ParsedVocabulary) (f []*File, e error) { } var refV vocabulary - refV, typePropertyConstructor, e = c.convertVocabulary(refP, v.References, typePropertyConstructor) + refV, e = c.convertVocabulary(refP, v.References) if e != nil { return } @@ -253,7 +252,7 @@ func (c Converter) Convert(p *rdf.ParsedVocabulary) (f []*File, e error) { done[vocabURI] = true } // Step 2: Intrinsically-known vocabularies won't appear in p.Order - recurV, err := c.convertReferenceVocabularyRecursively(done, p, v.References, typePropertyConstructor) + recurV, err := c.convertReferenceVocabularyRecursively(done, p, v.References) if err != nil { e = err return @@ -274,7 +273,7 @@ func (c Converter) Convert(p *rdf.ParsedVocabulary) (f []*File, e error) { // convertReferenceVocabularyRecursively will convert all references nested in // all vocabularies and results in a flattened converted map. -func (c Converter) convertReferenceVocabularyRecursively(skip map[string]bool, p *rdf.ParsedVocabulary, refs map[string]*vocabulary, typePropertyConstructor *codegen.Function) (v map[string]*vocabulary, e error) { +func (c *Converter) convertReferenceVocabularyRecursively(skip map[string]bool, p *rdf.ParsedVocabulary, refs map[string]*vocabulary) (v map[string]*vocabulary, e error) { v = make(map[string]*vocabulary) for k, _ := range p.References { if skip[k] { @@ -284,14 +283,14 @@ func (c Converter) convertReferenceVocabularyRecursively(skip map[string]bool, p refP.Vocab = *refP.References[k] delete(refP.References, k) var refV vocabulary - refV, typePropertyConstructor, e = c.convertVocabulary(refP, refs, typePropertyConstructor) + refV, e = c.convertVocabulary(refP, refs) if e != nil { return } v[k] = &refV // Recur var recurVocab map[string]*vocabulary - recurVocab, e = c.convertReferenceVocabularyRecursively(skip, refP, refs, typePropertyConstructor) + recurVocab, e = c.convertReferenceVocabularyRecursively(skip, refP, refs) if e != nil { return } @@ -305,7 +304,7 @@ func (c Converter) convertReferenceVocabularyRecursively(skip map[string]bool, p // convertToFiles takes the generators for a vocabulary and maps them into a // file structure. -func (c Converter) convertToFiles(v vocabulary) (f []*File, e error) { +func (c *Converter) convertToFiles(v vocabulary) (f []*File, e error) { pub := c.GenRoot.PublicPackage() // References for _, ref := range v.References { @@ -324,7 +323,7 @@ func (c Converter) convertToFiles(v vocabulary) (f []*File, e error) { return } f = append(f, files...) - pkgFiles, err := c.packageFiles(*ref) + pkgFiles, err := c.packageFiles(*ref, v.Manager) if err != nil { e = err return @@ -347,7 +346,7 @@ func (c Converter) convertToFiles(v vocabulary) (f []*File, e error) { return } f = append(f, files...) - pkgFiles, err := c.packageFiles(v) + pkgFiles, err := c.packageFiles(v, v.Manager) if err != nil { e = err return @@ -355,7 +354,7 @@ func (c Converter) convertToFiles(v vocabulary) (f []*File, e error) { f = append(f, pkgFiles...) // Init file var file *File - file, e = c.initFile(pub, v) + file, e = c.initFile(pub, v, v.Manager) if e != nil { return } @@ -395,8 +394,7 @@ func (c Converter) convertToFiles(v vocabulary) (f []*File, e error) { // // This feels very hacky and could be decoupled using standard design patterns, // but since there is no need, it isn't addressed now. -func (c Converter) convertVocabulary(p *rdf.ParsedVocabulary, refs map[string]*vocabulary, typePropertyCstr *codegen.Function) (v vocabulary, typePropertyConstructor *codegen.Function, e error) { - typePropertyConstructor = typePropertyCstr +func (c *Converter) convertVocabulary(p *rdf.ParsedVocabulary, refs map[string]*vocabulary) (v vocabulary, e error) { v = newVocabulary() v.Name = p.Vocab.Name v.URI = p.Vocab.URI @@ -406,21 +404,23 @@ func (c Converter) convertVocabulary(p *rdf.ParsedVocabulary, refs map[string]*v for k, prop := range p.Vocab.Properties { if prop.Functional { v.FProps[k], e = c.convertFunctionalProperty(prop, v.Values, p.Vocab, p.References, refs) - if typePropertyConstructor == nil && prop.Name == typePropertyName { - typePropertyConstructor = v.FProps[k].ConstructorFn() + if c.typeProperty == nil && prop.Name == typePropertyName { + c.typeProperty = &(v.FProps[k].PropertyGenerator) + c.typePropertyVocabName = v.Name } } else { v.NFProps[k], e = c.convertNonFunctionalProperty(prop, v.Values, p.Vocab, p.References, refs) - if typePropertyConstructor == nil && prop.Name == typePropertyName { - typePropertyConstructor = v.NFProps[k].ConstructorFn() + if c.typeProperty == nil && prop.Name == typePropertyName { + c.typeProperty = &(v.NFProps[k].PropertyGenerator) + c.typePropertyVocabName = v.Name } } if e != nil { return } } - if typePropertyConstructor == nil { - e = fmt.Errorf("convertVocabulary: either passed in or could not find \"type\" property and its constructor") + if c.typeProperty == nil { + e = fmt.Errorf("convertVocabulary: could not find \"type\" property and its constructor") return } // Instead of building a dependency tree, naively keep iterating through @@ -437,7 +437,7 @@ func (c Converter) convertVocabulary(p *rdf.ParsedVocabulary, refs map[string]*v for i, t := range allTypes { if c.allExtendsAreIn(p.Vocab.Registry, t, v.Types, refs) { var tg *gen.TypeGenerator - tg, e = c.convertType(t, p.Vocab, v.FProps, v.NFProps, v.Types, refs, typePropertyConstructor) + tg, e = c.convertType(t, p.Vocab, v.FProps, v.NFProps, v.Types, refs) if e != nil { return } @@ -458,7 +458,7 @@ func (c Converter) convertVocabulary(p *rdf.ParsedVocabulary, refs map[string]*v } // convertGenRoot creates code-wide code generators. -func (c Converter) convertGenRoot(v *vocabulary) (e error) { +func (c *Converter) convertGenRoot(v *vocabulary) (e error) { v.Manager, e = gen.NewManagerGenerator( c.GenRoot.PublicPackage(), v.allTypeArray(), @@ -471,7 +471,7 @@ func (c Converter) convertGenRoot(v *vocabulary) (e error) { // vocabulary. // // Returns all nils if the property has been defined in a later vocabulary. -func (c Converter) existingProperty(registry *rdf.RDFRegistry, r rdf.VocabularyReference, genRefs map[string]*vocabulary) (g gen.Property, e error) { +func (c *Converter) existingProperty(registry *rdf.RDFRegistry, r rdf.VocabularyReference, genRefs map[string]*vocabulary) (g gen.Property, e error) { var url string url, e = registry.ResolveAlias(r.Vocab) if e != nil { @@ -505,13 +505,12 @@ func (c Converter) existingProperty(registry *rdf.RDFRegistry, r rdf.VocabularyR // // Precondition: The types it extends from and the properties it references are // already converted into their applicable generators. -func (c Converter) convertType(t rdf.VocabularyType, +func (c *Converter) convertType(t rdf.VocabularyType, v rdf.Vocabulary, existingFProps map[string]*gen.FunctionalPropertyGenerator, existingNFProps map[string]*gen.NonFunctionalPropertyGenerator, existingTypes map[string]*gen.TypeGenerator, - genRefs map[string]*vocabulary, - typePropertyConstructor *codegen.Function) (tg *gen.TypeGenerator, e error) { + genRefs map[string]*vocabulary) (tg *gen.TypeGenerator, e error) { // Determine the gen package name var pm *gen.PackageManager pm, e = c.typePackageManager(t, v.Name) @@ -642,14 +641,13 @@ func (c Converter) convertType(t rdf.VocabularyType, wop, rangeProps, ext, - disjoint, - typePropertyConstructor) + disjoint) return } // convertFunctionalProperty converts an rdf.VocabularyProperty that is // functional (can only have one value) into a FunctionalPropertyGenerator. -func (c Converter) convertFunctionalProperty(p rdf.VocabularyProperty, +func (c *Converter) convertFunctionalProperty(p rdf.VocabularyProperty, kinds map[string]*gen.Kind, v rdf.Vocabulary, refs map[string]*rdf.Vocabulary, @@ -691,7 +689,7 @@ func (c Converter) convertFunctionalProperty(p rdf.VocabularyProperty, // convertNonFunctionalProperty converts an rdf.VocabularyProperty that is // non-functional (can have multiple values) into a // NonFunctionalPropertyGenerator. -func (c Converter) convertNonFunctionalProperty(p rdf.VocabularyProperty, +func (c *Converter) convertNonFunctionalProperty(p rdf.VocabularyProperty, kinds map[string]*gen.Kind, v rdf.Vocabulary, refs map[string]*rdf.Vocabulary, @@ -731,7 +729,7 @@ func (c Converter) convertNonFunctionalProperty(p rdf.VocabularyProperty, } // convertValue turns a rdf.VocabularyValue into a Kind. -func (c Converter) convertValue(v rdf.VocabularyValue) *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()) @@ -749,14 +747,14 @@ func (c Converter) convertValue(v rdf.VocabularyValue) *gen.Kind { } // convertTypeToName makes a Titled version of the VocabularyType's name. -func (c Converter) convertTypeToName(v rdf.VocabularyType) string { +func (c *Converter) convertTypeToName(v rdf.VocabularyType) string { return strings.Title(v.Name) } // propertyKinds determines what Kind names are referenced by the // rdf.VocabularyProperty, which may rely on the parsing registry for these // particular files. -func (c Converter) propertyKinds(v rdf.VocabularyProperty, +func (c *Converter) propertyKinds(v rdf.VocabularyProperty, kinds map[string]*gen.Kind, vocab rdf.Vocabulary, refs map[string]*rdf.Vocabulary) (k []gen.Kind, e error) { @@ -808,7 +806,7 @@ func (c Converter) propertyKinds(v rdf.VocabularyProperty, } // getValueRoot returns the subdirectory that contains value types. -func (c Converter) getValueRoot() *gen.PackageManager { +func (c *Converter) getValueRoot() *gen.PackageManager { return c.GenRoot.Sub("values") } @@ -816,7 +814,7 @@ func (c Converter) getValueRoot() *gen.PackageManager { // // It must match the result of vocabValuePackage. Therefore, convertTypeToKind // and convertValue must also use toIdentifier. -func (c Converter) valuePackage(v *gen.Kind) gen.Package { +func (c *Converter) valuePackage(v *gen.Kind) gen.Package { return c.getValueRoot().Sub(v.Name.LowerName).PublicPackage() } @@ -824,19 +822,19 @@ func (c Converter) valuePackage(v *gen.Kind) gen.Package { // // It must match the result of valuePackage. Therefore, convertTypeToKind and // convertValue must also use toIdentifier. -func (c Converter) vocabValuePackage(v rdf.VocabularyValue) gen.Package { +func (c *Converter) vocabValuePackage(v rdf.VocabularyValue) gen.Package { return c.getValueRoot().Sub(toIdentifier(v).LowerName).PublicPackage() } // typePackageManager returns a package manager for an individual type. It may // be the same as other types depending on the code generation policy. -func (c Converter) typePackageManager(v typeNamer, vocabName string) (pkg *gen.PackageManager, e error) { +func (c *Converter) typePackageManager(v typeNamer, vocabName string) (pkg *gen.PackageManager, e error) { return c.packageManager("type_"+v.TypeName(), vocabName) } // propertyPackageManager returns a package manager for an individual property. // It may be the same as other types depending on the code generation policy. -func (c Converter) propertyPackageManager(v propertyNamer, vocabName string) (pkg *gen.PackageManager, e error) { +func (c *Converter) propertyPackageManager(v propertyNamer, vocabName string) (pkg *gen.PackageManager, e error) { return c.packageManager("property_"+v.PropertyName(), vocabName) } @@ -849,7 +847,7 @@ func (c Converter) propertyPackageManager(v propertyNamer, vocabName string) (pk // The IndividualUnderRoot policy puts each property and each type in their own // package, and each of those packages has their own public and private // subpackages. -func (c Converter) packageManager(s, vocabName string) (pkg *gen.PackageManager, e error) { +func (c *Converter) packageManager(s, vocabName string) (pkg *gen.PackageManager, e error) { s = strings.ToLower(s) switch c.PackagePolicy { case FlatUnderRoot: @@ -864,9 +862,9 @@ func (c Converter) packageManager(s, vocabName string) (pkg *gen.PackageManager, // rootFiles creates files that are applied for all vocabularies. These files // are the ones typically used by developers. -func (c Converter) rootFiles(pkg gen.Package, vocabName string, v vocabulary, m *gen.ManagerGenerator) (f []*File, e error) { - pg := gen.NewPackageGenerator() - typeCtors, propCtors, ext, disj, extBy := pg.RootDefinitions(vocabName, m, v.typeArray(), v.propArray()) +func (c *Converter) rootFiles(pkg gen.Package, vocabName string, v vocabulary, m *gen.ManagerGenerator) (f []*File, e error) { + pg := gen.NewPackageGenerator(c.typePropertyVocabName, m, c.typeProperty) + typeCtors, propCtors, ext, disj, extBy := pg.RootDefinitions(vocabName, v.typeArray(), v.propArray()) lowerVocabName := strings.ToLower(vocabName) if file := funcsToFile(pkg, typeCtors, fmt.Sprintf("gen_pkg_%s_type_constructors.go", lowerVocabName)); file != nil { f = append(f, file) @@ -888,8 +886,8 @@ func (c Converter) rootFiles(pkg gen.Package, vocabName string, v vocabulary, m // initFile creates the file with the init function that hooks together the // runtime Manager. -func (c Converter) initFile(pkg gen.Package, root vocabulary) (f *File, e error) { - pg := gen.NewPackageGenerator() +func (c *Converter) initFile(pkg gen.Package, root vocabulary, m *gen.ManagerGenerator) (f *File, e error) { + pg := gen.NewPackageGenerator(c.typePropertyVocabName, m, c.typeProperty) globalVar, initFn := pg.InitDefinitions(pkg, root.allTypeArray(), root.allPropArray()) initFile := jen.NewFilePath(pkg.Path()) initFile.Add(globalVar).Line().Add(initFn.Definition()).Line() @@ -911,10 +909,10 @@ func (c Converter) initFile(pkg gen.Package, root vocabulary) (f *File, e error) // In the IndividualUnderRoot policy, multiple are needed. Since each type and // property are in their own package, one package file is needed in each of // those types' and properties' subpackage. -func (c Converter) packageFiles(v vocabulary) (f []*File, e error) { +func (c *Converter) packageFiles(v vocabulary, m *gen.ManagerGenerator) (f []*File, e error) { switch c.PackagePolicy { case FlatUnderRoot: - pg := gen.NewPackageGenerator() + pg := gen.NewPackageGenerator(c.typePropertyVocabName, m, c.typeProperty) if tArr := v.typeArray(); len(tArr) > 0 { // Only need one for all types. pubI := pg.PublicDefinitions(tArr) @@ -946,11 +944,15 @@ func (c Converter) packageFiles(v vocabulary) (f []*File, e error) { priv = pArr[0].GetPrivatePackage() } file := jen.NewFilePath(priv.Path()) - file.Add(s).Line() + for _, elem := range s { + file.Add(elem).Line() + } for _, elem := range i { file.Add(elem.Definition()).Line() } - file.Add(fn.Definition()).Line() + for _, elem := range fn { + file.Add(elem.Definition()).Line() + } f = append(f, &File{ F: file, FileName: "gen_pkg.go", @@ -968,7 +970,7 @@ func (c Converter) packageFiles(v vocabulary) (f []*File, e error) { case IndividualUnderRoot: for _, tg := range v.Types { var file []*File - file, e = c.typePackageFiles(tg, v.Name) + file, e = c.typePackageFiles(tg, v.Name, m) if e != nil { return } @@ -998,9 +1000,9 @@ func (c Converter) packageFiles(v vocabulary) (f []*File, e error) { // typePackageFile creates the package-level files necessary for a type if it // is being generated in its own package. -func (c Converter) typePackageFiles(tg *gen.TypeGenerator, vocabName string) (f []*File, e error) { +func (c *Converter) typePackageFiles(tg *gen.TypeGenerator, vocabName string, m *gen.ManagerGenerator) (f []*File, e error) { // Only need one for all types. - tpg := gen.NewTypePackageGenerator() + tpg := gen.NewTypePackageGenerator(c.typePropertyVocabName, m, c.typeProperty) pubI := tpg.PublicDefinitions([]*gen.TypeGenerator{tg}) // Public pub := tg.PublicPackage() @@ -1024,11 +1026,15 @@ func (c Converter) typePackageFiles(tg *gen.TypeGenerator, vocabName string) (f s, i, fn := tpg.PrivateDefinitions([]*gen.TypeGenerator{tg}) priv := tg.PrivatePackage() file = jen.NewFilePath(priv.Path()) - file.Add(s).Line() + for _, elem := range s { + file.Add(elem).Line() + } for _, elem := range i { file.Add(elem.Definition()).Line() } - file.Add(fn.Definition()).Line() + for _, elem := range fn { + file.Add(elem.Definition()).Line() + } f = append(f, &File{ F: file, FileName: "gen_pkg.go", @@ -1047,7 +1053,7 @@ func (c Converter) typePackageFiles(tg *gen.TypeGenerator, vocabName string) (f // propertyPackageFiles creates the package-level files necessary for a property // if it is being generated in its own package. -func (c Converter) propertyPackageFiles(pg *gen.PropertyGenerator, vocabName string) (f []*File, e error) { +func (c *Converter) propertyPackageFiles(pg *gen.PropertyGenerator, vocabName string) (f []*File, e error) { // Only need one for all types. ppg := gen.NewPropertyPackageGenerator() // Public Package Documentation -- this may collide, but it's all the @@ -1088,7 +1094,7 @@ func (c Converter) propertyPackageFiles(pg *gen.PropertyGenerator, vocabName str } // resolverFiles creates the files necessary for the resolvers. -func (c Converter) resolverFiles(pkg gen.Package, manGen *gen.ManagerGenerator, root vocabulary) (files []*File, e error) { +func (c *Converter) resolverFiles(pkg gen.Package, manGen *gen.ManagerGenerator, root vocabulary) (files []*File, e error) { rg := gen.NewResolverGenerator(root.allTypeArray(), manGen, pkg) jsonRes, typeRes, intRes, typePredRes, intPredRes, errDefs, isUnFn, iFaces := rg.Definition() // Utils @@ -1150,7 +1156,7 @@ func (c Converter) resolverFiles(pkg gen.Package, manGen *gen.ManagerGenerator, // allExtendsAreIn determines if a VocabularyType's parents are all already // converted to a TypeGenerator. -func (c Converter) allExtendsAreIn(registry *rdf.RDFRegistry, t rdf.VocabularyType, v map[string]*gen.TypeGenerator, genRefs map[string]*vocabulary) bool { +func (c *Converter) allExtendsAreIn(registry *rdf.RDFRegistry, t rdf.VocabularyType, v map[string]*gen.TypeGenerator, genRefs map[string]*vocabulary) bool { for _, e := range t.Extends { if len(e.Vocab) != 0 { _, err := existingType(registry, e, genRefs) @@ -1163,7 +1169,7 @@ func (c Converter) allExtendsAreIn(registry *rdf.RDFRegistry, t rdf.VocabularyTy } // toFiles converts a vocabulary's types and properties to files. -func (c Converter) toFiles(v vocabulary) (f []*File, e error) { +func (c *Converter) toFiles(v vocabulary) (f []*File, e error) { for _, i := range v.FProps { var pm *gen.PackageManager pm, e = c.propertyPackageManager(i, v.Name) diff --git a/astool/gen/pkg.go b/astool/gen/pkg.go index 8b97b62..a4b813b 100644 --- a/astool/gen/pkg.go +++ b/astool/gen/pkg.go @@ -149,16 +149,28 @@ func (p Package) Parent() *PackageManager { } const ( - managerInterfaceName = "privateManager" - setManagerFunctionName = "SetManager" + managerInterfaceName = "privateManager" + setManagerFunctionName = "SetManager" + setTypePropertyConstructorName = "SetTypePropertyConstructor" ) // TypePackageGenerator manages generating one-time files needed for types. -type TypePackageGenerator struct{} +type TypePackageGenerator struct { + typeVocabName string + m *ManagerGenerator + typeProperty *PropertyGenerator +} // NewTypePackageGenerator creates a new TypePackageGenerator. -func NewTypePackageGenerator() *TypePackageGenerator { - return &TypePackageGenerator{} +func NewTypePackageGenerator( + typeVocabName string, + m *ManagerGenerator, + typeProperty *PropertyGenerator) *TypePackageGenerator { + return &TypePackageGenerator{ + typeVocabName: typeVocabName, + m: m, + typeProperty: typeProperty, + } } // PublicDefinitions creates the public-facing code generated definitions needed @@ -175,11 +187,12 @@ func (t *TypePackageGenerator) PublicDefinitions(tgs []*TypeGenerator) (typeI *c // // Precondition: The passed-in generators are the complete set of type // generators within a package. len(tgs) > 0 -func (t *TypePackageGenerator) PrivateDefinitions(tgs []*TypeGenerator) (mgrVar *jen.Statement, mgrI []*codegen.Interface, setMgrFn *codegen.Function) { +func (t *TypePackageGenerator) PrivateDefinitions(tgs []*TypeGenerator) ([]*jen.Statement, []*codegen.Interface, []*codegen.Function) { pkg := tgs[0].PrivatePackage() s, i, f := privateManagerHookDefinitions(pkg, tgs, nil) interfaces := []*codegen.Interface{i, ContextInterface(pkg)} - return s, interfaces, f + cv, setCv := privateTypePropertyConstructor(pkg, toPublicConstructor(t.typeVocabName, t.m, t.typeProperty)) + return []*jen.Statement{s, cv}, interfaces, []*codegen.Function{f, setCv} } // PropertyPackageGenerator manages generating one-time files needed for @@ -202,20 +215,31 @@ func (p *PropertyPackageGenerator) PrivateDefinitions(pgs []*PropertyGenerator) // PackageGenerator maanges generating one-time files needed for both type and // property implementations. -type PackageGenerator struct{} - -// NewPackageGenerator creates a new PackageGenerator. -func NewPackageGenerator() *PackageGenerator { - return &PackageGenerator{} +type PackageGenerator struct { + typeVocabName string + m *ManagerGenerator + typeProperty *PropertyGenerator } +// NewPackageGenerator creates a new PackageGenerator. +func NewPackageGenerator(typeVocabName string, m *ManagerGenerator, typeProperty *PropertyGenerator) *PackageGenerator { + return &PackageGenerator{ + typeVocabName: typeVocabName, + m: m, + typeProperty: typeProperty, + } +} + +// InitDefinitions returns the root init function needed to inject proper global +// package-private variables needed at runtime. This is the dependency injection +// into the implementation. func (t *PackageGenerator) InitDefinitions(pkg Package, tgs []*TypeGenerator, pgs []*PropertyGenerator) (globalManager *jen.Statement, init *codegen.Function) { - return genInit(pkg, tgs, pgs) + return genInit(pkg, tgs, pgs, toPublicConstructor(t.typeVocabName, t.m, t.typeProperty)) } // RootDefinitions creates functions needed at the root level of the package declarations. -func (t *PackageGenerator) RootDefinitions(vocabName string, m *ManagerGenerator, tgs []*TypeGenerator, pgs []*PropertyGenerator) (typeCtors, propCtors, ext, disj, extBy []*codegen.Function) { - return rootDefinitions(vocabName, m, tgs, pgs) +func (t *PackageGenerator) RootDefinitions(vocabName string, tgs []*TypeGenerator, pgs []*PropertyGenerator) (typeCtors, propCtors, ext, disj, extBy []*codegen.Function) { + return rootDefinitions(vocabName, t.m, tgs, pgs) } // PublicDefinitions creates the public-facing code generated definitions needed @@ -232,7 +256,7 @@ func (t *PackageGenerator) PublicDefinitions(tgs []*TypeGenerator) *codegen.Inte // // Precondition: The passed-in generators are the complete set of type // generators within a package. One of tgs or pgs has at least one value. -func (t *PackageGenerator) PrivateDefinitions(tgs []*TypeGenerator, pgs []*PropertyGenerator) (*jen.Statement, []*codegen.Interface, *codegen.Function) { +func (t *PackageGenerator) PrivateDefinitions(tgs []*TypeGenerator, pgs []*PropertyGenerator) ([]*jen.Statement, []*codegen.Interface, []*codegen.Function) { var pkg Package if len(tgs) > 0 { pkg = tgs[0].PrivatePackage() @@ -241,7 +265,28 @@ func (t *PackageGenerator) PrivateDefinitions(tgs []*TypeGenerator, pgs []*Prope } s, i, f := privateManagerHookDefinitions(pkg, tgs, pgs) interfaces := []*codegen.Interface{i, ContextInterface(pkg)} - return s, interfaces, f + cv, setCv := privateTypePropertyConstructor(pkg, toPublicConstructor(t.typeVocabName, t.m, t.typeProperty)) + return []*jen.Statement{s, cv}, interfaces, []*codegen.Function{f, setCv} +} + +// privateTypePropertyConstructor creates common code needed by types to hook +// the type property constructor into this package at init time without +// statically linking to a specific implementation. +func privateTypePropertyConstructor(pkg Package, typePropertyConstructor *codegen.Function) (ctrVar *jen.Statement, setCtrVar *codegen.Function) { + sig := typePropertyConstructor.ToFunctionSignature().Signature() + ctrVar = jen.Var().Id(typePropertyConstructorName()).Add(sig) + setCtrVar = codegen.NewCommentedFunction( + pkg.Path(), + setTypePropertyConstructorName, + []jen.Code{ + jen.Id("f").Add(sig), + }, + /*ret=*/ nil, + []jen.Code{ + jen.Id(typePropertyConstructorName()).Op("=").Id("f"), + }, + fmt.Sprintf("%s sets the \"type\" property's constructor in the package-global variable. For internal use only, do not use as part of Application behavior. Must be called at golang init time. Permits ActivityStreams types to correctly set their \"type\" property at construction time, so users don't have to remember to do so each time. It is dependency injected so other go-fed compatible implementations could inject their own type.", setTypePropertyConstructorName)) + return } // privateManagerHookDefinitions creates common code needed by types and @@ -309,17 +354,7 @@ func rootDefinitions(vocabName string, m *ManagerGenerator, tgs []*TypeGenerator } // Property Constructors for _, pg := range pgs { - propCtors = append(propCtors, codegen.NewCommentedFunction( - m.pkg.Path(), - fmt.Sprintf("New%s%s", vocabName, pg.StructName()), - /*params=*/ nil, - []jen.Code{jen.Qual(pg.GetPublicPackage().Path(), pg.InterfaceName())}, - []jen.Code{ - jen.Return( - pg.ConstructorFn().Call(), - ), - }, - fmt.Sprintf("New%s%s creates a new %s", vocabName, pg.StructName(), pg.InterfaceName()))) + propCtors = append(propCtors, toPublicConstructor(vocabName, m, pg)) } // Extends for _, tg := range tgs { @@ -374,8 +409,11 @@ func rootDefinitions(vocabName string, m *ManagerGenerator, tgs []*TypeGenerator // init generates the code that implements the init calls per-type and // per-property package, so that the Manager is injected at runtime. -func genInit(pkg Package, tgs []*TypeGenerator, pgs []*PropertyGenerator) (globalManager *jen.Statement, init *codegen.Function) { - // init +func genInit(pkg Package, + tgs []*TypeGenerator, + pgs []*PropertyGenerator, + typePropertyConstructor *codegen.Function) (globalManager *jen.Statement, init *codegen.Function) { + // manager dependency injection inits globalManager = jen.Var().Id(managerInitName()).Op("*").Qual(pkg.Path(), managerName) callInitsMap := make(map[string]jen.Code, len(tgs)+len(pgs)) callInitsSlice := make([]string, 0, len(tgs)+len(pgs)) @@ -398,6 +436,22 @@ func genInit(pkg Package, tgs []*TypeGenerator, pgs []*PropertyGenerator) (globa for _, c := range callInitsSlice { callInits = append(callInits, callInitsMap[c]) } + // type property constructor injection inits. + // Resets the inits map and slice from above, to + // keep appending to the callInits result. + callInitsMap = make(map[string]jen.Code, len(tgs)) + callInitsSlice = make([]string, 0, len(tgs)) + for _, tg := range tgs { + key := tg.PrivatePackage().Path() + callInitsMap[key] = jen.Qual(tg.PrivatePackage().Path(), setTypePropertyConstructorName).Call( + typePropertyConstructor.QualifiedName(), + ) + callInitsSlice = append(callInitsSlice, key) + } + sort.Sort(sort.StringSlice(callInitsSlice)) + for _, c := range callInitsSlice { + callInits = append(callInits, callInitsMap[c]) + } init = codegen.NewCommentedFunction( pkg.Path(), "init", @@ -409,3 +463,19 @@ func genInit(pkg Package, tgs []*TypeGenerator, pgs []*PropertyGenerator) (globa fmt.Sprintf("init handles the 'magic' of creating a %s and dependency-injecting it into every other code-generated package. This gives the implementations access to create any type needed to deserialize, without relying on the other specific concrete implementations. In order to replace a go-fed created type with your own, be sure to have the manager call your own implementation's deserialize functions instead of the built-in type. Finally, each implementation views the %s as an interface with only a subset of funcitons available. This means this %s implements the union of those interfaces.", managerName, managerName, managerName)) return } + +// toPublicConstructor creates a public constructor function for the given +// property, vocab name, and manager. +func toPublicConstructor(vocabName string, m *ManagerGenerator, pg *PropertyGenerator) *codegen.Function { + return codegen.NewCommentedFunction( + m.pkg.Path(), + fmt.Sprintf("New%s%s", vocabName, pg.StructName()), + /*params=*/ nil, + []jen.Code{jen.Qual(pg.GetPublicPackage().Path(), pg.InterfaceName())}, + []jen.Code{ + jen.Return( + pg.ConstructorFn().Call(), + ), + }, + fmt.Sprintf("New%s%s creates a new %s", vocabName, pg.StructName(), pg.InterfaceName())) +} diff --git a/astool/gen/type.go b/astool/gen/type.go index ee91625..451cb8c 100644 --- a/astool/gen/type.go +++ b/astool/gen/type.go @@ -13,6 +13,7 @@ import ( const ( typeInterfaceName = "Type" typeMember = "Type" // This specifically must match the "type" property member generated! Kludge that this happens to just work. + typePropertyConstructor = "typePropertyConstructor" jsonLDContextInterfaceName = "jsonldContexter" extendedByMethod = "IsExtendedBy" extendingMethod = "IsExtending" @@ -29,6 +30,12 @@ const ( constructorName = "New" ) +// typePropertyConstructorName returns the package variable name for the +// constructor of a Type property. +func typePropertyConstructorName() string { + return typePropertyConstructor +} + // TypeInterface returns the Type Interface that is needed for ActivityStream // types to compile for methods dealing with extending, in the inheritance // sense. @@ -79,22 +86,21 @@ type Property interface { // TypeGenerator represents an ActivityStream type definition to generate in Go. type TypeGenerator struct { - vocabName string - vocabURI *url.URL - vocabAlias string - pm *PackageManager - typeName string - comment string - properties map[string]Property - withoutProperties map[string]Property - rangeProperties []Property - extends []*TypeGenerator - disjoint []*TypeGenerator - extendedBy []*TypeGenerator - m *ManagerGenerator - typePropertyConstructor *codegen.Function - cacheOnce sync.Once - cachedStruct *codegen.Struct + vocabName string + vocabURI *url.URL + vocabAlias string + pm *PackageManager + typeName string + comment string + properties map[string]Property + withoutProperties map[string]Property + rangeProperties []Property + extends []*TypeGenerator + disjoint []*TypeGenerator + extendedBy []*TypeGenerator + m *ManagerGenerator + cacheOnce sync.Once + cachedStruct *codegen.Struct } // NewTypeGenerator creates a new generator for a specific ActivityStreams Core @@ -123,21 +129,19 @@ func NewTypeGenerator(vocabName string, pm *PackageManager, typeName, comment string, properties, withoutProperties, rangeProperties []Property, - extends, disjoint []*TypeGenerator, - typePropertyConstructor *codegen.Function) (*TypeGenerator, error) { + extends, disjoint []*TypeGenerator) (*TypeGenerator, error) { t := &TypeGenerator{ - vocabName: vocabName, - vocabURI: vocabURI, - vocabAlias: vocabAlias, - pm: pm, - typeName: typeName, - comment: comment, - properties: make(map[string]Property, len(properties)), - withoutProperties: make(map[string]Property, len(withoutProperties)), - rangeProperties: rangeProperties, - extends: extends, - disjoint: disjoint, - typePropertyConstructor: typePropertyConstructor, + vocabName: vocabName, + vocabURI: vocabURI, + vocabAlias: vocabAlias, + pm: pm, + typeName: typeName, + comment: comment, + properties: make(map[string]Property, len(properties)), + withoutProperties: make(map[string]Property, len(withoutProperties)), + rangeProperties: rangeProperties, + extends: extends, + disjoint: disjoint, } for _, property := range properties { if err := t.AddPropertyGenerator(property); err != nil { @@ -963,7 +967,7 @@ func (t *TypeGenerator) constructorFn() *codegen.Function { jen.Op("*").Qual(t.PrivatePackage().Path(), t.TypeName()), }, []jen.Code{ - jen.Id("typeProp").Op(":=").Add(t.typePropertyConstructor.Call()), + jen.Id("typeProp").Op(":=").Id(typePropertyConstructorName()).Call(), jen.Id("typeProp").Dot("AppendString").Call(jen.Lit(t.TypeName())), jen.Return( jen.Op("&").Qual(t.PrivatePackage().Path(), t.TypeName()).Values(