/**
* 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
*
* http://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.Collections.Generic;
using System.Linq;
using Avro;
using Avro.IO;
using Avro.Generic;
namespace Avro.Specific
{
///
/// Generic wrapper class for writing data from specific objects
///
/// type name of specific object
public class SpecificWriter : GenericWriter
{
public SpecificWriter(Schema schema) : base(new SpecificDefaultWriter(schema)) { }
public SpecificWriter(SpecificDefaultWriter writer) : base(writer) { }
}
///
/// Class for writing data from any specific objects
///
public class SpecificDefaultWriter : DefaultWriter
{
///
/// Constructor
///
/// schema of the object to be written
public SpecificDefaultWriter(Schema schema) : base(schema) { }
///
/// Serialized a record using the given RecordSchema. It uses GetField method
/// to extract the field value from the given object.
///
/// The RecordSchema to use for serialization
/// The value to be serialized
/// The Encoder for serialization
protected override void WriteRecord(RecordSchema schema, object value, Encoder encoder)
{
var rec = value as ISpecificRecord;
if (rec == null)
throw new AvroTypeException("Record object is not derived from ISpecificRecord");
foreach (Field field in schema)
{
try
{
Write(field.Schema, rec.Get(field.Pos), encoder);
}
catch (Exception ex)
{
throw new AvroException(ex.Message + " in field " + field.Name);
}
}
}
///
/// Validates that the record is a fixed record object and that the schema in the object is the
/// same as the given writer schema. Writes the given fixed record into the given encoder
///
/// writer schema
/// fixed object to write
/// encoder to write to
protected override void WriteFixed(FixedSchema schema, object value, Encoder encoder)
{
var fixedrec = value as SpecificFixed;
if (fixedrec == null)
throw new AvroTypeException("Fixed object is not derived from SpecificFixed");
encoder.WriteFixed(fixedrec.Value);
}
///
/// Writes the given enum value into the given encoder.
///
/// writer schema
/// enum value
/// encoder to write to
protected override void WriteEnum(EnumSchema schema, object value, Encoder encoder)
{
if (value == null)
throw new AvroTypeException("value is null in SpecificDefaultWriter.WriteEnum");
encoder.WriteEnum(schema.Ordinal(value.ToString()));
}
///
/// Serialized an array. The default implementation calls EnsureArrayObject() to ascertain that the
/// given value is an array. It then calls GetArrayLength() and GetArrayElement()
/// to access the members of the array and then serialize them.
///
/// The ArraySchema for serialization
/// The value being serialized
/// The encoder for serialization
protected override void WriteArray(ArraySchema schema, object value, Encoder encoder)
{
var arr = value as System.Collections.IList;
if (arr == null)
throw new AvroTypeException("Array does not implement non-generic IList");
long l = arr.Count;
encoder.WriteArrayStart();
encoder.SetItemCount(l);
for (int i = 0; i < l; i++)
{
encoder.StartItem();
Write(schema.ItemSchema, arr[i], encoder);
}
encoder.WriteArrayEnd();
}
///
/// Writes the given map into the given encoder.
///
/// writer schema
/// map to write
/// encoder to write to
protected override void WriteMap(MapSchema schema, object value, Encoder encoder)
{
var map = value as System.Collections.IDictionary;
if (map == null)
throw new AvroTypeException("Map does not implement non-generic IDictionary");
encoder.WriteArrayStart();
encoder.SetItemCount(map.Count);
foreach (System.Collections.DictionaryEntry de in map)
{
encoder.StartItem();
encoder.WriteString(de.Key as string);
Write(schema.ValueSchema, de.Value, encoder);
}
encoder.WriteMapEnd();
}
///
/// Resolves the given value against the given UnionSchema and serializes the object against
/// the resolved schema member. The default implementation of this method uses
/// ResolveUnion to find the member schema within the UnionSchema.
///
/// The UnionSchema to resolve against
/// The value to be serialized
/// The encoder for serialization
protected override void WriteUnion(UnionSchema us, object value, Encoder encoder)
{
for (int i = 0; i < us.Count; i++)
{
if (Matches(us[i], value))
{
encoder.WriteUnionIndex(i);
Write(us[i], value, encoder);
return;
}
}
throw new AvroException("Cannot find a match for " + value.GetType() + " in " + us);
}
protected override bool Matches(Schema sc, object obj)
{
if (obj == null && sc.Tag != Avro.Schema.Type.Null) return false;
switch (sc.Tag)
{
case Schema.Type.Null:
return obj == null;
case Schema.Type.Boolean:
return obj is bool;
case Schema.Type.Int:
return obj is int;
case Schema.Type.Long:
return obj is long;
case Schema.Type.Float:
return obj is float;
case Schema.Type.Double:
return obj is double;
case Schema.Type.Bytes:
return obj is byte[];
case Schema.Type.String:
return obj is string;
case Schema.Type.Error:
case Schema.Type.Record:
return obj is ISpecificRecord &&
(((obj as ISpecificRecord).Schema) as RecordSchema).SchemaName.Equals((sc as RecordSchema).SchemaName);
case Schema.Type.Enumeration:
return obj.GetType().IsEnum && (sc as EnumSchema).Symbols.Contains(obj.ToString());
case Schema.Type.Array:
return obj is System.Collections.IList;
case Schema.Type.Map:
return obj is System.Collections.IDictionary;
case Schema.Type.Union:
return false; // Union directly within another union not allowed!
case Schema.Type.Fixed:
return obj is SpecificFixed &&
(((obj as SpecificFixed).Schema) as FixedSchema).SchemaName.Equals((sc as FixedSchema).SchemaName);
default:
throw new AvroException("Unknown schema type: " + sc.Tag);
}
}
}
}