passes.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. package api
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strings"
  6. )
  7. // updateTopLevelShapeReferences moves resultWrapper, locationName, and
  8. // xmlNamespace traits from toplevel shape references to the toplevel
  9. // shapes for easier code generation
  10. func (a *API) updateTopLevelShapeReferences() {
  11. for _, o := range a.Operations {
  12. // these are for REST-XML services
  13. if o.InputRef.LocationName != "" {
  14. o.InputRef.Shape.LocationName = o.InputRef.LocationName
  15. }
  16. if o.InputRef.Location != "" {
  17. o.InputRef.Shape.Location = o.InputRef.Location
  18. }
  19. if o.InputRef.Payload != "" {
  20. o.InputRef.Shape.Payload = o.InputRef.Payload
  21. }
  22. if o.InputRef.XMLNamespace.Prefix != "" {
  23. o.InputRef.Shape.XMLNamespace.Prefix = o.InputRef.XMLNamespace.Prefix
  24. }
  25. if o.InputRef.XMLNamespace.URI != "" {
  26. o.InputRef.Shape.XMLNamespace.URI = o.InputRef.XMLNamespace.URI
  27. }
  28. }
  29. }
  30. // writeShapeNames sets each shape's API and shape name values. Binding the
  31. // shape to its parent API.
  32. func (a *API) writeShapeNames() {
  33. for n, s := range a.Shapes {
  34. s.API = a
  35. s.ShapeName = n
  36. }
  37. }
  38. func (a *API) resolveReferences() {
  39. resolver := referenceResolver{API: a, visited: map[*ShapeRef]bool{}}
  40. for _, s := range a.Shapes {
  41. resolver.resolveShape(s)
  42. }
  43. for _, o := range a.Operations {
  44. o.API = a // resolve parent reference
  45. resolver.resolveReference(&o.InputRef)
  46. resolver.resolveReference(&o.OutputRef)
  47. }
  48. }
  49. // A referenceResolver provides a way to resolve shape references to
  50. // shape definitions.
  51. type referenceResolver struct {
  52. *API
  53. visited map[*ShapeRef]bool
  54. }
  55. // resolveReference updates a shape reference to reference the API and
  56. // its shape definition. All other nested references are also resolved.
  57. func (r *referenceResolver) resolveReference(ref *ShapeRef) {
  58. if ref.ShapeName == "" {
  59. return
  60. }
  61. if shape, ok := r.API.Shapes[ref.ShapeName]; ok {
  62. ref.API = r.API // resolve reference back to API
  63. ref.Shape = shape // resolve shape reference
  64. if r.visited[ref] {
  65. return
  66. }
  67. r.visited[ref] = true
  68. shape.refs = append(shape.refs, ref) // register the ref
  69. // resolve shape's references, if it has any
  70. r.resolveShape(shape)
  71. }
  72. }
  73. // resolveShape resolves a shape's Member Key Value, and nested member
  74. // shape references.
  75. func (r *referenceResolver) resolveShape(shape *Shape) {
  76. r.resolveReference(&shape.MemberRef)
  77. r.resolveReference(&shape.KeyRef)
  78. r.resolveReference(&shape.ValueRef)
  79. for _, m := range shape.MemberRefs {
  80. r.resolveReference(m)
  81. }
  82. }
  83. // renameToplevelShapes renames all top level shapes of an API to their
  84. // exportable variant. The shapes are also updated to include notations
  85. // if they are Input or Outputs.
  86. func (a *API) renameToplevelShapes() {
  87. for _, v := range a.Operations {
  88. if v.HasInput() {
  89. name := v.ExportedName + "Input"
  90. switch n := len(v.InputRef.Shape.refs); {
  91. case n == 1 && a.Shapes[name] == nil:
  92. v.InputRef.Shape.Rename(name)
  93. }
  94. }
  95. if v.HasOutput() {
  96. name := v.ExportedName + "Output"
  97. switch n := len(v.OutputRef.Shape.refs); {
  98. case n == 1 && a.Shapes[name] == nil:
  99. v.OutputRef.Shape.Rename(name)
  100. }
  101. }
  102. v.InputRef.Payload = a.ExportableName(v.InputRef.Payload)
  103. v.OutputRef.Payload = a.ExportableName(v.OutputRef.Payload)
  104. }
  105. }
  106. // fixStutterNames fixes all name struttering based on Go naming conventions.
  107. // "Stuttering" is when the prefix of a structure or function matches the
  108. // package name (case insensitive).
  109. func (a *API) fixStutterNames() {
  110. str, end := a.StructName(), ""
  111. if len(str) > 1 {
  112. l := len(str) - 1
  113. str, end = str[0:l], str[l:]
  114. }
  115. re := regexp.MustCompile(fmt.Sprintf(`\A(?i:%s)%s`, str, end))
  116. for name, op := range a.Operations {
  117. newName := re.ReplaceAllString(name, "")
  118. if newName != name {
  119. delete(a.Operations, name)
  120. a.Operations[newName] = op
  121. }
  122. op.ExportedName = newName
  123. }
  124. for k, s := range a.Shapes {
  125. newName := re.ReplaceAllString(k, "")
  126. if newName != s.ShapeName {
  127. s.Rename(newName)
  128. }
  129. }
  130. }
  131. // renameExportable renames all operation names to be exportable names.
  132. // All nested Shape names are also updated to the exportable variant.
  133. func (a *API) renameExportable() {
  134. for name, op := range a.Operations {
  135. newName := a.ExportableName(name)
  136. if newName != name {
  137. delete(a.Operations, name)
  138. a.Operations[newName] = op
  139. }
  140. op.ExportedName = newName
  141. }
  142. for k, s := range a.Shapes {
  143. // FIXME SNS has lower and uppercased shape names with the same name,
  144. // except the lowercased variant is used exclusively for string and
  145. // other primitive types. Renaming both would cause a collision.
  146. // We work around this by only renaming the structure shapes.
  147. if s.Type == "string" {
  148. continue
  149. }
  150. for mName, member := range s.MemberRefs {
  151. newName := a.ExportableName(mName)
  152. if newName != mName {
  153. delete(s.MemberRefs, mName)
  154. s.MemberRefs[newName] = member
  155. // also apply locationName trait so we keep the old one
  156. // but only if there's no locationName trait on ref or shape
  157. if member.LocationName == "" && member.Shape.LocationName == "" {
  158. member.LocationName = mName
  159. }
  160. }
  161. if newName == "SDKShapeTraits" {
  162. panic("Shape " + s.ShapeName + " uses reserved member name SDKShapeTraits")
  163. }
  164. }
  165. newName := a.ExportableName(k)
  166. if newName != s.ShapeName {
  167. s.Rename(newName)
  168. }
  169. s.Payload = a.ExportableName(s.Payload)
  170. // fix required trait names
  171. for i, n := range s.Required {
  172. s.Required[i] = a.ExportableName(n)
  173. }
  174. }
  175. for _, s := range a.Shapes {
  176. // fix enum names
  177. if s.IsEnum() {
  178. s.EnumConsts = make([]string, len(s.Enum))
  179. for i := range s.Enum {
  180. shape := s.ShapeName
  181. shape = strings.ToUpper(shape[0:1]) + shape[1:]
  182. s.EnumConsts[i] = shape + s.EnumName(i)
  183. }
  184. }
  185. }
  186. }
  187. // createInputOutputShapes creates toplevel input/output shapes if they
  188. // have not been defined in the API. This normalizes all APIs to always
  189. // have an input and output structure in the signature.
  190. func (a *API) createInputOutputShapes() {
  191. for _, v := range a.Operations {
  192. if !v.HasInput() {
  193. shape := a.makeIOShape(v.ExportedName + "Input")
  194. v.InputRef = ShapeRef{API: a, ShapeName: shape.ShapeName, Shape: shape}
  195. shape.refs = append(shape.refs, &v.InputRef)
  196. }
  197. if !v.HasOutput() {
  198. shape := a.makeIOShape(v.ExportedName + "Output")
  199. v.OutputRef = ShapeRef{API: a, ShapeName: shape.ShapeName, Shape: shape}
  200. shape.refs = append(shape.refs, &v.OutputRef)
  201. }
  202. }
  203. }
  204. // makeIOShape returns a pointer to a new Shape initialized by the name provided.
  205. func (a *API) makeIOShape(name string) *Shape {
  206. shape := &Shape{
  207. API: a, ShapeName: name, Type: "structure",
  208. MemberRefs: map[string]*ShapeRef{},
  209. }
  210. a.Shapes[name] = shape
  211. return shape
  212. }
  213. // removeUnusedShapes removes shapes from the API which are not referenced by any
  214. // other shape in the API.
  215. func (a *API) removeUnusedShapes() {
  216. for n, s := range a.Shapes {
  217. if len(s.refs) == 0 {
  218. delete(a.Shapes, n)
  219. }
  220. }
  221. }