package design

import (
	"fmt"
	"net/http"
	"path"
	"sort"
	"strings"

	"github.com/dimfeld/httppath"
	"github.com/goadesign/goa/dslengine"
)

type (
	// APIDefinition defines the global properties of the API.
	APIDefinition struct {
		// Name of API
		Name string
		// Title of API
		Title string
		// Description of API
		Description string
		// Version is the version of the API described by this design.
		Version string
		// Host is the default API hostname
		Host string
		// Schemes is the supported API URL schemes
		Schemes []string
		// BasePath is the common base path to all API endpoints
		BasePath string
		// Params define the common path parameters to all API endpoints
		Params *AttributeDefinition
		// Consumes lists the mime types supported by the API controllers
		Consumes []*EncodingDefinition
		// Produces lists the mime types generated by the API controllers
		Produces []*EncodingDefinition
		// Origins defines the CORS policies that apply to this API.
		Origins map[string]*CORSDefinition
		// TermsOfService describes or links to the API terms of service
		TermsOfService string
		// Contact provides the API users with contact information
		Contact *ContactDefinition
		// License describes the API license
		License *LicenseDefinition
		// Docs points to the API external documentation
		Docs *DocsDefinition
		// Resources is the set of exposed resources indexed by name
		Resources map[string]*ResourceDefinition
		// Types indexes the user defined types by name
		Types map[string]*UserTypeDefinition
		// MediaTypes indexes the API media types by canonical identifier
		MediaTypes map[string]*MediaTypeDefinition
		// Traits available to all API resources and actions indexed by name
		Traits map[string]*dslengine.TraitDefinition
		// Responses available to all API actions indexed by name
		Responses map[string]*ResponseDefinition
		// Response template factories available to all API actions indexed by name
		ResponseTemplates map[string]*ResponseTemplateDefinition
		// Built-in responses
		DefaultResponses map[string]*ResponseDefinition
		// Built-in response templates
		DefaultResponseTemplates map[string]*ResponseTemplateDefinition
		// DSLFunc contains the DSL used to create this definition if any
		DSLFunc func()
		// Metadata is a list of key/value pairs
		Metadata dslengine.MetadataDefinition
		// SecuritySchemes lists the available security schemes available
		// to the API.
		SecuritySchemes []*SecuritySchemeDefinition
		// Security defines security requirements for all the
		// resources and actions, unless overridden by Resource or
		// Action-level Security() calls.
		Security *SecurityDefinition
		// NoExamples indicates whether to bypass automatic example generation.
		NoExamples bool

		// rand is the random generator used to generate examples.
		rand *RandomGenerator
	}

	// ContactDefinition contains the API contact information.
	ContactDefinition struct {
		// Name of the contact person/organization
		Name string `json:"name,omitempty"`
		// Email address of the contact person/organization
		Email string `json:"email,omitempty"`
		// URL pointing to the contact information
		URL string `json:"url,omitempty"`
	}

	// LicenseDefinition contains the license information for the API.
	LicenseDefinition struct {
		// Name of license used for the API
		Name string `json:"name,omitempty"`
		// URL to the license used for the API
		URL string `json:"url,omitempty"`
	}

	// DocsDefinition points to external documentation.
	DocsDefinition struct {
		// Description of documentation.
		Description string `json:"description,omitempty"`
		// URL to documentation.
		URL string `json:"url,omitempty"`
	}

	// ResourceDefinition describes a REST resource.
	// It defines both a media type and a set of actions that can be executed through HTTP
	// requests.
	ResourceDefinition struct {
		// Resource name
		Name string
		// Schemes is the supported API URL schemes
		Schemes []string
		// Common URL prefix to all resource action HTTP requests
		BasePath string
		// Path and query string parameters that apply to all actions.
		Params *AttributeDefinition
		// Name of parent resource if any
		ParentName string
		// Optional description
		Description string
		// Default media type, describes the resource attributes
		MediaType string
		// Default view name if default media type is MediaTypeDefinition
		DefaultViewName string
		// Exposed resource actions indexed by name
		Actions map[string]*ActionDefinition
		// FileServers is the list of static asset serving endpoints
		FileServers []*FileServerDefinition
		// Action with canonical resource path
		CanonicalActionName string
		// Map of response definitions that apply to all actions indexed by name.
		Responses map[string]*ResponseDefinition
		// Request headers that apply to all actions.
		Headers *AttributeDefinition
		// Origins defines the CORS policies that apply to this resource.
		Origins map[string]*CORSDefinition
		// DSLFunc contains the DSL used to create this definition if any.
		DSLFunc func()
		// metadata is a list of key/value pairs
		Metadata dslengine.MetadataDefinition
		// Security defines security requirements for the Resource,
		// for actions that don't define one themselves.
		Security *SecurityDefinition
	}

	// CORSDefinition contains the definition for a specific origin CORS policy.
	CORSDefinition struct {
		// Parent API or resource
		Parent dslengine.Definition
		// Origin
		Origin string
		// List of authorized headers, "*" authorizes all
		Headers []string
		// List of authorized HTTP methods
		Methods []string
		// List of headers exposed to clients
		Exposed []string
		// How long to cache a preflight request response
		MaxAge uint
		// Sets Access-Control-Allow-Credentials header
		Credentials bool
		// Sets Whether the Origin string is a regular expression
		Regexp bool
	}

	// EncodingDefinition defines an encoder supported by the API.
	EncodingDefinition struct {
		// MIMETypes is the set of possible MIME types for the content being encoded or decoded.
		MIMETypes []string
		// PackagePath is the path to the Go package that implements the encoder/decoder.
		// The package must expose a `EncoderFactory` or `DecoderFactory` function
		// that the generated code calls. The methods must return objects that implement
		// the goa.EncoderFactory or goa.DecoderFactory interface respectively.
		PackagePath string
		// Function is the name of the Go function used to instantiate the encoder/decoder.
		// Defaults to NewEncoder and NewDecoder respecitively.
		Function string
		// Encoder is true if the definition is for a encoder, false if it's for a decoder.
		Encoder bool
	}

	// ResponseDefinition defines a HTTP response status and optional validation rules.
	ResponseDefinition struct {
		// Response name
		Name string
		// HTTP status
		Status int
		// Response description
		Description string
		// Response body type if any
		Type DataType
		// Response body media type if any
		MediaType string
		// Response view name if MediaType is MediaTypeDefinition
		ViewName string
		// Response header definitions
		Headers *AttributeDefinition
		// Parent action or resource
		Parent dslengine.Definition
		// Metadata is a list of key/value pairs
		Metadata dslengine.MetadataDefinition
		// Standard is true if the response definition comes from the goa default responses
		Standard bool
	}

	// ResponseTemplateDefinition defines a response template.
	// A response template is a function that takes an arbitrary number
	// of strings and returns a response definition.
	ResponseTemplateDefinition struct {
		// Response template name
		Name string
		// Response template function
		Template func(params ...string) *ResponseDefinition
	}

	// ActionDefinition defines a resource action.
	// It defines both an HTTP endpoint and the shape of HTTP requests and responses made to
	// that endpoint.
	// The shape of requests is defined via "parameters", there are path parameters
	// parameters and a payload parameter (request body).
	// (i.e. portions of the URL that define parameter values), query string
	ActionDefinition struct {
		// Action name, e.g. "create"
		Name string
		// Action description, e.g. "Creates a task"
		Description string
		// Docs points to the API external documentation
		Docs *DocsDefinition
		// Parent resource
		Parent *ResourceDefinition
		// Specific action URL schemes
		Schemes []string
		// Action routes
		Routes []*RouteDefinition
		// Map of possible response definitions indexed by name
		Responses map[string]*ResponseDefinition
		// Path and query string parameters
		Params *AttributeDefinition
		// Query string parameters only
		QueryParams *AttributeDefinition
		// Payload blueprint (request body) if any
		Payload *UserTypeDefinition
		// PayloadOptional is true if the request payload is optional, false otherwise.
		PayloadOptional bool
		// Request headers that need to be made available to action
		Headers *AttributeDefinition
		// Metadata is a list of key/value pairs
		Metadata dslengine.MetadataDefinition
		// Security defines security requirements for the action
		Security *SecurityDefinition
	}

	// FileServerDefinition defines an endpoint that servers static assets.
	FileServerDefinition struct {
		// Parent resource
		Parent *ResourceDefinition
		// Description for docs
		Description string
		// Docs points to the API external documentation
		Docs *DocsDefinition
		// FilePath is the file path to the static asset(s)
		FilePath string
		// RequestPath is the HTTP path that servers the assets.
		RequestPath string
		// Metadata is a list of key/value pairs
		Metadata dslengine.MetadataDefinition
		// Security defines security requirements for the file server.
		Security *SecurityDefinition
	}

	// LinkDefinition defines a media type link, it specifies a URL to a related resource.
	LinkDefinition struct {
		// Link name
		Name string
		// View used to render link if not "link"
		View string
		// URITemplate is the RFC6570 URI template of the link Href.
		URITemplate string

		// Parent media Type
		Parent *MediaTypeDefinition
	}

	// ViewDefinition defines which members and links to render when building a response.
	// The view is a JSON object whose property names must match the names of the parent media
	// type members.
	// The members fields are inherited from the parent media type but may be overridden.
	ViewDefinition struct {
		// Set of properties included in view
		*AttributeDefinition
		// Name of view
		Name string
		// Parent media Type
		Parent *MediaTypeDefinition
	}

	// RouteDefinition represents an action route.
	RouteDefinition struct {
		// Verb is the HTTP method, e.g. "GET", "POST", etc.
		Verb string
		// Path is the URL path e.g. "/tasks/:id"
		Path string
		// Parent is the action this route applies to.
		Parent *ActionDefinition
		// Metadata is a list of key/value pairs
		Metadata dslengine.MetadataDefinition
	}

	// AttributeDefinition defines a JSON object member with optional description, default
	// value and validations.
	AttributeDefinition struct {
		// Attribute type
		Type DataType
		// Attribute reference type if any
		Reference DataType
		// Optional description
		Description string
		// Optional validations
		Validation *dslengine.ValidationDefinition
		// Metadata is a list of key/value pairs
		Metadata dslengine.MetadataDefinition
		// Optional member default value
		DefaultValue interface{}
		// Optional member example value
		Example interface{}
		// Optional view used to render Attribute (only applies to media type attributes).
		View string
		// NonZeroAttributes lists the names of the child attributes that cannot have a
		// zero value (and thus whose presence does not need to be validated).
		NonZeroAttributes map[string]bool
		// DSLFunc contains the initialization DSL. This is used for user types.
		DSLFunc func()
	}

	// ContainerDefinition defines a generic container definition that contains attributes.
	// This makes it possible for plugins to use attributes in their own data structures.
	ContainerDefinition interface {
		// Attribute returns the container definition embedded attribute.
		Attribute() *AttributeDefinition
	}

	// ResourceIterator is the type of functions given to IterateResources.
	ResourceIterator func(r *ResourceDefinition) error

	// MediaTypeIterator is the type of functions given to IterateMediaTypes.
	MediaTypeIterator func(m *MediaTypeDefinition) error

	// UserTypeIterator is the type of functions given to IterateUserTypes.
	UserTypeIterator func(m *UserTypeDefinition) error

	// ActionIterator is the type of functions given to IterateActions.
	ActionIterator func(a *ActionDefinition) error

	// FileServerIterator is the type of functions given to IterateFileServers.
	FileServerIterator func(f *FileServerDefinition) error

	// HeaderIterator is the type of functions given to IterateHeaders.
	HeaderIterator func(name string, isRequired bool, h *AttributeDefinition) error

	// ResponseIterator is the type of functions given to IterateResponses.
	ResponseIterator func(r *ResponseDefinition) error
)

