bool ComparePrimitive(object other) { if (this.IsNull) { return(other == null); } switch (this.schema.DataType) { case DataType.Boolean: case DataType.Int32: case DataType.Int64: case DataType.Float64: case DataType.Decimal: case DataType.DateTime: case DataType.TimeSpan: case DataType.Guid: case DataType.String: case DataType.ItemPath: case DataType.Choice: return(object.Equals(this.Get(), other)); case DataType.Binary: return(EditablePrimitive.ByteArrayCompare(this.Get <byte[]>(), other as byte[])); } return(false); }
public IEditable CloneAsEditable(bool frozen) { var c = new EditablePrimitive(factory, this.schema, nullable); c.Set(data); if (frozen) { c.Freeze(); } return(c); }
public bool TryGetSchemaForType(Type type, out Schema schema) { lock (this) { if (schemaByType.TryGetValue(type, out schema)) { return(true); } } return(EditablePrimitive.TryGetPrimitiveSchemaForType(type, out schema)); }
public IEditable Create(Schema schema, bool nullable) { if (schema == null) { throw new ArgumentNullException("schema"); } if (EditablePrimitive.IsSupportedType(schema.DataType)) { var x = new EditablePrimitive(this, schema, nullable); if (!nullable && schema.DataType == DataType.Choice && schema.DeclarationItem != null) { var defaults = schema.DeclarationItem.NavigateTo(FieldPath.Create("Data", "Defaults")); if (!defaults.IsNull && defaults.Count > 0) { x.Set(defaults.GoTo(0).Get <int>()); } } return(x); } if (schema.DataType == DataType.Class) { if (schema == Schema.BuiltIn[BuiltInSchema.Variable]) { return(new EditableVariable(this, nullable)); } var x = new EditableObject(this, schema, nullable); if (!nullable && schema.DeclarationItem != null) { } return(x); } else if (schema.DataType == DataType.List || schema.DataType == DataType.MultiChoice) { var x = new EditableList(this, schema, nullable); if (!nullable && schema.DataType == DataType.MultiChoice && schema.DeclarationItem != null) { var defaults = schema.DeclarationItem.NavigateTo(FieldPath.Create("Data", "Defaults")); foreach (var d in defaults.Children) { x.Add().Set(d.Get <int>()); } } return(x); } else { throw new ArgumentException("Unsupported schema specified", "schema"); } }
public void WriteTo(BinaryWriter writer) { if (!this.Frozen) { this.CloneAsEditable(true).WriteTo(writer); return; } if (this.IsNull) { writer.Write(new byte[this.Schema.FixedSize]); int sizeWidth = EditablePrimitive.SizeWidthForSize(this.SerializedSize); EditablePrimitive.WriteSize(writer, this.SerializedSize, sizeWidth); writer.Write(new byte[this.SerializedSize - this.Schema.FixedSize - sizeWidth]); // fill variable offset slot with zero bytes } else { writer.Write((int)dataSchema.Id); // schema field bool dataIsNull = dataCursor.IsNull && data == null; byte nullBitmap = dataIsNull ? (byte)1 : (byte)3; writer.Write(nullBitmap); int sizeWidth = EditablePrimitive.SizeWidthForSize(serializedSize); EditablePrimitive.WriteSize(writer, serializedSize, sizeWidth); // write total serialized size int offset = 5 + 2 * sizeWidth; // set offset behind variable size offset slots EditablePrimitive.WriteSize(writer, offset, sizeWidth); // variable field offset slot // write size of data EditablePrimitive.WriteSize(writer, EditablePrimitive.GetTotalFieldSize(this.DataSize)); if (!dataCursor.IsNull) { var segment = dataCursor.GetFieldSegment(); writer.Write(segment.Array, segment.Offset, segment.Count); } else if (data != null) { data.WriteTo(writer); } } }
public bool TryGetTypeForSchema(Schema schema, out Type type) { if (schema.DataType == DataType.MultiChoice) { type = typeof(MultiChoice); return(true); } if (IsPrimitiveDataType(schema.DataType)) { return(EditablePrimitive.TryGetTypeForPrimitiveSchema(schema, out type)); } lock (this) { if (typeBySchema.TryGetValue(schema, out type)) { return(true); } // fallback to range mappings if (schema.DataType == DataType.List) { var elementType = FindRangeMappedTypeForSchema(schema.Fields.First().Schema); if (elementType != null) { type = typeof(List <>).MakeGenericType(elementType); return(true); } } else { type = FindRangeMappedTypeForSchema(schema); if (type != null) { return(true); } } } return(false); }
public void WriteTo(BinaryWriter writer) { if (!this.Frozen) { this.CloneAsEditable(true).WriteTo(writer); return; } if (this.IsNull) { EditablePrimitive.WriteSize(writer, 1); } else { int serializedSize = this.SerializedSize; int sizeWidth = EditablePrimitive.SizeWidthForSize(serializedSize); EditablePrimitive.WriteSize(writer, serializedSize, sizeWidth); EditablePrimitive.WriteSize(writer, this.Count, sizeWidth); if (itemNullable) { var nullBitmap = new byte[(list.Count + 7) / 8]; for (int i = 0; i < list.Count; ++i) { if (!list[i].IsNull) { nullBitmap[(i / 8)] |= (byte)(1 << (i % 8)); } } writer.Write(nullBitmap); } foreach (var i in this) { i.WriteTo(writer); } } }
public static void WriteTo(this ICursor source, JsonWriter writer, bool stripNullValues = true) { if (source == null || source.IsNull) { writer.WriteNull(); } else { switch (source.Schema.DataType) { case DataType.Class: if (source.Schema.Id == (int)BuiltInSchema.Variable) { var content = source.GoTo((int)VariableLayout.Data, true); if (!content.IsNull && content.Schema.DataType != DataType.String && content.Schema.DataType != DataType.Boolean && (content.Schema.DataType == DataType.List || content.Schema.DataType == DataType.MultiChoice || EditablePrimitive.IsSupportedType(content.Schema.DataType) ) ) { writer.WriteStartObject(); writer.WritePropertyName("@schema"); writer.WriteValue(source.Schema.Name); writer.WritePropertyName("DataSchema"); writer.WriteValue(content.Schema.Name); writer.WritePropertyName("Data"); content.WriteTo(writer, stripNullValues); writer.WriteEndObject(); } else { content.WriteTo(writer, stripNullValues); } } else { writer.WriteStartObject(); writer.WritePropertyName("@schema"); writer.WriteValue(source.Schema.Name); foreach (var f in source.Schema.Fields) { var field = source.GoTo(f, false); if (!stripNullValues || !field.IsNull) { writer.WritePropertyName(f.Name); field.WriteTo(writer, stripNullValues); } } writer.WriteEndObject(); } break; case DataType.MultiChoice: case DataType.List: { writer.WriteStartArray(); for (int i = 0; i < source.Count; ++i) { var field = source.GoTo(i, false); if (!stripNullValues || !field.IsNull) { field.WriteTo(writer, stripNullValues); } } writer.WriteEndArray(); } break; default: { EditablePrimitive.PrimitiveType typeHelper; if (EditablePrimitive.TryGetPrimitiveTypeHelper(source.Schema, out typeHelper)) { typeHelper.JsonWrite(writer, source.Get()); } else { throw new Exception(string.Format("Unable to convert data type '{0}' to JSON string.", source.Schema.DataType)); } } break; } } }
object FromCursorInternal(ICursor cursor, Type targetType) { if (cursor == null || cursor.IsNull) { return(null); } if (EditablePrimitive.IsSupportedType(cursor.Schema.DataType)) { return(cursor.Get()); } else if (cursor.Schema.Id == (int)BuiltInSchema.DateTimeOffset) { return(new DateTimeOffset(cursor.GoTo("Time").Get <DateTime>(), cursor.GoTo("Offset").Get <TimeSpan>())); } else if (cursor.Schema.Id == (int)BuiltInSchema.Version) { int major = cursor.GoTo("Major").Get <int>(); int minor = cursor.GoTo("Minor").Get <int>(); int build = cursor.GoTo("Build").Get <int>(); int revision = cursor.GoTo("Revision").Get <int>(); if (revision >= 0) { return(new Version(major, minor, build, revision)); } if (build >= 0) { return(new Version(major, minor, build)); } return(new Version(major, minor)); } else if (cursor.Schema.Id == (int)BuiltInSchema.Item) { return(DeserializeItem(cursor)); } Type type; if (!schemaTypeMap.TryGetTypeForSchema(cursor.Schema, out type)) { if (cursor.Schema.DataType == DataType.Choice) { return(new Choice(cursor.Schema, cursor.Get <int?>(), true)); } else { if (targetType != null && (targetType.GetTypeInfo().IsAssignableFrom(typeof(IEditable)) || targetType.GetTypeInfo().IsAssignableFrom(typeof(IEditableVariable)))) { return(editableFactory.Create(cursor)); } throw new MissingTypeMappingException(cursor.Schema); } } else { if (targetType != null && !targetType.GetTypeInfo().IsAssignableFrom(type)) { if (targetType.GetTypeInfo().IsAssignableFrom(typeof(IEditable)) || targetType.GetTypeInfo().IsAssignableFrom(typeof(IEditableVariable))) { return(editableFactory.Create(cursor)); } else if (targetType.IsArray && typeof(System.Collections.IEnumerable).GetTypeInfo().IsAssignableFrom(type)) { type = targetType; } else { throw new InvalidCastException(string.Format("Cannot cast from {0} to {1}.", type.Name, targetType.Name)); } } } object obj = Construct(type, cursor); ApplyToObject(obj, cursor); return(obj); }
public virtual void WriteTo(BinaryWriter writer) { if (!this.Frozen) { this.CloneAsEditable(true).WriteTo(writer); return; } int serializedSize; if (this.IsNull) { serializedSize = this.SerializedSize; writer.Write(new byte[schema.FixedSize]); if (this.Schema.VariableSize) { int sizeWidth = EditablePrimitive.SizeWidthForSize(serializedSize); EditablePrimitive.WriteSize(writer, serializedSize, sizeWidth); writer.Write(new byte[serializedSize - schema.FixedSize - sizeWidth]); // pad after skip size with zero bytes (object is null) } return; } int i = 0, offset = 0; #if DEBUG serializedSize = this.SerializedSize; //long start = writer.Seek(0, SeekOrigin.Current); #endif // fixed size part for (; i < fields.Length; ++i) { var f = fields[i]; if (f.Schema.VariableSize) { break; } offset += f.SerializedSize; f.WriteTo(writer); //#if DEBUG // Debug.Assert((writer.Seek(0, SeekOrigin.Current) - start) == offset); //#endif } Debug.Assert(schema.variableFieldCount == fields.Length - i); Debug.Assert(schema.NullBitmapOffset < 0 || schema.NullBitmapOffset == offset); if (schema.NullBitmap) { byte[] nullBitmap = new byte[(fields.Length + 7) / 8]; // write null bitmap for (int k = 0; k < fields.Length; ++k) { var f = fields[k]; Debug.Assert(!f.IsNull || schema.fields[k].Nullable); if (!f.IsNull) { nullBitmap[k / 8] |= (byte)(1 << (k % 8)); } } offset += nullBitmap.Length; writer.Write(nullBitmap); } if (schema.VariableSize) { Debug.Assert(schema.VariableSizeOffset == offset); #if !DEBUG serializedSize = this.SerializedSize; #endif int sizeWidth = EditablePrimitive.SizeWidthForSize(serializedSize); EditablePrimitive.WriteSize(writer, serializedSize, sizeWidth); offset += (1 + schema.variableFieldCount) * sizeWidth; // set offset behind variable size offset slots // store variable field offsets int[] variableOffsets = new int[schema.variableFieldCount]; int j = i; for (; i < fields.Length; ++i) { var f = fields[i]; Debug.Assert(f.Schema.VariableSize); Debug.Assert(!f.IsNull || schema.fields[i].Nullable); EditablePrimitive.WriteSize(writer, offset, sizeWidth); variableOffsets[i - j] = offset; if (!f.IsNull) { offset += f.SerializedSize; } } // write variable field data for (i = j; i < fields.Length; ++i) { //#if DEBUG // Debug.Assert((writer.Seek(0, SeekOrigin.Current) - start) == variableOffsets[i - j]); //#endif var f = fields[i]; if (!f.IsNull) { f.WriteTo(writer); } } } #if DEBUG Debug.Assert(offset == serializedSize); //Debug.Assert((writer.Seek(0, SeekOrigin.Current) - start) == serializedSize); #endif }
public void UpdateLayout() { if (this.DataType == DataType.Class) { if (fields == null) { return; } bool nullBitmap = fields.Any(x => x.Nullable); variableFieldCount = fields.Count(x => x.Schema.VariableSize); // ensure all fixed size fields come first fields = fields.Where(x => !x.Schema.VariableSize).Concat(fields.Where(x => x.Schema.VariableSize)).ToArray(); int fixedSize = 0; int variableSlotIndex = -1; for (int i = 0; i < fields.Length; ++i) { var f = fields[i]; f.Index = i; if (!f.Schema.VariableSize) { f.Offset = fixedSize; fixedSize += f.Schema.FixedSize; } else { f.Offset = variableSlotIndex--; } } if (nullBitmap) { NullBitmapOffset = fixedSize; fixedSize += (this.fields.Length + 7) / 8; // space for null bitmap bytes } if (variableFieldCount > 0) { VariableSizeOffset = fixedSize; } this.FixedSize = fixedSize; } else if (this.DataType == DataType.List || this.DataType == DataType.MultiChoice) { if (this.DataType != DataType.MultiChoice) { if (this.FieldCount == 0) { fields = new Field[] { new Field() { Nullable = false, Schema = BuiltIn[BuiltInSchema.Int32] } } } ; if (fields.Length != 1) { throw new Exception("Invalid field count for list schema"); } } this.VariableSizeOffset = 0; this.FixedSize = 0; } else { if (this.FieldCount != 0) { throw new Exception("Primitive types must not have fields specified"); } this.FixedSize = EditablePrimitive.FixedSizeOf(this.DataType); } }
Schema BuildSchemaForClass(Type type, string schemaName, List <Tuple <Type, Schema> > schemaList, bool onlyMissing) { var excludedProperties = new List <String>(); if (typeof(Item).IsAssignableFrom(type)) { excludedProperties.AddRange(typeof(Item).GetTypeInfo().GetProperties().Select(x => x.Name)); } var sb = new SchemaBuilder(schemaName, DataType.Class, null); foreach (var p in type.GetTypeInfo().GetProperties().Where(x => x.CanRead && x.CanWrite && !excludedProperties.Contains(x.Name))) { if (p.GetCustomAttribute <NonRecordFieldAttribute>() != null) { continue; } var fieldAttribute = p.GetCustomAttribute <RecordFieldAttribute>(); string caption = null; string description = null; int? maxLength = null; bool? nullable = null; if (fieldAttribute != null) { caption = fieldAttribute.Caption; description = fieldAttribute.Description; if (fieldAttribute.MaxLength > 0) { maxLength = fieldAttribute.MaxLength; } nullable = fieldAttribute.Nullable; } if (nullable == null && (Nullable.GetUnderlyingType(p.PropertyType) != null || typeof(INullable).IsAssignableFrom(p.PropertyType))) { nullable = true; } Schema schema; Type propertyType = Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType; if (propertyType == typeof(Choice) || propertyType == typeof(MultiChoice)) { if (fieldAttribute == null) { throw new Exception(string.Format("CacheFieldAttribute missing for property '{0}' of type {1}.", p.Name, propertyType)); } schema = schemaProvider.GetSchemaByName(fieldAttribute.SchemaName); if (p.PropertyType == typeof(Choice) && schema.DataType != DataType.Choice) { throw new Exception(string.Format("Choice field expected but field has data type: '{0}'", schema.DataType)); } else if (p.PropertyType == typeof(MultiChoice) && schema.DataType != DataType.MultiChoice) { throw new Exception(string.Format("MultiChoice field expected but field has data type: '{0}'", schema.DataType)); } sb.AddField(p.Name, schema, nullable.Value, maxLength, caption, description); } else if (EditablePrimitive.TryGetPrimitiveSchemaForType(p.PropertyType, out schema)) { // primitive data type first (covers nullable primitive types) nullable = nullable ?? (p.PropertyType == typeof(string) || p.PropertyType == typeof(byte[])); sb.AddField(p.Name, schema, nullable.Value, maxLength, caption, description); } else if (typeof(ICursor).IsAssignableFrom(p.PropertyType)) { // create variable field (if no schema was explicitely specified) if (fieldAttribute != null && fieldAttribute.SchemaName != null) { schema = schemaProvider.GetSchemaByName(fieldAttribute.SchemaName); } else { schema = Schema.BuiltIn[BuiltInSchema.Variable]; } sb.AddField(p.Name, schema, nullable ?? true, maxLength, caption, description); } else if (p.PropertyType == typeof(object)) { sb.AddField(p.Name, Schema.BuiltIn[BuiltInSchema.Variable], nullable ?? true, maxLength, caption, description); } else { if (propertyType.GetTypeInfo().IsEnum&& fieldAttribute != null) { schema = schemaProvider.GetSchemaByName(fieldAttribute.SchemaName); } else if (!typeMap.TryGetSchemaForType(propertyType, out schema)) { var t = schemaList.Find(x => x.Item1 == propertyType); if (t != null) { schema = t.Item2; } else { schema = BuildSchemaRecursively(propertyType, null, schemaList, onlyMissing); } } if (schema == null) { throw new MissingTypeMappingException(string.Format("Unable to generate a schema for property '{0}' (type'{1}') of class '{2}'.", p.Name, p.PropertyType.FullName, type.FullName)); } // a schema was registerd for the property type if (!nullable.HasValue) { nullable = !p.PropertyType.GetTypeInfo().IsValueType; } sb.AddField(p.Name, schema, nullable.Value, maxLength, caption, description); } } var result = sb.ToSchema(); var recordAttribute = type.GetTypeInfo().GetCustomAttribute <RecordAttribute>(); if (recordAttribute != null) { result.Id = recordAttribute.SchemaId; } schemaList.Add(Tuple.Create(type, result)); return(result); }
public void Set(object value) { if (this.Frozen) { throw new FrozenObjectReadonlyException(); } ICursor source = null; if (value != null) { source = value as ICursor; if (source == null) { source = new EditablePrimitive(factory, value); } else { var sourceVariable = source as IEditableVariable; if (sourceVariable != null) { if (!sourceVariable.DataCursor.IsNull) { this.DataCursor = sourceVariable.DataCursor; return; } if (!sourceVariable.IsNull) { source = source.Get() as ICursor; } else { source = null; } } else if (source.Schema.Id == (int)BuiltInSchema.Variable) { if (source.IsNull) { this.Set(null); } else { var schema = this.SchemaProvider.GetSchemaById(source.GoTo((int)VariableLayout.DataSchemaId).Get <int>()); this.DataCursor = new Cursor(this.SchemaProvider, schema, new ArraySegment <byte>(source.GoTo((int)VariableLayout.Data, false).Get <byte[]>())); } return; } } } if (source == null) { if (!nullable) { throw new InvalidOperationException("Tried to set null on a variable field that is not nullable."); } data = null; dataCursor = default(Cursor); return; } if (data == null || dataSchema != source.Schema) { data = factory.Create(source.Schema, nullable); dataSchema = source.Schema; } dataCursor = default(Cursor); data.Set(source); }
protected virtual int CalculateSerializedSize() { int variableSize = this.IsNull ? 0 : EditablePrimitive.GetTotalFieldSize(this.DataSize); return(EditableObject.CalculateSerializedSize(this.Schema, variableSize)); }