/// <summary>
        /// Opens the given file, reads it and returns a collection of CustomerStatementMessages.
        /// </summary>
        /// <param name="file">File to read.</param>
        /// <param name="format">Bank specific format of this file.</param>
        /// <param name="cultureInfo">Specifies the culture to use</param>
        /// <param name="mt940Parameters">Additional mt940 parameters</param>
        /// <returns>Returns a collection of customer statement messages, populated by the data of the given file.</returns>
        public static ICollection <CustomerStatementMessage> Parse(IMt940Format format, string file, CultureInfo cultureInfo, Parameters mt940Parameters = null)
        {
            if (format == null)
            {
                throw new ArgumentNullException(nameof(format));
            }

            if (string.IsNullOrWhiteSpace(file))
            {
                throw new ArgumentException("file can not be empty");
            }

            if (cultureInfo == null)
            {
                throw new ArgumentNullException(nameof(cultureInfo));
            }


            if (!File.Exists(file))
            {
                throw new FileNotFoundException("Can not find file.", file);
            }

            using (StreamReader reader = new StreamReader(File.OpenRead(file)))
            {
                return(Parse(format, reader, cultureInfo, mt940Parameters));
            }
        }
        private ICollection <CustomerStatementMessage> GetSample(IMt940Format format, string resourceName, CultureInfo cultureInfo)
        {
            var assembly = Assembly.GetExecutingAssembly();

            using (Stream stream = assembly.GetManifestResourceStream(resourceName))
            {
                var reader = new StreamReader(stream);
                return(Mt940Parser.Parse(format, reader, cultureInfo));
            }
        }
        public static ICollection <CustomerStatementMessage> Parse(IMt940Format format, TextReader fileStream, Parameters mt940Params = null)
        {
            if (format == null)
            {
                throw new ArgumentNullException(nameof(format));
            }

            if (fileStream == null)
            {
                throw new ArgumentNullException(nameof(fileStream));
            }

            return(Parse(format, fileStream, CultureInfo.CurrentCulture, mt940Params));
        }
        public static ICollection <CustomerStatementMessage> Parse(IMt940Format format, string file, Parameters mt940Params = null)
        {
            if (format == null)
            {
                throw new ArgumentNullException(nameof(format));
            }

            if (string.IsNullOrWhiteSpace(file))
            {
                throw new ArgumentException("file can not be empty", file);
            }

            return(Parse(format, file, CultureInfo.CurrentCulture, mt940Params));
        }
        public static ICollection <CustomerStatementMessage> ParseData(IMt940Format format, string data, Parameters mt940Params = null)
        {
            if (format == null)
            {
                throw new ArgumentNullException(nameof(format));
            }

            if (string.IsNullOrWhiteSpace(data))
            {
                throw new ArgumentException("data can not be empty", nameof(data));
            }

            if (mt940Params == null)
            {
                // default values
                mt940Params = new Parameters();
            }

            return(ParseData(format, data, CultureInfo.CurrentCulture, mt940Params));
        }
        /// <summary>
        /// Reads the given string to the end and parses the data to Customer Statement Messages.
        /// </summary>
        /// <param name="fileStream">Filestream to read.</param>
        /// <param name="format">Bank specific format of this file.</param>
        /// <param name="cultureInfo">Specifies the culture information to use</param>
        /// <param name="mt940Parameters">Additional mt940 parameters</param>
        /// <returns></returns>
        public static ICollection <CustomerStatementMessage> Parse(IMt940Format format, TextReader fileStream, CultureInfo cultureInfo, Parameters mt940Parameters = null)
        {
            if (format == null)
            {
                throw new ArgumentNullException(nameof(format));
            }

            if (fileStream == null)
            {
                throw new ArgumentNullException(nameof(fileStream));
            }

            if (cultureInfo == null)
            {
                throw new ArgumentNullException(nameof(cultureInfo));
            }

            var completeFile = fileStream.ReadToEnd();

            return(ParseData(format, completeFile, cultureInfo, mt940Parameters));
        }
        /// <summary>
        /// Reads the given string to the end and parses the data to Customer Statement Messages
        /// </summary>
        /// <param name="format"></param>
        /// <param name="data"></param>
        /// <param name="cultureInfo">Specifies the culture information to use</param>
        /// <param name="mt940Parameters">Additional mt940 parameters</param>
        /// <returns></returns>
        public static ICollection <CustomerStatementMessage> ParseData(IMt940Format format, string data, CultureInfo cultureInfo, Parameters mt940Parameters = null)
        {
            if (format == null)
            {
                throw new ArgumentNullException(nameof(format));
            }

            if (string.IsNullOrWhiteSpace(data))
            {
                throw new ArgumentException("data can not be empty", nameof(data));
            }

            if (mt940Parameters == null)
            {
                // default values
                mt940Parameters = new Parameters();
            }

            var listData = CreateStringTransactions(format, data);

            return(CreateObjectTransactions(format, listData, cultureInfo, mt940Parameters));
        }
 private ICollection <CustomerStatementMessage> GetSample(IMt940Format format, string resourceName)
 {
     return(GetSample(format, resourceName, CultureInfo.GetCultureInfo("nl-NL")));
 }
        /// <summary>
        /// This method accepts mt940 data file given as a string. The string
        /// is split by Environment.NewLine as each line contains a command.
        ///
        /// Every line that starts with a ':' is a mt940 'command'. Lines that
        /// does not start with a ':' belongs to the previous command.
        ///
        /// The method returns a collection of string arrays. Every item in
        /// the collection is a mt940 message.
        /// </summary>
        /// <param name="data">A string of MT940 data to parse.</param>
        /// <param name="format">Specifies the bank specific format</param>
        /// <returns>A collection :)</returns>
        private static ICollection <string[]> CreateStringTransactions(IMt940Format format, string data)
        {
            // Split on the new line seperator. In a MT940 messsage, every command is on a seperate line.
            // Assumption is made it is in the same format as the enviroments new line.
            var tokenized = data.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);

            // Create  an empty list of string arrays.
            var transactions = new List <string[]>();


            // Offset pointer, starts a the first line (zero based index).
            int pointer = 0;

            // Loop trough the entire file?
            while (pointer < tokenized.Length)
            {
                // Seperator, this is the Trailer! We split messages based on trailer! - Right, check.
                var trailerIndex = Array.IndexOf(tokenized, format.Trailer.Data, pointer);

                // When we found a trailer.. then..
                if (trailerIndex >= 0)
                {
                    // Create a new array the holds the correct number of elements.
                    string[] currentTransaction = new string[trailerIndex - pointer];

                    // Copy the data from the source array to our current transaction.
                    Array.Copy(tokenized, pointer, currentTransaction, 0, currentTransaction.Length);

                    // Walk trough the current message. Start at the current
                    // index and stop at the separator.
                    for (int index = currentTransaction.Length - 1;
                         index > format.Header.LineCount;
                         index--)
                    {
                        //
                        string transactionItem = currentTransaction[index];

                        System.Diagnostics.Debug.Assert(transactionItem != null);

                        // If the transactionItem doesn't start with : then the
                        // current line belongs to the previous one.
                        if (!transactionItem.StartsWith(":", StringComparison.Ordinal))
                        {
                            // Append ths current line to the previous line seperated by
                            // and NewLine.
                            currentTransaction[index - 1] += Environment.NewLine;
                            currentTransaction[index - 1] += transactionItem;

                            // Set the current item to null, it doesn't exist anymore.
                            currentTransaction[index] = null;
                        }
                    }

                    // Add the current transaction.
                    transactions.Add(currentTransaction);

                    // Next up!
                    pointer = (trailerIndex + 1);
                }
                else
                {
                    // Message doesn't contain a trailer. So it is invalid!
                    throw new InvalidDataException("Can not find trailer!");
                }
            }

            return(transactions);
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="format">IMt940Format implementation</param>
        /// <param name="data">A collection of string arrays formatted by CreateStringTransactions()</param>
        /// <param name="cultureInfo">The culture to use</param>
        /// <param name="mt940Params">Additional mt940 parameters</param>
        /// <see cref="CreateStringTransactions"></see>
        /// <returns></returns>
        /// TODO: This method is way to complex. It should be simplified.
        private static ICollection <CustomerStatementMessage> CreateObjectTransactions(
            IMt940Format format,
            ICollection <string[]> data,
            CultureInfo cultureInfo,
            Parameters mt940Params)
        {
            // Create a new list.
            var customerStatementList = new List <CustomerStatementMessage>();


            // For each string collection of commands.
            foreach (string[] line in data)
            {
                int transactionPointer = 0; // Start of the transaction.

                // Skip the header (for some reason)
                transactionPointer += format.Header.LineCount; // SWIFT HEADER.

                var transaction = default(Transaction);        // Set transaction to its default (null).
                var customerStatementMessage = new CustomerStatementMessage();

                // ReSharper disable AccessToModifiedClosure
                void addAndNullTransactionIfPresent()
                {
                    if (customerStatementMessage != null && transaction != null)
                    {
                        customerStatementMessage.Transactions.Add(transaction);
                    }
                    transaction = null;
                }

                // ReSharper restore AccessToModifiedClosure

                // Loop through the array.
                for (; transactionPointer < line.Length; transactionPointer++)
                {
                    // Set transactionLine to the current line.
                    string transactionLine = line[transactionPointer];

                    // Skip if null, CreateObjectTransactions kinda leaves a mess.
                    if (transactionLine != null)
                    {
                        // Get the command number.
                        var tag = transactionLine.Substring(transactionLine.IndexOf(':'), transactionLine.IndexOf(':', 1) + 1);

                        // Get the command data.
                        var transactionData = transactionLine.Substring(tag.Length);

                        // Fill the object the right data.
                        switch (tag)
                        {
                        case ":20:":
                            customerStatementMessage = customerStatementMessage.SetTransactionReference(transactionData);
                            break;

                        case ":21:":
                            customerStatementMessage = customerStatementMessage.SetRelatedMessage(transactionData);
                            break;

                        case ":25:":
                            customerStatementMessage = customerStatementMessage.SetAccount(transactionData, mt940Params.ClearAccount);
                            break;

                        case ":28:":
                        case ":28C:":
                            customerStatementMessage.SetSequenceNumber(transactionData, cultureInfo);
                            break;

                        case ":60m:":
                        case ":60F:":
                        case ":60M:":
                            customerStatementMessage = customerStatementMessage.SetOpeningBalance(new TransactionBalance(transactionData, cultureInfo));
                            break;

                        case ":61:":
                            addAndNullTransactionIfPresent();
                            transaction = new Transaction(transactionData, customerStatementMessage.OpeningBalance.Currency, cultureInfo);
                            break;

                        case ":86:":
                            /*
                             * If the previous line was a 61 (ie, we have a transaction), the 'Information to Account Owner'
                             * applies to the transaction, otherwise it applies to the whole message.
                             */
                            if (transaction == null)
                            {
                                customerStatementMessage = customerStatementMessage.SetDescription(transactionData);
                            }
                            else
                            {
                                transaction.Description = transactionData;
                            }
                            addAndNullTransactionIfPresent();
                            break;

                        case ":62F:":
                            addAndNullTransactionIfPresent();
                            customerStatementMessage = customerStatementMessage.SetClosingBalance(new TransactionBalance(transactionData, cultureInfo));
                            break;

                        case ":62m:":
                        case ":62M:":
                            customerStatementMessage = customerStatementMessage.SetClosingBalance(new TransactionBalance(transactionData, cultureInfo));
                            break;

                        case ":64:":
                            addAndNullTransactionIfPresent();
                            customerStatementMessage = customerStatementMessage.SetClosingAvailableBalance(new TransactionBalance(transactionData, cultureInfo));
                            break;

                        case ":65:":
                            customerStatementMessage = customerStatementMessage.SetForwardAvailableBalance(new TransactionBalance(transactionData, cultureInfo));
                            break;
                        }
                    }
                }

                customerStatementList.Add(customerStatementMessage);
            }

            // geting :86: details
            if (mt940Params.Codes.Count > 0)
            {
                var constantCode = mt940Params.ConstantCode();

                foreach (var item in customerStatementList)
                {
                    foreach (var tr in item.Transactions)
                    {
                        Parse86Details(tr, mt940Params, constantCode);
                    }
                }
            }

            return(customerStatementList);
        }