// NewAPIDefinition returns a new design with built-in response templates.
func NewAPIDefinition() *APIDefinition {
	api := &APIDefinition{
		DefaultResponseTemplates: make(map[string]*ResponseTemplateDefinition),
		DefaultResponses:         make(map[string]*ResponseDefinition),
	}
	t := func(params ...string) *ResponseDefinition {
		if len(params) < 1 {
			dslengine.ReportError("expected media type as argument when invoking response template OK")
			return nil
		}
		return &ResponseDefinition{
			Name:      OK,
			Status:    200,
			MediaType: params[0],
		}
	}
	api.DefaultResponseTemplates[OK] = &ResponseTemplateDefinition{
		Name:     OK,
		Template: t,
	}
	for _, p := range []struct {
		status int
		name   string
	}{
		{100, Continue},
		{101, SwitchingProtocols},
		{200, OK},
		{201, Created},
		{202, Accepted},
		{203, NonAuthoritativeInfo},
		{204, NoContent},
		{205, ResetContent},
		{206, PartialContent},
		{300, MultipleChoices},
		{301, MovedPermanently},
		{302, Found},
		{303, SeeOther},
		{304, NotModified},
		{305, UseProxy},
		{307, TemporaryRedirect},
		{400, BadRequest},
		{401, Unauthorized},
		{402, PaymentRequired},
		{403, Forbidden},
		{404, NotFound},
		{405, MethodNotAllowed},
		{406, NotAcceptable},
		{407, ProxyAuthRequired},
		{408, RequestTimeout},
		{409, Conflict},
		{410, Gone},
		{411, LengthRequired},
		{412, PreconditionFailed},
		{413, RequestEntityTooLarge},
		{414, RequestURITooLong},
		{415, UnsupportedMediaType},
		{416, RequestedRangeNotSatisfiable},
		{417, ExpectationFailed},
		{418, Teapot},
		{422, UnprocessableEntity},
		{500, InternalServerError},
		{501, NotImplemented},
		{502, BadGateway},
		{503, ServiceUnavailable},
		{504, GatewayTimeout},
		{505, HTTPVersionNotSupported},
	} {
		api.DefaultResponses[p.name] = &ResponseDefinition{
			Name:        p.name,
			Description: http.StatusText(p.status),
			Status:      p.status,
		}
	}
	return api
}

