Example #1
0
        public static ParsedMarkupExtensionInfo Parse(string raw, IXamlNamespaceResolver nsResolver, XamlSchemaContext sctx)
        {
            if (raw == null)
            {
                throw new ArgumentNullException(nameof(raw));
            }

            if (raw.Length == 0 || raw[0] != '{')
            {
                throw Error("Invalid markup extension attribute. It should begin with '{{', but was {0}", raw);
            }

            var ret = new ParsedMarkupExtensionInfo();
            int idx = raw.LastIndexOf('}');

            if (idx < 0)
            {
                throw Error("Expected '}}' in the markup extension attribute: '{0}'", raw);
            }

            raw = raw.Substring(1, idx - 1);
            idx = raw.IndexOf(' ');
            string name = idx < 0 ? raw : raw.Substring(0, idx);

            XamlTypeName xtn;

            if (!XamlTypeName.TryParse(name, nsResolver, out xtn))
            {
                throw Error("Failed to parse type name '{0}'", name);
            }

            var xt = sctx.GetXamlType(xtn) ?? new XamlType(nsResolver.GetNamespace(""), name, null, sctx);

            ret.Type = xt;

            if (idx < 0)
            {
                return(ret);
            }

            var valueWithoutBinding = raw.Substring(idx + 1, raw.Length - idx - 1);

            var vpairs = BindingMembersRegex.Matches(valueWithoutBinding)
                         .Cast <Match>()
                         .Select(m => m.Value.Trim())
                         .ToList();

            if (vpairs.Count == 0)
            {
                vpairs.Add(valueWithoutBinding);
            }

            List <string> posPrms    = null;
            XamlMember    lastMember = null;

            foreach (var vpair in vpairs)
            {
                idx = vpair.IndexOf('=');

                // FIXME: unescape string (e.g. comma)
                if (idx < 0)
                {
                    if (vpair.ElementAtOrDefault(0) == ')')
                    {
                        if (lastMember != null)
                        {
                            if (ret.Arguments[lastMember] is string s)
                            {
                                ret.Arguments[lastMember] = s + ')';
                            }
                        }
                        else
                        {
                            posPrms[posPrms.Count - 1] += ')';
                        }
                    }
                    else
                    {
                        if (posPrms == null)
                        {
                            posPrms = new List <string>();
                            ret.Arguments.Add(XamlLanguage.PositionalParameters, posPrms);
                        }

                        posPrms.Add(UnescapeValue(vpair.Trim()));
                    }
                }
                else
                {
                    var key = vpair.Substring(0, idx).Trim();
                    // FIXME: is unknown member always isAttacheable = false?
                    var xm = xt.GetMember(key) ?? new XamlMember(key, xt, false);

                    // Binding member values may be wrapped in quotes (single or double) e.g. 'A,B,C,D'.
                    // Remove those wrapping quotes from the resulting string value.
                    var valueString = RemoveWrappingStringQuotes(vpair.Substring(idx + 1).Trim());

                    var value = IsValidMarkupExtension(valueString)
                                                ? (object)Parse(valueString, nsResolver, sctx) : UnescapeValue(valueString);

                    ret.Arguments.Add(xm, value);
                    lastMember = xm;
                }
            }
            return(ret);
        }
