Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,5 @@ $RECYCLE.BIN/
assets/FlatFile.Core.Compiled.nuspec
assets/FlatFile.Delimited.Compiled.nuspec
assets/FlatFile.FixedLength.Compiled.nuspec

.vs/
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
The MIT License (MIT)

Copyright (c) 2018 Matt Hamilton
Copyright (c) 2014 Pavel Nosovich

Permission is hereby granted, free of charge, to any person obtaining a copy
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-
version: 0.2.{build}
version: 0.3.{build}

environment:
BuildEnvironment: appveyor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ public CsvHelperTypeConverterForCustomType()

public string ConvertToString(CsvHelperTypeConversion.TypeConverterOptions options, object value)
{
return converter.ConvertToString(value);
return converter.ConvertToString(value, null);
}

public object ConvertFromString(CsvHelperTypeConversion.TypeConverterOptions options, string text)
{
return converter.ConvertFromString(text);
return converter.ConvertFromString(text, null);
}

public bool CanConvertFrom(Type type)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace FlatFile.Benchmark.Converters
{
using System;
using System.Reflection;
using FlatFile.Benchmark.Entities;
using FlatFile.Core;

Expand All @@ -16,13 +17,13 @@ public bool CanConvertTo(Type type)
return type == typeof (CustomType);
}

public string ConvertToString(object source)
public string ConvertToString(object source, PropertyInfo sourceProperty)
{
var obj = (CustomType)source;
return string.Format("{0}|{1}|{2}", obj.First, obj.Second, obj.Third);
}

