gencomponents.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. package javagen
  2. import (
  3. "bufio"
  4. "bytes"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "path"
  9. "strings"
  10. "text/template"
  11. "git.i2edu.net/i2/go-zero/core/stringx"
  12. "git.i2edu.net/i2/go-zero/tools/goctl/api/spec"
  13. apiutil "git.i2edu.net/i2/go-zero/tools/goctl/api/util"
  14. "git.i2edu.net/i2/go-zero/tools/goctl/util"
  15. )
  16. const (
  17. componentTemplate = `// Code generated by goctl. DO NOT EDIT.
  18. package com.xhb.logic.http.packet.{{.packet}}.model;
  19. import org.jetbrains.annotations.NotNull;
  20. import org.jetbrains.annotations.Nullable;
  21. {{.imports}}
  22. public class {{.className}} extends {{.superClassName}} {
  23. {{.properties}}
  24. {{if .HasProperty}}
  25. public {{.className}}() {
  26. }
  27. public {{.className}}({{.params}}) {
  28. {{.constructorSetter}}
  29. }
  30. {{end}}
  31. {{.getSet}}
  32. }
  33. `
  34. getSetTemplate = `
  35. {{.indent}}{{.decorator}}
  36. {{.indent}}public {{.returnType}} get{{.property}}() {
  37. {{.indent}} return this.{{.tagValue}};
  38. {{.indent}}}
  39. {{.indent}}public void set{{.property}}({{.type}} {{.propertyValue}}) {
  40. {{.indent}} this.{{.tagValue}} = {{.propertyValue}};
  41. {{.indent}}}
  42. `
  43. boolTemplate = `
  44. {{.indent}}{{.decorator}}
  45. {{.indent}}public {{.returnType}} is{{.property}}() {
  46. {{.indent}} return this.{{.tagValue}};
  47. {{.indent}}}
  48. {{.indent}}public void set{{.property}}({{.type}} {{.propertyValue}}) {
  49. {{.indent}} this.{{.tagValue}} = {{.propertyValue}};
  50. {{.indent}}}
  51. `
  52. httpResponseData = "import com.xhb.core.response.HttpResponseData;"
  53. httpData = "import com.xhb.core.packet.HttpData;"
  54. )
  55. type componentsContext struct {
  56. api *spec.ApiSpec
  57. requestTypes []spec.Type
  58. responseTypes []spec.Type
  59. imports []string
  60. members []spec.Member
  61. }
  62. func genComponents(dir, packetName string, api *spec.ApiSpec) error {
  63. types := api.Types
  64. if len(types) == 0 {
  65. return nil
  66. }
  67. var requestTypes []spec.Type
  68. var responseTypes []spec.Type
  69. for _, group := range api.Service.Groups {
  70. for _, route := range group.Routes {
  71. if route.RequestType != nil {
  72. requestTypes = append(requestTypes, route.RequestType)
  73. }
  74. if route.ResponseType != nil {
  75. responseTypes = append(responseTypes, route.ResponseType)
  76. }
  77. }
  78. }
  79. context := componentsContext{api: api, requestTypes: requestTypes, responseTypes: responseTypes}
  80. for _, ty := range types {
  81. if err := context.createComponent(dir, packetName, ty); err != nil {
  82. return err
  83. }
  84. }
  85. return nil
  86. }
  87. func (c *componentsContext) createComponent(dir, packetName string, ty spec.Type) error {
  88. defineStruct, done, err := c.checkStruct(ty)
  89. if done {
  90. return err
  91. }
  92. modelFile := util.Title(ty.Name()) + ".java"
  93. filename := path.Join(dir, modelDir, modelFile)
  94. if err := util.RemoveOrQuit(filename); err != nil {
  95. return err
  96. }
  97. propertiesString, err := c.buildProperties(defineStruct)
  98. if err != nil {
  99. return err
  100. }
  101. getSetString, err := c.buildGetterSetter(defineStruct)
  102. if err != nil {
  103. return err
  104. }
  105. superClassName := "HttpData"
  106. for _, item := range c.responseTypes {
  107. if item.Name() == defineStruct.Name() {
  108. superClassName = "HttpResponseData"
  109. if !stringx.Contains(c.imports, httpResponseData) {
  110. c.imports = append(c.imports, httpResponseData)
  111. }
  112. break
  113. }
  114. }
  115. if superClassName == "HttpData" && !stringx.Contains(c.imports, httpData) {
  116. c.imports = append(c.imports, httpData)
  117. }
  118. params, constructorSetter, err := c.buildConstructor()
  119. if err != nil {
  120. return err
  121. }
  122. fp, created, err := apiutil.MaybeCreateFile(dir, modelDir, modelFile)
  123. if err != nil {
  124. return err
  125. }
  126. if !created {
  127. return nil
  128. }
  129. defer fp.Close()
  130. buffer := new(bytes.Buffer)
  131. t := template.Must(template.New("componentType").Parse(componentTemplate))
  132. err = t.Execute(buffer, map[string]interface{}{
  133. "properties": propertiesString,
  134. "params": params,
  135. "constructorSetter": constructorSetter,
  136. "getSet": getSetString,
  137. "packet": packetName,
  138. "imports": strings.Join(c.imports, "\n"),
  139. "className": util.Title(defineStruct.Name()),
  140. "superClassName": superClassName,
  141. "HasProperty": len(strings.TrimSpace(propertiesString)) > 0,
  142. })
  143. if err != nil {
  144. return err
  145. }
  146. _, err = fp.WriteString(formatSource(buffer.String()))
  147. return err
  148. }
  149. func (c *componentsContext) checkStruct(ty spec.Type) (spec.DefineStruct, bool, error) {
  150. defineStruct, ok := ty.(spec.DefineStruct)
  151. if !ok {
  152. return spec.DefineStruct{}, true, errors.New("unsupported type %s" + ty.Name())
  153. }
  154. for _, item := range c.requestTypes {
  155. if item.Name() == defineStruct.Name() {
  156. if len(defineStruct.GetFormMembers())+len(defineStruct.GetBodyMembers()) == 0 {
  157. return spec.DefineStruct{}, true, nil
  158. }
  159. }
  160. }
  161. return defineStruct, false, nil
  162. }
  163. func (c *componentsContext) buildProperties(defineStruct spec.DefineStruct) (string, error) {
  164. var builder strings.Builder
  165. if err := c.writeType(&builder, defineStruct); err != nil {
  166. return "", apiutil.WrapErr(err, "Type "+defineStruct.Name()+" generate error")
  167. }
  168. return builder.String(), nil
  169. }
  170. func (c *componentsContext) buildGetterSetter(defineStruct spec.DefineStruct) (string, error) {
  171. var builder strings.Builder
  172. if err := c.genGetSet(&builder, 1); err != nil {
  173. return "", apiutil.WrapErr(err, "Type "+defineStruct.Name()+" get or set generate error")
  174. }
  175. return builder.String(), nil
  176. }
  177. func (c *componentsContext) writeType(writer io.Writer, defineStruct spec.DefineStruct) error {
  178. c.members = make([]spec.Member, 0)
  179. err := c.writeMembers(writer, defineStruct, 1)
  180. if err != nil {
  181. return err
  182. }
  183. return nil
  184. }
  185. func (c *componentsContext) writeMembers(writer io.Writer, tp spec.Type, indent int) error {
  186. definedType, ok := tp.(spec.DefineStruct)
  187. if !ok {
  188. pointType, ok := tp.(spec.PointerType)
  189. if ok {
  190. return c.writeMembers(writer, pointType.Type, indent)
  191. }
  192. return fmt.Errorf("type %s not supported", tp.Name())
  193. }
  194. for _, member := range definedType.Members {
  195. if member.IsInline {
  196. err := c.writeMembers(writer, member.Type, indent)
  197. if err != nil {
  198. return err
  199. }
  200. continue
  201. }
  202. if member.IsBodyMember() || member.IsFormMember() {
  203. if err := writeProperty(writer, member, indent); err != nil {
  204. return err
  205. }
  206. c.members = append(c.members, member)
  207. }
  208. }
  209. return nil
  210. }
  211. func (c *componentsContext) buildConstructor() (string, string, error) {
  212. var params strings.Builder
  213. var constructorSetter strings.Builder
  214. for index, member := range c.members {
  215. tp, err := specTypeToJava(member.Type)
  216. if err != nil {
  217. return "", "", err
  218. }
  219. params.WriteString(fmt.Sprintf("%s %s", tp, util.Untitle(member.Name)))
  220. pn, err := member.GetPropertyName()
  221. if err != nil {
  222. return "", "", err
  223. }
  224. if index != len(c.members)-1 {
  225. params.WriteString(", ")
  226. }
  227. writeIndent(&constructorSetter, 2)
  228. constructorSetter.WriteString(fmt.Sprintf("this.%s = %s;", pn, util.Untitle(member.Name)))
  229. if index != len(c.members)-1 {
  230. constructorSetter.WriteString(util.NL)
  231. }
  232. }
  233. return params.String(), constructorSetter.String(), nil
  234. }
  235. func (c *componentsContext) genGetSet(writer io.Writer, indent int) error {
  236. members := c.members
  237. for _, member := range members {
  238. javaType, err := specTypeToJava(member.Type)
  239. if err != nil {
  240. return nil
  241. }
  242. property := util.Title(member.Name)
  243. templateStr := getSetTemplate
  244. if javaType == "boolean" {
  245. templateStr = boolTemplate
  246. property = strings.TrimPrefix(property, "Is")
  247. property = strings.TrimPrefix(property, "is")
  248. }
  249. t := template.Must(template.New(templateStr).Parse(getSetTemplate))
  250. var tmplBytes bytes.Buffer
  251. tyString := javaType
  252. decorator := ""
  253. javaPrimitiveType := []string{"int", "long", "boolean", "float", "double", "short"}
  254. if !stringx.Contains(javaPrimitiveType, javaType) {
  255. if member.IsOptional() || member.IsOmitEmpty() {
  256. decorator = "@Nullable "
  257. } else {
  258. decorator = "@NotNull "
  259. }
  260. tyString = decorator + tyString
  261. }
  262. tagName, err := member.GetPropertyName()
  263. if err != nil {
  264. return err
  265. }
  266. err = t.Execute(&tmplBytes, map[string]string{
  267. "property": property,
  268. "propertyValue": util.Untitle(member.Name),
  269. "tagValue": tagName,
  270. "type": tyString,
  271. "decorator": decorator,
  272. "returnType": javaType,
  273. "indent": indentString(indent),
  274. })
  275. if err != nil {
  276. return err
  277. }
  278. r := tmplBytes.String()
  279. r = strings.Replace(r, " boolean get", " boolean is", 1)
  280. writer.Write([]byte(r))
  281. }
  282. return nil
  283. }
  284. func formatSource(source string) string {
  285. var builder strings.Builder
  286. scanner := bufio.NewScanner(strings.NewReader(source))
  287. preIsBreakLine := false
  288. for scanner.Scan() {
  289. text := strings.TrimSpace(scanner.Text())
  290. if text == "" && preIsBreakLine {
  291. continue
  292. }
  293. preIsBreakLine = text == ""
  294. builder.WriteString(scanner.Text() + "\n")
  295. }
  296. if err := scanner.Err(); err != nil {
  297. fmt.Println(err)
  298. }
  299. return builder.String()
  300. }