passes.go 7.5 KB

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