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