Example #2
0
        // Note that it could return invalid (None) node to tell the caller that it is not really an object element.
        IEnumerable <XamlXmlNodeInfo> ReadObjectElement(XamlType parentType, XamlMember currentMember)
        {
            if (r.NodeType != XmlNodeType.Element)
            {
                //throw new XamlParseException (String.Format ("Element is expected, but got {0}", r.NodeType));
                yield return(Node(XamlNodeType.Value, ReadCurrentContentString(isFirstElementString: false)));

                if (r.NodeType != XmlNodeType.Element)
                {
                    r.ReadContentAsString();
                }

                yield break;
            }

            if (r.MoveToFirstAttribute())
            {
                do
                {
                    if (r.NamespaceURI == XamlLanguage.Xmlns2000Namespace)
                    {
                        yield return(Node(XamlNodeType.NamespaceDeclaration, new NamespaceDeclaration(r.Value, r.Prefix == "xmlns" ? r.LocalName : String.Empty)));
                    }
                } while (r.MoveToNextAttribute());
                r.MoveToElement();
            }

            var sti = GetStartTagInfo();

            using (PushIgnorables(sti.Members))
            {
                if (IsIgnored(r.Prefix))
                {
                    r.Skip();
                    yield break;
                }

                if (r.NodeType != XmlNodeType.Element)
                {
                    //throw new XamlParseException (String.Format ("Element is expected, but got {0}", r.NodeType));
                    yield return(Node(XamlNodeType.Value, r.Value));
                }

                var xt = sctx.GetXamlType(sti.TypeName);
                if (xt == null)
                {
                    // creates name-only XamlType. Also, it does not seem that it does not store this XamlType to XamlSchemaContext (Try GetXamlType(xtn) after reading such xaml node, it will return null).
                    xt = new XamlType(sti.Namespace, sti.Name, sti.TypeName.TypeArguments?.Select(xxtn => sctx.GetXamlType(xxtn)).ToArray(), sctx);
                }

                bool isGetObject = false;
                if (currentMember != null && !xt.CanAssignTo(currentMember.Type))
                {
                    if (currentMember.DeclaringType != null && currentMember.DeclaringType.ContentProperty == currentMember)
                    {
                        isGetObject = true;
                    }

                    // It could still be GetObject if current_member
                    // is not a directive and current type is not
                    // a markup extension.
                    // (I'm not very sure about the condition;
                    // it could be more complex.)
                    // seealso: bug #682131
                    else if (!(currentMember is XamlDirective) &&
                             !xt.IsMarkupExtension)
                    {
                        isGetObject = true;
                    }
                }

                if (isGetObject)
                {
                    yield return(Node(XamlNodeType.GetObject, currentMember.Type));

                    foreach (var ni in ReadMembers(parentType, currentMember.Type))
                    {
                        yield return(ni);
                    }
                    yield return(Node(XamlNodeType.EndObject, currentMember.Type));

                    yield break;
                }
                // else

                yield return(Node(XamlNodeType.StartObject, xt));

                // process attribute members (including MarkupExtensions)
                ProcessAttributesToMember(sctx, sti, xt);

                foreach (var pair in sti.Members)
                {
                    if (pair.Key == XamlLanguage.Ignorable)
                    {
                        continue;
                    }

                    yield return(Node(XamlNodeType.StartMember, pair.Key));

                    // Try markup extension
                    // FIXME: is this rule correct?
                    var v = pair.Value;
                    if (!String.IsNullOrEmpty(v) && v[0] == '{' && v.ElementAtOrDefault(1) != '}')
                    {
                        IEnumerable <XamlXmlNodeInfo> ProcessArgs(ParsedMarkupExtensionInfo info)
                        {
                            yield return(Node(XamlNodeType.StartObject, info.Type));

                            foreach (var xepair in info.Arguments)
                            {
                                yield return(Node(XamlNodeType.StartMember, xepair.Key));

                                switch (xepair.Value)
                                {
                                case List <string> list:
                                    foreach (var s in list)
                                    {
                                        yield return(Node(XamlNodeType.Value, s));
                                    }
                                    break;

                                case ParsedMarkupExtensionInfo inner:
                                    foreach (var innerInfo in ProcessArgs(inner))
                                    {
                                        yield return(innerInfo);
                                    }
                                    break;

                                default:
                                    yield return(Node(XamlNodeType.Value, xepair.Value));

                                    break;
                                }

                                yield return(Node(XamlNodeType.EndMember, xepair.Key));
                            }

                            yield return(Node(XamlNodeType.EndObject, info.Type));
                        }

                        var pai = ParsedMarkupExtensionInfo.Parse(v, xaml_namespace_resolver, sctx);
                        foreach (var info in ProcessArgs(pai))
                        {
                            yield return(info);
                        }
                    }
                    else
                    {
                        yield return(Node(XamlNodeType.Value, CleanupBindingEscape(pair.Value)));
                    }

                    yield return(Node(XamlNodeType.EndMember, pair.Key));
                }

                // process content members
                if (!r.IsEmptyElement)
                {
                    r.Read();
                    foreach (var ni in ReadMembers(parentType, xt))
                    {
                        yield return(ni);
                    }
                    r.ReadEndElement();
                }
                else
                {
                    r.Read();                     // consume empty element.
                }
                yield return(Node(XamlNodeType.EndObject, xt));
            }
        }
        public static ParsedMarkupExtensionInfo Parse(string raw, IXamlNamespaceResolver nsResolver, XamlSchemaContext sctx)
        {
            if (raw == null)
            {
                throw new ArgumentNullException(nameof(raw));
            }

            if (raw.Length == 0 || raw[0] != '{')
            {
                throw Error("Invalid markup extension attribute. It should begin with '{{', but was {0}", raw);
            }

            if (raw.Length >= 2 && raw[1] == '}')
            {
                throw Error("Markup extension can not begin with an '{}' escape: '{0}'", raw);
            }

            var ret = new ParsedMarkupExtensionInfo();

            if (raw[raw.Length - 1] != '}')
            {
                // Any character after the final closing bracket is not accepted. Therefore, the last character should be '}'.
                // Ideally, we should still ran the entire markup through the parser to get a more meaningful error.
                throw Error("Expected '}}' in the markup extension attribute: '{0}'", raw);
            }

            var nameSeparatorIndex = raw.IndexOf(' ');
            var name = nameSeparatorIndex != -1 ? raw.Substring(1, nameSeparatorIndex - 1) : raw.Substring(1, raw.Length - 2);

            if (!XamlTypeName.TryParse(name, nsResolver, out var xtn))
            {
                throw Error("Failed to parse type name '{0}'", name);
            }

            var xt = sctx.GetXamlType(xtn) ?? new XamlType(xtn.Namespace, xtn.Name, null, sctx);

            ret.Type = xt;

            if (nameSeparatorIndex < 0)
            {
                return(ret);
            }

            var valueWithoutBinding = raw.Substring(nameSeparatorIndex + 1, raw.Length - 1 - (nameSeparatorIndex + 1));
            var vpairs = SliceParameters(valueWithoutBinding, raw);

            List <string> posPrms    = null;
            XamlMember    lastMember = null;

            foreach (var vpair in vpairs)
            {
                var idx = vpair.IndexOf('=');

                // FIXME: unescape string (e.g. comma)
                if (idx < 0)
                {
                    if (vpair.ElementAtOrDefault(0) == ')')
                    {
                        if (lastMember != null)
                        {
                            if (ret.Arguments[lastMember] is string s)
                            {
                                ret.Arguments[lastMember] = s + ')';
                            }
                        }
                        else
                        {
                            posPrms[posPrms.Count - 1] += ')';
                        }
                    }
                    else
                    {
                        if (posPrms == null)
                        {
                            posPrms = new List <string>();
                            ret.Arguments.Add(XamlLanguage.PositionalParameters, posPrms);
                        }

                        posPrms.Add(UnescapeValue(vpair.Trim()));
                    }
                }
                else
                {
                    var key = vpair.Substring(0, idx).Trim();
                    // FIXME: is unknown member always isAttacheable = false?
                    var xm = xt.GetMember(key) ?? new XamlMember(key, xt, false);

                    // Binding member values may be wrapped in quotes (single or double) e.g. 'A,B,C,D'.
                    // Remove those wrapping quotes from the resulting string value.
                    var valueString = RemoveWrappingStringQuotes(vpair.Substring(idx + 1).Trim());

                    var value = IsValidMarkupExtension(valueString)
                                                ? (object)Parse(valueString, nsResolver, sctx) : UnescapeValue(valueString);

                    ret.Arguments.Add(xm, value);
                    lastMember = xm;
                }
            }
            return(ret);
        }