// DSLName is the name of the DSL as displayed to the user during execution.
func (a *APIDefinition) DSLName() string {
	return "goa API"
}

// DependsOn returns the other roots this root depends on, nothing for APIDefinition.
func (a *APIDefinition) DependsOn() []dslengine.Root {
	return nil
}

// IterateSets calls the given iterator possing in the API definition, user types, media types and
// finally resources.
func (a *APIDefinition) IterateSets(iterator dslengine.SetIterator) {
	// First run the top level API DSL to initialize responses and
	// response templates needed by resources.
	iterator([]dslengine.Definition{a})

	// Then run the user type DSLs
	typeAttributes := make([]dslengine.Definition, len(a.Types))
	i := 0
	a.IterateUserTypes(func(u *UserTypeDefinition) error {
		u.AttributeDefinition.DSLFunc = u.DSLFunc
		typeAttributes[i] = u.AttributeDefinition
		i++
		return nil
	})
	iterator(typeAttributes)

	// Then the media type DSLs
	mediaTypes := make([]dslengine.Definition, len(a.MediaTypes))
	i = 0
	a.IterateMediaTypes(func(mt *MediaTypeDefinition) error {
		mediaTypes[i] = mt
		i++
		return nil
	})
	iterator(mediaTypes)

	// Then, the Security schemes definitions
	var securitySchemes []dslengine.Definition
	for _, scheme := range a.SecuritySchemes {
		securitySchemes = append(securitySchemes, dslengine.Definition(scheme))
	}
	iterator(securitySchemes)

	// And now that we have everything the resources.  The resource
	// lifecycle handlers dispatch to their children elements, like
	// Actions, etc..
	resources := make([]dslengine.Definition, len(a.Resources))
	i = 0
	a.IterateResources(func(res *ResourceDefinition) error {
		resources[i] = res
		i++
		return nil
	})
	iterator(resources)
}

// Reset sets all the API definition fields to their zero value except the default responses and
// default response templates.
func (a *APIDefinition) Reset() {
	n := NewAPIDefinition()
	*a = *n
}

// Context returns the generic definition name used in error messages.
func (a *APIDefinition) Context() string {
	if a.Name != "" {
		return fmt.Sprintf("API %#v", a.Name)
	}
	return "unnamed API"
}

// IterateMediaTypes calls the given iterator passing in each media type sorted in alphabetical order.
// Iteration stops if an iterator returns an error and in this case IterateMediaTypes returns that
// error.
func (a *APIDefinition) IterateMediaTypes(it MediaTypeIterator) error {
	names := make([]string, len(a.MediaTypes))
	i := 0
	for n := range a.MediaTypes {
		names[i] = n
		i++
	}
	sort.Strings(names)
	for _, n := range names {
		if err := it(a.MediaTypes[n]); err != nil {
			return err
		}
	}
	return nil
}

// IterateUserTypes calls the given iterator passing in each user type sorted in alphabetical order.
// Iteration stops if an iterator returns an error and in this case IterateUserTypes returns that
// error.
func (a *APIDefinition) IterateUserTypes(it UserTypeIterator) error {
	names := make([]string, len(a.Types))
	i := 0
	for n := range a.Types {
		names[i] = n
		i++
	}
	sort.Strings(names)
	for _, n := range names {
		if err := it(a.Types[n]); err != nil {
			return err
		}
	}
	return nil
}

// IterateResponses calls the given iterator passing in each response sorted in alphabetical order.
// Iteration stops if an iterator returns an error and in this case IterateResponses returns that
// error.
func (a *APIDefinition) IterateResponses(it ResponseIterator) error {
	names := make([]string, len(a.Responses))
	i := 0
	for n := range a.Responses {
		names[i] = n
		i++
	}
	sort.Strings(names)
	for _, n := range names {
		if err := it(a.Responses[n]); err != nil {
			return err
		}
	}
	return nil
}

// RandomGenerator is seeded after the API name. It's used to generate examples.
func (a *APIDefinition) RandomGenerator() *RandomGenerator {
	if a.rand == nil {
		a.rand = NewRandomGenerator(a.Name)
	}
	return a.rand
}

// MediaTypeWithIdentifier returns the media type with a matching
// media type identifier. Two media type identifiers match if their
// values sans suffix match. So for example "application/vnd.foo+xml",
// "application/vnd.foo+json" and "application/vnd.foo" all match.
func (a *APIDefinition) MediaTypeWithIdentifier(id string) *MediaTypeDefinition {
	canonicalID := CanonicalIdentifier(id)
	for _, mt := range a.MediaTypes {
		if canonicalID == CanonicalIdentifier(mt.Identifier) {
			return mt
		}
	}
	return nil
}

// IterateResources calls the given iterator passing in each resource sorted in alphabetical order.
// Iteration stops if an iterator returns an error and in this case IterateResources returns that
// error.
func (a *APIDefinition) IterateResources(it ResourceIterator) error {
	names := make([]string, len(a.Resources))
	i := 0
	for n := range a.Resources {
		names[i] = n
		i++
	}
	sort.Strings(names)
	for _, n := range names {
		if err := it(a.Resources[n]); err != nil {
			return err
		}
	}
	return nil
}

// DSL returns the initialization DSL.
func (a *APIDefinition) DSL() func() {
	return a.DSLFunc
}

