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