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