passes.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  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. var jsonvalueShape = &Shape{
  62. ShapeName: "JSONValue",
  63. Type: "jsonvalue",
  64. ValueRef: ShapeRef{
  65. JSONValue: true,
  66. },
  67. }
  68. // resolveReference updates a shape reference to reference the API and
  69. // its shape definition. All other nested references are also resolved.
  70. func (r *referenceResolver) resolveReference(ref *ShapeRef) {
  71. if ref.ShapeName == "" {
  72. return
  73. }
  74. if shape, ok := r.API.Shapes[ref.ShapeName]; ok {
  75. if ref.JSONValue {
  76. ref.ShapeName = "JSONValue"
  77. r.API.Shapes[ref.ShapeName] = jsonvalueShape
  78. }
  79. ref.API = r.API // resolve reference back to API
  80. ref.Shape = shape // resolve shape reference
  81. if r.visited[ref] {
  82. return
  83. }
  84. r.visited[ref] = true
  85. shape.refs = append(shape.refs, ref) // register the ref
  86. // resolve shape's references, if it has any
  87. r.resolveShape(shape)
  88. }
  89. }
  90. // resolveShape resolves a shape's Member Key Value, and nested member
  91. // shape references.
  92. func (r *referenceResolver) resolveShape(shape *Shape) {
  93. r.resolveReference(&shape.MemberRef)
  94. r.resolveReference(&shape.KeyRef)
  95. r.resolveReference(&shape.ValueRef)
  96. for _, m := range shape.MemberRefs {
  97. r.resolveReference(m)
  98. }
  99. }
  100. // renameToplevelShapes renames all top level shapes of an API to their
  101. // exportable variant. The shapes are also updated to include notations
  102. // if they are Input or Outputs.
  103. func (a *API) renameToplevelShapes() {
  104. for _, v := range a.OperationList() {
  105. if v.HasInput() {
  106. name := v.ExportedName + "Input"
  107. switch {
  108. case a.Shapes[name] == nil:
  109. if service, ok := shamelist[a.name]; ok {
  110. if check, ok := service[v.Name]; ok && check.input {
  111. break
  112. }
  113. }
  114. v.InputRef.Shape.Rename(name)
  115. }
  116. }
  117. if v.HasOutput() {
  118. name := v.ExportedName + "Output"
  119. switch {
  120. case a.Shapes[name] == nil:
  121. if service, ok := shamelist[a.name]; ok {
  122. if check, ok := service[v.Name]; ok && check.output {
  123. break
  124. }
  125. }
  126. v.OutputRef.Shape.Rename(name)
  127. }
  128. }
  129. v.InputRef.Payload = a.ExportableName(v.InputRef.Payload)
  130. v.OutputRef.Payload = a.ExportableName(v.OutputRef.Payload)
  131. }
  132. }
  133. // fixStutterNames fixes all name struttering based on Go naming conventions.
  134. // "Stuttering" is when the prefix of a structure or function matches the
  135. // package name (case insensitive).
  136. func (a *API) fixStutterNames() {
  137. str, end := a.StructName(), ""
  138. if len(str) > 1 {
  139. l := len(str) - 1
  140. str, end = str[0:l], str[l:]
  141. }
  142. re := regexp.MustCompile(fmt.Sprintf(`\A(?i:%s)%s`, str, end))
  143. for name, op := range a.Operations {
  144. newName := re.ReplaceAllString(name, "")
  145. if newName != name {
  146. delete(a.Operations, name)
  147. a.Operations[newName] = op
  148. }
  149. op.ExportedName = newName
  150. }
  151. for k, s := range a.Shapes {
  152. newName := re.ReplaceAllString(k, "")
  153. if newName != s.ShapeName {
  154. s.Rename(newName)
  155. }
  156. }
  157. }
  158. // renameExportable renames all operation names to be exportable names.
  159. // All nested Shape names are also updated to the exportable variant.
  160. func (a *API) renameExportable() {
  161. for name, op := range a.Operations {
  162. newName := a.ExportableName(name)
  163. if newName != name {
  164. delete(a.Operations, name)
  165. a.Operations[newName] = op
  166. }
  167. op.ExportedName = newName
  168. }
  169. for k, s := range a.Shapes {
  170. // FIXME SNS has lower and uppercased shape names with the same name,
  171. // except the lowercased variant is used exclusively for string and
  172. // other primitive types. Renaming both would cause a collision.
  173. // We work around this by only renaming the structure shapes.
  174. if s.Type == "string" {
  175. continue
  176. }
  177. for mName, member := range s.MemberRefs {
  178. newName := a.ExportableName(mName)
  179. if newName != mName {
  180. delete(s.MemberRefs, mName)
  181. s.MemberRefs[newName] = member
  182. // also apply locationName trait so we keep the old one
  183. // but only if there's no locationName trait on ref or shape
  184. if member.LocationName == "" && member.Shape.LocationName == "" {
  185. member.LocationName = mName
  186. }
  187. }
  188. if newName == "_" {
  189. panic("Shape " + s.ShapeName + " uses reserved member name '_'")
  190. }
  191. }
  192. newName := a.ExportableName(k)
  193. if newName != s.ShapeName {
  194. s.Rename(newName)
  195. }
  196. s.Payload = a.ExportableName(s.Payload)
  197. // fix required trait names
  198. for i, n := range s.Required {
  199. s.Required[i] = a.ExportableName(n)
  200. }
  201. }
  202. for _, s := range a.Shapes {
  203. // fix enum names
  204. if s.IsEnum() {
  205. s.EnumConsts = make([]string, len(s.Enum))
  206. for i := range s.Enum {
  207. shape := s.ShapeName
  208. shape = strings.ToUpper(shape[0:1]) + shape[1:]
  209. s.EnumConsts[i] = shape + s.EnumName(i)
  210. }
  211. }
  212. }
  213. }
  214. // createInputOutputShapes creates toplevel input/output shapes if they
  215. // have not been defined in the API. This normalizes all APIs to always
  216. // have an input and output structure in the signature.
  217. func (a *API) createInputOutputShapes() {
  218. for _, op := range a.Operations {
  219. if !op.HasInput() {
  220. setAsPlacholderShape(&op.InputRef, op.ExportedName+"Input", a)
  221. }
  222. if !op.HasOutput() {
  223. setAsPlacholderShape(&op.OutputRef, op.ExportedName+"Output", a)
  224. }
  225. }
  226. }
  227. func setAsPlacholderShape(tgtShapeRef *ShapeRef, name string, a *API) {
  228. shape := a.makeIOShape(name)
  229. shape.Placeholder = true
  230. *tgtShapeRef = ShapeRef{API: a, ShapeName: shape.ShapeName, Shape: shape}
  231. shape.refs = append(shape.refs, tgtShapeRef)
  232. }
  233. // makeIOShape returns a pointer to a new Shape initialized by the name provided.
  234. func (a *API) makeIOShape(name string) *Shape {
  235. shape := &Shape{
  236. API: a, ShapeName: name, Type: "structure",
  237. MemberRefs: map[string]*ShapeRef{},
  238. }
  239. a.Shapes[name] = shape
  240. return shape
  241. }
  242. // removeUnusedShapes removes shapes from the API which are not referenced by any
  243. // other shape in the API.
  244. func (a *API) removeUnusedShapes() {
  245. for n, s := range a.Shapes {
  246. if len(s.refs) == 0 {
  247. delete(a.Shapes, n)
  248. }
  249. }
  250. }
  251. // Represents the service package name to EndpointsID mapping
  252. var custEndpointsKey = map[string]string{
  253. "applicationautoscaling": "application-autoscaling",
  254. }
  255. // Sents the EndpointsID field of Metadata with the value of the
  256. // EndpointPrefix if EndpointsID is not set. Also adds
  257. // customizations for services if EndpointPrefix is not a valid key.
  258. func (a *API) setMetadataEndpointsKey() {
  259. if len(a.Metadata.EndpointsID) != 0 {
  260. return
  261. }
  262. if v, ok := custEndpointsKey[a.PackageName()]; ok {
  263. a.Metadata.EndpointsID = v
  264. } else {
  265. a.Metadata.EndpointsID = a.Metadata.EndpointPrefix
  266. }
  267. }