|
|
|
package javagen
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"bytes"
|
|
|
|
_ "embed"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"path"
|
|
|
|
"strings"
|
|
|
|
"text/template"
|
|
|
|
|
|
|
|
"github.com/zeromicro/go-zero/core/stringx"
|
|
|
|
"github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
|
|
|
apiutil "github.com/zeromicro/go-zero/tools/goctl/api/util"
|
|
|
|
"github.com/zeromicro/go-zero/tools/goctl/util"
|
|
|
|
"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
httpResponseData = "import com.xhb.core.response.HttpResponseData;"
|
|
|
|
httpData = "import com.xhb.core.packet.HttpData;"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
//go:embed component.tpl
|
|
|
|
componentTemplate string
|
|
|
|
//go:embed getset.tpl
|
|
|
|
getSetTemplate string
|
|
|
|
//go:embed bool.tpl
|
|
|
|
boolTemplate string
|
|
|
|
)
|
|
|
|
|
|
|
|
type componentsContext struct {
|
|
|
|
api *spec.ApiSpec
|
|
|
|
requestTypes []spec.Type
|
|
|
|
responseTypes []spec.Type
|
|
|
|
imports []string
|
|
|
|
members []spec.Member
|
|
|
|
}
|
|
|
|
|
|
|
|
func genComponents(dir, packetName string, api *spec.ApiSpec) error {
|
|
|
|
types := api.Types
|
|
|
|
if len(types) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var requestTypes []spec.Type
|
|
|
|
var responseTypes []spec.Type
|
|
|
|
for _, group := range api.Service.Groups {
|
|
|
|
for _, route := range group.Routes {
|
|
|
|
if route.RequestType != nil {
|
|
|
|
requestTypes = append(requestTypes, route.RequestType)
|
|
|
|
}
|
|
|
|
if route.ResponseType != nil {
|
|
|
|
responseTypes = append(responseTypes, route.ResponseType)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
context := componentsContext{api: api, requestTypes: requestTypes, responseTypes: responseTypes}
|
|
|
|
for _, ty := range types {
|
|
|
|
if err := context.createComponent(dir, packetName, ty); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *componentsContext) createComponent(dir, packetName string, ty spec.Type) error {
|
|
|
|
defineStruct, done, err := c.checkStruct(ty)
|
|
|
|
if done {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
modelFile := util.Title(ty.Name()) + ".java"
|
|
|
|
filename := path.Join(dir, modelDir, modelFile)
|
|
|
|
if err := pathx.RemoveOrQuit(filename); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
propertiesString, err := c.buildProperties(defineStruct)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
getSetString, err := c.buildGetterSetter(defineStruct)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
superClassName := "HttpData"
|
|
|
|
for _, item := range c.responseTypes {
|
|
|
|
if item.Name() == defineStruct.Name() {
|
|
|
|
superClassName = "HttpResponseData"
|
|
|
|
if !stringx.Contains(c.imports, httpResponseData) {
|
|
|
|
c.imports = append(c.imports, httpResponseData)
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if superClassName == "HttpData" && !stringx.Contains(c.imports, httpData) {
|
|
|
|
c.imports = append(c.imports, httpData)
|
|
|
|
}
|
|
|
|
|
|
|
|
params, constructorSetter, err := c.buildConstructor()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
fp, created, err := apiutil.MaybeCreateFile(dir, modelDir, modelFile)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !created {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
defer fp.Close()
|
|
|
|
|
|
|
|
buffer := new(bytes.Buffer)
|
|
|
|
t := template.Must(template.New("componentType").Parse(componentTemplate))
|
|
|
|
err = t.Execute(buffer, map[string]any{
|
|
|
|
"properties": propertiesString,
|
|
|
|
"params": params,
|
|
|
|
"constructorSetter": constructorSetter,
|
|
|
|
"getSet": getSetString,
|
|
|
|
"packet": packetName,
|
|
|
|
"imports": strings.Join(c.imports, "\n"),
|
|
|
|
"className": util.Title(defineStruct.Name()),
|
|
|
|
"superClassName": superClassName,
|
|
|
|
"HasProperty": len(strings.TrimSpace(propertiesString)) > 0,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = fp.WriteString(formatSource(buffer.String()))
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *componentsContext) checkStruct(ty spec.Type) (spec.DefineStruct, bool, error) {
|
|
|
|
defineStruct, ok := ty.(spec.DefineStruct)
|
|
|
|
if !ok {
|
|
|
|
return spec.DefineStruct{}, true, errors.New("unsupported type %s" + ty.Name())
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, item := range c.requestTypes {
|
|
|
|
if item.Name() == defineStruct.Name() {
|
|
|
|
if len(defineStruct.GetFormMembers())+len(defineStruct.GetBodyMembers()) == 0 {
|
|
|
|
return spec.DefineStruct{}, true, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return defineStruct, false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *componentsContext) buildProperties(defineStruct spec.DefineStruct) (string, error) {
|
|
|
|
var builder strings.Builder
|
|
|
|
if err := c.writeType(&builder, defineStruct); err != nil {
|
|
|
|
return "", apiutil.WrapErr(err, "Type "+defineStruct.Name()+" generate error")
|
|
|
|
}
|
|
|
|
|
|
|
|
return builder.String(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *componentsContext) buildGetterSetter(defineStruct spec.DefineStruct) (string, error) {
|
|
|
|
var builder strings.Builder
|
|
|
|
if err := c.genGetSet(&builder, 1); err != nil {
|
|
|
|
return "", apiutil.WrapErr(err, "Type "+defineStruct.Name()+" get or set generate error")
|
|
|
|
}
|
|
|
|
|
|
|
|
return builder.String(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *componentsContext) writeType(writer io.Writer, defineStruct spec.DefineStruct) error {
|
|
|
|
c.members = make([]spec.Member, 0)
|
|
|
|
err := c.writeMembers(writer, defineStruct, 1)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *componentsContext) writeMembers(writer io.Writer, tp spec.Type, indent int) error {
|
|
|
|
definedType, ok := tp.(spec.DefineStruct)
|
|
|
|
if !ok {
|
|
|
|
pointType, ok := tp.(spec.PointerType)
|
|
|
|
if ok {
|
|
|
|
return c.writeMembers(writer, pointType.Type, indent)
|
|
|
|
}
|
|
|
|
return fmt.Errorf("type %s not supported", tp.Name())
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, member := range definedType.Members {
|
|
|
|
if member.IsInline {
|
|
|
|
err := c.writeMembers(writer, member.Type, indent)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if member.IsBodyMember() || member.IsFormMember() {
|
|
|
|
if err := writeProperty(writer, member, indent); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
c.members = append(c.members, member)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *componentsContext) buildConstructor() (string, string, error) {
|
|
|
|
var params strings.Builder
|
|
|
|
var constructorSetter strings.Builder
|
|
|
|
for index, member := range c.members {
|
|
|
|
tp, err := specTypeToJava(member.Type)
|
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
params.WriteString(fmt.Sprintf("%s %s", tp, util.Untitle(member.Name)))
|
|
|
|
pn, err := member.GetPropertyName()
|
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
if index != len(c.members)-1 {
|
|
|
|
params.WriteString(", ")
|
|
|
|
}
|
|
|
|
|
|
|
|
writeIndent(&constructorSetter, 2)
|
|
|
|
constructorSetter.WriteString(fmt.Sprintf("this.%s = %s;", pn, util.Untitle(member.Name)))
|
|
|
|
if index != len(c.members)-1 {
|
|
|
|
constructorSetter.WriteString(pathx.NL)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return params.String(), constructorSetter.String(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *componentsContext) genGetSet(writer io.Writer, indent int) error {
|
|
|
|
members := c.members
|
|
|
|
for _, member := range members {
|
|
|
|
javaType, err := specTypeToJava(member.Type)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
property := util.Title(member.Name)
|
|
|
|
templateStr := getSetTemplate
|
|
|
|
if javaType == "boolean" {
|
|
|
|
templateStr = boolTemplate
|
|
|
|
property = strings.TrimPrefix(property, "Is")
|
|
|
|
property = strings.TrimPrefix(property, "is")
|
|
|
|
}
|
|
|
|
t := template.Must(template.New(templateStr).Parse(getSetTemplate))
|
|
|
|
var tmplBytes bytes.Buffer
|
|
|
|
|
|
|
|
tyString := javaType
|
|
|
|
decorator := ""
|
|
|
|
javaPrimitiveType := []string{"int", "long", "boolean", "float", "double", "short"}
|
|
|
|
if !stringx.Contains(javaPrimitiveType, javaType) {
|
|
|
|
if member.IsOptional() || member.IsOmitEmpty() {
|
|
|
|
decorator = "@Nullable "
|
|
|
|
} else {
|
|
|
|
decorator = "@NotNull "
|
|
|
|
}
|
|
|
|
tyString = decorator + tyString
|
|
|
|
}
|
|
|
|
|
|
|
|
tagName, err := member.GetPropertyName()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = t.Execute(&tmplBytes, map[string]string{
|
|
|
|
"property": property,
|
|
|
|
"propertyValue": util.Untitle(member.Name),
|
|
|
|
"tagValue": tagName,
|
|
|
|
"type": tyString,
|
|
|
|
"decorator": decorator,
|
|
|
|
"returnType": javaType,
|
|
|
|
"indent": indentString(indent),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
r := tmplBytes.String()
|
|
|
|
r = strings.Replace(r, " boolean get", " boolean is", 1)
|
|
|
|
writer.Write([]byte(r))
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func formatSource(source string) string {
|
|
|
|
var builder strings.Builder
|
|
|
|
scanner := bufio.NewScanner(strings.NewReader(source))
|
|
|
|
preIsBreakLine := false
|
|
|
|
for scanner.Scan() {
|
|
|
|
text := strings.TrimSpace(scanner.Text())
|
|
|
|
if text == "" && preIsBreakLine {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
preIsBreakLine = text == ""
|
|
|
|
builder.WriteString(scanner.Text() + "\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return builder.String()
|
|
|
|
}
|