// Finalize sets the Consumes and Produces fields to the defaults if empty.
// Also it records built-in media types that are used by the user design.
func (a *APIDefinition) Finalize() {
	if len(a.Consumes) == 0 {
		a.Consumes = DefaultDecoders
	}
	if len(a.Produces) == 0 {
		a.Produces = DefaultEncoders
	}
	found := false
	a.IterateResources(func(r *ResourceDefinition) error {
		if found {
			return nil
		}
		return r.IterateActions(func(action *ActionDefinition) error {
			if found {
				return nil
			}
			for _, resp := range action.Responses {
				if resp.MediaType == ErrorMediaIdentifier {
					if a.MediaTypes == nil {
						a.MediaTypes = make(map[string]*MediaTypeDefinition)
					}
					a.MediaTypes[CanonicalIdentifier(ErrorMediaIdentifier)] = ErrorMedia
					found = true
					break
				}
			}
			return nil
		})
	})
}

// NewResourceDefinition creates a resource definition but does not
// execute the DSL.
func NewResourceDefinition(name string, dsl func()) *ResourceDefinition {
	return &ResourceDefinition{
		Name:      name,
		MediaType: "text/plain",
		DSLFunc:   dsl,
	}
}

// Context returns the generic definition name used in error messages.
func (r *ResourceDefinition) Context() string {
	if r.Name != "" {
		return fmt.Sprintf("resource %#v", r.Name)
	}
	return "unnamed resource"
}

// IterateActions calls the given iterator passing in each resource action sorted in alphabetical order.
// Iteration stops if an iterator returns an error and in this case IterateActions returns that
// error.
func (r *ResourceDefinition) IterateActions(it ActionIterator) error {
	names := make([]string, len(r.Actions))
	i := 0
	for n := range r.Actions {
		names[i] = n
		i++
	}
	sort.Strings(names)
	for _, n := range names {
		if err := it(r.Actions[n]); err != nil {
			return err
		}
	}
	return nil
}

// IterateFileServers calls the given iterator passing each resource file server sorted by file
// path. Iteration stops if an iterator returns an error and in this case IterateFileServers returns
// that error.
func (r *ResourceDefinition) IterateFileServers(it FileServerIterator) error {
	sort.Sort(ByFilePath(r.FileServers))
	for _, f := range r.FileServers {
		if err := it(f); err != nil {
			return err
		}
	}
	return nil
}

// IterateHeaders calls the given iterator passing in each response sorted in alphabetical order.
// Iteration stops if an iterator returns an error and in this case IterateHeaders returns that
// error.
func (r *ResourceDefinition) IterateHeaders(it HeaderIterator) error {
	return iterateHeaders(r.Headers, r.Headers.IsRequired, it)
}

// CanonicalAction returns the canonical action of the resource if any.
// The canonical action is used to compute hrefs to resources.
func (r *ResourceDefinition) CanonicalAction() *ActionDefinition {
	name := r.CanonicalActionName
	if name == "" {
		name = "show"
	}
	ca, _ := r.Actions[name]
	return ca
}

// URITemplate returns a URI template to this resource.
// The result is the empty string if the resource does not have a "show" action
// and does not define a different canonical action.
func (r *ResourceDefinition) URITemplate() string {
	ca := r.CanonicalAction()
	if ca == nil || len(ca.Routes) == 0 {
		return ""
	}
	return ca.Routes[0].FullPath()
}

// FullPath computes the base path to the resource actions concatenating the API and parent resource
// base paths as needed.
func (r *ResourceDefinition) FullPath() string {
	if strings.HasPrefix(r.BasePath, "//") {
		return httppath.Clean(r.BasePath)
	}
	var basePath string
	if p := r.Parent(); p != nil {
		if ca := p.CanonicalAction(); ca != nil {
			if routes := ca.Routes; len(routes) > 0 {
				// Note: all these tests should be true at code generation time
				// as DSL validation makes sure that parent resources have a
				// canonical path.
				basePath = path.Join(routes[0].FullPath())
			}
		}
	} else {
		basePath = Design.BasePath
	}
	return httppath.Clean(path.Join(basePath, r.BasePath))
}

// Parent returns the parent resource if any, nil otherwise.
func (r *ResourceDefinition) Parent() *ResourceDefinition {
	if r.ParentName != "" {
		if parent, ok := Design.Resources[r.ParentName]; ok {
			return parent
		}
	}
	return nil
}

// AllOrigins compute all CORS policies for the resource taking into account any API policy.
// The result is sorted alphabetically by policy origin.
func (r *ResourceDefinition) AllOrigins() []*CORSDefinition {
	all := make(map[string]*CORSDefinition)
	for n, o := range Design.Origins {
		all[n] = o
	}
	for n, o := range r.Origins {
		all[n] = o
	}
	names := make([]string, len(all))
	i := 0
	for n := range all {
		names[i] = n
		i++
	}
	sort.Strings(names)
	cors := make([]*CORSDefinition, len(names))
	for i, n := range names {
		cors[i] = all[n]
	}
	return cors
}

// PreflightPaths returns the paths that should handle OPTIONS requests.
func (r *ResourceDefinition) PreflightPaths() []string {
	var paths []string
	r.IterateActions(func(a *ActionDefinition) error {
		for _, r := range a.Routes {
			if r.Verb == "OPTIONS" {
				continue
			}
			found := false
			fp := r.FullPath()
			for _, p := range paths {
				if fp == p {
					found = true
					break
				}
			}
			if !found {
				paths = append(paths, fp)
			}
		}
		return nil
	})
	r.IterateFileServers(func(fs *FileServerDefinition) error {
		found := false
		fp := fs.RequestPath
		for _, p := range paths {
			if fp == p {
				found = true
				break
			}
		}
		if !found {
			paths = append(paths, fp)
		}
		return nil
	})
	return paths
}

// DSL returns the initialization DSL.
func (r *ResourceDefinition) DSL() func() {
	return r.DSLFunc
}

// Finalize is run post DSL execution. It merges response definitions, creates implicit action
// parameters, initializes querystring parameters, sets path parameters as non zero attributes
// and sets the fallbacks for security schemes.
func (r *ResourceDefinition) Finalize() {
	meta := r.Metadata["swagger:generate"]
	r.IterateFileServers(func(f *FileServerDefinition) error {
		if meta != nil {
			if _, ok := f.Metadata["swagger:generate"]; !ok {
				f.Metadata["swagger:generate"] = meta
			}
		}
		f.Finalize()
		return nil
	})
	r.IterateActions(func(a *ActionDefinition) error {
		if meta != nil {
			if _, ok := a.Metadata["swagger:generate"]; !ok {
				a.Metadata["swagger:generate"] = meta
			}
		}
		a.Finalize()
		return nil
	})
}

