123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- package api
- import (
- "fmt"
- "regexp"
- "strings"
- )
- // updateTopLevelShapeReferences moves resultWrapper, locationName, and
- // xmlNamespace traits from toplevel shape references to the toplevel
- // shapes for easier code generation
- func (a *API) updateTopLevelShapeReferences() {
- for _, o := range a.Operations {
- // these are for REST-XML services
- if o.InputRef.LocationName != "" {
- o.InputRef.Shape.LocationName = o.InputRef.LocationName
- }
- if o.InputRef.Location != "" {
- o.InputRef.Shape.Location = o.InputRef.Location
- }
- if o.InputRef.Payload != "" {
- o.InputRef.Shape.Payload = o.InputRef.Payload
- }
- if o.InputRef.XMLNamespace.Prefix != "" {
- o.InputRef.Shape.XMLNamespace.Prefix = o.InputRef.XMLNamespace.Prefix
- }
- if o.InputRef.XMLNamespace.URI != "" {
- o.InputRef.Shape.XMLNamespace.URI = o.InputRef.XMLNamespace.URI
- }
- }
- }
- // writeShapeNames sets each shape's API and shape name values. Binding the
- // shape to its parent API.
- func (a *API) writeShapeNames() {
- for n, s := range a.Shapes {
- s.API = a
- s.ShapeName = n
- }
- }
- func (a *API) resolveReferences() {
- resolver := referenceResolver{API: a, visited: map[*ShapeRef]bool{}}
- for _, s := range a.Shapes {
- resolver.resolveShape(s)
- }
- for _, o := range a.Operations {
- o.API = a // resolve parent reference
- resolver.resolveReference(&o.InputRef)
- resolver.resolveReference(&o.OutputRef)
- }
- }
- // A referenceResolver provides a way to resolve shape references to
- // shape definitions.
- type referenceResolver struct {
- *API
- visited map[*ShapeRef]bool
- }
- // resolveReference updates a shape reference to reference the API and
- // its shape definition. All other nested references are also resolved.
- func (r *referenceResolver) resolveReference(ref *ShapeRef) {
- if ref.ShapeName == "" {
- return
- }
- if shape, ok := r.API.Shapes[ref.ShapeName]; ok {
- ref.API = r.API // resolve reference back to API
- ref.Shape = shape // resolve shape reference
- if r.visited[ref] {
- return
- }
- r.visited[ref] = true
- shape.refs = append(shape.refs, ref) // register the ref
- // resolve shape's references, if it has any
- r.resolveShape(shape)
- }
- }
- // resolveShape resolves a shape's Member Key Value, and nested member
- // shape references.
- func (r *referenceResolver) resolveShape(shape *Shape) {
- r.resolveReference(&shape.MemberRef)
- r.resolveReference(&shape.KeyRef)
- r.resolveReference(&shape.ValueRef)
- for _, m := range shape.MemberRefs {
- r.resolveReference(m)
- }
- }
- // renameToplevelShapes renames all top level shapes of an API to their
- // exportable variant. The shapes are also updated to include notations
- // if they are Input or Outputs.
- func (a *API) renameToplevelShapes() {
- for _, v := range a.Operations {
- if v.HasInput() {
- name := v.ExportedName + "Input"
- switch n := len(v.InputRef.Shape.refs); {
- case n == 1 && a.Shapes[name] == nil:
- v.InputRef.Shape.Rename(name)
- }
- }
- if v.HasOutput() {
- name := v.ExportedName + "Output"
- switch n := len(v.OutputRef.Shape.refs); {
- case n == 1 && a.Shapes[name] == nil:
- v.OutputRef.Shape.Rename(name)
- }
- }
- v.InputRef.Payload = a.ExportableName(v.InputRef.Payload)
- v.OutputRef.Payload = a.ExportableName(v.OutputRef.Payload)
- }
- }
- // fixStutterNames fixes all name struttering based on Go naming conventions.
- // "Stuttering" is when the prefix of a structure or function matches the
- // package name (case insensitive).
- func (a *API) fixStutterNames() {
- str, end := a.StructName(), ""
- if len(str) > 1 {
- l := len(str) - 1
- str, end = str[0:l], str[l:]
- }
- re := regexp.MustCompile(fmt.Sprintf(`\A(?i:%s)%s`, str, end))
- for name, op := range a.Operations {
- newName := re.ReplaceAllString(name, "")
- if newName != name {
- delete(a.Operations, name)
- a.Operations[newName] = op
- }
- op.ExportedName = newName
- }
- for k, s := range a.Shapes {
- newName := re.ReplaceAllString(k, "")
- if newName != s.ShapeName {
- s.Rename(newName)
- }
- }
- }
- // renameExportable renames all operation names to be exportable names.
- // All nested Shape names are also updated to the exportable variant.
- func (a *API) renameExportable() {
- for name, op := range a.Operations {
- newName := a.ExportableName(name)
- if newName != name {
- delete(a.Operations, name)
- a.Operations[newName] = op
- }
- op.ExportedName = newName
- }
- for k, s := range a.Shapes {
- // FIXME SNS has lower and uppercased shape names with the same name,
- // except the lowercased variant is used exclusively for string and
- // other primitive types. Renaming both would cause a collision.
- // We work around this by only renaming the structure shapes.
- if s.Type == "string" {
- continue
- }
- for mName, member := range s.MemberRefs {
- newName := a.ExportableName(mName)
- if newName != mName {
- delete(s.MemberRefs, mName)
- s.MemberRefs[newName] = member
- // also apply locationName trait so we keep the old one
- // but only if there's no locationName trait on ref or shape
- if member.LocationName == "" && member.Shape.LocationName == "" {
- member.LocationName = mName
- }
- }
- if newName == "SDKShapeTraits" {
- panic("Shape " + s.ShapeName + " uses reserved member name SDKShapeTraits")
- }
- }
- newName := a.ExportableName(k)
- if newName != s.ShapeName {
- s.Rename(newName)
- }
- s.Payload = a.ExportableName(s.Payload)
- // fix required trait names
- for i, n := range s.Required {
- s.Required[i] = a.ExportableName(n)
- }
- }
- for _, s := range a.Shapes {
- // fix enum names
- if s.IsEnum() {
- s.EnumConsts = make([]string, len(s.Enum))
- for i := range s.Enum {
- shape := s.ShapeName
- shape = strings.ToUpper(shape[0:1]) + shape[1:]
- s.EnumConsts[i] = shape + s.EnumName(i)
- }
- }
- }
- }
- // createInputOutputShapes creates toplevel input/output shapes if they
- // have not been defined in the API. This normalizes all APIs to always
- // have an input and output structure in the signature.
- func (a *API) createInputOutputShapes() {
- for _, v := range a.Operations {
- if !v.HasInput() {
- shape := a.makeIOShape(v.ExportedName + "Input")
- v.InputRef = ShapeRef{API: a, ShapeName: shape.ShapeName, Shape: shape}
- shape.refs = append(shape.refs, &v.InputRef)
- }
- if !v.HasOutput() {
- shape := a.makeIOShape(v.ExportedName + "Output")
- v.OutputRef = ShapeRef{API: a, ShapeName: shape.ShapeName, Shape: shape}
- shape.refs = append(shape.refs, &v.OutputRef)
- }
- }
- }
- // makeIOShape returns a pointer to a new Shape initialized by the name provided.
- func (a *API) makeIOShape(name string) *Shape {
- shape := &Shape{
- API: a, ShapeName: name, Type: "structure",
- MemberRefs: map[string]*ShapeRef{},
- }
- a.Shapes[name] = shape
- return shape
- }
- // removeUnusedShapes removes shapes from the API which are not referenced by any
- // other shape in the API.
- func (a *API) removeUnusedShapes() {
- for n, s := range a.Shapes {
- if len(s.refs) == 0 {
- delete(a.Shapes, n)
- }
- }
- }
|