diff --git a/pkg/annotations/annotations.go b/pkg/annotations/annotations.go index 6164a62..e4bd397 100644 --- a/pkg/annotations/annotations.go +++ b/pkg/annotations/annotations.go @@ -63,3 +63,17 @@ func GoName(concept concepts.Annotated) string { } return fmt.Sprintf("%s", name) } + +// Reference checks if the given concept has a `reference` annotation. If it has it then it returns the value +// of the `path` parameter. It returns an empty string if there is no such annotation or parameter. +func ReferencePath(concept concepts.Annotated) string { + annotation := concept.GetAnnotation("ref") + if annotation == nil { + return "" + } + name := annotation.FindParameter("path") + if name == nil { + return "" + } + return fmt.Sprintf("%s", name) +} diff --git a/pkg/language/reader.go b/pkg/language/reader.go index bc65209..dac3c36 100644 --- a/pkg/language/reader.go +++ b/pkg/language/reader.go @@ -30,6 +30,7 @@ import ( "github.com/antlr/antlr4/runtime/Go/antlr" + "github.com/openshift-online/ocm-api-metamodel/pkg/annotations" "github.com/openshift-online/ocm-api-metamodel/pkg/concepts" "github.com/openshift-online/ocm-api-metamodel/pkg/names" "github.com/openshift-online/ocm-api-metamodel/pkg/nomenclator" @@ -421,6 +422,47 @@ func (r *Reader) ExitClassDecl(ctx *ClassDeclContext) { // Add the annotations: r.addAnnotations(typ, ctx.GetAnnotations()) + if path := annotations.ReferencePath(typ); path != "" { + if len(r.inputs) > 1 { + panic("refernced service with multiple inputs in undefined") + } + + if r.service.Versions().Len() > 1 { + panic("cannot infer which version to add reference with multiple versions") + } + + input := r.inputs[0] + currVersion := r.service.Versions()[0] + path = strings.TrimPrefix(path, "/") + components := strings.Split(path, "/") + referencedServiceName := components[0] + referencedVersion := components[1] + referencedType := components[2] + + // Create an ad-hoc reader and model for the specific referenced service. + refReader := NewReader(). + Reporter(r.reporter) + refReader.model = concepts.NewModel() + + // Initialize the indexes of undefined concepts: + refReader.undefinedTypes = make(map[string]*concepts.Type) + refReader.undefinedResources = make(map[string]*concepts.Resource) + refReader.undefinedErrors = make(map[string]*concepts.Error) + + // load the ad-hoc service and version referenced and find the correct locator. + refReader.loadService(fmt.Sprintf("%s/%s", input, referencedServiceName)) + refVersion := refReader.service.FindVersion(names.ParseUsingSeparator(referencedVersion, "_")) + for _, currType := range refVersion.Types() { + if strings.Compare(currType.Name().String(), referencedType) == 0 { + for _, attributes := range currType.Attributes() { + currVersion.AddType(attributes.Type()) + } + currVersion.AddType(currType) + return + } + } + } + // Add the attributes: memberCtxs := ctx.GetMembers() if len(memberCtxs) > 0 { @@ -737,6 +779,67 @@ func (r *Reader) ExitLocatorDecl(ctx *LocatorDeclContext) { // Add the annotations: r.addAnnotations(locator, ctx.GetAnnotations()) + if path := annotations.ReferencePath(locator); path != "" { + if len(r.inputs) > 1 { + panic("refernced service with multiple inputs in undefined") + } + + if r.service.Versions().Len() > 1 { + panic("cannot infer which version to add reference with multiple versions") + } + + input := r.inputs[0] + currVersion := r.service.Versions()[0] + path = strings.TrimPrefix(path, "/") + components := strings.Split(path, "/") + referencedServiceName := components[0] + referencedVersion := components[1] + referencedLocator := components[2] + + // Create an ad-hoc reader and model for the specific referenced service. + refReader := NewReader(). + Reporter(r.reporter) + refReader.model = concepts.NewModel() + + // Initialize the indexes of undefined concepts: + refReader.undefinedTypes = make(map[string]*concepts.Type) + refReader.undefinedResources = make(map[string]*concepts.Resource) + refReader.undefinedErrors = make(map[string]*concepts.Error) + + // load the ad-hoc service and version referenced and find the correct locator. + refReader.loadService(fmt.Sprintf("%s/%s", input, referencedServiceName)) + refVersion := refReader.service.FindVersion(names.ParseUsingSeparator(referencedVersion, "_")) + for _, currLocator := range refVersion.Root().Locators() { + if strings.Compare(currLocator.Name().String(), referencedLocator) == 0 { + for _, resource := range refVersion.Resources() { + if resource.IsRoot() { + // We do not need to copy over the root resource. + continue + } + + // Add any underlying resource to the current version. + currVersion.AddResource(resource) + // Add underlying methods. + for _, method := range resource.Methods() { + currVersion. + FindResource(resource.Name()). + AddMethod(method) + } + + // Remove any undefined resources as + // This is a no-op except for the reference target added + // by the locator as the compiler will attempt to compile it + r.removeUndefinedResource(resource) + } + + currVersion.AddTypes(refVersion.Types()) + locator = currLocator + ctx.SetResult(locator) + return + } + } + } + // Add the members: membersCtxs := ctx.GetMembers() if len(membersCtxs) > 0 {