// UserTypes returns all the user types used by the resource action payloads and parameters.
func (r *ResourceDefinition) UserTypes() map[string]*UserTypeDefinition {
	types := make(map[string]*UserTypeDefinition)
	for _, a := range r.Actions {
		for n, ut := range a.UserTypes() {
			types[n] = ut
		}
	}
	if len(types) == 0 {
		return nil
	}
	return types
}

// Context returns the generic definition name used in error messages.
func (cors *CORSDefinition) Context() string {
	return fmt.Sprintf("CORS policy for resource %s origin %s", cors.Parent.Context(), cors.Origin)
}

// Context returns the generic definition name used in error messages.
func (enc *EncodingDefinition) Context() string {
	return fmt.Sprintf("encoding for %s", strings.Join(enc.MIMETypes, ", "))
}

// Context returns the generic definition name used in error messages.
func (a *AttributeDefinition) Context() string {
	return ""
}

// AllRequired returns the list of all required fields from the underlying object.
// An attribute type can be itself an attribute (e.g. a MediaTypeDefinition or a UserTypeDefinition)
// This happens when the DSL uses references for example. So traverse the hierarchy and collect
// all the required validations.
func (a *AttributeDefinition) AllRequired() (required []string) {
	if a == nil || a.Validation == nil {
		return
	}
	required = a.Validation.Required
	if ds, ok := a.Type.(DataStructure); ok {
		required = append(required, ds.Definition().AllRequired()...)
	}
	return
}

// IsRequired returns true if the given string matches the name of a required
// attribute, false otherwise.
func (a *AttributeDefinition) IsRequired(attName string) bool {
	for _, name := range a.AllRequired() {
		if name == attName {
			return true
		}
	}
	return false
}

// HasDefaultValue returns true if the given attribute has a default value.
func (a *AttributeDefinition) HasDefaultValue(attName string) bool {
	if a.Type.IsObject() {
		att := a.Type.ToObject()[attName]
		return att.DefaultValue != nil
	}
	return false
}

// SetDefault sets the default for the attribute. It also converts HashVal
// and ArrayVal to map and slice respectively.
func (a *AttributeDefinition) SetDefault(def interface{}) {
	switch actual := def.(type) {
	case HashVal:
		a.DefaultValue = actual.ToMap()
	case ArrayVal:
		a.DefaultValue = actual.ToSlice()
	default:
		a.DefaultValue = actual
	}
}

// AddValues adds the Enum values to the attribute's validation definition.
// It also performs any conversion needed for HashVal and ArrayVal types.
func (a *AttributeDefinition) AddValues(values []interface{}) {
	if a.Validation == nil {
		a.Validation = &dslengine.ValidationDefinition{}
	}
	a.Validation.Values = make([]interface{}, len(values))
	for i, v := range values {
		switch actual := v.(type) {
		case HashVal:
			a.Validation.Values[i] = actual.ToMap()
		case ArrayVal:
			a.Validation.Values[i] = actual.ToSlice()
		default:
			a.Validation.Values[i] = actual
		}
	}
}

// AllNonZero returns the complete list of all non-zero attribute name.
func (a *AttributeDefinition) AllNonZero() []string {
	nzs := make([]string, len(a.NonZeroAttributes))
	i := 0
	for n := range a.NonZeroAttributes {
		nzs[i] = n
		i++
	}
	return nzs
}

// IsNonZero returns true if the given string matches the name of a non-zero
// attribute, false otherwise.
func (a *AttributeDefinition) IsNonZero(attName string) bool {
	return a.NonZeroAttributes[attName]
}

// IsPrimitivePointer returns true if the field generated for the given attribute should be a
// pointer to a primitive type. The target attribute must be an object.
func (a *AttributeDefinition) IsPrimitivePointer(attName string) bool {
	if !a.Type.IsObject() {
		panic("checking pointer field on non-object") // bug
	}
	att := a.Type.ToObject()[attName]
	if att == nil {
		return false
	}
	if att.Type.IsPrimitive() {
		return !a.IsRequired(attName) && !a.HasDefaultValue(attName) && !a.IsNonZero(attName)
	}
	return false
}

// SetExample sets the custom example. SetExample also handles the case when the user doesn't
// want any example or any auto-generated example.
func (a *AttributeDefinition) SetExample(example interface{}) bool {
	if example == nil {
		a.Example = "-" // set it to something else than nil so we know not to generate one
		return true
	}
	if a.Type == nil || a.Type.IsCompatible(example) {
		a.Example = example
		return true
	}
	return false
}

// GenerateExample returns the value of the Example field if not nil. Otherwise it traverses the
// attribute type and recursively generates an example. The result is saved in the Example field.
func (a *AttributeDefinition) GenerateExample(rand *RandomGenerator, seen []string) interface{} {
	if a.Example != nil {
		return a.Example
	}
	if Design.NoExamples {
		return nil
	}

	// Avoid infinite loops
	var key string
	if mt, ok := a.Type.(*MediaTypeDefinition); ok {
		key = mt.Identifier
	} else if ut, ok := a.Type.(*UserTypeDefinition); ok {
		key = ut.TypeName
	}
	if key != "" {
		count := 0
		for _, k := range seen {
			if k == key {
				count++
			}
		}
		if count > 1 {
			// Only go a couple of levels deep
			return nil
		}
		seen = append(seen, key)
	}

	switch {
	case a.Type.IsArray():
		a.Example = a.arrayExample(rand, seen)

	case a.Type.IsHash():
		a.Example = a.hashExample(rand, seen)

	case a.Type.IsObject():
		a.Example = a.objectExample(rand, seen)

	default:
		a.Example = newExampleGenerator(a, rand).Generate(seen)
	}

	return a.Example
}

func (a *AttributeDefinition) arrayExample(rand *RandomGenerator, seen []string) interface{} {
	ary := a.Type.ToArray()
	ln := newExampleGenerator(a, rand).ExampleLength()
	var res []interface{}
	for i := 0; i < ln; i++ {
		ex := ary.ElemType.GenerateExample(rand, seen)
		if ex != nil {
			res = append(res, ex)
		}
	}
	if len(res) == 0 {
		return nil
	}
	return ary.MakeSlice(res)
}

