private JsonSchema GenerateInternal()

in src/Elastic.Apm/Libraries/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs [203:330]


		private JsonSchema GenerateInternal(Type type, Required valueRequired, bool required)
		{
			ValidationUtils.ArgumentNotNull(type, nameof(type));

			var resolvedId = GetTypeId(type, false);
			var explicitId = GetTypeId(type, true);

			if (!StringUtils.IsNullOrEmpty(resolvedId))
			{
				var resolvedSchema = _resolver.GetSchema(resolvedId);
				if (resolvedSchema != null)
				{
					// resolved schema is not null but referencing member allows nulls
					// change resolved schema to allow nulls. hacky but what are ya gonna do?
					if (valueRequired != Required.Always && !HasFlag(resolvedSchema.Type, JsonSchemaType.Null))
						resolvedSchema.Type |= JsonSchemaType.Null;
					if (required && resolvedSchema.Required != true) resolvedSchema.Required = true;

					return resolvedSchema;
				}
			}

			// test for unresolved circular reference
			if (_stack.Any(tc => tc.Type == type))
				throw new JsonException(
					"Unresolved circular reference for type '{0}'. Explicitly define an Id for the type using a JsonObject/JsonArray attribute or automatically generate a type Id using the UndefinedSchemaIdHandling property."
						.FormatWith(CultureInfo.InvariantCulture, type));

			var contract = ContractResolver.ResolveContract(type);
			var converter = contract.Converter ?? contract.InternalConverter;

			Push(new TypeSchema(type, new JsonSchema()));

			if (explicitId != null) CurrentSchema.Id = explicitId;

			if (required) CurrentSchema.Required = true;
			CurrentSchema.Title = GetTitle(type);
			CurrentSchema.Description = GetDescription(type);

			if (converter != null)
			{
				// todo: Add GetSchema to JsonConverter and use here?
				CurrentSchema.Type = JsonSchemaType.Any;
			}
			else
			{
				switch (contract.ContractType)
				{
					case JsonContractType.Object:
						CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired);
						CurrentSchema.Id = GetTypeId(type, false);
						GenerateObjectSchema(type, (JsonObjectContract)contract);
						break;
					case JsonContractType.Array:
						CurrentSchema.Type = AddNullType(JsonSchemaType.Array, valueRequired);

						CurrentSchema.Id = GetTypeId(type, false);

						var arrayAttribute = JsonTypeReflector.GetCachedAttribute<JsonArrayAttribute>(type);
						var allowNullItem = arrayAttribute == null || arrayAttribute.AllowNullItems;

						var collectionItemType = ReflectionUtils.GetCollectionItemType(type);
						if (collectionItemType != null)
						{
							CurrentSchema.Items = new List<JsonSchema>();
							CurrentSchema.Items.Add(GenerateInternal(collectionItemType, !allowNullItem ? Required.Always : Required.Default, false));
						}
						break;
					case JsonContractType.Primitive:
						CurrentSchema.Type = GetJsonSchemaType(type, valueRequired);

						if (CurrentSchema.Type == JsonSchemaType.Integer && type.IsEnum() && !type.IsDefined(typeof(FlagsAttribute), true))
						{
							CurrentSchema.Enum = new List<JToken>();

							var enumValues = EnumUtils.GetEnumValuesAndNames(type);
							for (var i = 0; i < enumValues.Names.Length; i++)
							{
								var v = enumValues.Values[i];
								var value = JToken.FromObject(Enum.ToObject(type, v));

								CurrentSchema.Enum.Add(value);
							}
						}
						break;
					case JsonContractType.String:
						var schemaType = !ReflectionUtils.IsNullable(contract.UnderlyingType)
							? JsonSchemaType.String
							: AddNullType(JsonSchemaType.String, valueRequired);

						CurrentSchema.Type = schemaType;
						break;
					case JsonContractType.Dictionary:
						CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired);

						Type keyType;
						Type valueType;
						ReflectionUtils.GetDictionaryKeyValueTypes(type, out keyType, out valueType);

						if (keyType != null)
						{
							var keyContract = ContractResolver.ResolveContract(keyType);

							// can be converted to a string
							if (keyContract.ContractType == JsonContractType.Primitive)
								CurrentSchema.AdditionalProperties = GenerateInternal(valueType, Required.Default, false);
						}
						break;
#if HAVE_BINARY_SERIALIZATION
                    case JsonContractType.Serializable:
                        CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired);
                        CurrentSchema.Id = GetTypeId(type, false);
                        GenerateISerializableContract(type, (JsonISerializableContract)contract);
                        break;
#endif
#if HAVE_DYNAMIC
                    case JsonContractType.Dynamic:
#endif
					case JsonContractType.Linq:
						CurrentSchema.Type = JsonSchemaType.Any;
						break;
					default:
						throw new JsonException("Unexpected contract type: {0}".FormatWith(CultureInfo.InvariantCulture, contract));
				}
			}

			return Pop().Schema;
		}