예제 #1
0
        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);
        }
예제 #2
0
        public IEditable CloneAsEditable(bool frozen)
        {
            var c = new EditablePrimitive(factory, this.schema, nullable);

            c.Set(data);
            if (frozen)
            {
                c.Freeze();
            }
            return(c);
        }
예제 #3
0
        public bool TryGetSchemaForType(Type type, out Schema schema)
        {
            lock (this)
            {
                if (schemaByType.TryGetValue(type, out schema))
                {
                    return(true);
                }
            }

            return(EditablePrimitive.TryGetPrimitiveSchemaForType(type, out schema));
        }
예제 #4
0
        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");
            }
        }
예제 #5
0
        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);
                }
            }
        }
예제 #6
0
        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);
        }
예제 #7
0
        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);
                }
            }
        }
예제 #8
0
        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;
                }
            }
        }
예제 #9
0
        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);
        }
예제 #10
0
        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
        }
예제 #11
0
        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);
            }
        }
예제 #12
0
        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);
        }
예제 #13
0
        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);
        }
예제 #14
0
        protected virtual int CalculateSerializedSize()
        {
            int variableSize = this.IsNull ? 0 : EditablePrimitive.GetTotalFieldSize(this.DataSize);

            return(EditableObject.CalculateSerializedSize(this.Schema, variableSize));
        }