func (a *AttributeDefinition) hashExample(rand *RandomGenerator, seen []string) interface{} {
	h := a.Type.ToHash()
	ln := newExampleGenerator(a, rand).ExampleLength()
	res := make(map[interface{}]interface{})
	for i := 0; i < ln; i++ {
		k := h.KeyType.GenerateExample(rand, seen)
		v := h.ElemType.GenerateExample(rand, seen)
		if k != nil && v != nil {
			res[k] = v
		}
	}
	if len(res) == 0 {
		return nil
	}
	return h.MakeMap(res)
}

func (a *AttributeDefinition) objectExample(rand *RandomGenerator, seen []string) interface{} {
	// project media types
	actual := a
	if mt, ok := a.Type.(*MediaTypeDefinition); ok {
		v := a.View
		if v == "" {
			v = DefaultView
		}
		projected, _, err := mt.Project(v)
		if err != nil {
			panic(err) // bug
		}
		actual = projected.AttributeDefinition
	}

	// ensure fixed ordering so random values are computed with consistent seeds
	aObj := actual.Type.ToObject()
	keys := make([]string, len(aObj))
	i := 0
	for n := range aObj {
		keys[i] = n
		i++
	}
	sort.Strings(keys)

	res := make(map[string]interface{})
	for _, n := range keys {
		att := aObj[n]
		if ex := att.GenerateExample(rand, seen); ex != nil {
			res[n] = ex
		}
	}
	if len(res) > 0 {
		a.Example = res
	}

	return a.Example
}

// Merge merges the argument attributes into the target and returns the target overriding existing
// attributes with identical names.
// This only applies to attributes of type Object and Merge panics if the
// argument or the target is not of type Object.
func (a *AttributeDefinition) Merge(other *AttributeDefinition) *AttributeDefinition {
	if other == nil {
		return a
	}
	if a == nil {
		return other
	}
	left := a.Type.(Object)
	right := other.Type.(Object)
	if left == nil || right == nil {
		panic("cannot merge non object attributes") // bug
	}
	for n, v := range right {
		left[n] = v
	}
	return a
}

// Inherit merges the properties of existing target type attributes with the argument's.
// The algorithm is recursive so that child attributes are also merged.
func (a *AttributeDefinition) Inherit(parent *AttributeDefinition) {
	if !a.shouldInherit(parent) {
		return
	}

	a.inheritValidations(parent)
	a.inheritRecursive(parent)
}

// DSL returns the initialization DSL.
func (a *AttributeDefinition) DSL() func() {
	return a.DSLFunc
}

func (a *AttributeDefinition) inheritRecursive(parent *AttributeDefinition) {
	if !a.shouldInherit(parent) {
		return
	}

	for n, att := range a.Type.ToObject() {
		if patt, ok := parent.Type.ToObject()[n]; ok {
			if att.Description == "" {
				att.Description = patt.Description
			}
			att.inheritValidations(patt)
			if att.DefaultValue == nil {
				att.DefaultValue = patt.DefaultValue
			}
			if att.View == "" {
				att.View = patt.View
			}
			if att.Type == nil {
				att.Type = patt.Type
			} else if att.shouldInherit(patt) {
				for _, att := range att.Type.ToObject() {
					att.Inherit(patt.Type.ToObject()[n])
				}
			}
			if att.Example == nil {
				att.Example = patt.Example
			}
		}
	}
}

func (a *AttributeDefinition) inheritValidations(parent *AttributeDefinition) {
	if parent.Validation == nil {
		return
	}
	if a.Validation == nil {
		a.Validation = &dslengine.ValidationDefinition{}
	}
	a.Validation.AddRequired(parent.Validation.Required)
}

func (a *AttributeDefinition) shouldInherit(parent *AttributeDefinition) bool {
	return a != nil && a.Type.ToObject() != nil &&
		parent != nil && parent.Type.ToObject() != nil
}

// Context returns the generic definition name used in error messages.
func (c *ContactDefinition) Context() string {
	if c.Name != "" {
		return fmt.Sprintf("contact %s", c.Name)
	}
	return "unnamed contact"
}

// Context returns the generic definition name used in error messages.
func (l *LicenseDefinition) Context() string {
	if l.Name != "" {
		return fmt.Sprintf("license %s", l.Name)
	}
	return "unnamed license"
}

// Context returns the generic definition name used in error messages.
func (d *DocsDefinition) Context() string {
	return fmt.Sprintf("documentation for %s", Design.Name)
}

// Context returns the generic definition name used in error messages.
func (t *UserTypeDefinition) Context() string {
	if t.TypeName != "" {
		return fmt.Sprintf("type %#v", t.TypeName)
	}
	return "unnamed type"
}

// DSL returns the initialization DSL.
func (t *UserTypeDefinition) DSL() func() {
	return t.DSLFunc
}

// Context returns the generic definition name used in error messages.
func (r *ResponseDefinition) Context() string {
	var prefix, suffix string
	if r.Name != "" {
		prefix = fmt.Sprintf("response %#v", r.Name)
	} else {
		prefix = "unnamed response"
	}
	if r.Parent != nil {
		suffix = fmt.Sprintf(" of %s", r.Parent.Context())
	}
	return prefix + suffix
}

// Finalize sets the response media type from its type if the type is a media type and no media
// type is already specified.
func (r *ResponseDefinition) Finalize() {
	if r.Type == nil {
		return
	}
	if r.MediaType != "" && r.MediaType != "text/plain" {
		return
	}
	mt, ok := r.Type.(*MediaTypeDefinition)
	if !ok {
		return
	}
	r.MediaType = mt.Identifier
}

// Dup returns a copy of the response definition.
func (r *ResponseDefinition) Dup() *ResponseDefinition {
	res := ResponseDefinition{
		Name:        r.Name,
		Status:      r.Status,
		Description: r.Description,
		MediaType:   r.MediaType,
		ViewName:    r.ViewName,
	}
	if r.Headers != nil {
		res.Headers = DupAtt(r.Headers)
	}
	return &res
}

// Merge merges other into target. Only the fields of target that are not already set are merged.
func (r *ResponseDefinition) Merge(other *ResponseDefinition) {
	if other == nil {
		return
	}
	if r.Name == "" {
		r.Name = other.Name
	}
	if r.Status == 0 {
		r.Status = other.Status
	}
	if r.Description == "" {
		r.Description = other.Description
	}
	if r.MediaType == "" {
		r.MediaType = other.MediaType
		r.ViewName = other.ViewName
	}
	if other.Headers != nil {
		otherHeaders := other.Headers.Type.ToObject()
		if len(otherHeaders) > 0 {
			if r.Headers == nil {
				r.Headers = &AttributeDefinition{Type: Object{}}
			}
			headers := r.Headers.Type.ToObject()
			for n, h := range otherHeaders {
				if _, ok := headers[n]; !ok {
					headers[n] = h
				}
			}
		}
	}
}

