func typeStack(registry rdl.TypeRegistry, typeDef *rdl.Type) []*rdl.Type { var types []*rdl.Type types = append(types, typeDef) tName, tType, _ := rdl.TypeInfo(typeDef) for tName != rdl.TypeName(tType) { supertype := registry.FindType(tType) types = append(types, supertype) tName, tType, _ = rdl.TypeInfo(supertype) } return types }
// GenerateJavaModel generates the model code for the types defined in the RDL schema. func GenerateJavaModel(banner string, schema *rdl.Schema, outdir string, ns string) error { packageDir, err := javaGenerationDir(outdir, schema, ns) if err != nil { return err } registry := rdl.NewTypeRegistry(schema) for _, t := range schema.Types { tName, _, _ := rdl.TypeInfo(t) if strings.HasPrefix(string(tName), "rdl.") { continue } err := generateJavaType(banner, schema, registry, packageDir, t, ns) if err != nil { return err } } cName := capitalize(string(schema.Name)) + "Schema" out, file, _, err := outputWriter(packageDir, cName, ".java") if err != nil { return err } err = javaGenerateSchema(schema, cName, out, ns, banner) out.Flush() file.Close() if err != nil { return err } return nil }
func formatNumberType(out io.Writer, registry rdl.TypeRegistry, name string, types []*rdl.Type) { var options [][]string var minVal, maxVal *[]string baseType, _, _ := rdl.TypeInfo(types[len(types)-1]) topType := types[0].NumberTypeDef for i := len(types) - 1; i >= 0; i-- { switch types[i].Variant { case rdl.TypeVariantNumberTypeDef: t := types[i].NumberTypeDef c := "" if t != topType { c = "[from [" + string(t.Name) + "](#" + strings.ToLower(string(t.Name)) + ")]" } if t.Min != nil { minVal = &[]string{"min", fmt.Sprintf("%v", *t.Min), c} } if t.Max != nil { maxVal = &[]string{"max", fmt.Sprintf("%v", *t.Max), c} } } } if minVal != nil { options = append(options, *minVal) } if maxVal != nil { options = append(options, *maxVal) } if len(options) > 0 { fmt.Fprintf(out, "`%s` is a `%s` type with the following options:\n\n", name, baseType) formatTable(out, []string{"Option", "Value", "Notes"}, options) } else { fmt.Fprintf(out, "`%s` is a `%s` type.\n\n", topType.Name, baseType) } }
func (gen *modelGenerator) requiredImports(t *rdl.Type, imports map[string]string, visited map[rdl.TypeName]rdl.TypeName) { tName, _, _ := rdl.TypeInfo(t) if _, ok := visited[tName]; ok { return } visited[tName] = tName if strings.HasPrefix(string(tName), "rdl.") && !gen.rdl { imports[gen.librdl] = "rdl" } b := gen.registry.BaseType(t) switch b { case rdl.BaseTypeTimestamp, rdl.BaseTypeUUID: if !gen.rdl { imports[gen.librdl] = "rdl" } case rdl.BaseTypeEnum: imports["encoding/json"] = "" imports["fmt"] = "" break case rdl.BaseTypeUnion: imports["encoding/json"] = "" imports["fmt"] = "" break case rdl.BaseTypeArray: if t.ArrayTypeDef != nil { gen.requiredImports(gen.registry.FindType(t.ArrayTypeDef.Items), imports, visited) } case rdl.BaseTypeMap: if t.MapTypeDef != nil { gen.requiredImports(gen.registry.FindType(t.MapTypeDef.Keys), imports, visited) gen.requiredImports(gen.registry.FindType(t.MapTypeDef.Items), imports, visited) } case rdl.BaseTypeStruct: if !gen.rdl { imports[gen.librdl] = "rdl" } imports["encoding/json"] = "" if t.StructTypeDef != nil && t.StructTypeDef.Fields != nil { for _, f := range t.StructTypeDef.Fields { if !f.Optional { switch gen.registry.FindBaseType(f.Type) { case rdl.BaseTypeString, rdl.BaseTypeArray, rdl.BaseTypeMap, rdl.BaseTypeStruct: imports["fmt"] = "" } } if f.Items != "" { gen.requiredImports(gen.registry.FindType(f.Items), imports, visited) } else if f.Keys != "" { gen.requiredImports(gen.registry.FindType(f.Keys), imports, visited) } else { t := gen.registry.FindType(f.Type) if t != nil { gen.requiredImports(t, imports, visited) } } } } } }
func (gen *javaModelGenerator) emitTypeComment(t *rdl.Type) { tName, _, tComment := rdl.TypeInfo(t) s := string(tName) + " -" if tComment != "" { s += " " + tComment } gen.emit(formatComment(s, 0, 80)) }
func annotate(registry rdl.TypeRegistry, typename rdl.TypeRef) string { t := registry.FindType(typename) if t != nil { tName, tType, _ := rdl.TypeInfo(t) if tType != rdl.TypeRef(tName) { return "[" + string(typename) + "](#" + strings.ToLower(string(typename)) + ")" } } return string(typename) }
func (gen *javaModelGenerator) emitArray(t *rdl.Type) { if gen.err == nil { switch t.Variant { case rdl.TypeVariantArrayTypeDef: at := t.ArrayTypeDef gen.emitTypeComment(t) ftype := javaType(gen.registry, at.Type, false, at.Items, "") gen.emit(fmt.Sprintf("type %s %s\n\n", at.Name, ftype)) default: tName, tType, _ := rdl.TypeInfo(t) gtype := javaType(gen.registry, tType, false, "", "") gen.emitTypeComment(t) gen.emit(fmt.Sprintf("type %s %s\n\n", tName, gtype)) } } }
func (gen *modelGenerator) emitMap(t *rdl.Type) { if gen.err == nil { switch t.Variant { case rdl.TypeVariantMapTypeDef: mt := t.MapTypeDef gen.emitTypeComment(t) ftype := goType(gen.registry, mt.Type, false, mt.Items, mt.Keys, gen.precise, false) gen.emit(fmt.Sprintf("type %s %s\n\n", mt.Name, ftype)) default: tName, tType, _ := rdl.TypeInfo(t) gtype := goType(gen.registry, tType, false, "string", "", gen.precise, false) gen.emitTypeComment(t) gen.emit(fmt.Sprintf("type %s %s\n\n", tName, gtype)) } } }
func generateJavaType(banner string, schema *rdl.Schema, registry rdl.TypeRegistry, outdir string, t *rdl.Type, ns string) error { tName, _, _ := rdl.TypeInfo(t) bt := registry.BaseType(t) switch bt { case rdl.BaseTypeStruct: case rdl.BaseTypeUnion: case rdl.BaseTypeEnum: default: return nil } cName := capitalize(string(tName)) out, file, _, err := outputWriter(outdir, cName, ".java") if err != nil { return err } if file != nil { defer file.Close() } gen := &javaModelGenerator{registry, schema, string(tName), out, nil, ns, true} gen.emitHeader(banner, ns, bt, t) switch bt { case rdl.BaseTypeStruct: gen.emit("\n") gen.emitStruct(t, cName) case rdl.BaseTypeUnion: gen.emit("\n") gen.emitUnion(t) case rdl.BaseTypeArray: gen.emit("\n") gen.emitArray(t) case rdl.BaseTypeEnum: gen.emit("\n") gen.emitTypeComment(t) gen.emitEnum(t) } out.Flush() return gen.err }
func (gen *modelGenerator) emitType(t *rdl.Type) { if gen.err == nil { tName, _, _ := rdl.TypeInfo(t) if strings.HasPrefix(string(tName), "rdl.") { return } tName = rdl.TypeName(goTypeName(tName)) bt := gen.registry.BaseType(t) switch bt { case rdl.BaseTypeAny: gen.emit("\n") gen.emitTypeComment(t) gen.emit(fmt.Sprintf("type %s interface{}\n", tName)) case rdl.BaseTypeString, rdl.BaseTypeBool, rdl.BaseTypeInt8, rdl.BaseTypeInt16, rdl.BaseTypeInt32, rdl.BaseTypeInt64, rdl.BaseTypeFloat32, rdl.BaseTypeFloat64, rdl.BaseTypeSymbol: if gen.precise { gen.emit("\n") gen.emitTypeComment(t) gen.emit(fmt.Sprintf("type %s %s\n", tName, goType(gen.registry, rdl.TypeRef(bt.String()), false, "", "", gen.precise, false))) } case rdl.BaseTypeStruct: gen.emit("\n") gen.emitStruct(t) case rdl.BaseTypeUnion: gen.emit("\n") gen.emitUnion(t) case rdl.BaseTypeArray: gen.emit("\n") gen.emitArray(t) case rdl.BaseTypeMap: gen.emit("\n") gen.emitMap(t) case rdl.BaseTypeEnum: gen.emit("\n") gen.emitTypeComment(t) gen.emitEnum(t) } } }
func formatType(out io.Writer, registry rdl.TypeRegistry, typeDef *rdl.Type) { tName, _, tComment := rdl.TypeInfo(typeDef) fmt.Fprintf(out, "\n### %s\n", tName) if tComment != "" { fmt.Fprintf(out, "%s", formatBlock(tComment, 0, 80, "")) } types := typeStack(registry, typeDef) name := string(tName) switch typeDef.Variant { case rdl.TypeVariantStringTypeDef: formatStringType(out, registry, name, types) case rdl.TypeVariantNumberTypeDef: formatNumberType(out, registry, name, types) case rdl.TypeVariantStructTypeDef: formatStructType(out, registry, types) case rdl.TypeVariantUnionTypeDef: tt := typeDef.UnionTypeDef formatUnionType(out, registry, tt) case rdl.TypeVariantEnumTypeDef: tt := typeDef.EnumTypeDef formatEnumType(out, registry, tt) case rdl.TypeVariantArrayTypeDef: tt := typeDef.ArrayTypeDef name = name + "<" + string(tt.Items) + ">" formatArrayType(out, registry, name, types) case rdl.TypeVariantMapTypeDef: tt := typeDef.MapTypeDef name = name + "<" + string(tt.Keys) + "," + string(tt.Items) + ">" formatMapType(out, registry, name, types) case rdl.TypeVariantAliasTypeDef: formatAliasType(out, registry, typeDef.AliasTypeDef) case rdl.TypeVariantBytesTypeDef: formatBytesType(out, registry, name, types) case rdl.TypeVariantBaseType: //never happens } }
func (gen *modelGenerator) emitUnion(t *rdl.Type) { tName, _, _ := rdl.TypeInfo(t) ut := t.UnionTypeDef uName := capitalize(string(tName)) gen.emit(fmt.Sprintf("//\n// %sVariantTag - generated to support %s\n//\n", uName, uName)) gen.emit(fmt.Sprintf("type %sVariantTag int\n\n", uName)) gen.emit("//\n// Supporting constants\n//\n") gen.emit("const (\n") gen.emit(fmt.Sprintf("\t_ %sVariantTag = iota\n", uName)) for _, v := range ut.Variants { uV := capitalize(string(v)) gen.emit(fmt.Sprintf("\t%sVariant%s\n", uName, uV)) } gen.emit(")\n\n") maxKeyLen := len("Variant") for _, v := range ut.Variants { if len(v) > maxKeyLen { maxKeyLen = len(v) } } gen.emitTypeComment(t) gen.emit(fmt.Sprintf("type %s struct {\n", uName)) s := leftJustified("Variant", maxKeyLen) vtag := uName + "VariantTag" gen.emit(fmt.Sprintf("\t%s %s `json:\"-\" rdl:\"union\"`\n", s, vtag)) maxVarLen := maxKeyLen + 1 if len(vtag) > maxVarLen { maxVarLen = len(vtag) } for _, v := range ut.Variants { uV := capitalize(string(v)) vType := goType(gen.registry, v, true, "", "", gen.precise, true) tag := fmt.Sprintf("`json:\"%s,omitempty\"`", v) s := leftJustified(uV, maxKeyLen) gen.emit(fmt.Sprintf("\t%s %s %s\n", s, leftJustified(vType, maxVarLen), tag)) } gen.emit("}\n\n") gen.emit(fmt.Sprintf("func (u %s) String() string {\n", uName)) gen.emit("\tswitch u.Variant {\n") for _, v := range ut.Variants { uV := capitalize(string(v)) gen.emit(fmt.Sprintf("\tcase %sVariant%s:\n", uName, uV)) gen.emit(fmt.Sprintf("\t\treturn fmt.Sprintf(\"%%v\", u.%s)\n", uV)) } gen.emit("\tdefault:\n") gen.emit(fmt.Sprintf("\t\treturn \"<%s uninitialized>\"\n", uName)) gen.emit("\t}\n") gen.emit("}\n\n") gen.emit(fmt.Sprintf("//\n// Validate for %s\n//\n", uName)) gen.emit(fmt.Sprintf("func (p *%s) Validate() error {\n", uName)) gen.emit("\t") for _, v := range ut.Variants { gen.emit(fmt.Sprintf("if p.%s != nil {\n\t\tp.Variant = %sVariant%s\n\t} else ", v, uName, v)) } gen.emit(fmt.Sprintf("{\n\t\treturn fmt.Errorf(\"%s: Missing required variant\")\n\t}\n", uName)) gen.emit("\treturn nil\n") gen.emit("}\n") if gen.isUntaggedUnion(tName) { gen.emitUntaggedUnionSerializer(ut, tName) } else { gen.emit(fmt.Sprintf("\ntype raw%s %s\n\n", uName, uName)) gen.emit(fmt.Sprintf("//\n// UnmarshalJSON for %s\n//\n", uName)) gen.emit(fmt.Sprintf("func (p *%s) UnmarshalJSON(b []byte) error {\n", uName)) gen.emit(fmt.Sprintf("\tvar tmp raw%s\n", uName)) gen.emit("\tif err := json.Unmarshal(b, &tmp); err != nil {\n") gen.emit("\t\treturn err\n") gen.emit("\t}\n") gen.emit(fmt.Sprintf("\t*p = %s(tmp)\n", uName)) gen.emit("\treturn p.Validate()\n") gen.emit("}\n") } }
func swagger(schema *rdl.Schema) (*SwaggerDoc, error) { reg := rdl.NewTypeRegistry(schema) sname := string(schema.Name) swag := new(SwaggerDoc) swag.Swagger = "2.0" swag.Schemes = []string{"http"} //swag.Host = "localhost" swag.BasePath = "/api" title := "API" if sname != "" { title = "The " + sname + " API" swag.BasePath = "/api/" + sname } swag.Info = new(SwaggerInfo) swag.Info.Title = title if schema.Version != nil { swag.Info.Version = fmt.Sprintf("%d", *schema.Version) swag.BasePath += "/v" + fmt.Sprintf("%d", *schema.Version) } if schema.Comment != "" { swag.Info.Description = schema.Comment } if len(schema.Resources) > 0 { paths := make(map[string]map[string]*SwaggerAction) for _, r := range schema.Resources { path := r.Path actions, ok := paths[path] if !ok { actions = make(map[string]*SwaggerAction) paths[path] = actions } meth := strings.ToLower(r.Method) action, ok := actions[meth] if !ok { action = new(SwaggerAction) } action.Summary = r.Comment tag := string(r.Type) //fixme: RDL has no tags, the type is actually too fine grain for this action.Tags = []string{tag} //multiple tags include the resource in multiple sections action.Produces = []string{"application/json"} var ins []*SwaggerParameter if len(r.Inputs) > 0 { if r.Method == "POST" || r.Method == "PUT" { action.Consumes = []string{"application/json"} } for _, in := range r.Inputs { param := new(SwaggerParameter) param.Name = string(in.Name) param.Description = in.Comment required := true if in.Optional { required = false } param.Required = required if in.PathParam { param.In = "path" } else if in.QueryParam != "" { param.In = "query" param.Name = in.QueryParam //swagger has no formal arg concept } else if in.Header != "" { //swagger has no header params continue } else { param.In = "body" } ptype, pformat, ref := makeSwaggerTypeRef(reg, in.Type) param.Type = ptype param.Format = pformat param.Schema = ref ins = append(ins, param) } action.Parameters = ins } responses := make(map[string]*SwaggerResponse) expected := r.Expected addSwaggerResponse(responses, string(r.Type), expected, "") if len(r.Alternatives) > 0 { for _, alt := range r.Alternatives { addSwaggerResponse(responses, string(r.Type), alt, "") } } if len(r.Exceptions) > 0 { for sym, errdef := range r.Exceptions { errType := errdef.Type //xxx addSwaggerResponse(responses, errType, sym, errdef.Comment) } } action.Responses = responses //responses -> r.expected and r.exceptions //security -> r.auth //r.outputs? //action.description? //action.operationId IGNORE actions[meth] = action paths[path] = actions } swag.Paths = paths } if len(schema.Types) > 0 { defs := make(map[string]*SwaggerType) for _, t := range schema.Types { ref := makeSwaggerTypeDef(reg, t) if ref != nil { tName, _, _ := rdl.TypeInfo(t) defs[string(tName)] = ref } } if true { props := make(map[string]*SwaggerType) codeType := new(SwaggerType) t := "integer" codeType.Type = t f := "int32" codeType.Format = f props["code"] = codeType msgType := new(SwaggerType) t2 := "string" msgType.Type = t2 props["message"] = msgType prop := new(SwaggerType) prop.Required = []string{"code", "message"} prop.Properties = props defs["ResourceError"] = prop } swag.Definitions = defs } return swag, nil }