/// <summary>
        ///     Validates the specified constructor signature string.
        ///     It expects a constructor signature string which starts at the first parenthesis after the constructor's name
        ///     identifier.
        /// </summary>
        /// <param name="input">The constructor signature string.</param>
        /// <param name="validateTypeCallback">
        ///     The validate type callback, used for additional custom validation of parameter types
        ///     in the constructor signature.
        /// </param>
        /// <returns>A parse/validation results object.  <see cref="CSharpConstructorSignatureValidationResult" /></returns>
        /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="input" /> is <c>null</c>.</exception>
        public static CSharpConstructorSignatureValidationResult Validate(string input,
                                                                          CSharpParsedTypeValidateTypeCallback validateTypeCallback = null)
        {
            if (input == null)
            {
                throw new ArgumentNullException("input", "Constructor signature string cannot be null!");
            }


            var result = new CSharpConstructorSignatureValidationResult
            {
                Success             = true,
                ParameterForwarding = CSharpConstructorParameterForwarding.None
            };


            if (string.IsNullOrWhiteSpace(input))
            {
                result.Success          = false;
                result.ErrorDescription = "Constructor signature cannot be whitespace.";
                return(result);
            }

            States state = States.Start;
            string accum = "";

            var parameterTypes      = new GenericArray <CSharpClassNameValidationResult>();
            var parameterNames      = new HashSet <string>();
            var forwardedParameters = new GenericArray <string>();

            int unclosedGenericsBrackets = 0;

            //weeeeeeeeeeeeeeee!
            for (int index = 0; index < input.Length; index++)
            {
                var c = input[index];
                accum += c;

                if (c == '(')
                {
                    switch (state)
                    {
                    case States.Start:
                        state = States.WaitingForParamType;
                        accum = "";
                        continue;

                    case States.AccumulatingForwardingKeyword:
                        switch (accum)
                        {
                        case "base(":
                            result.ParameterForwarding = CSharpConstructorParameterForwarding.Base;
                            break;

                        case "this(":
                            result.ParameterForwarding = CSharpConstructorParameterForwarding.This;
                            break;

                        default:
                            result.Success          = false;
                            result.ErrorDescription = "Constructor forwarding keyword must be 'base' or 'this'.";
                            result.ErrorIndex       = index - 5;
                            return(result);
                        }
                        accum = "";
                        state = States.WaitingForForwardedParam;
                        continue;

                    case States.AfterForwardingKeyword:
                        state = States.WaitingForForwardedParam;
                        accum = "";
                        continue;

                    default:
                        result.Success          = false;
                        result.ErrorDescription = "Unexpected opening parenthesis.";
                        result.ErrorIndex       = index;
                        return(result);
                    }
                }

                if (c == ':')
                {
                    if (state == States.EndOfBasicSignature)
                    {
                        accum = "";
                        state = States.WaitingForForwardingKeyword;
                        continue;
                    }

                    result.Success          = false;
                    result.ErrorDescription = "CSharpIDValidator.IsValidIdentifier:' character.";
                    result.ErrorIndex       = index;
                    return(result);
                }


                if (c == '<')
                {
                    if (state == States.AccumulatingParamType)
                    {
                        unclosedGenericsBrackets++;
                        state = States.AccumulatingGenericType;
                        continue;
                    }
                    if (state == States.AccumulatingGenericType)
                    {
                        unclosedGenericsBrackets++;
                        continue;
                    }
                    result.Success          = false;
                    result.ErrorDescription = "CSharpIDValidator.IsValidIdentifier<' character.";
                    result.ErrorIndex       = index;
                    return(result);
                }

                if (c == '>')
                {
                    if (state == States.AccumulatingGenericType)
                    {
                        unclosedGenericsBrackets--;
                        if (unclosedGenericsBrackets == 0)
                        {
                            state = States.AccumulatingParamType;
                        }
                        continue;
                    }
                    result.Success          = false;
                    result.ErrorDescription = "CSharpIDValidator.IsValidIdentifier>' character.";
                    result.ErrorIndex       = index;
                    return(result);
                }

                if (c == ',' || c == ')')
                {
                    switch (state)
                    {
                    case States.WaitingForParamType:
                        if (parameterTypes.Count > 0 || (parameterTypes.Count == 0 && c == ','))
                        {
                            result.Success          = false;
                            result.ErrorDescription = "Missing parameter declaration.";
                            result.ErrorIndex       = index - 1;
                            return(result);
                        }

                        accum = "";
                        state = States.EndOfBasicSignature;
                        continue;

                    case States.WaitingForForwardedParam:
                        if (forwardedParameters.Count > 0 || (forwardedParameters.Count == 0 && c == ','))
                        {
                            result.Success          = false;
                            result.ErrorDescription = "Missing forwarded argument declaration.";
                            result.ErrorIndex       = index;
                            return(result);
                        }

                        accum = "";
                        state = States.EndOfForwardingSignature;
                        continue;

                    case States.AccumulatingParamType:
                        result.Success          = false;
                        result.ErrorDescription = "Missing parameter name.";
                        result.ErrorIndex       = index;
                        return(result);

                    case States.AccumulatingParamName:
                    {
                        var pname = accum.TrimEnd(',', ')').Trim();
                        if (!CSharpIDValidator.IsValidIdentifier(pname))
                        {
                            result.Success          = false;
                            result.ErrorDescription = string.Format("Invalid parameter name '{0}'.", pname);
                            result.ErrorIndex       = index - accum.Length;
                            return(result);
                        }
                        if (!parameterNames.Contains(pname))
                        {
                            parameterNames.Add(pname);
                        }
                        else
                        {
                            result.Success          = false;
                            result.ErrorDescription = string.Format("Parameter name '{0}' used more than once.",
                                                                    pname);
                            result.ErrorIndex = index - accum.Length;
                            return(result);
                        }
                        accum = "";
                        state = c == ',' ? States.WaitingForParamType : States.EndOfBasicSignature;
                        continue;
                    }

                    case States.AccumulatingForwardedParam:
                    {
                        var pname = accum.TrimEnd(',', ')').Trim();

                        if (!CSharpIDValidator.IsValidIdentifier(pname))
                        {
                            result.Success          = false;
                            result.ErrorDescription =
                                string.Format(
                                    "Invalid forwarded parameter '{0}', invalid identifier syntax.", pname);
                            result.ErrorIndex = index - accum.Length;
                            return(result);
                        }

                        if (!parameterNames.Contains(pname))
                        {
                            result.Success          = false;
                            result.ErrorDescription =
                                string.Format(
                                    "Invalid forwarded parameter '{0}', name was not previously declared.", pname);

                            result.ErrorIndex = index - accum.Length;
                            return(result);
                        }

                        forwardedParameters.Add(pname);

                        accum = "";
                        state = c == ',' ? States.WaitingForForwardedParam : States.EndOfForwardingSignature;
                        continue;
                    }

                    default:
                        if (c == ',' && state == States.AccumulatingGenericType)
                        {
                            continue;
                        }

                        result.Success          = false;
                        result.ErrorDescription = string.Format(
                            "CSharpIDValidator.IsValidIdentifier{0}' character.", c);
                        result.ErrorIndex = index;
                        return(result);
                    }
                }

                if (!char.IsWhiteSpace(c))
                {
                    switch (state)
                    {
                    case States.EndOfBasicSignature:
                    case States.EndOfForwardingSignature:
                        result.Success          = false;
                        result.ErrorDescription = string.Format("Unexpected character '{0}' after signature.", c);
                        result.ErrorIndex       = index;
                        return(result);

                    case States.Start:
                        result.Success          = false;
                        result.ErrorDescription = string.Format("Unexpected character '{0}', was expecting '('.", c);
                        result.ErrorIndex       = index;
                        return(result);

                    case States.WaitingForParamType:
                        state = States.AccumulatingParamType;
                        accum = "" + c;
                        continue;

                    case States.WaitingForParameterName:
                        state = States.AccumulatingParamName;
                        accum = "" + c;
                        continue;

                    case States.WaitingForForwardingKeyword:
                        state = States.AccumulatingForwardingKeyword;
                        accum = "" + c;
                        continue;

                    case States.WaitingForForwardedParam:
                        state = States.AccumulatingForwardedParam;
                        accum = "" + c;
                        continue;
                    }
                }

                if (char.IsWhiteSpace(c))
                {
                    switch (state)
                    {
                    case States.WaitingForParamType:
                    case States.WaitingForParameterName:
                    case States.EndOfBasicSignature:
                    case States.WaitingForForwardingKeyword:
                    case States.AfterForwardingKeyword:
                    case States.WaitingForForwardedParam:
                        accum = "";
                        continue;

                    case States.AccumulatingForwardingKeyword:
                        switch (accum)
                        {
                        case "base ":
                            result.ParameterForwarding = CSharpConstructorParameterForwarding.Base;
                            break;

                        case "this ":
                            result.ParameterForwarding = CSharpConstructorParameterForwarding.This;
                            break;

                        default:
                            result.Success          = false;
                            result.ErrorDescription = "Constructor forwarding keyword must be 'base' or 'this'.";
                            result.ErrorIndex       = index - 5;
                            return(result);
                        }
                        accum = "";
                        state = States.AfterForwardingKeyword;
                        continue;

                    case States.AccumulatingParamType:
                        var ptype             = accum.TrimEnd();
                        var validateParameter = CSharpClassNameValidator.ValidateInitialization(accum.TrimEnd(),
                                                                                                true, validateTypeCallback);
                        if (!validateParameter.Success)
                        {
                            result.Success          = false;
                            result.ErrorDescription = string.Format("Invalid parameter type '{0}': {1}", ptype,
                                                                    validateParameter.ErrorDescription);
                            result.ErrorIndex = (index - accum.Length) + validateParameter.ErrorIndex + 1;
                            return(result);
                        }

                        parameterTypes.Add(validateParameter);
                        state = States.WaitingForParameterName;
                        accum = "";
                        continue;
                    }
                }
            }

            if (state != States.EndOfBasicSignature && state != States.EndOfForwardingSignature)
            {
                result.Success          = false;
                result.ErrorDescription = "Incomplete constructor signature.";
                result.ErrorIndex       = input.Length - 1;
                return(result);
            }

            result.ParameterTypes      = parameterTypes;
            result.ParameterNames      = parameterNames.ToGenericArray();
            result.ForwardedParameters = forwardedParameters;

            return(result);
        }
        private static CSharpClassNameValidationResult _Validate(string input, ClassSigType signatureType,
                                                                 bool allowBuiltinAliases, CSharpParsedTypeValidateTypeCallback validateTypeCallback, int index)
        {
            if (input == null)
            {
                throw new ArgumentNullException("input", "Class name/signature string cannot be null!");
            }


            if (string.IsNullOrWhiteSpace(input))
            {
                return(new CSharpClassNameValidationResult
                {
                    Success = false,
                    ErrorDescription =
                        "Class name/signature cannot be whitespace.",
                    ErrorIndex = index,
                });
            }


            string fullyQualifiedName = "";
            var    genericArgs        = new GenericArray <CSharpClassNameValidationResult>();

            var qualifications = new GenericArray <Qualification>()
            {
                new Qualification(new StringBuilder(), index)
            };


            string genericPart = "";
            bool   isGeneric   = false;


            int genericBrackets = 0;


            foreach (var c in input)
            {
                //enter generic arguments
                if (c == '<')
                {
                    isGeneric = true;

                    genericBrackets++;
                }

                //check if we have gotten to the generic part of the type signature yet (if any)
                if (!isGeneric)
                {
                    if (c == '.')
                    {
                        //qualifier operator is not allowed anywhere in declaration signatures because
                        //they only consist of a raw type name and generic argument specifications
                        if (signatureType == ClassSigType.Declaration)
                        {
                            return(new CSharpClassNameValidationResult
                            {
                                Success = false,
                                ErrorDescription =
                                    string.Format(
                                        "'.' name qualifier operator is not valid in a class declaration/generic " +
                                        "type placeholder name."),
                                ErrorIndex = index,
                            });
                        }

                        //detect a missing qualifier section that's terminated with a dot.
                        if (string.IsNullOrWhiteSpace(qualifications.Last().ToString()))
                        {
                            return(new CSharpClassNameValidationResult
                            {
                                Success = false,
                                ErrorDescription =
                                    "\'..\' is an invalid use of the qualification operator.",
                                ErrorIndex = index,
                            });
                        }

                        qualifications.Add(new Qualification(new StringBuilder(), index + 1));
                    }
                    else
                    {
                        qualifications.Last().Builder.Append(c);
                    }
                }

                if (genericBrackets == 0 && !isGeneric)
                {
                    //accumulate to our fully qualified name
                    fullyQualifiedName += c;
                }
                else if (genericBrackets == 0 && isGeneric)
                {
                    //we have exited even pairs of brackets and are on the
                    //other side of the generic arguments at the end of the signature
                    //there should not be anything here

                    return(new CSharpClassNameValidationResult
                    {
                        Success = false,
                        ErrorDescription = "extra content found after generic argument list.",
                        ErrorIndex = index
                    });
                }
                else if (!(genericBrackets == 1 && c == '<'))
                {
                    //passed the start of the first < in the signature by 1
                    if ((c == ',' || c == '>') && genericBrackets == 1)
                    {
                        //we have accumulated a type argument suitable for recursive decent
                        //validate it recursively
                        var validateGenericArgument = _Validate(genericPart.Trim(), signatureType, allowBuiltinAliases,
                                                                validateTypeCallback, index - genericPart.Length);

                        //return immediately on failure
                        if (!validateGenericArgument.Success)
                        {
                            return(validateGenericArgument);
                        }

                        //add the validated 'tree'
                        genericArgs.Add(validateGenericArgument);

                        //reset the accumulator
                        genericPart = "";
                    }
                    else
                    {
                        //accumulate until we hit a comma or the > character
                        genericPart += c;
                    }
                }

                if (c == '>')
                {
                    //exit a generic type scope
                    genericBrackets--;
                }

                index++;
            }


            if (genericBrackets > 0)
            {
                //something is amiss with bracket matching
                return(new CSharpClassNameValidationResult
                {
                    Success = false,
                    ErrorDescription = "mismatched generic type brackets.",
                    ErrorIndex = index
                });
            }

            //there may be only one qualification, in which case
            //the base name is also the fully qualified name.
            var baseName = qualifications.Last();

            if (qualifications.Count > 1)
            {
                //uses qualified names, this is definitely an initialization signature
                //because the qualifier '.' operator returns an error in declaration signatures
                //above the recursive call to validate

                foreach (var name in qualifications)
                {
                    if (string.IsNullOrWhiteSpace(name.Builder.ToString()))
                    {
                        return(new CSharpClassNameValidationResult
                        {
                            Success = false,
                            ErrorDescription =
                                string.Format("qualified type name '{0}' is incomplete.",
                                              fullyQualifiedName),
                            ErrorIndex = name.StartIndex
                        });
                    }
                }


                foreach (var name in qualifications)
                {
                    //check for syntax errors in the qualified name pieces, they need to be valid ID's and not keywords
                    //IsValidIdentifier takes care of both these criteria
                    if (!CSharpIDValidator.IsValidIdentifier(name.Builder.ToString()))
                    {
                        //sound something funky
                        return(new CSharpClassNameValidationResult
                        {
                            Success = false,
                            ErrorDescription =
                                string.Format(
                                    "'{0}' is not valid in the given qualified type name '{1}'.", name.Builder,
                                    fullyQualifiedName),
                            ErrorIndex = name.StartIndex
                        });
                    }
                }
            }
            else
            {
                var shortName = baseName.Builder.ToString();

                //single type argument to a generic type
                if (string.IsNullOrWhiteSpace(shortName))
                {
                    return(new CSharpClassNameValidationResult
                    {
                        Success = false,
                        ErrorDescription = string.Format("missing generic {0} name.",
                                                         signatureType == ClassSigType.Initialization ? "type argument" : "placeholder type"),
                        ErrorIndex = baseName.StartIndex,
                    });
                }


                bool aliasInitialization = allowBuiltinAliases && CSharpKeywords.BuiltInTypeMap.ContainsKey(shortName) &&
                                           signatureType == ClassSigType.Initialization;

                if (!aliasInitialization &&
                    !CSharpIDValidator.IsValidIdentifier(baseName.Builder.ToString()))
                {
                    return(new CSharpClassNameValidationResult
                    {
                        Success = false,
                        ErrorDescription =
                            string.Format("'{0}' is not an allowed CSharp identifier.",
                                          baseName.Builder),
                        ErrorIndex = baseName.StartIndex
                    });
                }
            }


            var baseNameString = baseName.Builder.ToString();


            if (isGeneric && CSharpKeywords.IsTypeAliasKeyword(fullyQualifiedName))
            {
                return(new CSharpClassNameValidationResult
                {
                    Success = false,
                    ErrorDescription =
                        string.Format("Built in type alias '{0}' is not a generic type.",
                                      baseName.Builder),
                    ErrorIndex = baseName.StartIndex
                });
            }


            //success
            var classDescription = new CSharpClassNameValidationResult()
            {
                QualifiedName    = fullyQualifiedName,
                BaseName         = baseNameString,
                GenericArguments = genericArgs,
                IsGeneric        = isGeneric,
                Success          = true
            };


            if (isGeneric)
            {
                classDescription.FullSignature = fullyQualifiedName + "<" +
                                                 string.Join(", ",
                                                             classDescription.GenericArguments.Select(x => x.FullSignature)) +
                                                 ">";
            }
            else
            {
                classDescription.FullSignature = fullyQualifiedName;
            }


            if (validateTypeCallback == null || signatureType != ClassSigType.Initialization)
            {
                return(classDescription);
            }

            var typeCheckResult = validateTypeCallback(classDescription);

            if (typeCheckResult.IsValid)
            {
                return(classDescription);
            }

            classDescription.FullSignature    = null;
            classDescription.ErrorIndex       = qualifications.First().StartIndex;
            classDescription.ErrorDescription = typeCheckResult.ErrorMessage;
            classDescription.Success          = false;


            return(classDescription);
        }
 /// <summary>
 ///     Validates that the specified input string is syntactically valid C# type initialization signature, including
 ///     generic types.
 /// </summary>
 /// <param name="input">The input string containing the proposed type value.</param>
 /// <param name="validateTypeCallback">
 ///     A call back to allow you to verify the existence of the types in the type signature
 ///     as they are parsed.
 /// </param>
 /// <param name="allowBuiltInAliases">Allow built in aliases such as 'int' or 'char' to pass as class names</param>
 /// <returns>A parse/validation results object.  <see cref="CSharpClassNameValidationResult" /></returns>
 public static CSharpClassNameValidationResult ValidateInitialization(string input, bool allowBuiltInAliases,
                                                                      CSharpParsedTypeValidateTypeCallback validateTypeCallback = null)
 {
     return(_Validate(input, ClassSigType.Initialization, allowBuiltInAliases, validateTypeCallback, 0));
 }