// Context returns the generic definition name used in error messages.
func (r *ResponseTemplateDefinition) Context() string {
	if r.Name != "" {
		return fmt.Sprintf("response template %#v", r.Name)
	}
	return "unnamed response template"
}

// Context returns the generic definition name used in error messages.
func (a *ActionDefinition) Context() string {
	var prefix, suffix string
	if a.Name != "" {
		suffix = fmt.Sprintf("action %#v", a.Name)
	} else {
		suffix = "unnamed action"
	}
	if a.Parent != nil {
		prefix = a.Parent.Context() + " "
	}
	return prefix + suffix
}

// PathParams returns the path parameters of the action across all its routes.
func (a *ActionDefinition) PathParams() *AttributeDefinition {
	obj := make(Object)
	allParams := a.AllParams().Type.ToObject()
	for _, r := range a.Routes {
		for _, p := range r.Params() {
			if _, ok := obj[p]; !ok {
				obj[p] = allParams[p]
			}
		}
	}
	return &AttributeDefinition{Type: obj}
}

// AllParams returns the path and query string parameters of the action across all its routes.
func (a *ActionDefinition) AllParams() *AttributeDefinition {
	var res *AttributeDefinition
	if a.Params != nil {
		res = DupAtt(a.Params)
	} else {
		res = &AttributeDefinition{Type: Object{}}
	}
	if a.HasAbsoluteRoutes() {
		return res
	}
	if p := a.Parent.Parent(); p != nil {
		res = res.Merge(p.CanonicalAction().AllParams())
	} else {
		res = res.Merge(a.Parent.Params)
		res = res.Merge(Design.Params)
	}
	return res
}

// HasAbsoluteRoutes returns true if all the action routes are absolute.
func (a *ActionDefinition) HasAbsoluteRoutes() bool {
	for _, r := range a.Routes {
		if !r.IsAbsolute() {
			return false
		}
	}
	return true
}

// CanonicalScheme returns the preferred scheme for making requests. Favor secure schemes.
func (a *ActionDefinition) CanonicalScheme() string {
	if a.WebSocket() {
		for _, s := range a.EffectiveSchemes() {
			if s == "wss" {
				return s
			}
		}
		return "ws"
	}
	for _, s := range a.EffectiveSchemes() {
		if s == "https" {
			return s
		}
	}
	return "http"
}

// EffectiveSchemes return the URL schemes that apply to the action. Looks recursively into action
// resource, parent resources and API.
func (a *ActionDefinition) EffectiveSchemes() []string {
	// Compute the schemes
	schemes := a.Schemes
	if len(schemes) == 0 {
		res := a.Parent
		schemes = res.Schemes
		parent := res.Parent()
		for len(schemes) == 0 && parent != nil {
			schemes = parent.Schemes
			parent = parent.Parent()
		}
		if len(schemes) == 0 {
			schemes = Design.Schemes
		}
	}
	return schemes
}

// WebSocket returns true if the action scheme is "ws" or "wss" or both (directly or inherited
// from the resource or API)
func (a *ActionDefinition) WebSocket() bool {
	schemes := a.EffectiveSchemes()
	if len(schemes) == 0 {
		return false
	}
	for _, s := range schemes {
		if s != "ws" && s != "wss" {
			return false
		}
	}
	return true
}

// Finalize inherits security scheme and action responses from parent and top level design.
func (a *ActionDefinition) Finalize() {
	// Inherit security scheme
	if a.Security == nil {
		a.Security = a.Parent.Security // ResourceDefinition
		if a.Security == nil {
			a.Security = Design.Security
		}
	}

	if a.Security != nil && a.Security.Scheme.Kind == NoSecurityKind {
		a.Security = nil
	}

	if a.Payload != nil {
		a.Payload.Finalize()
	}

	a.mergeResponses()
	a.initImplicitParams()
	a.initQueryParams()
}

// UserTypes returns all the user types used by the action payload and parameters.
func (a *ActionDefinition) UserTypes() map[string]*UserTypeDefinition {
	types := make(map[string]*UserTypeDefinition)
	allp := a.AllParams().Type.ToObject()
	if a.Payload != nil {
		allp["__payload__"] = &AttributeDefinition{Type: a.Payload}
	}
	for n, ut := range UserTypes(allp) {
		types[n] = ut
	}
	for _, r := range a.Responses {
		if mt := Design.MediaTypeWithIdentifier(r.MediaType); mt != nil {
			types[mt.TypeName] = mt.UserTypeDefinition
			for n, ut := range UserTypes(mt.UserTypeDefinition) {
				types[n] = ut
			}
		}
	}
	if len(types) == 0 {
		return nil
	}
	return types
}

// IterateHeaders iterates over the resource-level and action-level headers,
// calling the given iterator passing in each response sorted in alphabetical order.
// Iteration stops if an iterator returns an error and in this case IterateHeaders returns that
// error.
func (a *ActionDefinition) IterateHeaders(it HeaderIterator) error {
	mergedHeaders := a.Parent.Headers.Merge(a.Headers)

	isRequired := func(name string) bool {
		// header required in either the Resource or Action scope?
		return a.Parent.Headers.IsRequired(name) || a.Headers.IsRequired(name)
	}

	return iterateHeaders(mergedHeaders, isRequired, it)
}

// IterateResponses calls the given iterator passing in each response sorted in alphabetical order.
// Iteration stops if an iterator returns an error and in this case IterateResponses returns that
// error.
func (a *ActionDefinition) IterateResponses(it ResponseIterator) error {
	names := make([]string, len(a.Responses))
	i := 0
	for n := range a.Responses {
		names[i] = n
		i++
	}
	sort.Strings(names)
	for _, n := range names {
		if err := it(a.Responses[n]); err != nil {
			return err
		}
	}
	return nil
}

// mergeResponses merges the parent resource and design responses.
func (a *ActionDefinition) mergeResponses() {
	for name, resp := range a.Parent.Responses {
		if _, ok := a.Responses[name]; !ok {
			if a.Responses == nil {
				a.Responses = make(map[string]*ResponseDefinition)
			}
			a.Responses[name] = resp.Dup()
		}
	}
	for name, resp := range a.Responses {
		resp.Finalize()
		if pr, ok := a.Parent.Responses[name]; ok {
			resp.Merge(pr)
		}
		if ar, ok := Design.Responses[name]; ok {
			resp.Merge(ar)
		}
		if dr, ok := Design.DefaultResponses[name]; ok {
			resp.Merge(dr)
		}
	}
}

