private IVariable ParseNumberVariable(IO.JsonReader jr)
        {
            // init
            IVariable result = null;

            // type?
            switch (jr.NodeType)
            {
            case IO.JsonNodeType.Integer:
                result = new NumberVariable((uint)(int)jr.Value);
                break;

            case IO.JsonNodeType.String:
                result = NumberVariable.TryParse((string)jr.Value);
                if (result == null)
                {
                    result = new TextVariable((string)jr.Value);
                }
                break;

            default:
                throw new DataFormatException(jr, "Integer or string value expected.");
            }

            // read
            jr.Read();

            // done
            return(result);
        }
        private IVariable ParseDateVariable(IO.JsonReader jr)
        {
            // init
            IVariable result = null;

            // node type
            switch (jr.NodeType)
            {
            case IO.JsonNodeType.String:
                result = new TextVariable((string)jr.Value);
                jr.Read();
                break;

            case IO.JsonNodeType.StartOfObject:
                // init
                int?[][]     dateParts = new int?[][] { };
                bool?        circa     = null;
                DateVariable?raw       = null;
                string       literal   = null;
                Season?      season    = null;

                // skip
                jr.Read();

                // read properties
                while (jr.NodeType == IO.JsonNodeType.PropertyName)
                {
                    // init
                    var propertyName = (string)jr.Value;
                    jr.Read();

                    // property
                    switch (propertyName.ToLower())
                    {
                    case "date-parts":
                        dateParts = this.ParseDateVariableDateParts(jr);
                        break;

                    case "raw":
                        raw = ParseRawDate(jr);
                        break;

                    case "literal":
                        literal = jr.ReadAsString();
                        break;

                    case "circa":
                        circa = jr.ReadAsBoolean();
                        break;

                    case "season":
                        season = this.ParseSeason(jr);
                        break;

                    default:
                        throw new DataFormatException(jr, "Unexpected property '{0}'.", propertyName);
                    }
                }

                // valid date?
                if (raw.HasValue)
                {
                    // raw
                    if (dateParts.Length > 0 || literal != null)
                    {
                        throw new DataFormatException(jr, "Date parts and literal properties are not allowed for raw date variables.");
                    }

                    // done
                    result = new DateVariable(raw.Value.YearFrom, raw.Value.SeasonFrom, raw.Value.MonthFrom, raw.Value.DayFrom, raw.Value.YearTo, raw.Value.SeasonTo, raw.Value.MonthTo, raw.Value.DayTo, circa ?? false);
                }
                else if (literal != null)
                {
                    // literal
                    if (dateParts.Length > 0 || raw != null || circa != null)
                    {
                        throw new DataFormatException(jr, "Date parts, raw and circa properties are not allowed for literal date variables.");
                    }

                    // done
                    result = new TextVariable(literal);
                }
                else
                {
                    // dateparts
                    switch (dateParts.Length)
                    {
                    case 0:
                        result = null;
                        break;

                    case 1:
                        result = new DateVariable(dateParts[0][0].Value, season, dateParts[0][1], dateParts[0][2], circa ?? false);
                        break;

                    case 2:
                        result = new DateVariable(dateParts[0][0].Value, season, dateParts[0][1], dateParts[0][2], dateParts[1][0].Value, season, dateParts[1][1], dateParts[1][2], circa ?? false);
                        break;

                    default:
                        throw new DataFormatException(jr, "Invalid date variable.");
                    }
                }

                // end object
                jr.Read(IO.JsonNodeType.EndOfObject);

                break;

            default:
                throw new DataFormatException(jr, "Json token '{0}' cannot be parsed into a text variable.", jr.NodeType);
            }

            // done
            return(result);
        }