public object ConvertFromString(string source)
public object ConvertFromString(string source, PropertyInfo targetProperty)
{
var values = source.Split('|');

Expand Down
34 changes: 28 additions & 6 deletions src/FlatFile.Core/Base/FlatFileEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ public abstract class FlatFileEngine<TFieldSettings, TLayoutDescriptor> : IFlatF
/// <summary>
/// The handle entry read error func
/// </summary>
private readonly Func<string, Exception, bool> _handleEntryReadError;
private readonly Func<FlatFileErrorContext, bool> _handleEntryReadError;

/// <summary>
/// Gets the line builder.
/// </summary>
/// <value>The line builder.</value>
protected abstract ILineBulder LineBuilder { get; }
protected abstract ILineBuilder LineBuilder { get; }

/// <summary>
/// Gets the line parser.
Expand All @@ -42,7 +42,7 @@ public abstract class FlatFileEngine<TFieldSettings, TLayoutDescriptor> : IFlatF
/// Initializes a new instance of the <see cref="FlatFileEngine{TFieldSettings, TLayoutDescriptor}"/> class.
/// </summary>
/// <param name="handleEntryReadError">The handle entry read error.</param>
protected FlatFileEngine(Func<string, Exception, bool> handleEntryReadError = null)
protected FlatFileEngine(Func<FlatFileErrorContext, bool> handleEntryReadError = null)
{
_handleEntryReadError = handleEntryReadError;
}
Expand All @@ -57,6 +57,18 @@ protected FlatFileEngine(Func<string, Exception, bool> handleEntryReadError = nu
public virtual IEnumerable<TEntity> Read<TEntity>(Stream stream) where TEntity : class, new()
{
var reader = new StreamReader(stream);
return Read<TEntity>(reader);
}

/// <summary>
/// Reads the specified text reader.
/// </summary>
/// <typeparam name="TEntity">The type of the t entity.</typeparam>
/// <param name="reader">The reader.</param>
/// <returns>IEnumerable&lt;TEntity&gt;.</returns>
/// <exception cref="ParseLineException">Impossible to parse line</exception>
public virtual IEnumerable<TEntity> Read<TEntity>(TextReader reader) where TEntity : class, new()
{
string line;
int lineNumber = 0;

Expand All @@ -68,7 +80,7 @@ protected FlatFileEngine(Func<string, Exception, bool> handleEntryReadError = nu
while ((line = reader.ReadLine()) != null)
{
if (string.IsNullOrEmpty(line) || string.IsNullOrEmpty(line.Trim())) continue;

bool ignoreEntry = false;
var entry = new TEntity();
try
Expand All @@ -85,7 +97,7 @@ protected FlatFileEngine(Func<string, Exception, bool> handleEntryReadError = nu
throw;
}

if (!_handleEntryReadError(line, ex))
if (!_handleEntryReadError(new FlatFileErrorContext(line, lineNumber, ex)))
{
throw;
}
Expand All @@ -104,7 +116,7 @@ protected FlatFileEngine(Func<string, Exception, bool> handleEntryReadError = nu
/// Processes the header.
/// </summary>
/// <param name="reader">The reader.</param>
protected virtual void ProcessHeader(StreamReader reader)
protected virtual void ProcessHeader(TextReader reader)
{
reader.ReadLine();
}
Expand Down Expand Up @@ -147,7 +159,17 @@ protected virtual void WriteEntry<TEntity>(TextWriter writer, int lineNumber, TE
public virtual void Write<TEntity>(Stream stream, IEnumerable<TEntity> entries) where TEntity : class, new()
{
TextWriter writer = new StreamWriter(stream);
Write(writer, entries);
}

/// <summary>
/// Writes to the specified text writer.
/// </summary>
/// <typeparam name="TEntity">The type of the t entity.</typeparam>
/// <param name="stream">The text writer.</param>
/// <param name="entries">The entries.</param>
public void Write<TEntity>(TextWriter writer, IEnumerable<TEntity> entries) where TEntity : class, new()
{
this.WriteHeader(writer);

int lineNumber = 0;
Expand Down
51 changes: 51 additions & 0 deletions src/FlatFile.Core/Base/FlatFileErrorContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
namespace FlatFile.Core.Base
{
using System;

/// <summary>
/// Provides information about a file parsing error.
/// </summary>
public struct FlatFileErrorContext
{
private readonly string line;
private readonly int lineNumber;
private readonly Exception exception;

/// <summary>
/// Initializes a new instance of <see cref="FlatFileErrorContext"/>.
/// </summary>
/// <param name="line">The content of the line on which the error occurred.</param>
/// <param name="lineNumber">The line numer at which the error occurred.</param>
/// <param name="exception">The error that occurred.</param>
public FlatFileErrorContext(string line, int lineNumber, Exception exception)
{
this.line = line;
this.lineNumber = lineNumber;
this.exception = exception;
}

/// <summary>
/// The content of the line on which the error occurred.
/// </summary>
public string Line
{
get { return line; }
}

/// <summary>
/// The line numer at which the error occurred.
/// </summary>
public int LineNumber
{
get { return lineNumber; }
}

/// <summary>
/// The error that occurred.
/// </summary>
public Exception Exception
{
get { return exception; }
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
namespace FlatFile.Core.Base
{
public abstract class LineBulderBase<TLayoutDescriptor, TFieldSettings> : ILineBulder
public abstract class LineBuilderBase<TLayoutDescriptor, TFieldSettings> : ILineBuilder
where TLayoutDescriptor : ILayoutDescriptor<TFieldSettings>
where TFieldSettings : IFieldSettingsContainer
{
private readonly TLayoutDescriptor _descriptor;

protected LineBulderBase(TLayoutDescriptor descriptor)
protected LineBuilderBase(TLayoutDescriptor descriptor)
{
this._descriptor = descriptor;
}
Expand All @@ -21,7 +21,7 @@ protected TLayoutDescriptor Descriptor
protected virtual string GetStringValueFromField(TFieldSettings field, object fieldValue)
{
string lineValue = fieldValue != null
? fieldValue.ToString()
? ConvertToString(field, fieldValue)
: field.NullValue ?? string.Empty;

lineValue = TransformFieldValue(field, lineValue);
Expand All @@ -33,5 +33,14 @@ protected virtual string TransformFieldValue(TFieldSettings field, string lineVa
{
return lineValue;
}

private static string ConvertToString(TFieldSettings field, object fieldValue)
{
var converter = field.TypeConverter;
if (converter != null && converter.CanConvertTo(typeof(string)) && converter.CanConvertFrom(field.PropertyInfo.PropertyType))
return field.TypeConverter.ConvertToString(fieldValue, field.PropertyInfo);

return fieldValue.ToString();
}
}
}
7 changes: 4 additions & 3 deletions src/FlatFile.Core/Base/LineParserBase.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace FlatFile.Core.Base
{
using System;
using System.Reflection;
using FlatFile.Core.Extensions;

public abstract class LineParserBase<TLayoutDescriptor, TFieldSettings> : ILineParser
Expand Down Expand Up @@ -39,7 +40,7 @@ protected virtual object GetFieldValueFromString(TFieldSettings fieldSettings, s

object obj;

if (!fieldSettings.TypeConverter.ConvertFromStringTo(memberValue, type, out obj))
if (!fieldSettings.TypeConverter.ConvertFromStringTo(memberValue, type, fieldSettings.PropertyInfo, out obj))
{
obj = memberValue.Convert(type);
}
Expand All @@ -56,11 +57,11 @@ protected virtual string TransformStringValue(TFieldSettings fieldSettingsBuilde
public static class TypeConverterExtensions
{

public static bool ConvertFromStringTo(this ITypeConverter converter, string source, Type targetType, out object obj)
public static bool ConvertFromStringTo(this ITypeConverter converter, string source, Type targetType, PropertyInfo targetProperty, out object obj)
{
if (converter != null && converter.CanConvertFrom(typeof(string)) && converter.CanConvertTo(targetType))
{
obj = converter.ConvertFromString(source);
obj = converter.ConvertFromString(source, targetProperty);
return true;
}

Expand Down
70 changes: 70 additions & 0 deletions src/FlatFile.Core/Base/MasterDetailTrackerBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;

namespace FlatFile.Core.Base
{
/// <summary>
/// Uses records that implement <see cref="IMasterRecord"/> and <see cref="IDetailRecord"/> to handle
/// master-detail record relationships.
/// </summary>
public class MasterDetailTrackerBase : IMasterDetailTracker
{
/// <summary>
/// Determines whether a record is a master record.
/// </summary>
readonly Func<object, bool> checkIsMasterRecord;
/// <summary>
/// Determines whether a record is a detail record.
/// </summary>
readonly Func<object, bool> checkIsDetailRecord;
/// <summary>
/// Handles confirmed detail records.
/// </summary>
readonly Action<object, object> handleDetailRecord;
/// <summary>
/// The last record parsed that implements <see cref="IMasterRecord"/>
/// </summary>
object lastMasterRecord;

/// <summary>
/// Initializes a new instance of the <see cref="MasterDetailTracker"/> class.
/// </summary>
/// <param name="checkIsMasterRecord">Determines whether a record is a master record.</param>
/// <param name="checkIsDetailRecord">Determines whether a record is a detail record.</param>
/// <param name="handleDetailRecord">Handles confirmed detail records.</param>
public MasterDetailTrackerBase(
Func<object, bool> checkIsMasterRecord,
Func<object, bool> checkIsDetailRecord,
Action<object, object> handleDetailRecord)
{
this.checkIsMasterRecord = checkIsMasterRecord;
this.checkIsDetailRecord = checkIsDetailRecord;
this.handleDetailRecord = handleDetailRecord;
}

public void HandleMasterDetail(object entry, out bool isDetailRecord)
{
isDetailRecord = false;

if (checkIsMasterRecord(entry))
{
// Found new master record
lastMasterRecord = entry;
return;
}

// Record is standalone or unassociated detail record
if (lastMasterRecord == null) return;

if (!checkIsDetailRecord(entry))
{
// Record is standalone, reset master
lastMasterRecord = null;
return;
}

// Add detail record and indicate that it should not be added to the results dictionary
handleDetailRecord(lastMasterRecord, entry);
isDetailRecord = true;
}
}
}
36 changes: 36 additions & 0 deletions src/FlatFile.Core/Base/TypeConverterBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Reflection;

namespace FlatFile.Core.Base
{
/// <summary>
/// A generic base class for converting between strings and a given type.
/// </summary>
/// <typeparam name="TValue">The type to convert to and from a string.</typeparam>
public abstract class TypeConverterBase<TValue> : ITypeConverter
{
public virtual bool CanConvertFrom(Type type)
{
return type == typeof(string) || type == typeof(TValue);
}

public virtual bool CanConvertTo(Type type)
{
return type == typeof(string) || type == typeof(TValue);
}

public object ConvertFromString(string source, PropertyInfo targetProperty)
{
return ConvertFrom(source, targetProperty);
}

protected abstract TValue ConvertFrom(string source, PropertyInfo targetProperty);

public string ConvertToString(object source, PropertyInfo sourceProperty)
{
return ConvertTo((TValue)source, sourceProperty);
}

protected abstract string ConvertTo(TValue source, PropertyInfo sourceProperty);
}
}
Loading