// initImplicitParams creates params for path segments that don't have one.
func (a *ActionDefinition) initImplicitParams() {
	for _, ro := range a.Routes {
		for _, wc := range ro.Params() {
			found := false
			search := func(params *AttributeDefinition) {
				if params == nil {
					return
				}
				att, ok := params.Type.ToObject()[wc]
				if ok {
					if a.Params == nil {
						a.Params = &AttributeDefinition{Type: Object{}}
					}
					a.Params.Type.ToObject()[wc] = att
					found = true
				}
			}
			search(a.Params)
			parent := a.Parent
			for !found && parent != nil {
				bp := parent.Params
				parent = parent.Parent()
				search(bp)
			}
			if found {
				continue
			}
			search(Design.Params)
			if found {
				continue
			}
			if a.Params == nil {
				a.Params = &AttributeDefinition{Type: Object{}}
			}
			a.Params.Type.ToObject()[wc] = &AttributeDefinition{Type: String}
		}
	}
}

// initQueryParams extract the query parameters from the action params.
func (a *ActionDefinition) initQueryParams() {
	// 3. Compute QueryParams from Params and set all path params as non zero attributes
	if params := a.AllParams(); params != nil {
		queryParams := DupAtt(params)
		queryParams.Type = Dup(queryParams.Type)
		if a.Params == nil {
			a.Params = &AttributeDefinition{Type: Object{}}
		}
		a.Params.NonZeroAttributes = make(map[string]bool)
		for _, route := range a.Routes {
			pnames := route.Params()
			for _, pname := range pnames {
				a.Params.NonZeroAttributes[pname] = true
				delete(queryParams.Type.ToObject(), pname)
				if queryParams.Validation != nil {
					req := queryParams.Validation.Required
					for i, n := range req {
						if n == pname {
							queryParams.Validation.Required = append(req[:i], req[i+1:]...)
							break
						}
					}
				}
			}
		}
		a.QueryParams = queryParams
	}
}

// Context returns the generic definition name used in error messages.
func (f *FileServerDefinition) Context() string {
	suffix := fmt.Sprintf("file server %s", f.FilePath)
	var prefix string
	if f.Parent != nil {
		prefix = f.Parent.Context() + " "
	}
	return prefix + suffix
}

// Finalize inherits security scheme from parent and top level design.
func (f *FileServerDefinition) Finalize() {
	// Make sure request path starts with a "/" so codegen can rely on it.
	if !strings.HasPrefix(f.RequestPath, "/") {
		f.RequestPath = "/" + f.RequestPath
	}
	// Inherit security
	if f.Security == nil {
		f.Security = f.Parent.Security // ResourceDefinition
		if f.Security == nil {
			f.Security = Design.Security
		}
	}
	if f.Security != nil && f.Security.Scheme.Kind == NoSecurityKind {
		f.Security = nil
	}
}

// IsDir returns true if the file server serves a directory, false otherwise.
func (f *FileServerDefinition) IsDir() bool {
	return WildcardRegex.MatchString(f.RequestPath)
}

// ByFilePath makes FileServerDefinition sortable for code generators.
type ByFilePath []*FileServerDefinition

func (b ByFilePath) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
func (b ByFilePath) Len() int           { return len(b) }
func (b ByFilePath) Less(i, j int) bool { return b[i].FilePath < b[j].FilePath }

// Context returns the generic definition name used in error messages.
func (l *LinkDefinition) Context() string {
	var prefix, suffix string
	if l.Name != "" {
		prefix = fmt.Sprintf("link %#v", l.Name)
	} else {
		prefix = "unnamed link"
	}
	if l.Parent != nil {
		suffix = fmt.Sprintf(" of %s", l.Parent.Context())
	}
	return prefix + suffix
}

// Attribute returns the linked attribute.
func (l *LinkDefinition) Attribute() *AttributeDefinition {
	p := l.Parent.ToObject()
	if p == nil {
		return nil
	}
	att, _ := p[l.Name]

	return att
}

// MediaType returns the media type of the linked attribute.
func (l *LinkDefinition) MediaType() *MediaTypeDefinition {
	att := l.Attribute()
	mt, _ := att.Type.(*MediaTypeDefinition)
	return mt
}

// Context returns the generic definition name used in error messages.
func (v *ViewDefinition) Context() string {
	var prefix, suffix string
	if v.Name != "" {
		prefix = fmt.Sprintf("view %#v", v.Name)
	} else {
		prefix = "unnamed view"
	}
	if v.Parent != nil {
		suffix = fmt.Sprintf(" of %s", v.Parent.Context())
	}
	return prefix + suffix
}

// Context returns the generic definition name used in error messages.
func (r *RouteDefinition) Context() string {
	return fmt.Sprintf(`route %s "%s" of %s`, r.Verb, r.Path, r.Parent.Context())
}

// Params returns the route parameters.
// For example for the route "GET /foo/:fooID" Params returns []string{"fooID"}.
func (r *RouteDefinition) Params() []string {
	return ExtractWildcards(r.FullPath())
}

// FullPath returns the action full path computed by concatenating the API and resource base paths
// with the action specific path.
func (r *RouteDefinition) FullPath() string {
	if r.IsAbsolute() {
		return httppath.Clean(r.Path[1:])
	}
	var base string
	if r.Parent != nil && r.Parent.Parent != nil {
		base = r.Parent.Parent.FullPath()
	}
	return httppath.Clean(path.Join(base, r.Path))
}

// IsAbsolute returns true if the action path should not be concatenated to the resource and API
// base paths.
func (r *RouteDefinition) IsAbsolute() bool {
	return strings.HasPrefix(r.Path, "//")
}

func iterateHeaders(headers *AttributeDefinition, isRequired func(name string) bool, it HeaderIterator) error {
	if headers == nil || !headers.Type.IsObject() {
		return nil
	}
	headersMap := headers.Type.ToObject()
	names := make([]string, len(headersMap))
	i := 0
	for n := range headersMap {
		names[i] = n
		i++
	}
	sort.Strings(names)
	for _, n := range names {
		header := headersMap[n]
		if err := it(n, isRequired(n), header); err != nil {
			return err
		}
	}
	return nil
}
