github.com/josephspurrier/go-swagger@v0.2.1-0.20221129144919-1f672a142a00/generator/model.go (about)

     1  // Copyright 2015 go-swagger maintainers
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package generator
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"log"
    21  	"path"
    22  	"path/filepath"
    23  	"sort"
    24  	"strconv"
    25  	"strings"
    26  
    27  	"github.com/go-openapi/analysis"
    28  	"github.com/go-openapi/loads"
    29  	"github.com/go-openapi/spec"
    30  	"github.com/go-openapi/swag"
    31  )
    32  
    33  const asMethod = "()"
    34  
    35  /*
    36  Rewrite specification document first:
    37  
    38  * anonymous objects
    39  * tuples
    40  * extensible objects (properties + additionalProperties)
    41  * AllOfs when they match the rewrite criteria (not a nullable allOf)
    42  
    43  Find string enums and generate specialized idiomatic enum with them
    44  
    45  Every action that happens tracks the path which is a linked list of refs
    46  
    47  
    48  */
    49  
    50  // GenerateModels generates all model files for some schema definitions
    51  func GenerateModels(modelNames []string, opts *GenOpts) error {
    52  	// overide any default or incompatible options setting
    53  	opts.IncludeModel = true
    54  	opts.IgnoreOperations = true
    55  	opts.ExistingModels = ""
    56  	opts.IncludeHandler = false
    57  	opts.IncludeMain = false
    58  	opts.IncludeSupport = false
    59  	generator, err := newAppGenerator("", modelNames, nil, opts)
    60  	if err != nil {
    61  		return err
    62  	}
    63  	return generator.Generate()
    64  }
    65  
    66  // GenerateDefinition generates a single model file for some schema definitions
    67  func GenerateDefinition(modelNames []string, opts *GenOpts) error {
    68  	if err := opts.CheckOpts(); err != nil {
    69  		return err
    70  	}
    71  
    72  	if err := opts.setTemplates(); err != nil {
    73  		return err
    74  	}
    75  
    76  	specDoc, _, err := opts.analyzeSpec()
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	modelNames = pruneEmpty(modelNames)
    82  	if len(modelNames) == 0 {
    83  		for k := range specDoc.Spec().Definitions {
    84  			modelNames = append(modelNames, k)
    85  		}
    86  	}
    87  
    88  	for _, modelName := range modelNames {
    89  		// lookup schema
    90  		model, ok := specDoc.Spec().Definitions[modelName]
    91  		if !ok {
    92  			return fmt.Errorf("model %q not found in definitions given by %q", modelName, opts.Spec)
    93  		}
    94  
    95  		// generate files
    96  		generator := definitionGenerator{
    97  			Name:    modelName,
    98  			Model:   model,
    99  			SpecDoc: specDoc,
   100  			Target: filepath.Join(
   101  				opts.Target,
   102  				filepath.FromSlash(opts.LanguageOpts.ManglePackagePath(opts.ModelPackage, ""))),
   103  			opts: opts,
   104  		}
   105  
   106  		if err := generator.Generate(); err != nil {
   107  			return err
   108  		}
   109  	}
   110  
   111  	return nil
   112  }
   113  
   114  type definitionGenerator struct {
   115  	Name    string
   116  	Model   spec.Schema
   117  	SpecDoc *loads.Document
   118  	Target  string
   119  	opts    *GenOpts
   120  }
   121  
   122  func (m *definitionGenerator) Generate() error {
   123  
   124  	mod, err := makeGenDefinition(m.Name, m.Target, m.Model, m.SpecDoc, m.opts)
   125  	if err != nil {
   126  		return fmt.Errorf("could not generate definitions for model %s on target %s: %v", m.Name, m.Target, err)
   127  	}
   128  
   129  	if m.opts.DumpData {
   130  		return dumpData(swag.ToDynamicJSON(mod))
   131  	}
   132  
   133  	if m.opts.IncludeModel {
   134  		log.Println("including additional model")
   135  		if err := m.generateModel(mod); err != nil {
   136  			return fmt.Errorf("could not generate model: %v", err)
   137  		}
   138  	}
   139  	log.Println("generated model", m.Name)
   140  
   141  	return nil
   142  }
   143  
   144  func (m *definitionGenerator) generateModel(g *GenDefinition) error {
   145  	debugLog("rendering definitions for %+v", *g)
   146  	return m.opts.renderDefinition(g)
   147  }
   148  
   149  func makeGenDefinition(name, pkg string, schema spec.Schema, specDoc *loads.Document, opts *GenOpts) (*GenDefinition, error) {
   150  	gd, err := makeGenDefinitionHierarchy(name, pkg, "", schema, specDoc, opts)
   151  
   152  	if err == nil && gd != nil {
   153  		// before yielding the schema to the renderer, we check if the top-level Validate method gets some content
   154  		// this means that the immediate content of the top level definitions has at least one validation.
   155  		//
   156  		// If none is found at this level and that no special case where no Validate() method is exposed at all
   157  		// (e.g. io.ReadCloser and interface{} types and their aliases), then there is an empty Validate() method which
   158  		// just return nil (the object abides by the runtime.Validatable interface, but knows it has nothing to validate).
   159  		//
   160  		// We do this at the top level because of the possibility of aliased types which always bubble up validation to types which
   161  		// are referring to them. This results in correct but inelegant code with empty validations.
   162  		gd.GenSchema.HasValidations = shallowValidationLookup(gd.GenSchema)
   163  	}
   164  	return gd, err
   165  }
   166  
   167  func shallowValidationLookup(sch GenSchema) bool {
   168  	// scan top level need for validations
   169  	//
   170  	// NOTE: this supersedes the previous NeedsValidation flag
   171  	// With the introduction of this shallow lookup, it is no more necessary
   172  	// to establish a distinction between HasValidations (e.g. carries on validations)
   173  	// and NeedsValidation (e.g. should have a Validate method with something in it).
   174  	// The latter was almost not used anyhow.
   175  
   176  	if sch.HasAdditionalProperties && sch.AdditionalProperties == nil {
   177  		log.Printf("warning: schema for additional properties in schema %q is empty. skipped", sch.Name)
   178  	}
   179  
   180  	if sch.IsArray && sch.HasValidations {
   181  		return true
   182  	}
   183  	if sch.IsStream || sch.IsInterface { // these types have no validation - aliased types on those do not implement the Validatable interface
   184  		return false
   185  	}
   186  	if sch.Required || hasFormatValidation(sch.resolvedType) {
   187  		return true
   188  	}
   189  	if sch.HasStringValidations() || sch.HasNumberValidations() || sch.HasEnum() || len(sch.ItemsEnum) > 0 || sch.HasObjectValidations() {
   190  		return true
   191  	}
   192  	for _, a := range sch.AllOf {
   193  		if a.HasValidations {
   194  			return true
   195  		}
   196  	}
   197  	for _, p := range sch.Properties {
   198  		// Using a base type within another structure triggers validation of the base type.
   199  		// The discriminator property in the base type definition itself does not.
   200  		if (p.HasValidations || p.Required) && !(sch.IsBaseType && p.Name == sch.DiscriminatorField) || (p.IsAliased || p.IsComplexObject) && !(p.IsInterface || p.IsStream) {
   201  			return true
   202  		}
   203  	}
   204  	if sch.IsTuple && (sch.AdditionalItems != nil && (sch.AdditionalItems.HasValidations || sch.AdditionalItems.Required)) {
   205  		return true
   206  	}
   207  	if sch.HasAdditionalProperties && sch.AdditionalProperties != nil && (sch.AdditionalProperties.IsInterface || sch.AdditionalProperties.IsStream) {
   208  		return false
   209  	}
   210  
   211  	if sch.HasAdditionalProperties && sch.AdditionalProperties != nil && (sch.AdditionalProperties.HasValidations || sch.AdditionalProperties.Required || sch.AdditionalProperties.IsAliased && !(sch.AdditionalProperties.IsInterface || sch.AdditionalProperties.IsStream)) {
   212  		return true
   213  	}
   214  
   215  	if sch.IsAliased && (sch.IsPrimitive && sch.HasValidations) { // non primitive aliased have either other attributes with validation (above) or shall not validate
   216  		return true
   217  	}
   218  	if sch.HasBaseType || sch.IsSubType {
   219  		return true
   220  	}
   221  	return false
   222  }
   223  
   224  func isExternal(schema spec.Schema) bool {
   225  	extType, ok := hasExternalType(schema.Extensions)
   226  	return ok && !extType.Embedded
   227  }
   228  
   229  func makeGenDefinitionHierarchy(name, pkg, container string, schema spec.Schema, specDoc *loads.Document, opts *GenOpts) (*GenDefinition, error) {
   230  	// Check if model is imported from external package using x-go-type
   231  	receiver := "m"
   232  	// models are resolved in the current package
   233  	modelPkg := opts.LanguageOpts.ManglePackageName(path.Base(filepath.ToSlash(pkg)), "definitions")
   234  	resolver := newTypeResolver("", "", specDoc).withDefinitionPackage(modelPkg)
   235  	resolver.ModelName = name
   236  	analyzed := analysis.New(specDoc.Spec())
   237  
   238  	di := discriminatorInfo(analyzed)
   239  
   240  	pg := schemaGenContext{
   241  		Path:                       "",
   242  		Name:                       name,
   243  		Receiver:                   receiver,
   244  		IndexVar:                   "i",
   245  		ValueExpr:                  receiver,
   246  		Schema:                     schema,
   247  		Required:                   false,
   248  		TypeResolver:               resolver,
   249  		Named:                      true,
   250  		ExtraSchemas:               make(map[string]GenSchema),
   251  		Discrimination:             di,
   252  		Container:                  container,
   253  		IncludeValidator:           opts.IncludeValidator,
   254  		IncludeModel:               opts.IncludeModel,
   255  		StrictAdditionalProperties: opts.StrictAdditionalProperties,
   256  		WithXML:                    opts.WithXML,
   257  		StructTags:                 opts.StructTags,
   258  	}
   259  	if err := pg.makeGenSchema(); err != nil {
   260  		return nil, fmt.Errorf("could not generate schema for %s: %v", name, err)
   261  	}
   262  	dsi, ok := di.Discriminators["#/definitions/"+name]
   263  	if ok {
   264  		// when these 2 are true then the schema will render as an interface
   265  		pg.GenSchema.IsBaseType = true
   266  		pg.GenSchema.IsExported = true
   267  		pg.GenSchema.DiscriminatorField = dsi.FieldName
   268  
   269  		if pg.GenSchema.Discriminates == nil {
   270  			pg.GenSchema.Discriminates = make(map[string]string)
   271  		}
   272  		pg.GenSchema.Discriminates[name] = dsi.GoType
   273  		pg.GenSchema.DiscriminatorValue = name
   274  
   275  		for _, v := range dsi.Children {
   276  			pg.GenSchema.Discriminates[v.FieldValue] = v.GoType
   277  		}
   278  
   279  		for j := range pg.GenSchema.Properties {
   280  			if !strings.HasSuffix(pg.GenSchema.Properties[j].ValueExpression, asMethod) {
   281  				pg.GenSchema.Properties[j].ValueExpression += asMethod
   282  			}
   283  		}
   284  	}
   285  
   286  	dse, ok := di.Discriminated["#/definitions/"+name]
   287  	if ok {
   288  		pg.GenSchema.DiscriminatorField = dse.FieldName
   289  		pg.GenSchema.DiscriminatorValue = dse.FieldValue
   290  		pg.GenSchema.IsSubType = true
   291  		knownProperties := make(map[string]struct{})
   292  
   293  		// find the referenced definitions
   294  		// check if it has a discriminator defined
   295  		// when it has a discriminator get the schema and run makeGenSchema for it.
   296  		// replace the ref with this new genschema
   297  		swsp := specDoc.Spec()
   298  		for i, ss := range schema.AllOf {
   299  			if pg.GenSchema.AllOf == nil {
   300  				log.Printf("warning: resolved schema for subtype %q.AllOf[%d] is empty. skipped", name, i)
   301  				continue
   302  			}
   303  			ref := ss.Ref
   304  			for ref.String() != "" {
   305  				var rsch *spec.Schema
   306  				var err error
   307  				rsch, err = spec.ResolveRef(swsp, &ref)
   308  				if err != nil {
   309  					return nil, err
   310  				}
   311  				if rsch != nil && rsch.Ref.String() != "" {
   312  					ref = rsch.Ref
   313  					continue
   314  				}
   315  				ref = spec.Ref{}
   316  				if rsch != nil && rsch.Discriminator != "" {
   317  					gs, err := makeGenDefinitionHierarchy(strings.TrimPrefix(ss.Ref.String(), "#/definitions/"), pkg, pg.GenSchema.Name, *rsch, specDoc, opts)
   318  					if err != nil {
   319  						return nil, err
   320  					}
   321  					gs.GenSchema.IsBaseType = true
   322  					gs.GenSchema.IsExported = true
   323  					pg.GenSchema.AllOf[i] = gs.GenSchema
   324  					schPtr := &(pg.GenSchema.AllOf[i])
   325  					if schPtr.AdditionalItems != nil {
   326  						schPtr.AdditionalItems.IsBaseType = true
   327  					}
   328  					if schPtr.AdditionalProperties != nil {
   329  						schPtr.AdditionalProperties.IsBaseType = true
   330  					}
   331  					for j := range schPtr.Properties {
   332  						schPtr.Properties[j].IsBaseType = true
   333  						knownProperties[schPtr.Properties[j].Name] = struct{}{}
   334  					}
   335  				}
   336  			}
   337  		}
   338  
   339  		// dedupe the fields
   340  		alreadySeen := make(map[string]struct{})
   341  		for i, ss := range pg.GenSchema.AllOf {
   342  			var remainingProperties GenSchemaList
   343  			for _, p := range ss.Properties {
   344  				if _, ok := knownProperties[p.Name]; !ok || ss.IsBaseType {
   345  					if _, seen := alreadySeen[p.Name]; !seen {
   346  						remainingProperties = append(remainingProperties, p)
   347  						alreadySeen[p.Name] = struct{}{}
   348  					}
   349  				}
   350  			}
   351  			pg.GenSchema.AllOf[i].Properties = remainingProperties
   352  		}
   353  
   354  	}
   355  
   356  	defaultImports := map[string]string{
   357  		"errors":   "github.com/go-openapi/errors",
   358  		"runtime":  "github.com/go-openapi/runtime",
   359  		"swag":     "github.com/go-openapi/swag",
   360  		"validate": "github.com/go-openapi/validate",
   361  	}
   362  
   363  	return &GenDefinition{
   364  		GenCommon: GenCommon{
   365  			Copyright:        opts.Copyright,
   366  			TargetImportPath: opts.LanguageOpts.baseImport(opts.Target),
   367  		},
   368  		Package:        modelPkg,
   369  		GenSchema:      pg.GenSchema,
   370  		DependsOn:      pg.Dependencies,
   371  		DefaultImports: defaultImports,
   372  		ExtraSchemas:   gatherExtraSchemas(pg.ExtraSchemas),
   373  		Imports:        findImports(&pg.GenSchema),
   374  		External:       isExternal(schema),
   375  	}, nil
   376  }
   377  
   378  func findImports(sch *GenSchema) map[string]string {
   379  	imp := make(map[string]string, 20)
   380  	t := sch.resolvedType
   381  	if t.Pkg != "" && t.PkgAlias != "" {
   382  		imp[t.PkgAlias] = t.Pkg
   383  	}
   384  	if t.IsEmbedded && t.ElemType != nil {
   385  		if t.ElemType.Pkg != "" && t.ElemType.PkgAlias != "" {
   386  			imp[t.ElemType.PkgAlias] = t.ElemType.Pkg
   387  		}
   388  	}
   389  	if sch.Items != nil {
   390  		sub := findImports(sch.Items)
   391  		for k, v := range sub {
   392  			imp[k] = v
   393  		}
   394  	}
   395  	if sch.AdditionalItems != nil {
   396  		sub := findImports(sch.AdditionalItems)
   397  		for k, v := range sub {
   398  			imp[k] = v
   399  		}
   400  	}
   401  	if sch.Object != nil {
   402  		sub := findImports(sch.Object)
   403  		for k, v := range sub {
   404  			imp[k] = v
   405  		}
   406  	}
   407  	if sch.Properties != nil {
   408  		for _, props := range sch.Properties {
   409  			p := props
   410  			sub := findImports(&p)
   411  			for k, v := range sub {
   412  				imp[k] = v
   413  			}
   414  		}
   415  	}
   416  	if sch.AdditionalProperties != nil {
   417  		sub := findImports(sch.AdditionalProperties)
   418  		for k, v := range sub {
   419  			imp[k] = v
   420  		}
   421  	}
   422  	if sch.AllOf != nil {
   423  		for _, props := range sch.AllOf {
   424  			p := props
   425  			sub := findImports(&p)
   426  			for k, v := range sub {
   427  				imp[k] = v
   428  			}
   429  		}
   430  	}
   431  	for k, v := range sch.ExtraImports {
   432  		if k != "" && v != "" {
   433  			imp[k] = v
   434  		}
   435  	}
   436  
   437  	return imp
   438  }
   439  
   440  type schemaGenContext struct {
   441  	Required                   bool
   442  	AdditionalProperty         bool
   443  	Untyped                    bool
   444  	Named                      bool
   445  	RefHandled                 bool
   446  	IsVirtual                  bool
   447  	IsTuple                    bool
   448  	IncludeValidator           bool
   449  	IncludeModel               bool
   450  	StrictAdditionalProperties bool
   451  	WithXML                    bool
   452  	Index                      int
   453  
   454  	Path         string
   455  	Name         string
   456  	ParamName    string
   457  	Accessor     string
   458  	Receiver     string
   459  	IndexVar     string
   460  	KeyVar       string
   461  	ValueExpr    string
   462  	Container    string
   463  	Schema       spec.Schema
   464  	TypeResolver *typeResolver
   465  	StructTags   []string
   466  
   467  	GenSchema      GenSchema
   468  	Dependencies   []string // NOTE: Dependencies is actually set nowhere
   469  	ExtraSchemas   map[string]GenSchema
   470  	Discriminator  *discor
   471  	Discriminated  *discee
   472  	Discrimination *discInfo
   473  
   474  	// force to use container in inlined definitions (for deconflicting)
   475  	UseContainerInName bool
   476  }
   477  
   478  func (sg *schemaGenContext) NewSliceBranch(schema *spec.Schema) *schemaGenContext {
   479  	debugLog("new slice branch %s (model: %s)", sg.Name, sg.TypeResolver.ModelName)
   480  	pg := sg.shallowClone()
   481  	indexVar := pg.IndexVar
   482  	if pg.Path == "" {
   483  		pg.Path = "strconv.Itoa(" + indexVar + ")"
   484  	} else {
   485  		pg.Path = pg.Path + "+ \".\" + strconv.Itoa(" + indexVar + ")"
   486  	}
   487  	// check who is parent, if it's a base type then rewrite the value expression
   488  	if sg.Discrimination != nil && sg.Discrimination.Discriminators != nil {
   489  		_, rewriteValueExpr := sg.Discrimination.Discriminators["#/definitions/"+sg.TypeResolver.ModelName]
   490  		if (pg.IndexVar == "i" && rewriteValueExpr) || sg.GenSchema.ElemType.IsBaseType {
   491  			if !sg.GenSchema.IsAliased {
   492  				pg.ValueExpr = sg.Receiver + "." + swag.ToJSONName(sg.GenSchema.Name) + "Field"
   493  			} else {
   494  				pg.ValueExpr = sg.Receiver
   495  			}
   496  		}
   497  	}
   498  	sg.GenSchema.IsBaseType = sg.GenSchema.ElemType.HasDiscriminator
   499  	pg.IndexVar = indexVar + "i"
   500  	pg.ValueExpr = pg.ValueExpr + "[" + indexVar + "]"
   501  	pg.Schema = *schema
   502  	pg.Required = false
   503  	if sg.IsVirtual {
   504  		pg.TypeResolver = sg.TypeResolver.NewWithModelName(sg.TypeResolver.ModelName)
   505  	}
   506  
   507  	// when this is an anonymous complex object, this needs to become a ref
   508  	return pg
   509  }
   510  
   511  func (sg *schemaGenContext) NewAdditionalItems(schema *spec.Schema) *schemaGenContext {
   512  	debugLog("new additional items\n")
   513  
   514  	pg := sg.shallowClone()
   515  	indexVar := pg.IndexVar
   516  	pg.Name = sg.Name + " items"
   517  	itemsLen := 0
   518  	if sg.Schema.Items != nil {
   519  		itemsLen = sg.Schema.Items.Len()
   520  	}
   521  	var mod string
   522  	if itemsLen > 0 {
   523  		mod = "+" + strconv.Itoa(itemsLen)
   524  	}
   525  	if pg.Path == "" {
   526  		pg.Path = "strconv.Itoa(" + indexVar + mod + ")"
   527  	} else {
   528  		pg.Path = pg.Path + "+ \".\" + strconv.Itoa(" + indexVar + mod + ")"
   529  	}
   530  	pg.IndexVar = indexVar
   531  	pg.ValueExpr = sg.ValueExpr + "." + pascalize(sg.GoName()) + "Items[" + indexVar + "]"
   532  	pg.Schema = spec.Schema{}
   533  	if schema != nil {
   534  		pg.Schema = *schema
   535  	}
   536  	pg.Required = false
   537  	return pg
   538  }
   539  
   540  func (sg *schemaGenContext) NewTupleElement(schema *spec.Schema, index int) *schemaGenContext {
   541  	debugLog("New tuple element\n")
   542  
   543  	pg := sg.shallowClone()
   544  	if pg.Path == "" {
   545  		pg.Path = "\"" + strconv.Itoa(index) + "\""
   546  	} else {
   547  		pg.Path = pg.Path + "+ \".\"+\"" + strconv.Itoa(index) + "\""
   548  	}
   549  	pg.ValueExpr = pg.ValueExpr + ".P" + strconv.Itoa(index)
   550  
   551  	pg.Required = true
   552  	pg.IsTuple = true
   553  	pg.Schema = *schema
   554  
   555  	return pg
   556  }
   557  
   558  func (sg *schemaGenContext) NewStructBranch(name string, schema spec.Schema) *schemaGenContext {
   559  	debugLog("new struct branch %s (parent %s)", sg.Name, sg.Container)
   560  	pg := sg.shallowClone()
   561  	if sg.Path == "" {
   562  		pg.Path = fmt.Sprintf("%q", name)
   563  	} else {
   564  		pg.Path = pg.Path + "+\".\"+" + fmt.Sprintf("%q", name)
   565  	}
   566  	pg.Name = name
   567  	pg.ValueExpr = pg.ValueExpr + "." + pascalize(goName(&schema, name))
   568  	pg.Schema = schema
   569  	for _, fn := range sg.Schema.Required {
   570  		if name == fn {
   571  			pg.Required = true
   572  			break
   573  		}
   574  	}
   575  	debugLog("made new struct branch %s (parent %s)", pg.Name, pg.Container)
   576  	return pg
   577  }
   578  
   579  func (sg *schemaGenContext) shallowClone() *schemaGenContext {
   580  	debugLog("cloning context %s\n", sg.Name)
   581  	pg := new(schemaGenContext)
   582  	*pg = *sg
   583  	if pg.Container == "" {
   584  		pg.Container = sg.Name
   585  	}
   586  	pg.GenSchema = GenSchema{StructTags: sg.StructTags}
   587  	pg.Dependencies = nil
   588  	pg.Named = false
   589  	pg.Index = 0
   590  	pg.IsTuple = false
   591  	pg.IncludeValidator = sg.IncludeValidator
   592  	pg.IncludeModel = sg.IncludeModel
   593  	pg.StrictAdditionalProperties = sg.StrictAdditionalProperties
   594  	return pg
   595  }
   596  
   597  func (sg *schemaGenContext) NewCompositionBranch(schema spec.Schema, index int) *schemaGenContext {
   598  	debugLog("new composition branch %s (parent: %s, index: %d)", sg.Name, sg.Container, index)
   599  	pg := sg.shallowClone()
   600  	pg.Schema = schema
   601  	pg.Name = "AO" + strconv.Itoa(index)
   602  	if sg.Name != sg.TypeResolver.ModelName {
   603  		pg.Name = sg.Name + pg.Name
   604  	}
   605  	pg.Index = index
   606  	debugLog("made new composition branch %s (parent: %s)", pg.Name, pg.Container)
   607  	return pg
   608  }
   609  
   610  func (sg *schemaGenContext) NewAdditionalProperty(schema spec.Schema) *schemaGenContext {
   611  	debugLog("new additional property %s (expr: %s)", sg.Name, sg.ValueExpr)
   612  	pg := sg.shallowClone()
   613  	pg.Schema = schema
   614  	if pg.KeyVar == "" {
   615  		pg.ValueExpr = sg.ValueExpr
   616  	}
   617  	pg.KeyVar += "k"
   618  	pg.ValueExpr += "[" + pg.KeyVar + "]"
   619  	pg.Path = pg.KeyVar
   620  	pg.GenSchema.Suffix = "Value"
   621  	if sg.Path != "" {
   622  		pg.Path = sg.Path + "+\".\"+" + pg.KeyVar
   623  	}
   624  	// propagates the special IsNullable override for maps of slices and
   625  	// maps of aliased types.
   626  	pg.GenSchema.IsMapNullOverride = sg.GenSchema.IsMapNullOverride
   627  	return pg
   628  }
   629  
   630  func hasContextValidations(model *spec.Schema) bool {
   631  	// always assume ref needs context validate
   632  	// TODO: find away to determine ref needs context validate or not
   633  	if model.ReadOnly || model.Ref.String() != "" {
   634  		return true
   635  	}
   636  	return false
   637  }
   638  
   639  func hasValidations(model *spec.Schema, isRequired bool) bool {
   640  	if isRequired {
   641  		return true
   642  	}
   643  
   644  	v := model.Validations()
   645  	if v.HasNumberValidations() || v.HasStringValidations() || v.HasArrayValidations() || v.HasEnum() || v.HasObjectValidations() {
   646  		return true
   647  	}
   648  
   649  	// since this was added to deal with discriminator, we'll fix this when testing discriminated types
   650  	if len(model.Properties) > 0 && model.Discriminator == "" {
   651  		return true
   652  	}
   653  
   654  	// lift validations from allOf branches
   655  	for _, s := range model.AllOf {
   656  		schema := s
   657  		if s.Ref.String() != "" || hasValidations(&schema, false) {
   658  			return true
   659  		}
   660  	}
   661  
   662  	return false
   663  }
   664  
   665  func hasFormatValidation(tpe resolvedType) bool {
   666  	if tpe.IsCustomFormatter && !tpe.IsStream && !tpe.IsBase64 {
   667  		return true
   668  	}
   669  	if tpe.IsArray && tpe.ElemType != nil {
   670  		return hasFormatValidation(*tpe.ElemType)
   671  	}
   672  	return false
   673  }
   674  
   675  func (sg *schemaGenContext) schemaValidations() sharedValidations {
   676  	model := sg.Schema
   677  
   678  	isRequired := sg.Required
   679  	if model.Default != nil || model.ReadOnly {
   680  		// when readOnly or default is specified, this disables Required validation (Swagger-specific)
   681  		isRequired = false
   682  		if sg.Required {
   683  			log.Printf("warn: properties with a default value or readOnly should not be required [%s]", sg.Name)
   684  		}
   685  	}
   686  
   687  	v := model.Validations()
   688  	return sharedValidations{
   689  		Required:            sg.Required, /* TODO(fred): guard for cases with discriminator field, default and readOnly*/
   690  		SchemaValidations:   v,
   691  		HasSliceValidations: v.HasArrayValidations() || v.HasEnum(),
   692  		HasValidations:      hasValidations(&model, isRequired),
   693  	}
   694  }
   695  
   696  func mergeValidation(other *schemaGenContext) bool {
   697  	// NOTE: NeesRequired and NeedsValidation are deprecated
   698  	if other.GenSchema.AdditionalProperties != nil && other.GenSchema.AdditionalProperties.HasValidations {
   699  		return true
   700  	}
   701  	if other.GenSchema.AdditionalItems != nil && other.GenSchema.AdditionalItems.HasValidations {
   702  		return true
   703  	}
   704  	for _, sch := range other.GenSchema.AllOf {
   705  		if sch.HasValidations {
   706  			return true
   707  		}
   708  	}
   709  	return other.GenSchema.HasValidations
   710  }
   711  
   712  func (sg *schemaGenContext) MergeResult(other *schemaGenContext, liftsRequired bool) {
   713  	sg.GenSchema.HasValidations = sg.GenSchema.HasValidations || mergeValidation(other)
   714  	sg.GenSchema.HasContextValidations = sg.GenSchema.HasContextValidations || other.GenSchema.HasContextValidations
   715  
   716  	if liftsRequired && other.GenSchema.AdditionalProperties != nil && other.GenSchema.AdditionalProperties.Required {
   717  		sg.GenSchema.Required = true
   718  	}
   719  	if liftsRequired && other.GenSchema.Required {
   720  		sg.GenSchema.Required = other.GenSchema.Required
   721  	}
   722  
   723  	if other.GenSchema.HasBaseType {
   724  		sg.GenSchema.HasBaseType = other.GenSchema.HasBaseType
   725  	}
   726  
   727  	sg.Dependencies = append(sg.Dependencies, other.Dependencies...)
   728  
   729  	// lift extra schemas
   730  	for k, v := range other.ExtraSchemas {
   731  		sg.ExtraSchemas[k] = v
   732  	}
   733  	if other.GenSchema.IsMapNullOverride {
   734  		sg.GenSchema.IsMapNullOverride = true
   735  	}
   736  
   737  	// lift extra imports
   738  	if other.GenSchema.Pkg != "" && other.GenSchema.PkgAlias != "" {
   739  		sg.GenSchema.ExtraImports[other.GenSchema.PkgAlias] = other.GenSchema.Pkg
   740  	}
   741  	for k, v := range other.GenSchema.ExtraImports {
   742  		sg.GenSchema.ExtraImports[k] = v
   743  	}
   744  }
   745  
   746  func (sg *schemaGenContext) buildProperties() error {
   747  	debugLog("building properties %s (parent: %s)", sg.Name, sg.Container)
   748  
   749  	for k, v := range sg.Schema.Properties {
   750  		debugLogAsJSON("building property %s[%q] (IsTuple: %t) (IsBaseType: %t) (HasValidations: %t)",
   751  			sg.Name, k, sg.IsTuple, sg.GenSchema.IsBaseType, sg.GenSchema.HasValidations, v)
   752  
   753  		vv := v
   754  
   755  		// check if this requires de-anonymizing, if so lift this as a new struct and extra schema
   756  		tpe, err := sg.TypeResolver.ResolveSchema(&vv, true, sg.IsTuple || swag.ContainsStrings(sg.Schema.Required, k))
   757  		if err != nil {
   758  			return err
   759  		}
   760  		if sg.Schema.Discriminator == k {
   761  			tpe.IsNullable = false
   762  		}
   763  
   764  		var hasValidation bool
   765  		if tpe.IsComplexObject && tpe.IsAnonymous && len(v.Properties) > 0 {
   766  			// this is an anonymous complex construct: build a new new type for it
   767  			pg := sg.makeNewStruct(sg.makeRefName()+swag.ToGoName(k), v)
   768  			pg.IsTuple = sg.IsTuple
   769  			if sg.Path != "" {
   770  				pg.Path = sg.Path + "+ \".\"+" + fmt.Sprintf("%q", k)
   771  			} else {
   772  				pg.Path = fmt.Sprintf("%q", k)
   773  			}
   774  			if err := pg.makeGenSchema(); err != nil {
   775  				return err
   776  			}
   777  			if v.Discriminator != "" {
   778  				pg.GenSchema.IsBaseType = true
   779  				pg.GenSchema.IsExported = true
   780  				pg.GenSchema.HasBaseType = true
   781  			}
   782  
   783  			vv = *spec.RefProperty("#/definitions/" + pg.Name)
   784  			hasValidation = pg.GenSchema.HasValidations
   785  			sg.ExtraSchemas[pg.Name] = pg.GenSchema
   786  			// NOTE: MergeResult lifts validation status and extra schemas
   787  			sg.MergeResult(pg, false)
   788  		}
   789  
   790  		emprop := sg.NewStructBranch(k, vv)
   791  		emprop.IsTuple = sg.IsTuple
   792  
   793  		if err := emprop.makeGenSchema(); err != nil {
   794  			return err
   795  		}
   796  
   797  		// whatever the validations says, if we have an interface{}, do not validate
   798  		// NOTE: this may be the case when the type is left empty and we get a Enum validation.
   799  		if emprop.GenSchema.IsInterface || emprop.GenSchema.IsStream {
   800  			emprop.GenSchema.HasValidations = false
   801  		} else if hasValidation || emprop.GenSchema.HasValidations || emprop.GenSchema.Required || emprop.GenSchema.IsAliased || len(emprop.GenSchema.AllOf) > 0 {
   802  			emprop.GenSchema.HasValidations = true
   803  			sg.GenSchema.HasValidations = true
   804  		}
   805  
   806  		// generates format validation on property
   807  		emprop.GenSchema.HasValidations = emprop.GenSchema.HasValidations || hasFormatValidation(tpe)
   808  
   809  		if emprop.Schema.Ref.String() != "" {
   810  			// expand the schema of this property, so we take informed decisions about its type
   811  			ref := emprop.Schema.Ref
   812  			var sch *spec.Schema
   813  			for ref.String() != "" {
   814  				var rsch *spec.Schema
   815  				var err error
   816  				specDoc := sg.TypeResolver.Doc
   817  				rsch, err = spec.ResolveRef(specDoc.Spec(), &ref)
   818  				if err != nil {
   819  					return err
   820  				}
   821  				if rsch == nil {
   822  					return errors.New("spec.ResolveRef returned nil schema")
   823  				}
   824  				if rsch != nil && rsch.Ref.String() != "" {
   825  					ref = rsch.Ref
   826  					continue
   827  				}
   828  				ref = spec.Ref{}
   829  				sch = rsch
   830  			}
   831  
   832  			if emprop.Discrimination != nil {
   833  				if _, ok := emprop.Discrimination.Discriminators[emprop.Schema.Ref.String()]; ok {
   834  					emprop.GenSchema.IsBaseType = true
   835  					emprop.GenSchema.IsNullable = false
   836  					emprop.GenSchema.HasBaseType = true
   837  				}
   838  				if _, ok := emprop.Discrimination.Discriminated[emprop.Schema.Ref.String()]; ok {
   839  					emprop.GenSchema.IsSubType = true
   840  				}
   841  			}
   842  
   843  			// set property name
   844  			var nm = filepath.Base(emprop.Schema.Ref.GetURL().Fragment)
   845  
   846  			tr := sg.TypeResolver.NewWithModelName(goName(&emprop.Schema, swag.ToGoName(nm)))
   847  			ttpe, err := tr.ResolveSchema(sch, false, true)
   848  			if err != nil {
   849  				return err
   850  			}
   851  			if ttpe.IsAliased {
   852  				emprop.GenSchema.IsAliased = true
   853  			}
   854  
   855  			// lift validations
   856  			hv := hasValidations(sch, false)
   857  
   858  			// include format validation, excluding binary
   859  			hv = hv || hasFormatValidation(ttpe)
   860  
   861  			// a base type property is always validated against the base type
   862  			// exception: for the base type definition itself (see shallowValidationLookup())
   863  			if (hv || emprop.GenSchema.IsBaseType) && !(emprop.GenSchema.IsInterface || emprop.GenSchema.IsStream) {
   864  				emprop.GenSchema.HasValidations = true
   865  			}
   866  			if ttpe.HasAdditionalItems && sch.AdditionalItems.Schema != nil {
   867  				// when AdditionalItems specifies a Schema, there is a validation
   868  				// check if we stepped upon an exception
   869  				child, err := tr.ResolveSchema(sch.AdditionalItems.Schema, false, true)
   870  				if err != nil {
   871  					return err
   872  				}
   873  				if !child.IsInterface && !child.IsStream {
   874  					emprop.GenSchema.HasValidations = true
   875  				}
   876  			}
   877  			if ttpe.IsMap && sch.AdditionalProperties != nil && sch.AdditionalProperties.Schema != nil {
   878  				// when AdditionalProperties specifies a Schema, there is a validation
   879  				// check if we stepped upon an exception
   880  				child, err := tr.ResolveSchema(sch.AdditionalProperties.Schema, false, true)
   881  				if err != nil {
   882  					return err
   883  				}
   884  				if !child.IsInterface && !child.IsStream {
   885  					emprop.GenSchema.HasValidations = true
   886  				}
   887  			}
   888  		}
   889  
   890  		if sg.Schema.Discriminator == k {
   891  			// this is the discriminator property:
   892  			// it is required, but forced as non-nullable,
   893  			// since we never fill it with a zero-value
   894  			// TODO: when no other property than discriminator, there is no validation
   895  			emprop.GenSchema.IsNullable = false
   896  		}
   897  		if emprop.GenSchema.IsBaseType {
   898  			sg.GenSchema.HasBaseType = true
   899  		}
   900  		sg.MergeResult(emprop, false)
   901  
   902  		// when discriminated, data is accessed via a getter func
   903  		if emprop.GenSchema.HasDiscriminator {
   904  			emprop.GenSchema.ValueExpression += asMethod
   905  		}
   906  
   907  		emprop.GenSchema.Extensions = emprop.Schema.Extensions
   908  
   909  		// set custom serializer tag
   910  		if customTag, found := tpe.Extensions[xGoCustomTag]; found {
   911  			tagAsStr, ok := customTag.(string)
   912  			if ok {
   913  				emprop.GenSchema.CustomTag = tagAsStr
   914  			} else {
   915  				log.Printf("warning: expect %s extension to be a string, got: %v. Skipped", xGoCustomTag, customTag)
   916  			}
   917  		}
   918  		sg.GenSchema.Properties = append(sg.GenSchema.Properties, emprop.GenSchema)
   919  	}
   920  	sort.Sort(sg.GenSchema.Properties)
   921  
   922  	return nil
   923  }
   924  
   925  func (sg *schemaGenContext) buildAllOf() error {
   926  	if len(sg.Schema.AllOf) == 0 {
   927  		return nil
   928  	}
   929  
   930  	var hasArray, hasNonArray int
   931  
   932  	sort.Sort(sg.GenSchema.AllOf)
   933  	if sg.Container == "" {
   934  		sg.Container = sg.Name
   935  	}
   936  	debugLogAsJSON("building all of for %d entries", len(sg.Schema.AllOf), sg.Schema)
   937  	for i, schema := range sg.Schema.AllOf {
   938  		sch := schema
   939  		tpe, ert := sg.TypeResolver.ResolveSchema(&sch, sch.Ref.String() == "", false)
   940  		if ert != nil {
   941  			return ert
   942  		}
   943  
   944  		// check for multiple arrays in allOf branches.
   945  		// Although a valid JSON-Schema construct, it is not suited for serialization.
   946  		// This is the same if we attempt to serialize an array with another object.
   947  		// We issue a generation warning on this.
   948  		if tpe.IsArray {
   949  			hasArray++
   950  		} else {
   951  			hasNonArray++
   952  		}
   953  		debugLogAsJSON("trying", sch)
   954  		if (tpe.IsAnonymous && len(sch.AllOf) > 0) || (sch.Ref.String() == "" && !tpe.IsComplexObject && (tpe.IsArray || tpe.IsInterface || tpe.IsPrimitive)) {
   955  			// cases where anonymous structures cause the creation of a new type:
   956  			// - nested allOf: this one is itself a AllOf: build a new type for it
   957  			// - anonymous simple types for edge cases: array, primitive, interface{}
   958  			// NOTE: when branches are aliased or anonymous, the nullable property in the branch type is lost.
   959  			name := swag.ToVarName(goName(&sch, sg.makeRefName()+"AllOf"+strconv.Itoa(i)))
   960  			debugLog("building anonymous nested allOf in %s: %s", sg.Name, name)
   961  			ng := sg.makeNewStruct(name, sch)
   962  			if err := ng.makeGenSchema(); err != nil {
   963  				return err
   964  			}
   965  
   966  			newsch := spec.RefProperty("#/definitions/" + ng.Name)
   967  			sg.Schema.AllOf[i] = *newsch
   968  
   969  			pg := sg.NewCompositionBranch(*newsch, i)
   970  			if err := pg.makeGenSchema(); err != nil {
   971  				return err
   972  			}
   973  
   974  			// lift extra schemas & validations from new type
   975  			pg.MergeResult(ng, true)
   976  
   977  			// lift validations when complex or ref'ed:
   978  			// - parent always calls its Validatable child
   979  			// - child may or may not have validations
   980  			//
   981  			// Exception: child is not Validatable when interface or stream
   982  			if !pg.GenSchema.IsInterface && !pg.GenSchema.IsStream {
   983  				sg.GenSchema.HasValidations = true
   984  			}
   985  
   986  			// add the newly created type to the list of schemas to be rendered inline
   987  			pg.ExtraSchemas[ng.Name] = ng.GenSchema
   988  
   989  			sg.MergeResult(pg, true)
   990  
   991  			sg.GenSchema.AllOf = append(sg.GenSchema.AllOf, pg.GenSchema)
   992  
   993  			continue
   994  		}
   995  
   996  		comprop := sg.NewCompositionBranch(sch, i)
   997  		if err := comprop.makeGenSchema(); err != nil {
   998  			return err
   999  		}
  1000  		if comprop.GenSchema.IsMap && comprop.GenSchema.HasAdditionalProperties && comprop.GenSchema.AdditionalProperties != nil && !comprop.GenSchema.IsInterface {
  1001  			// the anonymous branch is a map for AdditionalProperties: rewrite value expression
  1002  			comprop.GenSchema.ValueExpression = comprop.GenSchema.ValueExpression + "." + comprop.Name
  1003  			comprop.GenSchema.AdditionalProperties.ValueExpression = comprop.GenSchema.ValueExpression + "[" + comprop.GenSchema.AdditionalProperties.KeyVar + "]"
  1004  		}
  1005  
  1006  		// lift validations when complex or ref'ed
  1007  		if (comprop.GenSchema.IsComplexObject || comprop.Schema.Ref.String() != "") && !(comprop.GenSchema.IsInterface || comprop.GenSchema.IsStream) {
  1008  			comprop.GenSchema.HasValidations = true
  1009  		}
  1010  		sg.MergeResult(comprop, true)
  1011  		sg.GenSchema.AllOf = append(sg.GenSchema.AllOf, comprop.GenSchema)
  1012  	}
  1013  
  1014  	if hasArray > 1 || (hasArray > 0 && hasNonArray > 0) {
  1015  		log.Printf("warning: cannot generate serializable allOf with conflicting array definitions in %s", sg.Container)
  1016  	}
  1017  
  1018  	// AllOf types are always considered nullable, except when an extension says otherwise
  1019  	if override, ok := sg.TypeResolver.isNullableOverride(&sg.Schema); ok {
  1020  		sg.GenSchema.IsNullable = override
  1021  	} else {
  1022  		sg.GenSchema.IsNullable = true
  1023  	}
  1024  
  1025  	// prevent IsAliased to bubble up (e.g. when a single branch is itself aliased)
  1026  	sg.GenSchema.IsAliased = sg.GenSchema.IsAliased && len(sg.GenSchema.AllOf) < 2
  1027  
  1028  	return nil
  1029  }
  1030  
  1031  type mapStack struct {
  1032  	Type     *spec.Schema
  1033  	Next     *mapStack
  1034  	Previous *mapStack
  1035  	ValueRef *schemaGenContext
  1036  	Context  *schemaGenContext
  1037  	NewObj   *schemaGenContext
  1038  }
  1039  
  1040  func newMapStack(context *schemaGenContext) (first, last *mapStack, err error) {
  1041  	ms := &mapStack{
  1042  		Type:    &context.Schema,
  1043  		Context: context,
  1044  	}
  1045  
  1046  	l := ms
  1047  	for l.HasMore() {
  1048  		tpe, err := l.Context.TypeResolver.ResolveSchema(l.Type.AdditionalProperties.Schema, true, true)
  1049  		if err != nil {
  1050  			return nil, nil, err
  1051  		}
  1052  
  1053  		if !tpe.IsMap {
  1054  			// reached the end of the rabbit hole
  1055  			if tpe.IsComplexObject && tpe.IsAnonymous {
  1056  				// found an anonymous object: create the struct from a newly created definition
  1057  				nw := l.Context.makeNewStruct(l.Context.makeRefName()+" Anon", *l.Type.AdditionalProperties.Schema)
  1058  				sch := spec.RefProperty("#/definitions/" + nw.Name)
  1059  				l.NewObj = nw
  1060  
  1061  				l.Type.AdditionalProperties.Schema = sch
  1062  				l.ValueRef = l.Context.NewAdditionalProperty(*sch)
  1063  			}
  1064  
  1065  			// other cases where to stop are: a $ref or a simple object
  1066  			break
  1067  		}
  1068  
  1069  		// continue digging for maps
  1070  		l.Next = &mapStack{
  1071  			Previous: l,
  1072  			Type:     l.Type.AdditionalProperties.Schema,
  1073  			Context:  l.Context.NewAdditionalProperty(*l.Type.AdditionalProperties.Schema),
  1074  		}
  1075  		l = l.Next
  1076  	}
  1077  
  1078  	// return top and bottom entries of this stack of AdditionalProperties
  1079  	return ms, l, nil
  1080  }
  1081  
  1082  // Build rewinds the stack of additional properties, building schemas from bottom to top
  1083  func (mt *mapStack) Build() error {
  1084  	if mt.NewObj == nil && mt.ValueRef == nil && mt.Next == nil && mt.Previous == nil {
  1085  		csch := mt.Type.AdditionalProperties.Schema
  1086  		cp := mt.Context.NewAdditionalProperty(*csch)
  1087  		d := mt.Context.TypeResolver.Doc
  1088  
  1089  		asch, err := analysis.Schema(analysis.SchemaOpts{
  1090  			Root:     d.Spec(),
  1091  			BasePath: d.SpecFilePath(),
  1092  			Schema:   csch,
  1093  		})
  1094  		if err != nil {
  1095  			return err
  1096  		}
  1097  		cp.Required = !asch.IsSimpleSchema && !asch.IsMap
  1098  
  1099  		// when the schema is an array or an alias, this may result in inconsistent
  1100  		// nullable status between the map element and the array element (resp. the aliased type).
  1101  		//
  1102  		// Example: when an object has no property and only additionalProperties,
  1103  		// which turn out to be arrays of some other object.
  1104  
  1105  		// save the initial override
  1106  		hadOverride := cp.GenSchema.IsMapNullOverride
  1107  		if err := cp.makeGenSchema(); err != nil {
  1108  			return err
  1109  		}
  1110  
  1111  		// if we have an override at the top of stack, propagates it down nested arrays
  1112  		if hadOverride && cp.GenSchema.IsArray {
  1113  			// do it for nested arrays: override is also about map[string][][]... constructs
  1114  			it := &cp.GenSchema
  1115  			for it.Items != nil && it.IsArray {
  1116  				it.Items.IsMapNullOverride = hadOverride
  1117  				it = it.Items
  1118  			}
  1119  		}
  1120  		// cover other cases than arrays (aliased types)
  1121  		cp.GenSchema.IsMapNullOverride = hadOverride
  1122  
  1123  		mt.Context.MergeResult(cp, false)
  1124  		mt.Context.GenSchema.AdditionalProperties = &cp.GenSchema
  1125  
  1126  		// lift validations
  1127  		if (csch.Ref.String() != "" || cp.GenSchema.IsAliased) && !(cp.GenSchema.IsInterface || cp.GenSchema.IsStream) {
  1128  			// - we stopped on a ref, or anything else that require we call its Validate() method
  1129  			// - if the alias / ref is on an interface (or stream) type: no validation
  1130  			mt.Context.GenSchema.HasValidations = true
  1131  			mt.Context.GenSchema.AdditionalProperties.HasValidations = true
  1132  		}
  1133  
  1134  		debugLog("early mapstack exit, nullable: %t for %s", cp.GenSchema.IsNullable, cp.GenSchema.Name)
  1135  		return nil
  1136  	}
  1137  	cur := mt
  1138  	for cur != nil {
  1139  		if cur.NewObj != nil {
  1140  			// a new model has been created during the stack construction (new ref on anonymous object)
  1141  			if err := cur.NewObj.makeGenSchema(); err != nil {
  1142  				return err
  1143  			}
  1144  		}
  1145  
  1146  		if cur.ValueRef != nil {
  1147  			if err := cur.ValueRef.makeGenSchema(); err != nil {
  1148  				return nil
  1149  			}
  1150  		}
  1151  
  1152  		if cur.NewObj != nil {
  1153  			// newly created model from anonymous object is declared as extra schema
  1154  			cur.Context.MergeResult(cur.NewObj, false)
  1155  
  1156  			// propagates extra schemas
  1157  			cur.Context.ExtraSchemas[cur.NewObj.Name] = cur.NewObj.GenSchema
  1158  		}
  1159  
  1160  		if cur.ValueRef != nil {
  1161  			// this is the genSchema for this new anonymous AdditionalProperty
  1162  			if err := cur.Context.makeGenSchema(); err != nil {
  1163  				return err
  1164  			}
  1165  
  1166  			// if there is a ValueRef, we must have a NewObj (from newMapStack() construction)
  1167  			cur.ValueRef.GenSchema.HasValidations = cur.NewObj.GenSchema.HasValidations
  1168  			cur.Context.MergeResult(cur.ValueRef, false)
  1169  			cur.Context.GenSchema.AdditionalProperties = &cur.ValueRef.GenSchema
  1170  		}
  1171  
  1172  		if cur.Previous != nil {
  1173  			// we have a parent schema: build a schema for current AdditionalProperties
  1174  			if err := cur.Context.makeGenSchema(); err != nil {
  1175  				return err
  1176  			}
  1177  		}
  1178  		if cur.Next != nil {
  1179  			// we previously made a child schema: lifts things from that one
  1180  			// - Required is not lifted (in a cascade of maps, only the last element is actually checked for Required)
  1181  			cur.Context.MergeResult(cur.Next.Context, false)
  1182  			cur.Context.GenSchema.AdditionalProperties = &cur.Next.Context.GenSchema
  1183  
  1184  			// lift validations
  1185  			c := &cur.Next.Context.GenSchema
  1186  			if (cur.Next.Context.Schema.Ref.String() != "" || c.IsAliased) && !(c.IsInterface || c.IsStream) {
  1187  				// - we stopped on a ref, or anything else that require we call its Validate()
  1188  				// - if the alias / ref is on an interface (or stream) type: no validation
  1189  				cur.Context.GenSchema.HasValidations = true
  1190  				cur.Context.GenSchema.AdditionalProperties.HasValidations = true
  1191  			}
  1192  		}
  1193  		if cur.ValueRef != nil {
  1194  			cur.Context.MergeResult(cur.ValueRef, false)
  1195  			cur.Context.GenSchema.AdditionalProperties = &cur.ValueRef.GenSchema
  1196  		}
  1197  
  1198  		if cur.Context.GenSchema.AdditionalProperties != nil {
  1199  			// propagate overrides up the resolved schemas, but leaves any ExtraSchema untouched
  1200  			cur.Context.GenSchema.AdditionalProperties.IsMapNullOverride = cur.Context.GenSchema.IsMapNullOverride
  1201  		}
  1202  		cur = cur.Previous
  1203  	}
  1204  
  1205  	return nil
  1206  }
  1207  
  1208  func (mt *mapStack) HasMore() bool {
  1209  	return mt.Type.AdditionalProperties != nil && (mt.Type.AdditionalProperties.Schema != nil || mt.Type.AdditionalProperties.Allows)
  1210  }
  1211  
  1212  /* currently unused:
  1213  func (mt *mapStack) Dict() map[string]interface{} {
  1214  	res := make(map[string]interface{})
  1215  	res["context"] = mt.Context.Schema
  1216  	if mt.Next != nil {
  1217  		res["next"] = mt.Next.Dict()
  1218  	}
  1219  	if mt.NewObj != nil {
  1220  		res["obj"] = mt.NewObj.Schema
  1221  	}
  1222  	if mt.ValueRef != nil {
  1223  		res["value"] = mt.ValueRef.Schema
  1224  	}
  1225  	return res
  1226  }
  1227  */
  1228  
  1229  func (sg *schemaGenContext) buildAdditionalProperties() error {
  1230  	if sg.Schema.AdditionalProperties == nil {
  1231  		return nil
  1232  	}
  1233  	addp := *sg.Schema.AdditionalProperties
  1234  
  1235  	wantsAdditional := addp.Schema != nil || addp.Allows
  1236  	sg.GenSchema.HasAdditionalProperties = wantsAdditional
  1237  	if !wantsAdditional {
  1238  		return nil
  1239  	}
  1240  
  1241  	// flag swap
  1242  	if sg.GenSchema.IsComplexObject {
  1243  		sg.GenSchema.IsAdditionalProperties = true
  1244  		sg.GenSchema.IsComplexObject = false
  1245  		sg.GenSchema.IsMap = false
  1246  	}
  1247  
  1248  	if addp.Schema == nil {
  1249  		// this is for AdditionalProperties:true|false
  1250  		if addp.Allows {
  1251  			// additionalProperties: true is rendered as: map[string]interface{}
  1252  			addp.Schema = &spec.Schema{}
  1253  
  1254  			addp.Schema.Typed("object", "")
  1255  			sg.GenSchema.HasAdditionalProperties = true
  1256  			sg.GenSchema.IsComplexObject = false
  1257  			sg.GenSchema.IsMap = true
  1258  
  1259  			sg.GenSchema.ValueExpression += "." + swag.ToGoName(sg.Name+" additionalProperties")
  1260  			cp := sg.NewAdditionalProperty(*addp.Schema)
  1261  			cp.Name += "AdditionalProperties"
  1262  			cp.Required = false
  1263  			if err := cp.makeGenSchema(); err != nil {
  1264  				return err
  1265  			}
  1266  			sg.MergeResult(cp, false)
  1267  			sg.GenSchema.AdditionalProperties = &cp.GenSchema
  1268  			debugLog("added interface{} schema for additionalProperties[allows == true], IsInterface=%t", cp.GenSchema.IsInterface)
  1269  		}
  1270  		return nil
  1271  	}
  1272  
  1273  	if !sg.GenSchema.IsMap && (sg.GenSchema.IsAdditionalProperties && sg.Named) {
  1274  		// we have a complex object with an AdditionalProperties schema
  1275  
  1276  		tpe, ert := sg.TypeResolver.ResolveSchema(addp.Schema, addp.Schema.Ref.String() == "", false)
  1277  		if ert != nil {
  1278  			return ert
  1279  		}
  1280  
  1281  		if tpe.IsComplexObject && tpe.IsAnonymous {
  1282  			// if the AdditionalProperties is an anonymous complex object, generate a new type for it
  1283  			pg := sg.makeNewStruct(sg.makeRefName()+" Anon", *addp.Schema)
  1284  			if err := pg.makeGenSchema(); err != nil {
  1285  				return err
  1286  			}
  1287  			sg.MergeResult(pg, false)
  1288  			sg.ExtraSchemas[pg.Name] = pg.GenSchema
  1289  
  1290  			sg.Schema.AdditionalProperties.Schema = spec.RefProperty("#/definitions/" + pg.Name)
  1291  			sg.IsVirtual = true
  1292  
  1293  			comprop := sg.NewAdditionalProperty(*sg.Schema.AdditionalProperties.Schema)
  1294  			if err := comprop.makeGenSchema(); err != nil {
  1295  				return err
  1296  			}
  1297  
  1298  			comprop.GenSchema.Required = true
  1299  			comprop.GenSchema.HasValidations = true
  1300  
  1301  			comprop.GenSchema.ValueExpression = sg.GenSchema.ValueExpression + "." + swag.ToGoName(sg.GenSchema.Name) + "[" + comprop.KeyVar + "]"
  1302  
  1303  			sg.GenSchema.AdditionalProperties = &comprop.GenSchema
  1304  			sg.GenSchema.HasAdditionalProperties = true
  1305  			sg.GenSchema.ValueExpression += "." + swag.ToGoName(sg.GenSchema.Name)
  1306  
  1307  			sg.MergeResult(comprop, false)
  1308  
  1309  			return nil
  1310  		}
  1311  
  1312  		// this is a regular named schema for AdditionalProperties
  1313  		sg.GenSchema.ValueExpression += "." + swag.ToGoName(sg.GenSchema.Name)
  1314  		comprop := sg.NewAdditionalProperty(*addp.Schema)
  1315  		d := sg.TypeResolver.Doc
  1316  		asch, err := analysis.Schema(analysis.SchemaOpts{
  1317  			Root:     d.Spec(),
  1318  			BasePath: d.SpecFilePath(),
  1319  			Schema:   addp.Schema,
  1320  		})
  1321  		if err != nil {
  1322  			return err
  1323  		}
  1324  		comprop.Required = !asch.IsSimpleSchema && !asch.IsMap
  1325  		if err := comprop.makeGenSchema(); err != nil {
  1326  			return err
  1327  		}
  1328  
  1329  		sg.MergeResult(comprop, false)
  1330  		sg.GenSchema.AdditionalProperties = &comprop.GenSchema
  1331  		sg.GenSchema.AdditionalProperties.ValueExpression = sg.GenSchema.ValueExpression + "[" + comprop.KeyVar + "]"
  1332  
  1333  		// rewrite value expression for arrays and arrays of arrays in maps (rendered as map[string][][]...)
  1334  		if sg.GenSchema.AdditionalProperties.IsArray {
  1335  			// maps of slices are where an override may take effect
  1336  			sg.GenSchema.AdditionalProperties.Items.IsMapNullOverride = sg.GenSchema.AdditionalProperties.IsMapNullOverride
  1337  			sg.GenSchema.AdditionalProperties.Items.ValueExpression = sg.GenSchema.ValueExpression + "[" + comprop.KeyVar + "]" + "[" + sg.GenSchema.AdditionalProperties.IndexVar + "]"
  1338  			ap := sg.GenSchema.AdditionalProperties.Items
  1339  			for ap != nil && ap.IsArray {
  1340  				ap.Items.IsMapNullOverride = ap.IsMapNullOverride
  1341  				ap.Items.ValueExpression = ap.ValueExpression + "[" + ap.IndexVar + "]"
  1342  				ap = ap.Items
  1343  			}
  1344  		}
  1345  
  1346  		// lift validation
  1347  		if (sg.GenSchema.AdditionalProperties.IsComplexObject || sg.GenSchema.AdditionalProperties.IsAliased || sg.GenSchema.AdditionalProperties.Required) && !(sg.GenSchema.AdditionalProperties.IsInterface || sg.GenSchema.IsStream) {
  1348  			sg.GenSchema.HasValidations = true
  1349  		}
  1350  		return nil
  1351  	}
  1352  
  1353  	if sg.GenSchema.IsMap && wantsAdditional {
  1354  		// this is itself an AdditionalProperties schema with some AdditionalProperties.
  1355  		// this also runs for aliased map types (with zero properties save additionalProperties)
  1356  		//
  1357  		// find out how deep this rabbit hole goes
  1358  		// descend, unwind and rewrite
  1359  		// This needs to be depth first, so it first goes as deep as it can and then
  1360  		// builds the result in reverse order.
  1361  		_, ls, err := newMapStack(sg)
  1362  		if err != nil {
  1363  			return err
  1364  		}
  1365  		return ls.Build()
  1366  	}
  1367  
  1368  	if sg.GenSchema.IsAdditionalProperties && !sg.Named {
  1369  		// for an anonymous object, first build the new object
  1370  		// and then replace the current one with a $ref to the
  1371  		// new object
  1372  		newObj := sg.makeNewStruct(sg.GenSchema.Name+" P"+strconv.Itoa(sg.Index), sg.Schema)
  1373  		if err := newObj.makeGenSchema(); err != nil {
  1374  			return err
  1375  		}
  1376  
  1377  		hasMapNullOverride := sg.GenSchema.IsMapNullOverride
  1378  		sg.GenSchema = GenSchema{StructTags: sg.StructTags}
  1379  		sg.Schema = *spec.RefProperty("#/definitions/" + newObj.Name)
  1380  		if err := sg.makeGenSchema(); err != nil {
  1381  			return err
  1382  		}
  1383  		sg.MergeResult(newObj, false)
  1384  
  1385  		sg.GenSchema.IsMapNullOverride = hasMapNullOverride
  1386  		if sg.GenSchema.IsArray {
  1387  			sg.GenSchema.Items.IsMapNullOverride = hasMapNullOverride
  1388  		}
  1389  
  1390  		sg.GenSchema.HasValidations = newObj.GenSchema.HasValidations
  1391  		sg.ExtraSchemas[newObj.Name] = newObj.GenSchema
  1392  		return nil
  1393  	}
  1394  	return nil
  1395  }
  1396  
  1397  func (sg *schemaGenContext) makeNewStruct(name string, schema spec.Schema) *schemaGenContext {
  1398  	debugLog("making new struct: name: %s, container: %s", name, sg.Container)
  1399  	sp := sg.TypeResolver.Doc.Spec()
  1400  	name = swag.ToGoName(name)
  1401  	if sg.TypeResolver.ModelName != sg.Name {
  1402  		name = swag.ToGoName(sg.TypeResolver.ModelName + " " + name)
  1403  	}
  1404  	if sp.Definitions == nil {
  1405  		sp.Definitions = make(spec.Definitions)
  1406  	}
  1407  	sp.Definitions[name] = schema
  1408  	pg := schemaGenContext{
  1409  		Path:                       "",
  1410  		Name:                       name,
  1411  		Receiver:                   sg.Receiver,
  1412  		IndexVar:                   "i",
  1413  		ValueExpr:                  sg.Receiver,
  1414  		Schema:                     schema,
  1415  		Required:                   false,
  1416  		Named:                      true,
  1417  		ExtraSchemas:               make(map[string]GenSchema),
  1418  		Discrimination:             sg.Discrimination,
  1419  		Container:                  sg.Container,
  1420  		IncludeValidator:           sg.IncludeValidator,
  1421  		IncludeModel:               sg.IncludeModel,
  1422  		StrictAdditionalProperties: sg.StrictAdditionalProperties,
  1423  		StructTags:                 sg.StructTags,
  1424  	}
  1425  	if schema.Ref.String() == "" {
  1426  		pg.TypeResolver = sg.TypeResolver.NewWithModelName(name)
  1427  	}
  1428  	pg.GenSchema.IsVirtual = true
  1429  
  1430  	sg.ExtraSchemas[name] = pg.GenSchema
  1431  	return &pg
  1432  }
  1433  
  1434  func (sg *schemaGenContext) buildArray() error {
  1435  	tpe, err := sg.TypeResolver.ResolveSchema(sg.Schema.Items.Schema, true, false)
  1436  	if err != nil {
  1437  		return err
  1438  	}
  1439  
  1440  	// check if the element is a complex object, if so generate a new type for it
  1441  	if tpe.IsComplexObject && tpe.IsAnonymous {
  1442  		pg := sg.makeNewStruct(sg.makeRefName()+" items"+strconv.Itoa(sg.Index), *sg.Schema.Items.Schema)
  1443  		if err := pg.makeGenSchema(); err != nil {
  1444  			return err
  1445  		}
  1446  		sg.MergeResult(pg, false)
  1447  		sg.ExtraSchemas[pg.Name] = pg.GenSchema
  1448  		sg.Schema.Items.Schema = spec.RefProperty("#/definitions/" + pg.Name)
  1449  		sg.IsVirtual = true
  1450  		return sg.makeGenSchema()
  1451  	}
  1452  
  1453  	// create the generation schema for items
  1454  	elProp := sg.NewSliceBranch(sg.Schema.Items.Schema)
  1455  
  1456  	// when building a slice of maps, the map item is not required
  1457  	// items from maps of aliased or nullable type remain required
  1458  
  1459  	// NOTE(fredbi): since this is reset below, this Required = true serves the obscure purpose
  1460  	// of indirectly lifting validations from the slice. This is carried out differently now.
  1461  	// elProp.Required = true
  1462  
  1463  	if err := elProp.makeGenSchema(); err != nil {
  1464  		return err
  1465  	}
  1466  
  1467  	sg.MergeResult(elProp, false)
  1468  
  1469  	sg.GenSchema.IsBaseType = elProp.GenSchema.IsBaseType
  1470  	sg.GenSchema.ItemsEnum = elProp.GenSchema.Enum
  1471  	elProp.GenSchema.Suffix = "Items"
  1472  
  1473  	elProp.GenSchema.IsNullable = tpe.IsNullable && !tpe.HasDiscriminator
  1474  	if elProp.GenSchema.IsNullable {
  1475  		sg.GenSchema.GoType = "[]*" + elProp.GenSchema.GoType
  1476  	} else {
  1477  		sg.GenSchema.GoType = "[]" + elProp.GenSchema.GoType
  1478  	}
  1479  
  1480  	sg.GenSchema.IsArray = true
  1481  
  1482  	schemaCopy := elProp.GenSchema
  1483  
  1484  	schemaCopy.Required = false
  1485  
  1486  	// validations of items
  1487  	// include format validation, excluding binary and base64 format validation
  1488  	hv := hasValidations(sg.Schema.Items.Schema, false) || hasFormatValidation(schemaCopy.resolvedType)
  1489  
  1490  	// base types of polymorphic types must be validated
  1491  	// NOTE: IsNullable is not useful to figure out a validation: we use Refed and IsAliased below instead
  1492  	if hv || elProp.GenSchema.IsBaseType {
  1493  		schemaCopy.HasValidations = true
  1494  	}
  1495  
  1496  	if (elProp.Schema.Ref.String() != "" || elProp.GenSchema.IsAliased) && !(elProp.GenSchema.IsInterface || elProp.GenSchema.IsStream) {
  1497  		schemaCopy.HasValidations = true
  1498  	}
  1499  
  1500  	// lift validations
  1501  	sg.GenSchema.HasValidations = sg.GenSchema.HasValidations || schemaCopy.HasValidations
  1502  	sg.GenSchema.HasSliceValidations = sg.Schema.Validations().HasArrayValidations() || sg.Schema.Validations().HasEnum()
  1503  
  1504  	// prevents bubbling custom formatter flag
  1505  	sg.GenSchema.IsCustomFormatter = false
  1506  
  1507  	sg.GenSchema.Items = &schemaCopy
  1508  	if sg.Named {
  1509  		sg.GenSchema.AliasedType = sg.GenSchema.GoType
  1510  	}
  1511  
  1512  	return nil
  1513  }
  1514  
  1515  func (sg *schemaGenContext) buildItems() error {
  1516  	if sg.Schema.Items == nil {
  1517  		// in swagger, arrays MUST have an items schema
  1518  		return nil
  1519  	}
  1520  
  1521  	// in Items spec, we have either Schema (array) or Schemas (tuple)
  1522  	presentsAsSingle := sg.Schema.Items.Schema != nil
  1523  	if presentsAsSingle && sg.Schema.AdditionalItems != nil { // unsure if this a valid of invalid schema
  1524  		return fmt.Errorf("single schema (%s) can't have additional items", sg.Name)
  1525  	}
  1526  	if presentsAsSingle {
  1527  		return sg.buildArray()
  1528  	}
  1529  
  1530  	// This is a tuple, build a new model that represents this
  1531  	if sg.Named {
  1532  		sg.GenSchema.Name = sg.Name
  1533  		sg.GenSchema.GoType = sg.TypeResolver.goTypeName(sg.Name)
  1534  		for i, sch := range sg.Schema.Items.Schemas {
  1535  			s := sch
  1536  			elProp := sg.NewTupleElement(&s, i)
  1537  
  1538  			if s.Ref.String() == "" {
  1539  				tpe, err := sg.TypeResolver.ResolveSchema(&s, s.Ref.String() == "", true)
  1540  				if err != nil {
  1541  					return err
  1542  				}
  1543  				if tpe.IsComplexObject && tpe.IsAnonymous {
  1544  					// if the tuple element is an anonymous complex object, build a new type for it
  1545  					pg := sg.makeNewStruct(sg.makeRefName()+" Items"+strconv.Itoa(i), s)
  1546  					if err := pg.makeGenSchema(); err != nil {
  1547  						return err
  1548  					}
  1549  					elProp.Schema = *spec.RefProperty("#/definitions/" + pg.Name)
  1550  					elProp.MergeResult(pg, false)
  1551  					elProp.ExtraSchemas[pg.Name] = pg.GenSchema
  1552  				}
  1553  			}
  1554  
  1555  			if err := elProp.makeGenSchema(); err != nil {
  1556  				return err
  1557  			}
  1558  			if elProp.GenSchema.IsInterface || elProp.GenSchema.IsStream {
  1559  				elProp.GenSchema.HasValidations = false
  1560  			}
  1561  			sg.MergeResult(elProp, false)
  1562  
  1563  			elProp.GenSchema.Name = "p" + strconv.Itoa(i)
  1564  			sg.GenSchema.Properties = append(sg.GenSchema.Properties, elProp.GenSchema)
  1565  			sg.GenSchema.IsTuple = true
  1566  		}
  1567  		return nil
  1568  	}
  1569  
  1570  	// for an anonymous object, first build the new object
  1571  	// and then replace the current one with a $ref to the
  1572  	// new tuple object
  1573  	var sch spec.Schema
  1574  	sch.Typed("object", "")
  1575  	sch.Properties = make(map[string]spec.Schema, len(sg.Schema.Items.Schemas))
  1576  	for i, v := range sg.Schema.Items.Schemas {
  1577  		sch.Required = append(sch.Required, "P"+strconv.Itoa(i))
  1578  		sch.Properties["P"+strconv.Itoa(i)] = v
  1579  	}
  1580  	sch.AdditionalItems = sg.Schema.AdditionalItems
  1581  	tup := sg.makeNewStruct(sg.GenSchema.Name+"Tuple"+strconv.Itoa(sg.Index), sch)
  1582  	tup.IsTuple = true
  1583  	if err := tup.makeGenSchema(); err != nil {
  1584  		return err
  1585  	}
  1586  	tup.GenSchema.IsTuple = true
  1587  	tup.GenSchema.IsComplexObject = false
  1588  	tup.GenSchema.Title = tup.GenSchema.Name + " a representation of an anonymous Tuple type"
  1589  	tup.GenSchema.Description = ""
  1590  	sg.ExtraSchemas[tup.Name] = tup.GenSchema
  1591  
  1592  	sg.Schema = *spec.RefProperty("#/definitions/" + tup.Name)
  1593  	if err := sg.makeGenSchema(); err != nil {
  1594  		return err
  1595  	}
  1596  	sg.MergeResult(tup, false)
  1597  	return nil
  1598  }
  1599  
  1600  func (sg *schemaGenContext) buildAdditionalItems() error {
  1601  	wantsAdditionalItems :=
  1602  		sg.Schema.AdditionalItems != nil &&
  1603  			(sg.Schema.AdditionalItems.Allows || sg.Schema.AdditionalItems.Schema != nil)
  1604  
  1605  	sg.GenSchema.HasAdditionalItems = wantsAdditionalItems
  1606  	if wantsAdditionalItems {
  1607  		// check if the element is a complex object, if so generate a new type for it
  1608  		tpe, err := sg.TypeResolver.ResolveSchema(sg.Schema.AdditionalItems.Schema, true, true)
  1609  		if err != nil {
  1610  			return err
  1611  		}
  1612  		if tpe.IsComplexObject && tpe.IsAnonymous {
  1613  			pg := sg.makeNewStruct(sg.makeRefName()+" Items", *sg.Schema.AdditionalItems.Schema)
  1614  			if err := pg.makeGenSchema(); err != nil {
  1615  				return err
  1616  			}
  1617  			sg.Schema.AdditionalItems.Schema = spec.RefProperty("#/definitions/" + pg.Name)
  1618  			pg.GenSchema.HasValidations = true
  1619  			sg.MergeResult(pg, false)
  1620  			sg.ExtraSchemas[pg.Name] = pg.GenSchema
  1621  		}
  1622  
  1623  		it := sg.NewAdditionalItems(sg.Schema.AdditionalItems.Schema)
  1624  		// if AdditionalItems are themselves arrays, bump the index var
  1625  		if tpe.IsArray {
  1626  			it.IndexVar += "i"
  1627  		}
  1628  
  1629  		if tpe.IsInterface {
  1630  			it.Untyped = true
  1631  		}
  1632  
  1633  		if err := it.makeGenSchema(); err != nil {
  1634  			return err
  1635  		}
  1636  
  1637  		// lift validations when complex is not anonymous or ref'ed
  1638  		if (tpe.IsComplexObject || it.Schema.Ref.String() != "") && !(tpe.IsInterface || tpe.IsStream) {
  1639  			it.GenSchema.HasValidations = true
  1640  		}
  1641  
  1642  		sg.MergeResult(it, true)
  1643  		sg.GenSchema.AdditionalItems = &it.GenSchema
  1644  	}
  1645  	return nil
  1646  }
  1647  
  1648  func (sg *schemaGenContext) buildXMLNameWithTags() error {
  1649  	// render some "xml" struct tag under one the following conditions:
  1650  	// - consumes/produces in spec contains xml
  1651  	// - struct tags CLI option contains xml
  1652  	// - XML object present in spec for this schema
  1653  	if sg.WithXML || swag.ContainsStrings(sg.StructTags, "xml") || sg.Schema.XML != nil {
  1654  		sg.GenSchema.XMLName = sg.Name
  1655  
  1656  		if sg.Schema.XML != nil {
  1657  			if sg.Schema.XML.Name != "" {
  1658  				sg.GenSchema.XMLName = sg.Schema.XML.Name
  1659  			}
  1660  			if sg.Schema.XML.Attribute {
  1661  				sg.GenSchema.XMLName += ",attr"
  1662  			}
  1663  		}
  1664  	}
  1665  	return nil
  1666  }
  1667  
  1668  func (sg *schemaGenContext) shortCircuitNamedRef() (bool, error) {
  1669  	// This if block ensures that a struct gets
  1670  	// rendered with the ref as embedded ref.
  1671  	//
  1672  	// NOTE: this assumes that all $ref point to a definition,
  1673  	// i.e. the spec is canonical, as guaranteed by minimal flattening.
  1674  	//
  1675  	// TODO: RefHandled is actually set nowhere
  1676  	if sg.RefHandled || !sg.Named || sg.Schema.Ref.String() == "" {
  1677  		return false, nil
  1678  	}
  1679  	debugLogAsJSON("short circuit named ref: %q", sg.Schema.Ref.String(), sg.Schema)
  1680  
  1681  	// Simple aliased types (arrays, maps and primitives)
  1682  	//
  1683  	// Before deciding to make a struct with a composition branch (below),
  1684  	// check if the $ref points to a simple type or polymorphic (base) type.
  1685  	//
  1686  	// If this is the case, just realias this simple type, without creating a struct.
  1687  	asch, era := analysis.Schema(analysis.SchemaOpts{
  1688  		Root:     sg.TypeResolver.Doc.Spec(),
  1689  		BasePath: sg.TypeResolver.Doc.SpecFilePath(),
  1690  		Schema:   &sg.Schema,
  1691  	})
  1692  	if era != nil {
  1693  		return false, era
  1694  	}
  1695  
  1696  	if asch.IsArray || asch.IsMap || asch.IsKnownType || asch.IsBaseType {
  1697  		tpx, ers := sg.TypeResolver.ResolveSchema(&sg.Schema, false, true)
  1698  		if ers != nil {
  1699  			return false, ers
  1700  		}
  1701  		tpe := resolvedType{}
  1702  		tpe.IsMap = asch.IsMap
  1703  		tpe.IsArray = asch.IsArray
  1704  		tpe.IsPrimitive = asch.IsKnownType
  1705  
  1706  		tpe.IsAliased = true
  1707  		tpe.AliasedType = ""
  1708  		tpe.IsComplexObject = false
  1709  		tpe.IsAnonymous = false
  1710  		tpe.IsCustomFormatter = false
  1711  		tpe.IsBaseType = tpx.IsBaseType
  1712  
  1713  		tpe.GoType = sg.TypeResolver.goTypeName(path.Base(sg.Schema.Ref.String()))
  1714  		tpe.Pkg = sg.TypeResolver.definitionPkg
  1715  
  1716  		tpe.IsNullable = tpx.IsNullable // TODO
  1717  		tpe.IsInterface = tpx.IsInterface
  1718  		tpe.IsStream = tpx.IsStream
  1719  		tpe.IsEmbedded = tpx.IsEmbedded
  1720  
  1721  		tpe.SwaggerType = tpx.SwaggerType
  1722  		sch := spec.Schema{}
  1723  		pg := sg.makeNewStruct(sg.Name, sch)
  1724  		if err := pg.makeGenSchema(); err != nil {
  1725  			return true, err
  1726  		}
  1727  		sg.MergeResult(pg, true)
  1728  		sg.GenSchema = pg.GenSchema
  1729  		sg.GenSchema.resolvedType = tpe
  1730  		sg.GenSchema.resolvedType.IsSuperAlias = true
  1731  		sg.GenSchema.IsBaseType = tpe.IsBaseType
  1732  
  1733  		return true, nil
  1734  	}
  1735  
  1736  	// Aliased object: use golang struct composition.
  1737  	// This is rendered as a struct with type field, i.e. :
  1738  	// Alias struct {
  1739  	//		AliasedType
  1740  	// }
  1741  	nullableOverride := sg.GenSchema.IsNullable
  1742  
  1743  	tpe := resolvedType{}
  1744  	tpe.GoType = sg.TypeResolver.goTypeName(sg.Name)
  1745  	tpe.Pkg = sg.TypeResolver.definitionPkg
  1746  	tpe.SwaggerType = "object"
  1747  	tpe.IsComplexObject = true
  1748  	tpe.IsMap = false
  1749  	tpe.IsArray = false
  1750  	tpe.IsAnonymous = false
  1751  	tpe.IsNullable = sg.TypeResolver.isNullable(&sg.Schema)
  1752  
  1753  	item := sg.NewCompositionBranch(sg.Schema, 0)
  1754  	if err := item.makeGenSchema(); err != nil {
  1755  		return true, err
  1756  	}
  1757  	sg.GenSchema.resolvedType = tpe
  1758  	sg.GenSchema.IsNullable = sg.GenSchema.IsNullable || nullableOverride
  1759  	// prevent format from bubbling up in composed type
  1760  	item.GenSchema.IsCustomFormatter = false
  1761  
  1762  	sg.MergeResult(item, true)
  1763  	sg.GenSchema.AllOf = append(sg.GenSchema.AllOf, item.GenSchema)
  1764  	return true, nil
  1765  }
  1766  
  1767  // liftSpecialAllOf attempts to simplify the rendering of allOf constructs by lifting simple things into the current schema.
  1768  func (sg *schemaGenContext) liftSpecialAllOf() error {
  1769  	// if there is only a $ref or a primitive and an x-isnullable schema then this is a nullable pointer
  1770  	// so this should not compose several objects, just 1
  1771  	// if there is a ref with a discriminator then we look for x-class on the current definition to know
  1772  	// the value of the discriminator to instantiate the class
  1773  	if len(sg.Schema.AllOf) < 2 {
  1774  		return nil
  1775  	}
  1776  	var seenSchema int
  1777  	var seenNullable bool
  1778  	var schemaToLift spec.Schema
  1779  
  1780  	for _, schema := range sg.Schema.AllOf {
  1781  		sch := schema
  1782  		tpe, err := sg.TypeResolver.ResolveSchema(&sch, true, true)
  1783  		if err != nil {
  1784  			return err
  1785  		}
  1786  		if sg.TypeResolver.isNullable(&sch) {
  1787  			seenNullable = true
  1788  		}
  1789  		if len(sch.Type) > 0 || len(sch.Properties) > 0 || sch.Ref.GetURL() != nil || len(sch.AllOf) > 0 {
  1790  			seenSchema++
  1791  			if seenSchema > 1 {
  1792  				// won't do anything if several candidates for a lift
  1793  				break
  1794  			}
  1795  			if (!tpe.IsAnonymous && tpe.IsComplexObject) || tpe.IsPrimitive {
  1796  				// lifting complex objects here results in inlined structs in the model
  1797  				schemaToLift = sch
  1798  			}
  1799  		}
  1800  	}
  1801  
  1802  	if seenSchema == 1 {
  1803  		// when there only a single schema to lift in allOf, replace the schema by its allOf definition
  1804  		debugLog("lifted schema in allOf for %s", sg.Name)
  1805  		sg.Schema = schemaToLift
  1806  		sg.GenSchema.IsNullable = seenNullable
  1807  	}
  1808  	return nil
  1809  }
  1810  
  1811  func (sg *schemaGenContext) buildAliased() error {
  1812  	if !sg.GenSchema.IsPrimitive && !sg.GenSchema.IsMap && !sg.GenSchema.IsArray && !sg.GenSchema.IsInterface {
  1813  		return nil
  1814  	}
  1815  
  1816  	if sg.GenSchema.IsPrimitive {
  1817  		if sg.GenSchema.SwaggerType == "string" && sg.GenSchema.SwaggerFormat == "" {
  1818  			sg.GenSchema.IsAliased = sg.GenSchema.GoType != sg.GenSchema.SwaggerType
  1819  		}
  1820  		if sg.GenSchema.IsNullable && sg.Named {
  1821  			sg.GenSchema.IsNullable = false
  1822  		}
  1823  	}
  1824  
  1825  	if sg.GenSchema.IsInterface {
  1826  		sg.GenSchema.IsAliased = sg.GenSchema.GoType != iface
  1827  	}
  1828  
  1829  	if sg.GenSchema.IsMap {
  1830  		sg.GenSchema.IsAliased = !strings.HasPrefix(sg.GenSchema.GoType, "map[")
  1831  	}
  1832  
  1833  	if sg.GenSchema.IsArray {
  1834  		sg.GenSchema.IsAliased = !strings.HasPrefix(sg.GenSchema.GoType, "[]")
  1835  	}
  1836  	return nil
  1837  }
  1838  
  1839  func (sg schemaGenContext) makeRefName() string {
  1840  	// figure out a longer name for deconflicting anonymous models.
  1841  	// This is used when makeNewStruct() is followed by the creation of a new ref to definitions
  1842  	if sg.UseContainerInName && sg.Container != sg.Name {
  1843  		return sg.Container + swag.ToGoName(sg.Name)
  1844  	}
  1845  	return sg.Name
  1846  }
  1847  
  1848  func (sg *schemaGenContext) GoName() string {
  1849  	return goName(&sg.Schema, sg.Name)
  1850  }
  1851  
  1852  func goName(sch *spec.Schema, orig string) string {
  1853  	name, _ := sch.Extensions.GetString(xGoName)
  1854  	if name != "" {
  1855  		return name
  1856  	}
  1857  	return orig
  1858  }
  1859  
  1860  func (sg *schemaGenContext) derefMapElement(outer *GenSchema, sch *GenSchema, elem *GenSchema) {
  1861  	derefType := strings.TrimPrefix(elem.GoType, "*")
  1862  
  1863  	if outer.IsAliased {
  1864  		nesting := strings.TrimSuffix(strings.TrimSuffix(outer.AliasedType, elem.GoType), "*")
  1865  		outer.AliasedType = nesting + derefType
  1866  		outer.GoType = derefType
  1867  	} else {
  1868  		nesting := strings.TrimSuffix(strings.TrimSuffix(outer.GoType, elem.GoType), "*")
  1869  		outer.GoType = nesting + derefType
  1870  	}
  1871  
  1872  	elem.GoType = derefType
  1873  }
  1874  
  1875  func (sg *schemaGenContext) checkNeedsPointer(outer *GenSchema, sch *GenSchema, elem *GenSchema) {
  1876  	derefType := strings.TrimPrefix(elem.GoType, "*")
  1877  	switch {
  1878  	case outer.IsAliased && !strings.HasSuffix(outer.AliasedType, "*"+derefType):
  1879  		// override nullability of map of primitive elements: render element of aliased or anonymous map as a pointer
  1880  		outer.AliasedType = strings.TrimSuffix(outer.AliasedType, derefType) + "*" + derefType
  1881  	case sch != nil:
  1882  		// nullable primitive
  1883  		if sch.IsAnonymous && !strings.HasSuffix(outer.GoType, "*"+derefType) {
  1884  			sch.GoType = strings.TrimSuffix(sch.GoType, derefType) + "*" + derefType
  1885  		}
  1886  	case outer.IsAnonymous && !strings.HasSuffix(outer.GoType, "*"+derefType):
  1887  		outer.GoType = strings.TrimSuffix(outer.GoType, derefType) + "*" + derefType
  1888  	}
  1889  }
  1890  
  1891  // buildMapOfNullable equalizes the nullablity status for aliased and anonymous maps of simple things,
  1892  // with the nullability of its innermost element.
  1893  //
  1894  // NOTE: at the moment, we decide to align the type of the outer element (map) to the type of the inner element
  1895  // The opposite could be done and result in non nullable primitive elements. If we do so, the validation
  1896  // code needs to be adapted by removing IsZero() and Required() calls in codegen.
  1897  func (sg *schemaGenContext) buildMapOfNullable(sch *GenSchema) {
  1898  	outer := &sg.GenSchema
  1899  	if sch == nil {
  1900  		sch = outer
  1901  	}
  1902  	if sch.IsMap && (outer.IsAliased || outer.IsAnonymous) {
  1903  		elem := sch.AdditionalProperties
  1904  		for elem != nil {
  1905  			if elem.IsPrimitive && elem.IsNullable {
  1906  				sg.checkNeedsPointer(outer, nil, elem)
  1907  			} else if elem.IsArray {
  1908  				// override nullability of array of primitive elements:
  1909  				// render element of aliased or anonyous map as a pointer
  1910  				it := elem.Items
  1911  				for it != nil {
  1912  					switch {
  1913  					case it.IsPrimitive && it.IsNullable:
  1914  						sg.checkNeedsPointer(outer, sch, it)
  1915  					case it.IsMap:
  1916  						sg.buildMapOfNullable(it)
  1917  					case !it.IsPrimitive && !it.IsArray && it.IsComplexObject && it.IsNullable:
  1918  						// structs in map are not rendered as pointer by default
  1919  						// unless some x-nullable overrides says so
  1920  						_, forced := it.Extensions[xNullable]
  1921  						if !forced {
  1922  							_, forced = it.Extensions[xIsNullable]
  1923  						}
  1924  						if !forced {
  1925  							sg.derefMapElement(outer, sch, it)
  1926  						}
  1927  					}
  1928  					it = it.Items
  1929  				}
  1930  			}
  1931  			elem = elem.AdditionalProperties
  1932  		}
  1933  	}
  1934  }
  1935  
  1936  func (sg *schemaGenContext) makeGenSchema() error {
  1937  	debugLogAsJSON("making gen schema (anon: %t, req: %t, tuple: %t) %s\n",
  1938  		!sg.Named, sg.Required, sg.IsTuple, sg.Name, sg.Schema)
  1939  
  1940  	sg.GenSchema.Example = ""
  1941  	if sg.Schema.Example != nil {
  1942  		data, err := asJSON(sg.Schema.Example)
  1943  		if err != nil {
  1944  			return err
  1945  		}
  1946  		// Deleting the unnecessary double quotes for string types
  1947  		// otherwise the generate spec will generate as "\"foo\""
  1948  		sg.GenSchema.Example = strings.Trim(data, "\"")
  1949  	}
  1950  	sg.GenSchema.ExternalDocs = trimExternalDoc(sg.Schema.ExternalDocs)
  1951  	sg.GenSchema.IsExported = true
  1952  	sg.GenSchema.Path = sg.Path
  1953  	sg.GenSchema.IndexVar = sg.IndexVar
  1954  	sg.GenSchema.Location = body
  1955  	sg.GenSchema.ValueExpression = sg.ValueExpr
  1956  	sg.GenSchema.KeyVar = sg.KeyVar
  1957  	sg.GenSchema.OriginalName = sg.Name
  1958  	sg.GenSchema.Name = sg.GoName()
  1959  	sg.GenSchema.Title = sg.Schema.Title
  1960  	sg.GenSchema.Description = trimBOM(sg.Schema.Description)
  1961  	sg.GenSchema.ReceiverName = sg.Receiver
  1962  	sg.GenSchema.sharedValidations = sg.schemaValidations()
  1963  	sg.GenSchema.ReadOnly = sg.Schema.ReadOnly
  1964  	sg.GenSchema.IncludeValidator = sg.IncludeValidator
  1965  	sg.GenSchema.IncludeModel = sg.IncludeModel
  1966  	sg.GenSchema.StrictAdditionalProperties = sg.StrictAdditionalProperties
  1967  	sg.GenSchema.Default = sg.Schema.Default
  1968  	sg.GenSchema.StructTags = sg.StructTags
  1969  	sg.GenSchema.ExtraImports = make(map[string]string)
  1970  
  1971  	var err error
  1972  	returns, err := sg.shortCircuitNamedRef()
  1973  	if err != nil {
  1974  		return err
  1975  	}
  1976  	if returns {
  1977  		return nil
  1978  	}
  1979  	debugLogAsJSON("after short circuit named ref", sg.Schema)
  1980  
  1981  	if e := sg.liftSpecialAllOf(); e != nil {
  1982  		return e
  1983  	}
  1984  	nullableOverride := sg.GenSchema.IsNullable
  1985  	debugLogAsJSON("after lifting special all of", sg.Schema)
  1986  
  1987  	if sg.Container == "" {
  1988  		sg.Container = sg.GenSchema.Name
  1989  	}
  1990  	if e := sg.buildAllOf(); e != nil {
  1991  		return e
  1992  	}
  1993  
  1994  	var tpe resolvedType
  1995  	if sg.Untyped {
  1996  		tpe, err = sg.TypeResolver.ResolveSchema(nil, !sg.Named, sg.IsTuple || sg.Required || sg.GenSchema.Required)
  1997  	} else {
  1998  		tpe, err = sg.TypeResolver.ResolveSchema(&sg.Schema, !sg.Named, sg.IsTuple || sg.Required || sg.GenSchema.Required)
  1999  	}
  2000  	if err != nil {
  2001  		return err
  2002  	}
  2003  
  2004  	debugLog("gschema rrequired: %t, nullable: %t", sg.GenSchema.Required, sg.GenSchema.IsNullable)
  2005  	tpe.IsNullable = tpe.IsNullable || nullableOverride
  2006  	sg.GenSchema.resolvedType = tpe
  2007  	sg.GenSchema.IsBaseType = tpe.IsBaseType
  2008  	sg.GenSchema.HasDiscriminator = tpe.HasDiscriminator
  2009  
  2010  	// include format validations, excluding binary
  2011  	sg.GenSchema.HasValidations = sg.GenSchema.HasValidations || hasFormatValidation(tpe)
  2012  
  2013  	// include context validations
  2014  	sg.GenSchema.HasContextValidations = sg.GenSchema.HasContextValidations || hasContextValidations(&sg.Schema) && !tpe.IsInterface && !tpe.IsStream && !tpe.SkipExternalValidation
  2015  
  2016  	// usage of a polymorphic base type is rendered with getter funcs on private properties.
  2017  	// In the case of aliased types, the value expression remains unchanged to the receiver.
  2018  	if tpe.IsArray && tpe.ElemType != nil && tpe.ElemType.IsBaseType && sg.GenSchema.ValueExpression != sg.GenSchema.ReceiverName {
  2019  		sg.GenSchema.ValueExpression += asMethod
  2020  	}
  2021  
  2022  	if tpe.IsExternal { // anonymous external types
  2023  		extType, pkg, alias := sg.TypeResolver.knownDefGoType(sg.GenSchema.Name, sg.Schema, sg.TypeResolver.goTypeName)
  2024  		if pkg != "" && alias != "" {
  2025  			sg.GenSchema.ExtraImports[alias] = pkg
  2026  		}
  2027  
  2028  		if !tpe.IsEmbedded {
  2029  			sg.GenSchema.resolvedType = tpe
  2030  			sg.GenSchema.Required = sg.Required
  2031  			// assume we validate everything but interface and io.Reader - validation may be disabled by using the noValidation hint
  2032  			sg.GenSchema.HasValidations = !(tpe.IsInterface || tpe.IsStream || tpe.SkipExternalValidation)
  2033  			sg.GenSchema.IsAliased = sg.GenSchema.HasValidations
  2034  
  2035  			log.Printf("INFO: type %s is external, with inferred spec type %s, referred to as %s", sg.GenSchema.Name, sg.GenSchema.GoType, extType)
  2036  			sg.GenSchema.GoType = extType
  2037  			sg.GenSchema.AliasedType = extType
  2038  			return nil
  2039  		}
  2040  		// TODO: case for embedded types as anonymous definitions
  2041  		return fmt.Errorf("ERROR: inline definitions embedded types are not supported")
  2042  	}
  2043  
  2044  	debugLog("gschema nullable: %t", sg.GenSchema.IsNullable)
  2045  	if e := sg.buildAdditionalProperties(); e != nil {
  2046  		return e
  2047  	}
  2048  
  2049  	// rewrite value expression from top-down
  2050  	cur := &sg.GenSchema
  2051  	for cur.AdditionalProperties != nil {
  2052  		cur.AdditionalProperties.ValueExpression = cur.ValueExpression + "[" + cur.AdditionalProperties.KeyVar + "]"
  2053  		cur = cur.AdditionalProperties
  2054  	}
  2055  
  2056  	prev := sg.GenSchema
  2057  	if sg.Untyped {
  2058  		debugLogAsJSON("untyped resolve:%t", sg.Named || sg.IsTuple || sg.Required || sg.GenSchema.Required, sg.Schema)
  2059  		tpe, err = sg.TypeResolver.ResolveSchema(nil, !sg.Named, sg.Named || sg.IsTuple || sg.Required || sg.GenSchema.Required)
  2060  	} else {
  2061  		debugLogAsJSON("typed resolve, isAnonymous(%t), n: %t, t: %t, sgr: %t, sr: %t, isRequired(%t), BaseType(%t)",
  2062  			!sg.Named, sg.Named, sg.IsTuple, sg.Required, sg.GenSchema.Required,
  2063  			sg.Named || sg.IsTuple || sg.Required || sg.GenSchema.Required, sg.GenSchema.IsBaseType, sg.Schema)
  2064  		tpe, err = sg.TypeResolver.ResolveSchema(&sg.Schema, !sg.Named, sg.Named || sg.IsTuple || sg.Required || sg.GenSchema.Required)
  2065  	}
  2066  	if err != nil {
  2067  		return err
  2068  	}
  2069  	otn := tpe.IsNullable // for debug only
  2070  	tpe.IsNullable = tpe.IsNullable || nullableOverride
  2071  	sg.GenSchema.resolvedType = tpe
  2072  	sg.GenSchema.IsComplexObject = prev.IsComplexObject
  2073  	sg.GenSchema.IsMap = prev.IsMap
  2074  	sg.GenSchema.IsAdditionalProperties = prev.IsAdditionalProperties
  2075  	sg.GenSchema.IsBaseType = sg.GenSchema.HasDiscriminator
  2076  
  2077  	debugLogAsJSON("gschema nnullable:IsNullable:%t,resolver.IsNullable:%t,nullableOverride:%t",
  2078  		sg.GenSchema.IsNullable, otn, nullableOverride, sg.Schema)
  2079  	if err := sg.buildProperties(); err != nil {
  2080  		return err
  2081  	}
  2082  
  2083  	if err := sg.buildXMLNameWithTags(); err != nil {
  2084  		return err
  2085  	}
  2086  
  2087  	if err := sg.buildAdditionalItems(); err != nil {
  2088  		return err
  2089  	}
  2090  
  2091  	if err := sg.buildItems(); err != nil {
  2092  		return err
  2093  	}
  2094  
  2095  	if err := sg.buildAliased(); err != nil {
  2096  		return err
  2097  	}
  2098  
  2099  	sg.buildMapOfNullable(nil)
  2100  
  2101  	// extra serializers & interfaces
  2102  
  2103  	// generate MarshalBinary for:
  2104  	// - tuple
  2105  	// - struct
  2106  	// - map
  2107  	// - aliased primitive of a formatter type which is not a stringer
  2108  	//
  2109  	// but not for:
  2110  	// - interface{}
  2111  	// - io.Reader
  2112  	gs := sg.GenSchema
  2113  	sg.GenSchema.WantsMarshalBinary = !(gs.IsInterface || gs.IsStream || gs.IsBaseType) &&
  2114  		(gs.IsTuple || gs.IsComplexObject || gs.IsAdditionalProperties || (gs.IsPrimitive && gs.IsAliased && gs.IsCustomFormatter && !strings.Contains(gs.Zero(), `("`)))
  2115  
  2116  	debugLog("finished gen schema for %q", sg.Name)
  2117  	return nil
  2118  }