public AmpPathElement(string key) :
            base(key)
        {
            var literal          = new StringBuilder();
            var canonicalBuilder = new StringBuilder();

            var tok   = new List <object>();
            int index = 0;

            while (index < key.Length)
            {
                char c = key[index];

                // beginning of reference
                if (c == '&')
                {
                    // store off any literal text captured thus far
                    if (literal.Length > 0)
                    {
                        tok.Add(literal.ToString());
                        canonicalBuilder.Append(literal);
                        literal.Clear();
                    }

                    int          refEnd = FindEndOfReference(key.Substring(index + 1));
                    AmpReference ref_   = new AmpReference(key.Substring(index, refEnd + 1));
                    canonicalBuilder.Append(ref_.GetCanonicalForm());

                    tok.Add(ref_);
                    index += refEnd;
                }
                else
                {
                    literal.Append(c);
                }
                index++;
            }
            if (literal.Length > 0)
            {
                tok.Add(literal.ToString());
                canonicalBuilder.Append(literal.ToString());
            }
            tok.TrimExcess();

            _tokens        = tok.AsReadOnly();
            _canonicalForm = canonicalBuilder.ToString();
        }
        public string Evaluate(WalkedPath walkedPath)
        {
            // Walk thru our tokens and build up a string
            // Use the supplied Path to fill in our token References
            StringBuilder output = new StringBuilder();

            foreach (object token in _tokens)
            {
                if (token is string stoken)
                {
                    output.Append(stoken);
                }
                else
                {
                    AmpReference   ref_           = (AmpReference)token;
                    MatchedElement matchedElement = walkedPath.ElementFromEnd(ref_.GetPathIndex()).MatchedElement;
                    string         value          = matchedElement.GetSubKeyRef(ref_.GetKeyGroup());
                    output.Append(value);
                }
            }

            return(output.ToString());
        }
        public ArrayPathElement(string key) :
            base(key)
        {
            if (key[0] != '[' || key[key.Length - 1] != ']')
            {
                throw new SpecException("Invalid ArrayPathElement key:" + key);
            }

            ArrayPathType        apt;
            IPathReference       r   = null;
            TransposePathElement tpe = null;
            string aI = "";

            if (key.Length == 2)
            {
                apt            = ArrayPathType.AUTO_EXPAND;
                _canonicalForm = "[]";
            }
            else
            {
                string meat      = key.Substring(1, key.Length - 2);
                char   firstChar = meat[0];

                if (AmpReference.TOKEN == firstChar)
                {
                    r              = new AmpReference(meat);
                    apt            = ArrayPathType.REFERENCE;
                    _canonicalForm = "[" + r.GetCanonicalForm() + "]";
                }
                else if (HashReference.TOKEN == firstChar)
                {
                    r   = new HashReference(meat);
                    apt = ArrayPathType.HASH;

                    _canonicalForm = "[" + r.GetCanonicalForm() + "]";
                }
                else if ('@' == firstChar)
                {
                    apt = ArrayPathType.TRANSPOSE;

                    tpe            = TransposePathElement.Parse(meat);
                    _canonicalForm = "[" + tpe.GetCanonicalForm() + "]";
                }
                else
                {
                    aI = VerifyStringIsNonNegativeInteger(meat);
                    if (aI != null)
                    {
                        apt            = ArrayPathType.EXPLICIT_INDEX;
                        _canonicalForm = "[" + aI + "]";
                    }
                    else
                    {
                        throw new SpecException("Bad explict array index:" + meat + " from key:" + key);
                    }
                }
            }

            _transposePathElement = tpe;
            _arrayPathType        = apt;
            _ref        = r;
            _arrayIndex = aI;
        }