| // Copyright 2017 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package main |
| |
| import ( |
| "errors" |
| "fmt" |
| "log" |
| "sort" |
| "strings" |
| |
| "github.com/googleapis/gnostic/jsonschema" |
| ) |
| |
| // Domain models a collection of types that is defined by a schema. |
| type Domain struct { |
| TypeModels map[string]*TypeModel // models of the types in the domain |
| Prefix string // type prefix to use |
| Schema *jsonschema.Schema // top-level schema |
| TypeNameOverrides map[string]string // a configured mapping from patterns to type names |
| PropertyNameOverrides map[string]string // a configured mapping from patterns to property names |
| ObjectTypeRequests map[string]*TypeRequest // anonymous types implied by type instantiation |
| MapTypeRequests map[string]string // "NamedObject" types that will be used to implement ordered maps |
| Version string // OpenAPI Version ("v2" or "v3") |
| } |
| |
| // NewDomain creates a domain representation. |
| func NewDomain(schema *jsonschema.Schema, version string) *Domain { |
| cc := &Domain{} |
| cc.TypeModels = make(map[string]*TypeModel, 0) |
| cc.TypeNameOverrides = make(map[string]string, 0) |
| cc.PropertyNameOverrides = make(map[string]string, 0) |
| cc.ObjectTypeRequests = make(map[string]*TypeRequest, 0) |
| cc.MapTypeRequests = make(map[string]string, 0) |
| cc.Schema = schema |
| cc.Version = version |
| return cc |
| } |
| |
| // TypeNameForStub returns a capitalized name to use for a generated type. |
| func (domain *Domain) TypeNameForStub(stub string) string { |
| |
| |
| return domain.Prefix + strings.ToUpper(stub[0:1]) + stub[1:len(stub)] |
| } |
| |
| // typeNameForReference returns a capitalized name to use for a generated type based on a JSON reference |
| func (domain *Domain) typeNameForReference(reference string) string { |
| parts := strings.Split(reference, "/") |
| first := parts[0] |
| last := parts[len(parts)-1] |
| if first == "#" { |
| return domain.TypeNameForStub(last) |
| } |
| return "Schema" |
| } |
| |
| // propertyNameForReference returns a property name to use for a JSON reference |
| func (domain *Domain) propertyNameForReference(reference string) *string { |
| parts := strings.Split(reference, "/") |
| first := parts[0] |
| last := parts[len(parts)-1] |
| if first == "#" { |
| return &last |
| } |
| return nil |
| } |
| |
| // arrayItemTypeForSchema determines the item type for arrays defined by a schema |
| func (domain *Domain) arrayItemTypeForSchema(propertyName string, schema *jsonschema.Schema) string { |
| // default |
| itemTypeName := "Any" |
| |
| if schema.Items != nil { |
| |
| if schema.Items.SchemaArray != nil { |
| |
| if len(*(schema.Items.SchemaArray)) > 0 { |
| ref := (*schema.Items.SchemaArray)[0].Ref |
| if ref != nil { |
| itemTypeName = domain.typeNameForReference(*ref) |
| } else { |
| types := (*schema.Items.SchemaArray)[0].Type |
| if types == nil { |
| // do nothing |
| } else if (types.StringArray != nil) && len(*(types.StringArray)) == 1 { |
| itemTypeName = (*types.StringArray)[0] |
| } else if (types.StringArray != nil) && len(*(types.StringArray)) > 1 { |
| itemTypeName = fmt.Sprintf("%+v", types.StringArray) |
| } else if types.String != nil { |
| itemTypeName = *(types.String) |
| } else { |
| itemTypeName = "UNKNOWN" |
| } |
| } |
| } |
| |
| } else if schema.Items.Schema != nil { |
| types := schema.Items.Schema.Type |
| |
| if schema.Items.Schema.Ref != nil { |
| itemTypeName = domain.typeNameForReference(*schema.Items.Schema.Ref) |
| } else if schema.Items.Schema.OneOf != nil { |
| // this type is implied by the "oneOf" |
| itemTypeName = domain.TypeNameForStub(propertyName + "Item") |
| domain.ObjectTypeRequests[itemTypeName] = |
| NewTypeRequest(itemTypeName, propertyName, schema.Items.Schema) |
| } else if types == nil { |
| // do nothing |
| } else if (types.StringArray != nil) && len(*(types.StringArray)) == 1 { |
| itemTypeName = (*types.StringArray)[0] |
| } else if (types.StringArray != nil) && len(*(types.StringArray)) > 1 { |
| itemTypeName = fmt.Sprintf("%+v", types.StringArray) |
| } else if types.String != nil { |
| itemTypeName = *(types.String) |
| } else { |
| itemTypeName = "UNKNOWN" |
| } |
| } |
| |
| } |
| return itemTypeName |
| } |
| |
| func (domain *Domain) buildTypeProperties(typeModel *TypeModel, schema *jsonschema.Schema) { |
| if schema.Properties != nil { |
| for _, pair := range *(schema.Properties) { |
| propertyName := pair.Name |
| propertySchema := pair.Value |
| if propertySchema.Ref != nil { |
| // the property schema is a reference, so we will add a property with the type of the referenced schema |
| propertyTypeName := domain.typeNameForReference(*(propertySchema.Ref)) |
| typeProperty := NewTypeProperty() |
| typeProperty.Name = propertyName |
| typeProperty.Type = propertyTypeName |
| typeModel.addProperty(typeProperty) |
| } else if propertySchema.Type != nil { |
| // the property schema specifies a type, so add a property with the specified type |
| if propertySchema.TypeIs("string") { |
| typeProperty := NewTypePropertyWithNameAndType(propertyName, "string") |
| if propertySchema.Description != nil { |
| typeProperty.Description = *propertySchema.Description |
| } |
| if propertySchema.Enumeration != nil { |
| allowedValues := make([]string, 0) |
| for _, enumValue := range *propertySchema.Enumeration { |
| if enumValue.String != nil { |
| allowedValues = append(allowedValues, *enumValue.String) |
| } |
| } |
| typeProperty.StringEnumValues = allowedValues |
| } |
| typeModel.addProperty(typeProperty) |
| } else if propertySchema.TypeIs("boolean") { |
| typeProperty := NewTypePropertyWithNameAndType(propertyName, "bool") |
| if propertySchema.Description != nil { |
| typeProperty.Description = *propertySchema.Description |
| } |
| typeModel.addProperty(typeProperty) |
| } else if propertySchema.TypeIs("number") { |
| typeProperty := NewTypePropertyWithNameAndType(propertyName, "float") |
| if propertySchema.Description != nil { |
| typeProperty.Description = *propertySchema.Description |
| } |
| typeModel.addProperty(typeProperty) |
| } else if propertySchema.TypeIs("integer") { |
| typeProperty := NewTypePropertyWithNameAndType(propertyName, "int") |
| if propertySchema.Description != nil { |
| typeProperty.Description = *propertySchema.Description |
| } |
| typeModel.addProperty(typeProperty) |
| } else if propertySchema.TypeIs("object") { |
| // the property has an "anonymous" object schema, so define a new type for it and request its creation |
| anonymousObjectTypeName := domain.TypeNameForStub(propertyName) |
| domain.ObjectTypeRequests[anonymousObjectTypeName] = |
| NewTypeRequest(anonymousObjectTypeName, propertyName, propertySchema) |
| // add a property with the type of the requested type |
| typeProperty := NewTypePropertyWithNameAndType(propertyName, anonymousObjectTypeName) |
| if propertySchema.Description != nil { |
| typeProperty.Description = *propertySchema.Description |
| } |
| typeModel.addProperty(typeProperty) |
| } else if propertySchema.TypeIs("array") { |
| // the property has an array type, so define it as a repeated property of the specified type |
| propertyTypeName := domain.arrayItemTypeForSchema(propertyName, propertySchema) |
| typeProperty := NewTypePropertyWithNameAndType(propertyName, propertyTypeName) |
| typeProperty.Repeated = true |
| if propertySchema.Description != nil { |
| typeProperty.Description = *propertySchema.Description |
| } |
| if typeProperty.Type == "string" { |
| itemSchema := propertySchema.Items.Schema |
| if itemSchema != nil { |
| if itemSchema.Enumeration != nil { |
| allowedValues := make([]string, 0) |
| for _, enumValue := range *itemSchema.Enumeration { |
| if enumValue.String != nil { |
| allowedValues = append(allowedValues, *enumValue.String) |
| } |
| } |
| typeProperty.StringEnumValues = allowedValues |
| } |
| } |
| } |
| typeModel.addProperty(typeProperty) |
| } else { |
| log.Printf("ignoring %+v, which has an unsupported property type '%s'", propertyName, propertySchema.Type.Description()) |
| } |
| } else if propertySchema.IsEmpty() { |
| // an empty schema can contain anything, so add an accessor for a generic object |
| typeName := "Any" |
| typeProperty := NewTypePropertyWithNameAndType(propertyName, typeName) |
| typeModel.addProperty(typeProperty) |
| } else if propertySchema.OneOf != nil { |
| anonymousObjectTypeName := domain.TypeNameForStub(propertyName + "Item") |
| domain.ObjectTypeRequests[anonymousObjectTypeName] = |
| NewTypeRequest(anonymousObjectTypeName, propertyName, propertySchema) |
| typeProperty := NewTypePropertyWithNameAndType(propertyName, anonymousObjectTypeName) |
| typeModel.addProperty(typeProperty) |
| } else if propertySchema.AnyOf != nil { |
| anonymousObjectTypeName := domain.TypeNameForStub(propertyName + "Item") |
| domain.ObjectTypeRequests[anonymousObjectTypeName] = |
| NewTypeRequest(anonymousObjectTypeName, propertyName, propertySchema) |
| typeProperty := NewTypePropertyWithNameAndType(propertyName, anonymousObjectTypeName) |
| typeModel.addProperty(typeProperty) |
| } else { |
| log.Printf("ignoring %s.%s, which has an unrecognized schema:\n%+v", typeModel.Name, propertyName, propertySchema.String()) |
| } |
| } |
| } |
| } |
| |
| func (domain *Domain) buildTypeRequirements(typeModel *TypeModel, schema *jsonschema.Schema) { |
| if schema.Required != nil { |
| typeModel.Required = (*schema.Required) |
| } |
| } |
| |
| func (domain *Domain) buildPatternPropertyAccessors(typeModel *TypeModel, schema *jsonschema.Schema) { |
| if schema.PatternProperties != nil { |
| typeModel.OpenPatterns = make([]string, 0) |
| for _, pair := range *(schema.PatternProperties) { |
| propertyPattern := pair.Name |
| propertySchema := pair.Value |
| typeModel.OpenPatterns = append(typeModel.OpenPatterns, propertyPattern) |
| if propertySchema.Ref != nil { |
| typeName := domain.typeNameForReference(*propertySchema.Ref) |
| if _, ok := domain.TypeNameOverrides[typeName]; ok { |
| typeName = domain.TypeNameOverrides[typeName] |
| } |
| propertyName := domain.typeNameForReference(*propertySchema.Ref) |
| if _, ok := domain.PropertyNameOverrides[propertyName]; ok { |
| propertyName = domain.PropertyNameOverrides[propertyName] |
| } |
| propertyTypeName := fmt.Sprintf("Named%s", typeName) |
| property := NewTypePropertyWithNameTypeAndPattern(propertyName, propertyTypeName, propertyPattern) |
| property.Implicit = true |
| property.MapType = typeName |
| property.Repeated = true |
| domain.MapTypeRequests[property.MapType] = property.MapType |
| typeModel.addProperty(property) |
| } |
| } |
| } |
| } |
| |
| func (domain *Domain) buildAdditionalPropertyAccessors(typeModel *TypeModel, schema *jsonschema.Schema) { |
| if schema.AdditionalProperties != nil { |
| if schema.AdditionalProperties.Boolean != nil { |
| if *schema.AdditionalProperties.Boolean == true { |
| typeModel.Open = true |
| propertyName := "additionalProperties" |
| typeName := "NamedAny" |
| property := NewTypePropertyWithNameAndType(propertyName, typeName) |
| property.Implicit = true |
| property.MapType = "Any" |
| property.Repeated = true |
| domain.MapTypeRequests[property.MapType] = property.MapType |
| typeModel.addProperty(property) |
| return |
| } |
| } else if schema.AdditionalProperties.Schema != nil { |
| typeModel.Open = true |
| schema := schema.AdditionalProperties.Schema |
| if schema.Ref != nil { |
| propertyName := "additionalProperties" |
| mapType := domain.typeNameForReference(*schema.Ref) |
| typeName := fmt.Sprintf("Named%s", mapType) |
| property := NewTypePropertyWithNameAndType(propertyName, typeName) |
| property.Implicit = true |
| property.MapType = mapType |
| property.Repeated = true |
| domain.MapTypeRequests[property.MapType] = property.MapType |
| typeModel.addProperty(property) |
| return |
| } else if schema.Type != nil { |
| typeName := *schema.Type.String |
| if typeName == "string" { |
| propertyName := "additionalProperties" |
| typeName := "NamedString" |
| property := NewTypePropertyWithNameAndType(propertyName, typeName) |
| property.Implicit = true |
| property.MapType = "string" |
| property.Repeated = true |
| domain.MapTypeRequests[property.MapType] = property.MapType |
| typeModel.addProperty(property) |
| return |
| } else if typeName == "array" { |
| if schema.Items != nil { |
| itemType := *schema.Items.Schema.Type.String |
| if itemType == "string" { |
| propertyName := "additionalProperties" |
| typeName := "NamedStringArray" |
| property := NewTypePropertyWithNameAndType(propertyName, typeName) |
| property.Implicit = true |
| property.MapType = "StringArray" |
| property.Repeated = true |
| domain.MapTypeRequests[property.MapType] = property.MapType |
| typeModel.addProperty(property) |
| return |
| } |
| } |
| } |
| } else if schema.OneOf != nil { |
| propertyTypeName := domain.TypeNameForStub(typeModel.Name + "Item") |
| propertyName := "additionalProperties" |
| typeName := fmt.Sprintf("Named%s", propertyTypeName) |
| property := NewTypePropertyWithNameAndType(propertyName, typeName) |
| property.Implicit = true |
| property.MapType = propertyTypeName |
| property.Repeated = true |
| domain.MapTypeRequests[property.MapType] = property.MapType |
| typeModel.addProperty(property) |
| |
| domain.ObjectTypeRequests[propertyTypeName] = |
| NewTypeRequest(propertyTypeName, propertyName, schema) |
| } |
| } |
| } |
| } |
| |
| func (domain *Domain) buildOneOfAccessors(typeModel *TypeModel, schema *jsonschema.Schema) { |
| oneOfs := schema.OneOf |
| if oneOfs == nil { |
| return |
| } |
| typeModel.Open = true |
| typeModel.OneOfWrapper = true |
| for _, oneOf := range *oneOfs { |
| if oneOf.Ref != nil { |
| ref := *oneOf.Ref |
| typeName := domain.typeNameForReference(ref) |
| propertyName := domain.propertyNameForReference(ref) |
| |
| if propertyName != nil { |
| typeProperty := NewTypePropertyWithNameAndType(*propertyName, typeName) |
| typeModel.addProperty(typeProperty) |
| } |
| } else if oneOf.Type != nil && oneOf.Type.String != nil { |
| switch *oneOf.Type.String { |
| case "boolean": |
| typeProperty := NewTypePropertyWithNameAndType("boolean", "bool") |
| typeModel.addProperty(typeProperty) |
| case "integer": |
| typeProperty := NewTypePropertyWithNameAndType("integer", "int") |
| typeModel.addProperty(typeProperty) |
| case "number": |
| typeProperty := NewTypePropertyWithNameAndType("number", "float") |
| typeModel.addProperty(typeProperty) |
| case "string": |
| typeProperty := NewTypePropertyWithNameAndType("string", "string") |
| typeModel.addProperty(typeProperty) |
| default: |
| log.Printf("Unsupported oneOf:\n%+v", oneOf.String()) |
| } |
| } else { |
| log.Printf("Unsupported oneOf:\n%+v", oneOf.String()) |
| } |
| |
| } |
| } |
| |
| func schemaIsContainedInArray(s1 *jsonschema.Schema, s2 *jsonschema.Schema) bool { |
| if s2.TypeIs("array") { |
| if s2.Items.Schema != nil { |
| if s1.IsEqual(s2.Items.Schema) { |
| return true |
| } |
| } |
| } |
| return false |
| } |
| |
| func (domain *Domain) addAnonymousAccessorForSchema( |
| typeModel *TypeModel, |
| schema *jsonschema.Schema, |
| repeated bool) { |
| ref := schema.Ref |
| if ref != nil { |
| typeName := domain.typeNameForReference(*ref) |
| propertyName := domain.propertyNameForReference(*ref) |
| if propertyName != nil { |
| property := NewTypePropertyWithNameAndType(*propertyName, typeName) |
| property.Repeated = true |
| typeModel.addProperty(property) |
| typeModel.IsItemArray = true |
| } |
| } else { |
| typeName := "string" |
| propertyName := "value" |
| property := NewTypePropertyWithNameAndType(propertyName, typeName) |
| property.Repeated = true |
| typeModel.addProperty(property) |
| typeModel.IsStringArray = true |
| } |
| } |
| |
| func (domain *Domain) buildAnyOfAccessors(typeModel *TypeModel, schema *jsonschema.Schema) { |
| anyOfs := schema.AnyOf |
| if anyOfs == nil { |
| return |
| } |
| if len(*anyOfs) == 2 { |
| if schemaIsContainedInArray((*anyOfs)[0], (*anyOfs)[1]) { |
| //log.Printf("ARRAY OF %+v", (*anyOfs)[0].String()) |
| schema := (*anyOfs)[0] |
| domain.addAnonymousAccessorForSchema(typeModel, schema, true) |
| } else if schemaIsContainedInArray((*anyOfs)[1], (*anyOfs)[0]) { |
| //log.Printf("ARRAY OF %+v", (*anyOfs)[1].String()) |
| schema := (*anyOfs)[1] |
| domain.addAnonymousAccessorForSchema(typeModel, schema, true) |
| } else { |
| for _, anyOf := range *anyOfs { |
| ref := anyOf.Ref |
| if ref != nil { |
| typeName := domain.typeNameForReference(*ref) |
| propertyName := domain.propertyNameForReference(*ref) |
| if propertyName != nil { |
| property := NewTypePropertyWithNameAndType(*propertyName, typeName) |
| typeModel.addProperty(property) |
| } |
| } else { |
| typeName := "bool" |
| propertyName := "boolean" |
| property := NewTypePropertyWithNameAndType(propertyName, typeName) |
| typeModel.addProperty(property) |
| } |
| } |
| } |
| } else { |
| log.Printf("Unhandled anyOfs:\n%s", schema.String()) |
| } |
| } |
| |
| func (domain *Domain) buildDefaultAccessors(typeModel *TypeModel, schema *jsonschema.Schema) { |
| typeModel.Open = true |
| propertyName := "additionalProperties" |
| typeName := "NamedAny" |
| property := NewTypePropertyWithNameAndType(propertyName, typeName) |
| property.MapType = "Any" |
| property.Repeated = true |
| domain.MapTypeRequests[property.MapType] = property.MapType |
| typeModel.addProperty(property) |
| } |
| |
| // BuildTypeForDefinition creates a type representation for a schema definition. |
| func (domain *Domain) BuildTypeForDefinition( |
| typeName string, |
| propertyName string, |
| schema *jsonschema.Schema) *TypeModel { |
| if (schema.Type == nil) || (*schema.Type.String == "object") { |
| return domain.buildTypeForDefinitionObject(typeName, propertyName, schema) |
| } |
| return nil |
| } |
| |
| func (domain *Domain) buildTypeForDefinitionObject( |
| typeName string, |
| propertyName string, |
| schema *jsonschema.Schema) *TypeModel { |
| typeModel := NewTypeModel() |
| typeModel.Name = typeName |
| if schema.IsEmpty() { |
| domain.buildDefaultAccessors(typeModel, schema) |
| } else { |
| if schema.Description != nil { |
| typeModel.Description = *schema.Description |
| } |
| domain.buildTypeProperties(typeModel, schema) |
| domain.buildTypeRequirements(typeModel, schema) |
| domain.buildPatternPropertyAccessors(typeModel, schema) |
| domain.buildAdditionalPropertyAccessors(typeModel, schema) |
| domain.buildOneOfAccessors(typeModel, schema) |
| domain.buildAnyOfAccessors(typeModel, schema) |
| } |
| return typeModel |
| } |
| |
| // Build builds a domain model. |
| func (domain *Domain) Build() (err error) { |
| if (domain.Schema == nil) || (domain.Schema.Definitions == nil) { |
| return errors.New("missing definitions section") |
| } |
| // create a type for the top-level schema |
| typeName := domain.Prefix + "Document" |
| typeModel := NewTypeModel() |
| typeModel.Name = typeName |
| domain.buildTypeProperties(typeModel, domain.Schema) |
| domain.buildTypeRequirements(typeModel, domain.Schema) |
| domain.buildPatternPropertyAccessors(typeModel, domain.Schema) |
| domain.buildAdditionalPropertyAccessors(typeModel, domain.Schema) |
| domain.buildOneOfAccessors(typeModel, domain.Schema) |
| domain.buildAnyOfAccessors(typeModel, domain.Schema) |
| if len(typeModel.Properties) > 0 { |
| domain.TypeModels[typeName] = typeModel |
| } |
| |
| // create a type for each object defined in the schema |
| if domain.Schema.Definitions != nil { |
| for _, pair := range *(domain.Schema.Definitions) { |
| definitionName := pair.Name |
| definitionSchema := pair.Value |
| typeName := domain.TypeNameForStub(definitionName) |
| typeModel := domain.BuildTypeForDefinition(typeName, definitionName, definitionSchema) |
| if typeModel != nil { |
| domain.TypeModels[typeName] = typeModel |
| } |
| } |
| } |
| // iterate over anonymous object types to be instantiated and generate a type for each |
| for typeName, typeRequest := range domain.ObjectTypeRequests { |
| domain.TypeModels[typeRequest.Name] = |
| domain.buildTypeForDefinitionObject(typeName, typeRequest.PropertyName, typeRequest.Schema) |
| } |
| |
| // iterate over map item types to be instantiated and generate a type for each |
| mapTypeNames := make([]string, 0) |
| for mapTypeName := range domain.MapTypeRequests { |
| mapTypeNames = append(mapTypeNames, mapTypeName) |
| } |
| sort.Strings(mapTypeNames) |
| |
| for _, mapTypeName := range mapTypeNames { |
| typeName := "Named" + strings.Title(mapTypeName) |
| typeModel := NewTypeModel() |
| typeModel.Name = typeName |
| typeModel.Description = fmt.Sprintf( |
| "Automatically-generated message used to represent maps of %s as ordered (name,value) pairs.", |
| mapTypeName) |
| typeModel.IsPair = true |
| typeModel.PairValueType = mapTypeName |
| |
| nameProperty := NewTypeProperty() |
| nameProperty.Name = "name" |
| nameProperty.Type = "string" |
| nameProperty.Description = "Map key" |
| typeModel.addProperty(nameProperty) |
| |
| valueProperty := NewTypeProperty() |
| valueProperty.Name = "value" |
| valueProperty.Type = mapTypeName |
| valueProperty.Description = "Mapped value" |
| typeModel.addProperty(valueProperty) |
| |
| domain.TypeModels[typeName] = typeModel |
| } |
| |
| // add a type for string arrays |
| stringArrayType := NewTypeModel() |
| stringArrayType.Name = "StringArray" |
| stringProperty := NewTypeProperty() |
| stringProperty.Name = "value" |
| stringProperty.Type = "string" |
| stringProperty.Repeated = true |
| stringArrayType.addProperty(stringProperty) |
| domain.TypeModels[stringArrayType.Name] = stringArrayType |
| |
| // add a type for "Any" |
| anyType := NewTypeModel() |
| anyType.Name = "Any" |
| anyType.Open = true |
| anyType.IsBlob = true |
| valueProperty := NewTypeProperty() |
| valueProperty.Name = "value" |
| valueProperty.Type = "google.protobuf.Any" |
| anyType.addProperty(valueProperty) |
| yamlProperty := NewTypeProperty() |
| yamlProperty.Name = "yaml" |
| yamlProperty.Type = "string" |
| anyType.addProperty(yamlProperty) |
| domain.TypeModels[anyType.Name] = anyType |
| return err |
| } |
| |
| func (domain *Domain) sortedTypeNames() []string { |
| typeNames := make([]string, 0) |
| for typeName := range domain.TypeModels { |
| typeNames = append(typeNames, typeName) |
| } |
| sort.Strings(typeNames) |
| return typeNames |
| } |
| |
| // Description returns a string representation of a domain. |
| func (domain *Domain) Description() string { |
| typeNames := domain.sortedTypeNames() |
| result := "" |
| for _, typeName := range typeNames { |
| result += domain.TypeModels[typeName].description() |
| } |
| return result |
| } |