diff --git a/tools/exp/convert/convert.go b/tools/exp/convert/convert.go index 2268f09..558766b 100644 --- a/tools/exp/convert/convert.go +++ b/tools/exp/convert/convert.go @@ -5,6 +5,7 @@ import ( "github.com/cjslep/activity/tools/exp/props" "github.com/cjslep/activity/tools/exp/rdf" "github.com/cjslep/activity/tools/exp/types" + "github.com/dave/jennifer/jen" "strings" ) @@ -27,8 +28,17 @@ func newVocabulary() vocabulary { type PropertyPackagePolicy int const ( - FlatUnderRoot PropertyPackagePolicy = iota - IndividualUnderRoot + PropertyFlatUnderRoot PropertyPackagePolicy = iota + PropertyIndividualUnderRoot + PropertyFlatUnderVocabularyRoot +) + +type TypePackagePolicy int + +const ( + TypeFlatUnderRoot TypePackagePolicy = iota + TypeIndividualUnderRoot + TypeFlatUnderVocabularyRoot ) type Converter struct { @@ -36,9 +46,21 @@ type Converter struct { VocabularyRoot string PropertyPackagePolicy PropertyPackagePolicy PropertyPackageRoot string + TypePackageRoot string + TypePackagePolicy TypePackagePolicy } -func (c Converter) convert(p *rdf.ParsedVocabulary) (v vocabulary, e error) { +func (c Converter) Convert(p *rdf.ParsedVocabulary) (f []*jen.File, e error) { + var v vocabulary + v, e = c.convertVocabulary(p) + if e != nil { + return + } + // TODO + return +} + +func (c Converter) convertVocabulary(p *rdf.ParsedVocabulary) (v vocabulary, e error) { v = newVocabulary() for k, val := range p.Vocab.Values { v.Kinds[k] = c.convertValue(val) @@ -53,7 +75,93 @@ func (c Converter) convert(p *rdf.ParsedVocabulary) (v vocabulary, e error) { return } } - // TODO: Types in dependency order. + // Instead of building a dependency tree, naively keep iterating through + // 'allTypes' until it is empty (good) or we get stuck (return error). + allTypes := make([]rdf.VocabularyType, 0, len(p.Vocab.Types)) + for _, t := range p.Vocab.Types { + allTypes = append(allTypes, t) + } + for { + if len(allTypes) == 0 { + break + } + stuck := true + for _, t := range allTypes { + var tg *types.TypeGenerator + tg, e = c.convertType(t, p.Vocab, v.FProps, v.NFProps, v.Types) + if e != nil { + return + } + v.Types[t.Name] = tg + } + if stuck { + e = fmt.Errorf("converting types got stuck in dependency cycle") + return + } + } + return +} + +func (c Converter) convertType(t rdf.VocabularyType, + v rdf.Vocabulary, + existingFProps map[string]*props.FunctionalPropertyGenerator, + existingNFProps map[string]*props.NonFunctionalPropertyGenerator, + existingTypes map[string]*types.TypeGenerator) (tg *types.TypeGenerator, e error) { + // Determine the types package name + var pkg string + pkg, e = c.typePackageName(t) + if e != nil { + return + } + // Determine the properties for this type + var p []types.Property + for k, prop := range v.Properties { + for _, ref := range prop.Domain { + if len(ref.Vocab) != 0 { + e = fmt.Errorf("unhandled use case: property domain outside its vocabulary") + return + } else if ref.Name == t.Name { + if prop.Functional { + p = append(p, existingFProps[prop.Name]) + } else { + p = append(p, existingNFProps[prop.Name]) + } + break + } + } + } + // Determine what this type extends + var ext []*types.TypeGenerator + for _, ex := range t.Extends { + if len(ex.Vocab) != 0 { + // TODO: This should be fixed + e = fmt.Errorf("unhandled use case: type extends another type outside its vocabulary") + return + } else { + ext = append(ext, existingTypes[ex.Name]) + } + } + tg, e = types.NewTypeGenerator( + pkg, + strings.Title(t.Name), // Must be same in convertTypeToKind + t.Notes, + p, + ext, + nil) + if e != nil { + return + } + // Apply disjoint if both sides are available. + for _, disj := range t.DisjointWith { + if len(disj.Vocab) != 0 { + // TODO: This should be fixed + e = fmt.Errorf("unhandled use case: type is disjoint with another type outside its vocabulary") + return + } else if disjointType, ok := existingTypes[disj.Name]; ok { + disjointType.AddDisjoint(tg) + tg.AddDisjoint(disjointType) + } + } return } @@ -116,7 +224,7 @@ func (c Converter) convertValue(v rdf.VocabularyValue) (k *props.Kind) { func (c Converter) convertTypeToKind(v rdf.VocabularyType) (k *props.Kind) { k = &props.Kind{ Name: c.toIdentifier(v), - ConcreteKind: strings.Title(v.Name), + ConcreteKind: strings.Title(v.Name), // Must be same in convertType Nilable: true, // TODO SerializeFn: types.SerializeFn, @@ -172,25 +280,58 @@ func (c Converter) propertyKinds(v rdf.VocabularyProperty, return } +func (c Converter) typePackageName(v rdf.VocabularyType) (pkg string, e error) { + switch c.TypePackagePolicy { + case TypeFlatUnderRoot: + pkg = c.TypePackageRoot + case TypeIndividualUnderRoot: + pkg = v.Name + case TypeFlatUnderVocabularyRoot: + pkg = c.VocabularyRoot + default: + e = fmt.Errorf("unrecognized TypePackagePolicy: %v", c.TypePackagePolicy) + } + return +} + func (c Converter) propertyPackageName(v rdf.VocabularyProperty) (pkg string, e error) { switch c.PropertyPackagePolicy { - case FlatUnderRoot: + case PropertyFlatUnderRoot: pkg = c.PropertyPackageRoot - case IndividualUnderRoot: + case PropertyIndividualUnderRoot: pkg = v.Name + case PropertyFlatUnderVocabularyRoot: + pkg = c.VocabularyRoot default: e = fmt.Errorf("unrecognized PropertyPackagePolicy: %v", c.PropertyPackagePolicy) } return } +// TODO: Use this? +func (c Converter) typePackageFile(v rdf.VocabularyType) (pkg string, e error) { + switch c.TypePackagePolicy { + case TypeFlatUnderRoot: + pkg = fmt.Sprintf("%s/%s", c.VocabularyRoot, c.TypePackageRoot) + case TypeIndividualUnderRoot: + pkg = fmt.Sprintf("%s/%s/%s", c.VocabularyRoot, c.TypePackageRoot, v.Name) + case TypeFlatUnderVocabularyRoot: + pkg = c.VocabularyRoot + default: + e = fmt.Errorf("unrecognized TypePackagePolicy: %v", c.TypePackagePolicy) + } + return +} + // TODO: Use this? func (c Converter) propertyPackageFile(v rdf.VocabularyProperty) (pkg string, e error) { switch c.PropertyPackagePolicy { - case FlatUnderRoot: + case PropertyFlatUnderRoot: pkg = fmt.Sprintf("%s/%s", c.VocabularyRoot, c.PropertyPackageRoot) - case IndividualUnderRoot: + case PropertyIndividualUnderRoot: pkg = fmt.Sprintf("%s/%s/%s", c.VocabularyRoot, c.PropertyPackageRoot, v.Name) + case PropertyFlatUnderVocabularyRoot: + pkg = c.VocabularyRoot default: e = fmt.Errorf("unrecognized PropertyPackagePolicy: %v", c.PropertyPackagePolicy) } diff --git a/tools/exp/types/type.go b/tools/exp/types/type.go index 3f861a6..ee19ec5 100644 --- a/tools/exp/types/type.go +++ b/tools/exp/types/type.go @@ -83,6 +83,11 @@ func NewTypeGenerator(packageName, typeName, comment string, return t, nil } +// AddDisjoint adds another TypeGenerator that is disjoint to this one. +func (t *TypeGenerator) AddDisjoint(o *TypeGenerator) { + t.disjoint = append(t.disjoint, o) +} + // Comment returns the comment for this type. func (t *TypeGenerator) Comment() string { return t.comment @@ -134,6 +139,7 @@ func (t *TypeGenerator) Definition() *codegen.Struct { t.cacheOnce.Do(func() { members := make([]jen.Code, 0, len(t.properties)) for name, property := range t.properties { + // TODO: Properties of parents that are extended, minus DoesNotApplyTo members = append(members, jen.Id(name).Id(property.StructName())) } t.cachedStruct = codegen.NewStruct(