using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json.Linq;
namespace TestLibrary
{
/// <summary>
/// JSON 타입
/// </summary>
public class JSONType
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 제너레이터
/// </summary>
private IJSONClassGenerator generator;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 값 타입 - ValueType
/// <summary>
/// 값 타입
/// </summary>
public JSONValueType ValueType { get; private set; }
#endregion
#region 내부 타입 - InternalType
/// <summary>
/// 내부 타입
/// </summary>
public JSONType InternalType { get; private set; }
#endregion
#region 할당명 - AssignedName
/// <summary>
/// 할당명
/// </summary>
public string AssignedName { get; private set; }
#endregion
#region 캐시 필수 여부 - MustCache
/// <summary>
/// 캐시 필수 여부
/// </summary>
public bool MustCache
{
get
{
switch(ValueType)
{
case JSONValueType.Array : return true;
case JSONValueType.Object : return true;
case JSONValueType.Anything : return true;
case JSONValueType.Dictionary : return true;
case JSONValueType.NonConstrained : return true;
default : return false;
}
}
}
#endregion
#region 필드 정보 리스트 - FieldInfoList
/// <summary>
/// 필드 정보 리스트
/// </summary>
public IList<FieldInfo> FieldInfoList { get; internal set; }
#endregion
#region 루트 여부 - IsRoot
/// <summary>
/// 루트 여부
/// </summary>
public bool IsRoot { get; internal set; }
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - JSONType(generator, token)
/// <summary>
/// 생성자
/// </summary>
/// <param name="generator">제너레이터</param>
/// <param name="token">토큰</param>
public JSONType(IJSONClassGenerator generator, JToken token) : this(generator)
{
ValueType = GetFirstValueType(token);
if(ValueType == JSONValueType.Array)
{
JArray array = (JArray)token;
InternalType = GetCommonType(generator, array.ToArray());
}
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Internal
#region 생성자 - JSONType(generator, type)
/// <summary>
/// 생성자
/// </summary>
/// <param name="generator">제너레이터</param>
/// <param name="type">타입</param>
internal JSONType(IJSONClassGenerator generator, JSONValueType type) : this(generator)
{
ValueType = type;
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 생성자 - JSONType(generator)
/// <summary>
/// 생성자
/// </summary>
/// <param name="generator">JSON 클래스 제너레이터 인터페이스</param>
private JSONType(IJSONClassGenerator generator)
{
this.generator = generator;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 공통 타입 구하기 - GetCommonType(generator, tokenArray)
/// <summary>
/// 공통 타입 구하기
/// </summary>
/// <param name="generator">제너레이터</param>
/// <param name="tokenArray">토큰 배열</param>
/// <returns>공통 타입 </returns>
public static JSONType GetCommonType(IJSONClassGenerator generator, JToken[] tokenArray)
{
if(tokenArray.Length == 0)
{
return new JSONType(generator, JSONValueType.NonConstrained);
}
JSONType commonType = new JSONType(generator, tokenArray[0]).MaybeMakeNullable(generator);
for(int i = 1; i < tokenArray.Length; i++)
{
JSONType currentType = new JSONType(generator, tokenArray[i]);
commonType = commonType.GetCommonType(currentType);
}
return commonType;
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Internal
#region NULL 구하기 - GetNull(generator)
/// <summary>
/// NULL 구하기
/// </summary>
/// <param name="generator">제너레이터</param>
/// <returns>NULL</returns>
internal static JSONType GetNull(IJSONClassGenerator generator)
{
return new JSONType(generator, JSONValueType.NullableSomething);
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Private
#region 첫번째 값 타입 구하기 - GetFirstValueType(token)
/// <summary>
/// 첫번째 값 타입 구하기
/// </summary>
/// <param name="token">토큰</param>
/// <returns>첫번째 값 타입</returns>
private static JSONValueType GetFirstValueType(JToken token)
{
JTokenType type = token.Type;
if(type == JTokenType.Integer)
{
if((long)((JValue)token).Value < int.MaxValue)
{
return JSONValueType.Integer;
}
else
{
return JSONValueType.Long;
}
}
switch(type)
{
case JTokenType.Array : return JSONValueType.Array;
case JTokenType.Boolean : return JSONValueType.Boolean;
case JTokenType.Float : return JSONValueType.Float;
case JTokenType.Null : return JSONValueType.NullableSomething;
case JTokenType.Undefined : return JSONValueType.NullableSomething;
case JTokenType.String : return JSONValueType.String;
case JTokenType.Object : return JSONValueType.Object;
case JTokenType.Date : return JSONValueType.Date;
default : return JSONValueType.Anything;
}
}
#endregion
#region NULL 여부 구하기 - IsNull(valueType)
/// <summary>
/// NULL 여부 구하기
/// </summary>
/// <param name="valueType">값 타입</param>
/// <returns>NULL 여부</returns>
private static bool IsNull(JSONValueType valueType)
{
return valueType == JSONValueType.NullableSomething;
}
#endregion
#region NULL 여부 구하기 - IsNull(tokenType)
/// <summary>
/// NULL 여부 구하기
/// </summary>
/// <param name="tokenType">토큰 타입</param>
/// <returns>NULL 여부</returns>
private static bool IsNull(JTokenType tokenType)
{
return tokenType == JTokenType.Null || tokenType == JTokenType.Undefined;
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Public
#region 공통 타입 구하기 - GetCommonType(type)
/// <summary>
/// 공통 타입 구하기
/// </summary>
/// <param name="type">타입</param>
/// <returns>공통 타입</returns>
public JSONType GetCommonType(JSONType type)
{
JSONValueType commonValueType = GetCommonValueType(ValueType, type.ValueType);
if(commonValueType == JSONValueType.Array)
{
if(type.ValueType == JSONValueType.NullableSomething)
{
return this;
}
if(ValueType == JSONValueType.NullableSomething)
{
return type;
}
JSONType commonInternalType = InternalType.GetCommonType(type.InternalType).MaybeMakeNullable(generator);
if(commonInternalType != InternalType)
{
return new JSONType(generator, JSONValueType.Array)
{
InternalType = commonInternalType
};
}
}
if(ValueType == commonValueType)
{
return this;
}
return new JSONType(generator, commonValueType).MaybeMakeNullable(generator);
}
#endregion
#region 명칭 할당하기 - AssignName(name)
/// <summary>
/// 명칭 할당하기
/// </summary>
/// <param name="name">명칭</param>
public void AssignName(string name)
{
AssignedName = name;
}
#endregion
#region 렌더링 명칭 구하기 - GetReaderName()
/// <summary>
/// 렌더링 명칭 구하기
/// </summary>
/// <returns>렌더링 명칭</returns>
public string GetReaderName()
{
if(ValueType == JSONValueType.Anything || ValueType == JSONValueType.NullableSomething || ValueType == JSONValueType.NonConstrained)
{
return "ReadObject";
}
if(ValueType == JSONValueType.Object)
{
return string.Format("ReadStronglyTypedObject<{0}>", AssignedName);
}
else if(ValueType == JSONValueType.Array)
{
return string.Format("ReadArray<{0}>", InternalType.GetTypeName());
}
else
{
return string.Format("Read{0}", Enum.GetName(typeof(JSONValueType), ValueType));
}
}
#endregion
#region 내면 유형 구하기 - GetInnermostType()
/// <summary>
/// 내면 유형 구하기
/// </summary>
/// <returns>내면 유형</returns>
public JSONType GetInnermostType()
{
if(ValueType != JSONValueType.Array)
{
throw new InvalidOperationException();
}
if(InternalType.ValueType != JSONValueType.Array)
{
return InternalType;
}
return InternalType.GetInnermostType();
}
#endregion
#region 타입명 구하기 - GetTypeName()
/// <summary>
/// 타입명 구하기
/// </summary>
/// <returns>타입명</returns>
public string GetTypeName()
{
return this.generator.CodeWriter.GetTypeName(this, this.generator);
}
#endregion
#region JSON 토큰 타입 구하기 - GetJTokenType()
/// <summary>
/// JSON 토큰 타입 구하기
/// </summary>
/// <returns>JSON 토큰 타입</returns>
public string GetJTokenType()
{
switch(ValueType)
{
case JSONValueType.Boolean :
case JSONValueType.Integer :
case JSONValueType.Long :
case JSONValueType.Float :
case JSONValueType.Date :
case JSONValueType.NullableBoolean :
case JSONValueType.NullableInteger :
case JSONValueType.NullableLong :
case JSONValueType.NullableFloat :
case JSONValueType.NullableDate :
case JSONValueType.String : return "JValue";
case JSONValueType.Array : return "JArray";
case JSONValueType.Dictionary : return "JObject";
case JSONValueType.Object : return "JObject";
default : return "JToken";
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Internal
#region 혹시나 NULL 가능 만들기 - MaybeMakeNullable(generator)
/// <summary>
/// 혹시나 NULL 가능 만들기
/// </summary>
/// <param name="generator">제너레이터</param>
/// <returns>JSON 타입</returns>
internal JSONType MaybeMakeNullable(IJSONClassGenerator generator)
{
if(!generator.AlwaysUseNullableValues)
{
return this;
}
return GetCommonType(JSONType.GetNull(generator));
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Private
#region 공통 값 타입 구하기 - GetCommonValueType(valueType1, valueType2)
/// <summary>
/// 공통 값 타입 구하기
/// </summary>
/// <param name="valueType1">값 타입 1</param>
/// <param name="valueType2">값 타입 2</param>
/// <returns>공통 값 타입</returns>
private JSONValueType GetCommonValueType(JSONValueType valueType1, JSONValueType valueType2)
{
if(valueType1 == JSONValueType.NonConstrained)
{
return valueType2;
}
if(valueType2 == JSONValueType.NonConstrained)
{
return valueType1;
}
switch(valueType1)
{
case JSONValueType.Boolean :
if(IsNull(valueType2))
{
return JSONValueType.NullableBoolean;
}
if(valueType2 == JSONValueType.Boolean)
{
return valueType1;
}
break;
case JSONValueType.NullableBoolean :
if(IsNull(valueType2))
{
return valueType1;
}
if(valueType2 == JSONValueType.Boolean)
{
return valueType1;
}
break;
case JSONValueType.Integer :
if(IsNull(valueType2))
{
return JSONValueType.NullableInteger;
}
if(valueType2 == JSONValueType.Float)
{
return JSONValueType.Float;
}
if(valueType2 == JSONValueType.Long)
{
return JSONValueType.Long;
}
if(valueType2 == JSONValueType.Integer)
{
return valueType1;
}
break;
case JSONValueType.NullableInteger :
if(IsNull(valueType2))
{
return valueType1;
}
if(valueType2 == JSONValueType.Float)
{
return JSONValueType.NullableFloat;
}
if(valueType2 == JSONValueType.Long)
{
return JSONValueType.NullableLong;
}
if(valueType2 == JSONValueType.Integer)
{
return valueType1;
}
break;
case JSONValueType.Float :
if(IsNull(valueType2))
{
return JSONValueType.NullableFloat;
}
if(valueType2 == JSONValueType.Float)
{
return valueType1;
}
if(valueType2 == JSONValueType.Integer)
{
return valueType1;
}
if(valueType2 == JSONValueType.Long)
{
return valueType1;
}
break;
case JSONValueType.NullableFloat :
if(IsNull(valueType2))
{
return valueType1;
}
if(valueType2 == JSONValueType.Float)
{
return valueType1;
}
if(valueType2 == JSONValueType.Integer)
{
return valueType1;
}
if(valueType2 == JSONValueType.Long)
{
return valueType1;
}
break;
case JSONValueType.Long :
if(IsNull(valueType2))
{
return JSONValueType.NullableLong;
}
if(valueType2 == JSONValueType.Float)
{
return JSONValueType.Float;
}
if(valueType2 == JSONValueType.Integer)
{
return valueType1;
}
break;
case JSONValueType.NullableLong :
if(IsNull(valueType2))
{
return valueType1;
}
if(valueType2 == JSONValueType.Float)
{
return JSONValueType.NullableFloat;
}
if(valueType2 == JSONValueType.Integer)
{
return valueType1;
}
if(valueType2 == JSONValueType.Long)
{
return valueType1;
}
break;
case JSONValueType.Date :
if(IsNull(valueType2))
{
return JSONValueType.NullableDate;
}
if(valueType2 == JSONValueType.Date)
{
return JSONValueType.Date;
}
break;
case JSONValueType.NullableDate :
if(IsNull(valueType2))
{
return valueType1;
}
if(valueType2 == JSONValueType.Date)
{
return valueType1;
}
break;
case JSONValueType.NullableSomething :
if(IsNull(valueType2))
{
return valueType1;
}
if(valueType2 == JSONValueType.String)
{
return JSONValueType.String;
}
if(valueType2 == JSONValueType.Integer)
{
return JSONValueType.NullableInteger;
}
if(valueType2 == JSONValueType.Float)
{
return JSONValueType.NullableFloat;
}
if(valueType2 == JSONValueType.Long)
{
return JSONValueType.NullableLong;
}
if(valueType2 == JSONValueType.Boolean)
{
return JSONValueType.NullableBoolean;
}
if(valueType2 == JSONValueType.Date)
{
return JSONValueType.NullableDate;
}
if(valueType2 == JSONValueType.Array)
{
return JSONValueType.Array;
}
if(valueType2 == JSONValueType.Object)
{
return JSONValueType.Object;
}
break;
case JSONValueType.Object :
if(IsNull(valueType2))
{
return valueType1;
}
if(valueType2 == JSONValueType.Object)
{
return valueType1;
}
if(valueType2 == JSONValueType.Dictionary)
{
throw new ArgumentException();
}
break;
case JSONValueType.Dictionary :
throw new ArgumentException();
case JSONValueType.Array :
if(IsNull(valueType2))
{
return valueType1;
}
if(valueType2 == JSONValueType.Array)
{
return valueType1;
}
break;
case JSONValueType.String :
if(IsNull(valueType2))
{
return valueType1;
}
if(valueType2 == JSONValueType.String)
{
return valueType1;
}
break;
}
return JSONValueType.Anything;
}
#endregion
}
}