/// <inheritdoc /> public virtual void Connect(IDatabase database, TableFlags flags, RowLayout layout) => BaseTable.Connect(database, flags, layout);
/// <inheritdoc /> public virtual void UseLayout(RowLayout layout) => BaseTable.UseLayout(layout);
/// <inheritdoc /> public override void Connect(IDatabase database, TableFlags flags, RowLayout layout) => BaseTable.Connect(database, flags, layout);
/// <inheritdoc /> public abstract void Connect(IDatabase database, TableFlags flags, RowLayout layout);
/// <summary>Scans a Table for matches with the current search.</summary> /// <param name="preselected">The preselected ids.</param> /// <param name="layout">Layout of the table.</param> /// <param name="indices">FieldIndices or null.</param> /// <param name="table">The table to scan.</param> /// <returns>All rows matching this search.</returns> public IEnumerable <Row> Scan(IEnumerable <Row> preselected, RowLayout layout, IFieldIndex[] indices, ITable table) { if (table == null) { throw new ArgumentNullException(nameof(table)); } if (layout == null) { throw new ArgumentNullException(nameof(layout)); } LoadLayout(layout); switch (Mode) { case SearchMode.None: { if (Inverted) { throw new InvalidOperationException("Cannot invert an empty search!"); } return(table.GetRows() ?? preselected); } case SearchMode.And: { var resultA = SearchA.Scan(preselected, layout, indices, table); var result = SearchB.Scan(resultA, layout, indices, table); return(!Inverted ? result : Invert(preselected ?? table.GetRows(), result)); } case SearchMode.Or: { var result = SearchA.Scan(preselected, layout, indices, table); var resultB = SearchB.Scan(preselected, layout, indices, table); result = result.Union(resultB).ToList(); return(Inverted ? Invert(preselected ?? table.GetRows(), result) : result); } case SearchMode.In: { IEnumerable <Row> result = new List <Row>(); foreach (var value in (Set <object>)FieldValue) { result = result.Union(table.GetRows(FieldEquals(FieldName, value))); } return(Inverted ? Invert(preselected ?? table.GetRows(), result) : result); } case SearchMode.Smaller: case SearchMode.Greater: case SearchMode.SmallerOrEqual: case SearchMode.GreaterOrEqual: case SearchMode.Like: case SearchMode.Equals: { if (Mode == SearchMode.Equals) { // check if we can do an index search if ((indices != null) && (indices[FieldNumber] != null)) { // field has an index var result = indices[FieldNumber].Find(FieldValue).Select(r => new Row(Layout, r, true)); if (preselected != null) { result = result.Intersect(preselected); } return(Inverted ? Invert(preselected ?? table.GetRows(), result) : result); } #if DEBUG // field has no index, need table scan if ((preselected == null) && (table.RowCount > 1000)) { Debug.WriteLine( $"Warning: Doing slow memory search on Table {layout.Name} Field {FieldName}! You should consider adding an index!"); } #endif } // scan without index { var result = new List <Row>(); if (preselected != null) { foreach (var row in preselected) { if (Check(row)) { result.Add(row); } } } else { foreach (var row in table.GetRows()) { if (Check(row)) { result.Add(row); } } } return(result); } } default: throw new NotImplementedException($"Mode {Mode} not implemented!"); } }
internal static RowLayout LoadFieldDefinition(DataReader reader, out int version) { var dateTimeKind = DateTimeKind.Unspecified; var dateTimeType = DateTimeType.Undefined; var stringEncoding = StringEncoding.UTF8; if (reader.ReadString(8) != "DatTable") { throw new FormatException(); } version = reader.Read7BitEncodedInt32(); if ((version < 1) || (version > 4)) { throw new InvalidDataException("Unknown Table version!"); } // read name and create layout var layoutName = reader.ReadString(); var fieldCount = reader.Read7BitEncodedInt32(); var fields = new FieldProperties[fieldCount]; for (var i = 0; i < fieldCount; i++) { var fieldName = reader.ReadString(); var dataType = (DataType)reader.Read7BitEncodedInt32(); var fieldFlags = (FieldFlags)reader.Read7BitEncodedInt32(); var databaseDataType = dataType; switch (dataType) { case DataType.Enum: databaseDataType = DataType.Int64; break; case DataType.User: case DataType.String: databaseDataType = DataType.String; if (version > 2) { stringEncoding = (StringEncoding)reader.Read7BitEncodedInt32(); } else { stringEncoding = StringEncoding.UTF8; } break; case DataType.TimeSpan: if (version > 3) { dateTimeType = (DateTimeType)reader.Read7BitEncodedInt32(); } break; case DataType.DateTime: if (version > 1) { dateTimeKind = (DateTimeKind)reader.Read7BitEncodedInt32(); dateTimeType = (DateTimeType)reader.Read7BitEncodedInt32(); } else { dateTimeKind = DateTimeKind.Utc; dateTimeType = DateTimeType.BigIntHumanReadable; } break; } Type valueType = null; if ((dataType & DataType.MaskRequireValueType) != 0) { var typeName = reader.ReadString(); valueType = AppDom.FindType(typeName.BeforeFirst(','), typeName.AfterFirst(',').Trim()); } var field = fields[i] = new FieldProperties { Index = i, Flags = fieldFlags, DataType = dataType, ValueType = valueType, Name = fieldName, TypeAtDatabase = databaseDataType, NameAtDatabase = fieldName, DateTimeType = dateTimeType, DateTimeKind = dateTimeKind, StringEncoding = stringEncoding }; field.Validate(); } return(RowLayout.CreateUntyped(layoutName, fields)); }
/// <summary>Builds the csharp code file containing the row layout structure.</summary> /// <param name="layout">The layout to use.</param> /// <param name="databaseName">The database name (only used for the structure name).</param> /// <param name="tableName">The table name (only used for the structure name).</param> /// <param name="className">The name of the class to generate.</param> /// <param name="structFile">The struct file name (defaults to classname.cs).</param> /// <param name="namingStrategy">Naming strategy for classes, properties, structures and fields.</param> /// <returns>The struct file name.</returns> public static GenerateTableCodeResult GenerateStructFile(this RowLayout layout, string databaseName = null, string tableName = null, string className = null, string structFile = null, NamingStrategy namingStrategy = NamingStrategy.CamelCase) => GenerateStruct(layout, databaseName, tableName, className, namingStrategy).SaveStructFile(structFile);
/// <summary>Not supported.</summary> /// <param name="layout">Unused parameter.</param> public override void UseLayout(RowLayout layout) => throw new NotSupportedException();
/// <summary> /// Parses a single row of data from the specified string. /// </summary> /// <param name="properties">The csv properties.</param> /// <param name="layout">The row layout.</param> /// <param name="data">The buffer to parse.</param> /// <returns>Returns a new <see cref="Row"/> instance.</returns> public static Row ParseRow(CsvProperties properties, RowLayout layout, string data) { if (!properties.Valid) { throw new ArgumentException("CsvProperties invalid!", nameof(properties)); } if (layout == null) { throw new ArgumentNullException(nameof(layout)); } if (data == null) { throw new ArgumentNullException(nameof(data)); } try { var fieldCount = layout.FieldCount; var fieldNumber = 0; var ident = new Queue <char>(); var identInARowCount = 0; var currentValue = new List <char>(); var i = -1; var values = new object[fieldCount]; while (fieldNumber < fieldCount) { ++i; if ((i == data.Length) && (fieldNumber == (fieldCount - 1))) { break; } if (i >= data.Length) { throw new InvalidDataException("Unexpected end of input!"); } if (properties.Separator == data[i]) { if (ident.Count == 0) { identInARowCount = 0; if (properties.StringMarker.HasValue) { values[fieldNumber] = layout.ParseValue(fieldNumber, new string(currentValue.ToArray()).Unescape(), properties.StringMarker.Value.ToString(), properties.Format); } else { values[fieldNumber] = layout.ParseValue(fieldNumber, new string(currentValue.ToArray()).Unescape(), string.Empty, properties.Format); } fieldNumber++; currentValue.Clear(); continue; } } if (properties.StringMarker == data[i]) { identInARowCount++; if ((ident.Count > 0) && (ident.Peek() == data[i])) { ident.Dequeue(); if (identInARowCount > 1) { // escaped char currentValue.Add(data[i]); } } else { ident.Enqueue(data[i]); } } else { identInARowCount = 0; currentValue.Add(data[i]); } } if (ident.Count > 0) { throw new InvalidDataException("Invalid ident/escaping!"); } if (properties.StringMarker.HasValue) { values[fieldNumber] = layout.ParseValue(fieldNumber, new string(currentValue.ToArray()).Unescape(), properties.StringMarker.Value.ToString(), properties.Format); } else { values[fieldNumber] = layout.ParseValue(fieldNumber, new string(currentValue.ToArray()).Unescape(), string.Empty, properties.Format); } fieldNumber++; if (i < data.Length) { if (properties.Separator == data[i]) { i++; } if (i < data.Length) { throw new InvalidDataException("Additional data at end of line!"); } } return(new Row(layout, values, false)); } catch (EndOfStreamException) { if (data.Length > 0) { throw; } return(null); } }
/// <inheritdoc /> public ITable GetTable(RowLayout layout, TableFlags flags = default) => database.GetTable(layout, flags);
/// <summary> /// Initializes a new instance of the <see cref="CsvReader"/> class. /// </summary> /// <param name="properties">Properties to apply to the reader.</param> /// <param name="layout">Layout to use when reading the csv data.</param> /// <param name="stream">Stream to read data from.</param> /// <param name="closeBaseStream">if set to <c>true</c> [close base stream on close].</param> public CsvReader(RowLayout layout, Stream stream, CsvProperties properties = default, bool closeBaseStream = false) { Layout = layout; BaseStream = stream ?? throw new ArgumentNullException(nameof(stream)); Properties = properties.Valid ? properties : CsvProperties.Default; CloseBaseStream = closeBaseStream; reader = new DataReader(stream, Properties.Encoding, Properties.NewLineMode); if (!Properties.NoHeader) { var header = reader.ReadLine(); currentRowNumber++; var fields = header.Split(Properties.Separator); if (!Properties.AllowFieldMatching) { if (fields.Length != Layout.FieldCount) { if ((fields.Length - 1) != Layout.FieldCount) { throw new InvalidDataException($"Invalid header fieldcount (expected '{Layout.FieldCount}' got '{fields.Length}')!"); } } } else { if (fields.Length != Layout.FieldCount) { fieldNumberMatching = new int[Layout.FieldCount]; } } var count = Math.Min(Layout.FieldCount, fields.Length); for (var i = 0; i < count; i++) { var fieldName = fields[i].UnboxText(false); var fieldIndex = Layout.GetFieldIndex(fieldName, false); if (fieldIndex < 0) { throw new InvalidDataException( $"Error loading CSV Header! Got field name '{fieldName}' instead of '{Layout[i].Name}' at type '{Layout.Name}'"); } if (!Properties.AllowFieldMatching) { if (fieldIndex != i) { throw new InvalidDataException($"Fieldposition of Field '{fieldName}' does not match!"); } if (!string.Equals(Layout[fieldIndex].Name, fieldName, StringComparison.Ordinal)) { throw new InvalidDataException( $"Invalid header value at field number '{i}' name '{fieldName}' expected '{Layout[fieldIndex].Name}'!"); } } else { if ((fieldNumberMatching == null) && (fieldIndex != i)) { fieldNumberMatching = new int[Layout.FieldCount]; } } } if (fieldNumberMatching != null) { var i = 0; for (; i < count; i++) { var fieldName = fields[i].UnboxText(false); fieldNumberMatching[i] = Layout.GetFieldIndex(fieldName, false); } for (; i < Layout.FieldCount; i++) { fieldNumberMatching[i] = -1; } } } }
/// <summary> /// Initializes a new instance of the <see cref="CsvReader"/> class. /// </summary> /// <param name="properties">Properties to apply to the reader.</param> /// <param name="layout">Layout to use when reading from the csv file.</param> /// <param name="fileName">Filename to write to.</param> public CsvReader(RowLayout layout, string fileName, CsvProperties properties = default) : this(layout, File.OpenRead(fileName), properties, true) { }
/// <inheritdoc /> public abstract void UseLayout(RowLayout layout);
static void SerializeData(DataWriter writer, RowLayout layout, Row row) { for (var i = 0; i < layout.FieldCount; i++) { var dataType = layout[i].DataType; switch (dataType) { case DataType.Binary: { var data = (byte[])row[i]; if (data == null) { data = new byte[0]; } writer.Write7BitEncoded32(data.Length); writer.Write(data); break; } case DataType.Bool: writer.Write((bool)row[i]); break; case DataType.TimeSpan: writer.Write((TimeSpan)row[i]); break; case DataType.DateTime: writer.Write((DateTime)row[i]); break; case DataType.Single: writer.Write((float)row[i]); break; case DataType.Double: writer.Write((double)row[i]); break; case DataType.Int8: writer.Write((sbyte)row[i]); break; case DataType.Int16: writer.Write((short)row[i]); break; case DataType.UInt8: writer.Write((byte)row[i]); break; case DataType.UInt16: writer.Write((ushort)row[i]); break; case DataType.Int32: writer.Write7BitEncoded32((int)row[i]); break; case DataType.Int64: writer.Write7BitEncoded64((long)row[i]); break; case DataType.UInt32: writer.Write7BitEncoded32((uint)row[i]); break; case DataType.UInt64: writer.Write7BitEncoded64((ulong)row[i]); break; case DataType.Char: writer.Write((char)row[i]); break; case DataType.Decimal: writer.Write((decimal)row[i]); break; case DataType.String: case DataType.User: { var data = row[i]; var str = data?.ToString(); writer.WritePrefixed(str); break; } case DataType.Enum: { var value = Convert.ToInt64(row[i], CultureInfo.InvariantCulture); writer.Write7BitEncoded64(value); break; } default: throw new NotImplementedException($"Datatype {dataType} not implemented!"); } } }
/// <summary>Obtains a row value as string using the string format defined at the rowlayout.</summary> /// <param name="layout">The layout.</param> /// <param name="index">The field index.</param> /// <returns>The string to display.</returns> public string GetDisplayString(RowLayout layout, int index) { var value = Values[index]; return(value == null ? string.Empty : layout.GetDisplayString(index, value)); }
static Row DeserializeData(DataReader reader, RowLayout layout) { var values = new object[layout.FieldCount]; for (var i = 0; i < layout.FieldCount; i++) { var dataType = layout[i].DataType; switch (dataType) { case DataType.Binary: var size = reader.Read7BitEncodedInt32(); values[i] = reader.ReadBytes(size); break; case DataType.Bool: values[i] = reader.ReadBool(); break; case DataType.DateTime: values[i] = reader.ReadDateTime(); break; case DataType.TimeSpan: values[i] = reader.ReadTimeSpan(); break; case DataType.Int8: values[i] = reader.ReadInt8(); break; case DataType.Int16: values[i] = reader.ReadInt16(); break; case DataType.Int32: values[i] = reader.Read7BitEncodedInt32(); break; case DataType.Int64: values[i] = reader.Read7BitEncodedInt64(); break; case DataType.UInt32: values[i] = reader.Read7BitEncodedUInt32(); break; case DataType.UInt64: values[i] = reader.Read7BitEncodedUInt64(); break; case DataType.UInt8: values[i] = reader.ReadUInt8(); break; case DataType.UInt16: values[i] = reader.ReadUInt16(); break; case DataType.Char: values[i] = reader.ReadChar(); break; case DataType.Single: values[i] = reader.ReadSingle(); break; case DataType.Double: values[i] = reader.ReadDouble(); break; case DataType.Decimal: values[i] = reader.ReadDecimal(); break; case DataType.String: values[i] = reader.ReadString(); break; case DataType.Enum: values[i] = reader.Read7BitEncodedInt64(); break; case DataType.User: values[i] = reader.ReadString(); break; default: throw new NotImplementedException($"Datatype {dataType} not implemented!"); } } return(new Row(layout, values, false)); }
void WriteFieldDefinition(DataWriter writer, RowLayout layout, int version) { if (version < 1) { throw new ArgumentOutOfRangeException(nameof(version)); } if (version > 4) { throw new NotSupportedException("Version not supported!"); } try { writer.Write("DatTable"); writer.Write7BitEncoded32(version); writer.WritePrefixed(layout.Name); writer.Write7BitEncoded32(layout.FieldCount); for (var i = 0; i < layout.FieldCount; i++) { var field = layout[i]; writer.WritePrefixed(field.Name); writer.Write7BitEncoded32((int)field.DataType); writer.Write7BitEncoded32((int)field.Flags); switch (field.DataType) { case DataType.User: case DataType.String: if (version > 2) { writer.Write7BitEncoded32((int)field.StringEncoding); } break; case DataType.DateTime: if (version > 1) { writer.Write7BitEncoded32((int)field.DateTimeKind); writer.Write7BitEncoded32((int)field.DateTimeType); } break; case DataType.TimeSpan: if (version > 3) { writer.Write7BitEncoded32((int)field.DateTimeType); } break; } if ((field.DataType & DataType.MaskRequireValueType) != 0) { var typeName = field.ValueType.AssemblyQualifiedName; var parts = typeName.Split(','); typeName = $"{parts[0]},{parts[1]}"; writer.WritePrefixed(typeName); } } writer.Flush(); } catch (Exception ex) { throw new InvalidOperationException("Could not write field definition!", ex); } }
/// <inheritdoc /> public abstract ITable CreateTable(RowLayout layout, TableFlags flags = default);
/// <summary>Initializes a new instance of the <see cref="DatWriter" /> class.</summary> /// <param name="layout">Table layout.</param> /// <param name="fileName">Filename to write to.</param> public DatWriter(RowLayout layout, string fileName) : this(layout, File.Create(fileName)) { }
/// <summary>Builds the csharp code containing the row layout structure.</summary> /// <param name="layout">The layout to use.</param> /// <param name="databaseName">The database name (only used for the structure name).</param> /// <param name="tableName">The table name (only used for the structure name).</param> /// <param name="className">The name of the class to generate.</param> /// <param name="nameSpace">The namespace to use for the class (defaults to "Database").</param> /// <param name="namingStrategy">Naming strategy for classes, properties, structures and fields.</param> /// <returns>Returns a string containing csharp code.</returns> public static GenerateTableCodeResult GenerateStruct(this RowLayout layout, string databaseName, string tableName = null, string className = null, string nameSpace = null, NamingStrategy namingStrategy = NamingStrategy.CamelCase) { if (layout == null) { throw new ArgumentNullException(nameof(layout)); } #region GetName() string[] GetNameParts(string text) => text.ReplaceInvalidChars(ASCII.Strings.Letters + ASCII.Strings.Digits, "_").Split('_').SelectMany(s => s.SplitCamelCase()).ToArray(); string GetName(string text) => namingStrategy switch { NamingStrategy.CamelCase => GetNameParts(text).JoinCamelCase(), NamingStrategy.SnakeCase => GetNameParts(text).JoinSnakeCase(), NamingStrategy.Exact => text, _ => throw new NotImplementedException($"Unknown NamingStrategy {namingStrategy}.") }; #endregion if (nameSpace == null) { nameSpace = "Database"; } if (databaseName == null) { throw new ArgumentNullException(nameof(databaseName)); } if (tableName == null) { tableName = layout.Name; } var fieldNameLookup = new Dictionary <int, string>(); var idCount = layout.Identifier.Count(); var idFields = (idCount == 0 ? layout : layout.Identifier).ToList(); var code = new StringBuilder(); code.AppendLine("//-----------------------------------------------------------------------"); code.AppendLine("// <summary>"); code.AppendLine("// Autogenerated table class"); code.AppendLine($"// Using {typeof(ITableExtensions).Assembly.FullName}"); code.AppendLine("// </summary>"); code.AppendLine("// <auto-generated />"); code.AppendLine("//-----------------------------------------------------------------------"); code.AppendLine(); code.AppendLine("using System;"); code.AppendLine("using System.Globalization;"); code.AppendLine("using Cave.Data;"); #region Build lookup tables void BuildLookupTables() { var uniqueFieldNames = new IndexedSet <string>(); foreach (var field in layout) { var sharpName = GetName(field.Name); var i = 0; while (uniqueFieldNames.Contains(sharpName)) { sharpName = GetName(field.Name) + ++i; } uniqueFieldNames.Add(sharpName); fieldNameLookup[field.Index] = sharpName; } } BuildLookupTables(); #endregion if (className == null) { className = GetName(databaseName) + GetName(tableName) + "Row"; } code.AppendLine(); code.AppendLine($"namespace {nameSpace}"); code.AppendLine("{"); code.AppendLine($"\t/// <summary>Table structure for {layout.Name}.</summary>"); code.AppendLine($"\t[Table(\"{layout.Name}\")]"); code.AppendLine($"\tpublic partial struct {className} : IEquatable<{className}>"); code.AppendLine("\t{"); #region static Parse() code.AppendLine($"\t\t/// <summary>Converts the string representation of a row to its {className} equivalent.</summary>"); code.AppendLine("\t\t/// <param name=\"data\">A string that contains a row to convert.</param>"); code.AppendLine($"\t\t/// <returns>A new {className} instance.</returns>"); code.AppendLine($"\t\tpublic static {className} Parse(string data) => Parse(data, CultureInfo.InvariantCulture);"); code.AppendLine(); code.AppendLine($"\t\t/// <summary>Converts the string representation of a row to its {className} equivalent.</summary>"); code.AppendLine("\t\t/// <param name=\"data\">A string that contains a row to convert.</param>"); code.AppendLine("\t\t/// <param name=\"provider\">The format provider (optional).</param>"); code.AppendLine($"\t\t/// <returns>A new {className} instance.</returns>"); code.AppendLine($"\t\tpublic static {className} Parse(string data, IFormatProvider provider) => CsvReader.ParseRow<{className}>(data, provider);"); #endregion #region Add fields foreach (var field in layout) { code.AppendLine(); code.AppendLine($"\t\t/// <summary>{field} {field.Description}.</summary>"); if (!string.IsNullOrEmpty(field.Description)) { code.AppendLine($"\t\t[Description(\"{field} {field.Description}\")]"); } code.Append("\t\t[Field("); var i = 0; void AddAttribute <T>(T value, Func <string> content) { if (Equals(value, default)) { return; } if (i++ > 0) { code.Append(", "); } code.Append(content()); } if (field.Flags != 0) { code.Append("Flags = "); var flagCount = 0; foreach (var flag in field.Flags.GetFlags()) { if (flagCount++ > 0) { code.Append(" | "); } code.Append("FieldFlags."); code.Append(flag); } code.Append(", "); } var sharpName = fieldNameLookup[field.Index]; if (sharpName != field.Name) { AddAttribute(field.Name, () => $"Name = \"{field.Name}\""); } if (field.MaximumLength < int.MaxValue) { AddAttribute(field.MaximumLength, () => $"Length = {(int)field.MaximumLength}"); } AddAttribute(field.AlternativeNames, () => $"AlternativeNames = \"{field.AlternativeNames.Join(", ")}\""); AddAttribute(field.DisplayFormat, () => $"DisplayFormat = \"{field.DisplayFormat.EscapeUtf8()}\""); code.AppendLine(")]"); if ((field.DateTimeKind != DateTimeKind.Unspecified) || (field.DateTimeType != DateTimeType.Undefined)) { code.AppendLine($"\t\t[DateTimeFormat({field.DateTimeKind}, {field.DateTimeType})]"); } if (field.StringEncoding != 0) { code.AppendLine($"\t\t[Cave.IO.StringFormat(Cave.IO.StringEncoding.{field.StringEncoding})]"); } code.AppendLine($"\t\tpublic {field.DotNetTypeName} {sharpName};"); } #endregion #region ToString() { code.AppendLine(); code.AppendLine("\t\t/// <summary>Gets a string representation of this row.</summary>"); code.AppendLine("\t\t/// <returns>Returns a string that can be parsed by <see cref=\"Parse(string)\"/>.</returns>"); code.AppendLine("\t\tpublic override string ToString() => ToString(CultureInfo.InvariantCulture);"); code.AppendLine(); code.AppendLine("\t\t/// <summary>Gets a string representation of this row.</summary>"); code.AppendLine("\t\t/// <returns>Returns a string that can be parsed by <see cref=\"Parse(string, IFormatProvider)\"/>.</returns>"); code.AppendLine("\t\tpublic string ToString(IFormatProvider provider) => CsvWriter.RowToString(this, provider);"); } #endregion #region GetHashCode() { code.AppendLine(); if (idCount == 1) { var idField = layout.Identifier.First(); var idFieldName = fieldNameLookup[idField.Index]; code.AppendLine($"\t\t/// <summary>Gets the hash code for the identifier of this row (field {idFieldName}).</summary>"); code.AppendLine("\t\t/// <returns>A hash code for the identifier of this row.</returns>"); code.Append("\t\tpublic override int GetHashCode() => "); code.Append(idFieldName); code.AppendLine(".GetHashCode();"); } else { if (idCount == 0) { code.AppendLine("\t\t/// <summary>Gets the hash code based on all values of this row (no identififer defined).</summary>"); } else { var names = idFields.Select(field => fieldNameLookup[field.Index]).Join(", "); code.AppendLine($"\t\t/// <summary>Gets the hash code for the identifier of this row (fields {names}).</summary>"); } code.AppendLine("\t\t/// <returns>A hash code for the identifier of this row.</returns>"); code.AppendLine("\t\tpublic override int GetHashCode() =>"); var first = true; foreach (var idField in idFields) { if (first) { first = false; } else { code.AppendLine(" ^"); } code.Append($"\t\t\t{fieldNameLookup[idField.Index]}.GetHashCode()"); } code.AppendLine(";"); } } #endregion #region Equals() { code.AppendLine(); code.AppendLine("\t\t/// <inheritdoc/>"); code.AppendLine($"\t\tpublic override bool Equals(object other) => other is {className} row && Equals(row);"); code.AppendLine(); code.AppendLine("\t\t/// <inheritdoc/>"); code.AppendLine($"\t\tpublic bool Equals({className} other)"); code.AppendLine("\t\t{"); code.AppendLine("\t\t\treturn"); { var first = true; foreach (var field in layout) { if (first) { first = false; } else { code.AppendLine(" &&"); } var name = fieldNameLookup[field.Index]; code.Append($"\t\t\t\tEquals(other.{name}, {name})"); } code.AppendLine(";"); } code.AppendLine("\t\t}"); } #endregion code.AppendLine("\t}"); code.AppendLine("}"); code.Replace("\t", " "); return(new() { ClassName = className, TableName = tableName, DatabaseName = databaseName, Code = code.ToString() }); }
/// <summary>Creates a string representation of the specified row.</summary> /// <param name="properties">The csv properties.</param> /// <param name="layout">The row layout.</param> /// <param name="row">The row.</param> /// <returns>Returns a new string representing the row.</returns> public static string RowToString(CsvProperties properties, RowLayout layout, Row row) { var result = new StringBuilder(); var values = row.Values; for (var i = 0; i < layout.FieldCount; i++) { if (i > 0) { result.Append(properties.Separator); } if ((values != null) && (values[i] != null)) { var field = layout[i]; switch (field.DataType) { case DataType.Binary: { var str = Base64.NoPadding.Encode((byte[])values[i]); result.Append(str); break; } case DataType.Bool: case DataType.Int8: case DataType.Int16: case DataType.Int32: case DataType.Int64: case DataType.UInt8: case DataType.UInt16: case DataType.UInt32: case DataType.UInt64: { if (!properties.SaveDefaultValues && values[i].Equals(0)) { break; } var str = values[i].ToString(); result.Append(str); break; } case DataType.Char: { if (!properties.SaveDefaultValues && values[i].Equals((char)0)) { break; } var str = values[i].ToString(); result.Append(str); break; } case DataType.Decimal: { if (!properties.SaveDefaultValues && values[i].Equals(0m)) { break; } var value = (decimal)values[i]; result.Append(value.ToString(properties.Format)); break; } case DataType.Single: { if (!properties.SaveDefaultValues && values[i].Equals(0f)) { break; } var value = (float)values[i]; result.Append(value.ToString("R", properties.Format)); break; } case DataType.Double: { if (!properties.SaveDefaultValues && values[i].Equals(0d)) { break; } var value = (double)values[i]; result.Append(value.ToString("R", properties.Format)); break; } case DataType.TimeSpan: { if (!properties.SaveDefaultValues && values[i].Equals(TimeSpan.Zero)) { break; } var str = field.GetString(values[i], $"{properties.StringMarker}", properties.Format); result.Append(str); break; } case DataType.DateTime: { if (!properties.SaveDefaultValues && values[i].Equals(new DateTime(0))) { break; } string str; if (properties.DateTimeFormat != null) { str = ((DateTime)values[i]).ToString(properties.DateTimeFormat, properties.Format); } else { str = field.GetString(values[i], $"{properties.StringMarker}", properties.Format); } result.Append(str); break; } case DataType.User: case DataType.String: { if (!properties.SaveDefaultValues && values[i].Equals(string.Empty)) { break; } var str = values[i] == null ? string.Empty : values[i].ToString(); str = str.EscapeUtf8(); if (properties.StringMarker.HasValue) { str = str.Replace($"{properties.StringMarker}", $"{properties.StringMarker}{properties.StringMarker}"); result.Append(properties.StringMarker); } if (str.Length == 0) { result.Append(" "); } else { if (properties.StringMarker.HasValue) { if (str.StartsWith(properties.StringMarker.ToString())) { result.Append(" "); } } result.Append(str); if (properties.StringMarker.HasValue) { if (str.EndsWith(properties.StringMarker.ToString())) { result.Append(" "); } } } if (properties.StringMarker.HasValue) { result.Append(properties.StringMarker); } break; } case DataType.Enum: { if (!properties.SaveDefaultValues && Convert.ToInt32(values[i]).Equals(0)) { break; } var str = values[i].ToString(); result.Append(str); break; } default: throw new NotImplementedException($"DataType {layout[i].DataType} is not implemented!"); } } } return(result.ToString()); }
internal static Row ReadCurrentRow(DataReader reader, int version, RowLayout layout) { if (version < 1) { throw new ArgumentOutOfRangeException(nameof(version)); } if (version > 4) { throw new NotSupportedException("Version not supported!"); } var dataStart = reader.BaseStream.Position; var dataSize = reader.Read7BitEncodedInt32(); if (dataSize == 0) { return(null); } var values = new object[layout.FieldCount]; for (var i = 0; i < layout.FieldCount; i++) { var field = layout[i]; var dataType = field.DataType; switch (dataType) { case DataType.Binary: if (version >= 3) { values[i] = reader.ReadBytes(); } else { var size = reader.ReadInt32(); values[i] = reader.ReadBytes(size); } break; case DataType.Bool: values[i] = reader.ReadBool(); break; case DataType.DateTime: values[i] = new DateTime(reader.ReadInt64(), field.DateTimeKind); break; case DataType.TimeSpan: values[i] = new TimeSpan(reader.ReadInt64()); break; case DataType.Int8: values[i] = reader.ReadInt8(); break; case DataType.Int16: values[i] = reader.ReadInt16(); break; case DataType.Int32: if (version == 1) { values[i] = reader.ReadInt32(); break; } values[i] = reader.Read7BitEncodedInt32(); break; case DataType.Int64: if (version == 1) { values[i] = reader.ReadInt64(); break; } values[i] = reader.Read7BitEncodedInt64(); break; case DataType.UInt32: if (version == 1) { values[i] = reader.ReadUInt32(); break; } values[i] = reader.Read7BitEncodedUInt32(); break; case DataType.UInt64: if (version == 1) { values[i] = reader.ReadUInt64(); break; } values[i] = reader.Read7BitEncodedUInt64(); break; case DataType.UInt8: values[i] = reader.ReadUInt8(); break; case DataType.UInt16: values[i] = reader.ReadUInt16(); break; case DataType.Char: values[i] = reader.ReadChar(); break; case DataType.Single: values[i] = reader.ReadSingle(); break; case DataType.Double: values[i] = reader.ReadDouble(); break; case DataType.Decimal: values[i] = reader.ReadDecimal(); break; case DataType.String: values[i] = reader.ReadString(); break; case DataType.Enum: values[i] = layout.EnumValue(i, reader.Read7BitEncodedInt64()); break; case DataType.User: values[i] = layout.ParseValue(i, reader.ReadString(), null); break; default: throw new NotImplementedException($"Datatype {dataType} not implemented!"); } } var skip = (dataStart + dataSize) - reader.BaseStream.Position; if (skip < 0) { throw new FormatException(); } if (skip > 0) { reader.BaseStream.Seek(skip, SeekOrigin.Current); } return(new Row(layout, values, false)); }
/// <summary>Initializes a new instance of the <see cref="CsvWriter" /> class.</summary> /// <param name="layout">The table layout.</param> /// <param name="fileName">Filename to write to.</param> /// <param name="properties">Extended properties.</param> public CsvWriter(RowLayout layout, string fileName, CsvProperties properties = default) : this(layout, File.Create(fileName), properties, true) { }
/// <summary>Not supported.</summary> /// <param name="layout">Unused parameter.</param> public override void UseLayout(RowLayout layout) => RowLayout.CheckLayout(Layout, layout);
/// <inheritdoc /> public virtual void UseLayout(RowLayout layout) { Storage.CheckLayout(Layout, layout); Layout = layout; }