gencomponents.go 7.9 KB

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