private static Delegate CreateSetDefaultDelegate(
                [NotNull] Type delegateType,
                [NotNull] Type elementType)
            {
                Debug.Assert(delegateType != null);
                Debug.Assert(elementType != null);

                var method = new DynamicMethod(
                    $"{DynamicMethodPrefix}Set{elementType.Name}Default",
                    typeof(void),
                    new[] { elementType },
                    elementType.Module);
                ILGenerator generator = method.GetILGenerator();

                // We need to create the switch for each property
                IEnumerable <PropertyInfo> properties = SerializationHelpers
                                                        .GetAttributeProperties(elementType)
                                                        .Select(info => info.Property);

                foreach (PropertyInfo property in properties)
                {
                    var defaultValueAttribute = Attribute.GetCustomAttribute(property, typeof(DefaultValueAttribute)) as DefaultValueAttribute;
                    if (defaultValueAttribute is null)
                    {
                        continue;
                    }

                    MethodInfo setMethod = property.GetSetMethod();
                    if (setMethod is null)
                    {
                        throw new InvalidOperationException($"Property {property.Name} is not settable.");
                    }
                    if (property.PropertyType.IsArray)
                    {
                        throw new NotSupportedException("Default values for array types are not implemented.");
                    }

                    object value = defaultValueAttribute.Value;
                    if (value is null)
                    {
                        throw new NotSupportedException($"Null default value is not supported for property {property.Name}.");
                    }
                    if (value.GetType() != property.PropertyType)
                    {
                        throw new InvalidOperationException($"Invalid default value type for property {property.Name}.");
                    }

                    generator.Emit(OpCodes.Ldarg_0);
                    EmitValue(generator, property, value);
                    EmitCall(generator, setMethod);
                }

                generator.Emit(OpCodes.Ret);

                // Let's bake the method
                return(method.CreateDelegate(delegateType));
            }
示例#2
0
            private static Delegate CreateWriteDelegate([NotNull] Type nodeType, [NotNull] Type delegateType)
            {
                Debug.Assert(nodeType != null);
                Debug.Assert(delegateType != null);

                var method = new DynamicMethod(
                    $"{DynamicMethodPrefix}Write{delegateType.Name}_{nodeType.Name}",
                    typeof(void),
                    new[] { typeof(XmlWriter), nodeType },
                    nodeType.Module);

                ILGenerator generator = method.GetILGenerator();

                foreach (PropertySerializationInfo info in SerializationHelpers.GetAttributeProperties(nodeType))
                {
                    EmitWriteProperty(info, generator);
                }

                generator.Emit(OpCodes.Ret);

                // Let's bake the method
                return(method.CreateDelegate(delegateType));
            }
