blob: 42ae766bd23a7990543fdb1cfc332d598ca67558 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Reflection;
using System.Collections;
namespace Avro.Reflect
{
internal class DotnetProperty
{
private PropertyInfo _property;
public IAvroFieldConverter Converter { get; set; }
private bool IsPropertyCompatible(Avro.Schema schema)
{
Type propType;
var schemaTag = schema.Tag;
if (Converter == null)
{
propType = _property.PropertyType;
}
else
{
propType = Converter.GetAvroType();
}
switch (schemaTag)
{
case Avro.Schema.Type.Null:
return (Nullable.GetUnderlyingType(propType) != null) || (!propType.IsValueType);
case Avro.Schema.Type.Boolean:
return propType == typeof(bool);
case Avro.Schema.Type.Int:
return propType == typeof(int);
case Avro.Schema.Type.Long:
return propType == typeof(long);
case Avro.Schema.Type.Float:
return propType == typeof(float);
case Avro.Schema.Type.Double:
return propType == typeof(double);
case Avro.Schema.Type.Bytes:
return propType == typeof(byte[]);
case Avro.Schema.Type.String:
return typeof(string).IsAssignableFrom(propType);
case Avro.Schema.Type.Record:
//TODO: this probably should work for struct too
return propType.IsClass;
case Avro.Schema.Type.Enumeration:
return propType.IsEnum;
case Avro.Schema.Type.Array:
return typeof(IEnumerable).IsAssignableFrom(propType);
case Avro.Schema.Type.Map:
return typeof(IDictionary).IsAssignableFrom(propType);
case Avro.Schema.Type.Union:
return true;
case Avro.Schema.Type.Fixed:
return propType == typeof(byte[]);
case Avro.Schema.Type.Error:
return propType.IsClass;
case Avro.Schema.Type.Logical:
var logicalSchema = (LogicalSchema)schema;
var type = logicalSchema.LogicalType.GetCSharpType(false);
return type == propType;
}
return false;
}
public DotnetProperty(PropertyInfo property, Avro.Schema schema, IAvroFieldConverter converter, ClassCache cache)
{
_property = property;
Converter = converter;
if (!IsPropertyCompatible(schema))
{
if (Converter == null)
{
var c = cache.GetDefaultConverter(schema.Tag, _property.PropertyType);
if (c != null)
{
Converter = c;
return;
}
}
throw new AvroException($"Property {property.Name} in object {property.DeclaringType} isn't compatible with Avro schema type {schema.Tag}");
}
}
public DotnetProperty(PropertyInfo property, Avro.Schema schema, ClassCache cache)
: this(property, schema, null, cache)
{
}
public virtual Type GetPropertyType()
{
if (Converter != null)
{
return Converter.GetAvroType();
}
return _property.PropertyType;
}
public virtual object GetValue(object o, Schema s)
{
if (Converter != null)
{
return Converter.ToAvroType(_property.GetValue(o), s);
}
return _property.GetValue(o);
}
public virtual void SetValue(object o, object v, Schema s)
{
if (Converter != null)
{
_property.SetValue(o, Converter.FromAvroType(v, s));
}
else
{
_property.SetValue(o, v);
}
}
}
}