/// <summary> /// Parses a property index expression in the form <c>(sys:Int32)42,(sys:Int32)24,SampleString</c>, /// where sys is mapped to the system namespace. /// Inside indexers, multiple indexer parameters are separated by commas (,). /// The type of each parameter can be specified with parentheses. /// </summary> /// <param name="context">The current parser context.</param> /// <param name="expression">The index expression to parse. The index expression is /// the expression between the <c>[</c> and <c>]</c> characters, for example in /// <c>PropertyName[10,20]</c>, the index expression is <c>10,20</c>.</param> /// <returns>Object array containing index parameters from the /// <paramref name="expression"/>.</returns> public static object[] ParseIndexExpression(IParserContext context, string expression) { IList <object> indices = new List <object>(); string[] indexStrings = expression.Split(new char[] { ',' }); foreach (string s in indexStrings) { string valStr = s; Type explicitType = null; if (valStr.StartsWith("(")) { // Explicit type is given int i = valStr.IndexOf(')'); explicitType = ParseType(context, s.Substring(1, i - 1)); valStr = valStr.Substring(i + 1); } object indexResult = valStr; if (explicitType != null) { if (!TypeConverter.Convert(indexResult, explicitType, out indexResult)) { throw new XamlParserException("Could not convert '{0}' to type '{1}'", indexResult, explicitType.Name); } } indices.Add(indexResult); } object[] result = new object[indexStrings.Length]; indices.CopyTo(result, 0); return(result); }
/// <summary> /// Convertes all objects in the specified <paramref name="objects"/> array to the specified /// <paramref name="types"/>. The number of types may be greater than the number of objects; /// this supports type conversion for both mandatory and optional parameters. /// </summary> /// <param name="objects">The array of objects to be type-converted.</param> /// <param name="types">Desired types the objects should be converted to. /// Indices in the <paramref name="types"/> array correspond to indices /// of the <paramref name="objects"/> array. The <paramref name="types"/> /// array may contain more elements than the <paramref name="objects"/> array.</param> /// <param name="convertedObjects">Returns the array of converted objects. /// The size of this returned array is the same as the size of the /// <paramref name="objects"/> array.</param> /// <returns><c>true</c>, if the conversion was successful for all objects /// in the input array, else <c>false</c>.</returns> /// <exception cref="XamlBindingException">If the number of objects given is greater than /// the number of types given.</exception> public static bool ConvertTypes(IEnumerable <object> objects, Type[] types, out object[] convertedObjects) { // Convert objects to index types convertedObjects = null; List <object> result = new List <object>(types.Length); int current = 0; foreach (object obj in objects) { if (current >= types.Length) { return(false); } object converted; if (!TypeConverter.Convert(obj, types[current], out converted)) { return(false); } result.Add(converted); current++; } convertedObjects = result.ToArray(); return(true); }
/// <summary> /// Checks the existance of a <c>Name</c> and the <c>x:Name</c> and <c>x:Key</c> /// pseudo member nodes in both attribute syntax and member element syntax. /// Other nodes from the <c>x:</c> namespace will be rejected by throwing /// an exception. /// Names given by a <c>x:Name</c> will be automatically assigned to the /// current element's "Name" attribute, if it is present. /// Except <c>Name</c>, this method ignores "normal" members and events, /// which will be handled in method /// <see cref="HandleMemberOrEventAssignment"/>. /// </summary> /// <remarks> /// Implementation node: We separate this method from method /// <see cref="HandleMemberOrEventAssignment"/> /// because both methods handle attributes of different scopes. The <c>x:</c> /// attributes are attributes which affect the outside world, as they associate /// a name or key to the current element in the outer scope. In contrast to /// this, "Normal" members are set on the current element and do not affect /// the outer scope. /// </remarks> /// <param name="memberDeclarationNode">Node containing a <c>x:</c> attribute /// for the current element. This node can either /// be an <see cref="XmlAttribute"/> node (for attribute syntax) or an /// <see cref="XmlElement"/> node (for member element syntax). /// This method ignores "normal" member and event attributes.</param> /// <param name="name">Will be set if the node to handle is an <c>x:Name</c>.</param> /// <param name="key">Will be set if the node to handle is an <c>x:Key</c>.</param> protected void CheckNameOrKey(XmlNode memberDeclarationNode, ref string name, ref object key) { ElementContextInfo elementContext = _elementContextStack.CurrentElementContext; // Name if ((memberDeclarationNode.Prefix == string.Empty || memberDeclarationNode.NamespaceURI == elementContext.Element.NamespaceURI) && memberDeclarationNode.LocalName == "Name") { name = Convert(ParseValue(memberDeclarationNode), typeof(string)) as string; return; } if (memberDeclarationNode.NamespaceURI != XamlNamespaceHandler.XAML_NS_URI) { // Ignore other attributes not located in the x: namespace return; } // x: attributes if (memberDeclarationNode.LocalName == "Name") // x:Name { // x:Name if (name == null) { object value = ParseValue(memberDeclarationNode); name = TypeConverter.Convert(value, typeof(string)) as string; // Assign name to "Name" member, if one exists IDataDescriptor dd; if (ReflectionHelper.FindMemberDescriptor( elementContext.Instance, "Name", out dd)) { // Member assignment HandleMemberAssignment(dd, value); } } else { throw new XamlBindingException("Attribute '{0}' was specified multiple times", memberDeclarationNode.Name); } } else if (memberDeclarationNode.LocalName == "Key") { // x:Key object value = ParseValue(memberDeclarationNode); if (key == null) { key = value; } else { throw new XamlBindingException("Attribute '{0}' was specified multiple times", memberDeclarationNode.Name); } } else if (memberDeclarationNode is XmlAttribute) { // TODO: Is it correct to reject all usages of x:Attr attributes except x:Key and x:Name? throw new XamlParserException("Attribute '{0}' cannot be used here", memberDeclarationNode.Name); } }
protected object Convert(object source) { if (!_negate) { return(source); } // If negate, we need to convert to bool object value = false; if (source != null) { value = source; if (!TypeConverter.Convert(value, typeof(bool), out value)) { return(value != null); } } return(!(bool)value); }
protected static object Convert(object val, Type targetType) { try { object result; if (TypeConverter.Convert(val, targetType, out result)) { return(result); } throw new XamlBindingException("Could not convert object '{0}' to type '{1}'", val, targetType.Name); } catch (Exception e) { if (e is XamlBindingException) { throw; } throw new XamlBindingException("Could not convert object '{0}' to type '{1}'", val, targetType.Name); } }
/// <summary> /// Checks if the specified <paramref name="maybeCollectionTarget"/> parameter /// is an object which is not null and which supports any collection element adding /// facility. The specified <paramref name="value"/> will then be assigned to the /// collection. /// </summary> /// <remarks> /// <para> /// This method will be needed in two cases: /// <list type="number"> /// <item>A property should be assigned, which has a collection-like type. These properties /// won't be assigned directly but they are expected to have a not-null collection value. /// Adding children to them will cause calling some "Add" method on the existing instance.</item> /// <item>Child elements should be assigned to an object which doesn't support /// the <see cref="IContentEnabled"/> interface. So if there is no content property, /// the parser tries to add children directly to the object, if it is supported.</item> /// </list> /// In both cases, there is already an existing (collection-like) instance, to that the /// specified <paramref name="value"/> should be assigned, so this method has no /// need to create the collection itself. /// </para> /// <para> /// If this method cannot handle the specified target object /// <paramref name="maybeCollectionTarget"/>, or if this parameter is <c>null</c>, /// it will return <c>false</c> to signal that it couldn't do the assignment. /// </para> /// </remarks> /// <param name="maybeCollectionTarget">Parameter holding an instance which may be /// a collection type to add the <paramref name="value"/> to. May also be <c>null</c>, /// which will result in a return value of <c>false</c>.</param> /// <param name="value">The value to assign to the target object. If the value has /// a collection type compatible with the <paramref name="maybeCollectionTarget"/> target, /// the contents will be transferred to the target object, else this parameter value /// will be added to the target itself.</param> /// <returns><c>true</c>, if the method could handle the assignment, else <c>false</c>.</returns> public static bool CheckHandleCollectionAssignment(object maybeCollectionTarget, object value) { if (maybeCollectionTarget == null || value == null) { return(false); } Type targetType = maybeCollectionTarget.GetType(); // Check for List Type resultType; Type entryType; MethodInfo method; FindImplementedListType(targetType, out resultType, out entryType); if (resultType != null) { method = entryType == null?targetType.GetMethod("Add") : targetType.GetMethod("Add", new Type[] { entryType }); // Have to cast to ICollection, because the type converter cannot cope with the situation corretcly if we cast to IEnumerable ICollection col; object converted; var collectionTypeConverter = TypeConverter.CustomCollectionTypeConverter; if (collectionTypeConverter != null && collectionTypeConverter(value, targetType, out converted)) { col = (ICollection)converted; } else { col = (ICollection)TypeConverter.Convert(value, typeof(ICollection)); } if (col == null) { // The type converter converts null to null rather than to an empty collection, so we have to handle this case explicitly method.Invoke(maybeCollectionTarget, new object[] { null }); } else { foreach (object child in col) { method.Invoke(maybeCollectionTarget, new object[] { child }); } } return(true); } // Check for Dictionary Type keyType; Type valueType; FindImplementedDictionaryType(targetType, out resultType, out keyType, out valueType); Type sourceDictType; Type sourceKeyType; Type sourceValueType; FindImplementedDictionaryType(value.GetType(), out sourceDictType, out sourceKeyType, out sourceValueType); if (resultType != null && sourceDictType != null) { PropertyInfo targetItemProperty = keyType == null?targetType.GetProperty("Item") : targetType.GetProperty("Item", new Type[] { keyType }); MethodInfo targetItemSetter = targetItemProperty.GetSetMethod(); foreach (KeyValuePair <object, object> kvp in (IEnumerable)value) { targetItemSetter.Invoke(maybeCollectionTarget, new object[] { kvp.Key, kvp.Value }); } return(true); } // Check for IAddChild if (IsIAddChild(maybeCollectionTarget.GetType(), out method, out entryType)) { foreach (object child in (ICollection)TypeConverter.Convert(value, typeof(ICollection))) { method.Invoke(maybeCollectionTarget, new object[] { TypeConverter.Convert(child, entryType) }); } return(true); } return(false); }