示例#3
0
            private void WriteAttributeDefinitions([NotNull] string nodeName, [NotNull] Type nodeType)
            {
                Debug.Assert(nodeName != null);
                Debug.Assert(nodeType != null);

                foreach (PropertySerializationInfo info in SerializationHelpers.GetAttributeProperties(nodeType))
                {
                    PropertyInfo property     = info.Property;
                    string       name         = info.Name;
                    Type         propertyType = property.PropertyType;

                    // <key id="d1" for="edge" attr.name="weight" attr.type="double"/>
                    _writer.WriteStartElement("key", GraphMLXmlResolver.GraphMLNamespace);
                    _writer.WriteAttributeString(IdAttribute, name);
                    _writer.WriteAttributeString("for", nodeName);
                    _writer.WriteAttributeString("attr.name", name);

                    string typeCodeStr;
                    try
                    {
                        typeCodeStr = ConstructTypeCode(propertyType);
                    }
                    catch (NotSupportedException)
                    {
                        throw new NotSupportedException(
                                  $"Property type {property.DeclaringType}.{property.Name} not supported by the GraphML schema.");
                    }

                    _writer.WriteAttributeString("attr.type", typeCodeStr);

                    // <default>...</default>
                    if (info.TryGetDefaultValue(out object defaultValue))
                    {
                        _writer.WriteStartElement("default");
                        Type defaultValueType = defaultValue.GetType();
                        switch (Type.GetTypeCode(defaultValueType))
                        {
                        case TypeCode.Boolean:
                            _writer.WriteString(XmlConvert.ToString((bool)defaultValue));
                            break;

                        case TypeCode.Int32:
                            _writer.WriteString(XmlConvert.ToString((int)defaultValue));
                            break;

                        case TypeCode.Int64:
                            _writer.WriteString(XmlConvert.ToString((long)defaultValue));
                            break;

                        case TypeCode.Single:
                            _writer.WriteString(XmlConvert.ToString((float)defaultValue));
                            break;

                        case TypeCode.Double:
                            _writer.WriteString(XmlConvert.ToString((double)defaultValue));
                            break;

                        case TypeCode.String:
                            _writer.WriteString((string)defaultValue);
                            break;

                        case TypeCode.Object:
                            if (defaultValueType.IsArray)
                            {
                                throw new NotImplementedException("Default values for array types are not implemented.");
                            }
                            throw new NotSupportedException(
                                      $"Property type {property.DeclaringType}.{property.Name} not supported by the GraphML schema.");

                        default:
                            throw new NotSupportedException(
                                      $"Property type {property.DeclaringType}.{property.Name} not supported by the GraphML schema.");
                        }

                        _writer.WriteEndElement();
                    }

                    _writer.WriteEndElement();
                }
            }
            private static Delegate CreateReadDelegate(
                [NotNull] Type delegateType,
                [NotNull] Type elementType)
            {
                Debug.Assert(delegateType != null);
                Debug.Assert(elementType != null);

                var method = new DynamicMethod(
                    $"{DynamicMethodPrefix}Read{elementType.Name}",
                    typeof(void),
                    // reader, namespaceUri
                    new[] { typeof(XmlReader), typeof(string), elementType },
                    elementType.Module);
                ILGenerator generator = method.GetILGenerator();

                generator.DeclareLocal(typeof(string));

                generator.Emit(OpCodes.Ldarg_0);
                generator.Emit(OpCodes.Ldstr, "key");
                generator.EmitCall(OpCodes.Callvirt, Metadata.GetAttributeMethod, null);
                generator.Emit(OpCodes.Stloc_0);

                // We need to create the switch for each property
                Label next    = generator.DefineLabel();
                Label @return = generator.DefineLabel();
                bool  first   = true;

                foreach (PropertySerializationInfo info in SerializationHelpers.GetAttributeProperties(elementType))
                {
                    PropertyInfo property = info.Property;
                    if (!first)
                    {
                        generator.MarkLabel(next);
                        next = generator.DefineLabel();
                    }
                    first = false;

                    generator.Emit(OpCodes.Ldloc_0);
                    generator.Emit(OpCodes.Ldstr, info.Name);
                    generator.EmitCall(OpCodes.Call, Metadata.StringEqualsMethod, null);

                    // If false jump to next
                    generator.Emit(OpCodes.Brfalse, next);

                    // Do our stuff
                    if (!Metadata.TryGetReadContentMethod(property.PropertyType, out MethodInfo readMethod))
                    {
                        throw new NotSupportedException($"Property {property.Name} has a non supported type.");
                    }

                    // Do we have a set method?
                    MethodInfo setMethod = property.GetSetMethod();
                    if (setMethod is null)
                    {
                        throw new InvalidOperationException($"Property {property.DeclaringType}.{property.Name} has no set method.");
                    }

                    // reader.ReadXXX
                    generator.Emit(OpCodes.Ldarg_2); // element
                    generator.Emit(OpCodes.Ldarg_0); // reader
                    generator.Emit(OpCodes.Ldstr, "data");
                    generator.Emit(OpCodes.Ldarg_1); // namespace URI

                    // When writing scalar values we call member methods of XmlReader, while for array values
                    // we call our own static methods. These two types of methods seem to need different OpCode.
                    generator.EmitCall(
                        readMethod.DeclaringType == typeof(XmlReaderExtensions)
                            ? OpCodes.Call
                            : OpCodes.Callvirt,
                        readMethod,
                        null);
                    generator.EmitCall(OpCodes.Callvirt, setMethod, null);

                    // Jump to do while
                    generator.Emit(OpCodes.Br, @return);
                }

                // We don't know this parameter.. we throw
                generator.MarkLabel(next);
                generator.Emit(OpCodes.Ldloc_0);
                generator.Emit(OpCodes.Newobj, Metadata.ArgumentExceptionCtor);
                generator.Emit(OpCodes.Throw);

                generator.MarkLabel(@return);
                generator.Emit(OpCodes.Ret);

                // Let's bake the method
                return(method.CreateDelegate(delegateType));
            }
            private static Delegate CreateReadDelegate(
                [NotNull] Type delegateType,
                [NotNull] Type elementType)
            {
                Debug.Assert(delegateType != null);
                Debug.Assert(elementType != null);

                var method = new DynamicMethod(
                    $"{DynamicMethodPrefix}Read{elementType.Name}",
                    typeof(void),
                    new[] { typeof(XmlReader), typeof(string), elementType },
                    elementType.Module);
                ILGenerator generator = method.GetILGenerator();

                // Ldarg_0 = reader
                // Ldarg_1 = namespace URI
                // Ldarg_2 = element

                generator.DeclareLocal(typeof(string));

                GetXmlAttribute("key");
                generator.Emit(OpCodes.Stloc_0);

                // We need to create the switch for each property
                Label next    = generator.DefineLabel();
                Label @return = generator.DefineLabel();
                bool  first   = true;

                foreach (PropertySerializationInfo info in SerializationHelpers.GetAttributeProperties(elementType))
                {
                    PropertyInfo property = info.Property;
                    if (!first)
                    {
                        generator.MarkLabel(next);
                        next = generator.DefineLabel();
                    }
                    first = false;

                    generator.Emit(OpCodes.Ldloc_0);
                    generator.Emit(OpCodes.Ldstr, info.Name);
                    EmitCall(generator, Metadata.StringEqualsMethod);

                    // If false jump to next
                    generator.Emit(OpCodes.Brfalse, next);

                    // Do our stuff
                    if (!Metadata.TryGetReadContentMethod(property.PropertyType, out MethodInfo readMethod))
                    {
                        throw new NotSupportedException($"Property {property.Name} has a non supported type.");
                    }

                    // Do we have a set method?
                    MethodInfo setMethod = property.GetSetMethod();
                    if (setMethod is null)
                    {
                        throw new InvalidOperationException($"Property {property.DeclaringType}.{property.Name} has no setter.");
                    }

                    // element.xxx = reader.ReadXXX
                    generator.Emit(OpCodes.Ldarg_2); // element
                    generator.Emit(OpCodes.Ldarg_0); // reader
                    generator.Emit(OpCodes.Ldstr, "data");
                    generator.Emit(OpCodes.Ldarg_1); // namespace URI
                    EmitCall(generator, readMethod);
                    EmitCall(generator, setMethod);

                    // Jump to do while
                    generator.Emit(OpCodes.Br, @return);
                }

                // We don't know this parameter.. we throw
                generator.MarkLabel(next);
                generator.Emit(OpCodes.Ldloc_0);
                generator.Emit(OpCodes.Newobj, Metadata.ArgumentExceptionCtor);
                generator.Emit(OpCodes.Throw);

                generator.MarkLabel(@return);
                generator.Emit(OpCodes.Ret);

                // Let's bake the method
                return(method.CreateDelegate(delegateType));

                #region Local function

                void GetXmlAttribute(string attributeName)
                {
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldstr, attributeName);
                    EmitCall(generator, Metadata.GetAttributeMethod);
                }

                #endregion
            }