using System; using System.Text; using System.Data; using System.Globalization; using System.Collections.Generic; using FastReport.Data; namespace FastReport.Utils { /// /// The pseudo-random generator. /// public class FRRandom { #region Fields private readonly Random random; private static readonly char[] lowerLetters = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; private static readonly char[] upperLetters = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; #endregion Fields #region Public Methods /// /// Gets a random letter in same case that source character. /// /// The source character. /// The random character. public char NextLetter(char source) { if (char.IsLower(source)) return lowerLetters[random.Next(lowerLetters.Length)]; else if (char.IsUpper(source)) return upperLetters[random.Next(upperLetters.Length)]; return source; } /// /// Gets random int value from 0 to 9. /// /// Random int value. public int NextDigit() { return random.Next(10); } /// /// Gets random int value from 0 to max. /// /// The maximum for random digit. /// Random int value. public int NextDigit(int max) { return random.Next(max + 1); } /// /// Gets random int value from min to max. /// /// The minimum for random digit. /// The maximum for random digit. /// Random int value. public int NextDigit(int min, int max) { return random.Next(min, max + 1); } /// /// Gets number of random digits from 0 to 9. /// /// The number of digits. /// Number of random digits. public string NextDigits(int number) { if (number <= 0) return ""; StringBuilder sb = new StringBuilder(); for (int i = 0; i < number; i++) { sb.Append(NextDigit()); } return sb.ToString(); } /// /// Gets the random byte value. /// /// Random byte value. public byte NextByte() { return (byte)random.Next(byte.MaxValue); } /// /// Gets random byte array with specified number of elements. /// /// The number of elements in array. /// Random byte array. public byte[] NextBytes(int number) { byte[] bytes = new byte[number]; random.NextBytes(bytes); return bytes; } /// /// Gets the randomized char value. /// /// Random char value. public char NextChar() { return Convert.ToChar(random.Next(char.MaxValue)); } /// /// Gets the random day from start to DataTime.Today. /// /// The starting DateTime value. /// Random DateTime value. public DateTime NextDay(DateTime start) { if (start > DateTime.Today) return DateTime.Today; int range = (DateTime.Today - start).Days; return start.AddDays(random.Next(range)); } /// /// Gets the randomized TimeSpan value beetwin specified hours. /// /// The starting hour (0 - 24). /// The ending hour (0 - 24). /// Random TimeSpan value. public TimeSpan NextTimeSpanBetweenHours(int start, int end) { if (start < 0) start = 0; if (end > 24) end = 24; if (start > end) { int temp = start; start = end; end = temp; } TimeSpan startTs = TimeSpan.FromHours(start); TimeSpan endTs = TimeSpan.FromHours(end); int maxMinutes = (int)(endTs - startTs).TotalMinutes; int randomMinutes = random.Next(maxMinutes); TimeSpan result = startTs.Add(TimeSpan.FromMinutes(randomMinutes)); return result; } /// /// Gets the randomized decimal value with same number of digits that in source value. /// /// The source decimal value. /// Random decimal value based on source. public decimal RandomizeDecimal(decimal source) { StringBuilder sb = new StringBuilder(); string[] parts = source.ToString(CultureInfo.InvariantCulture).ToUpper().Split('E'); string e = ""; if (parts.Length > 1) e = "E" + parts[1]; parts = parts[0].Split('.'); if (parts.Length > 0) { int length = parts[0].Length; if (source < 0.0m) { sb.Append("-"); length--; } sb.Append(NextDigit(1, 9)); sb.Append(NextDigits(length - 1)); } if (parts.Length > 1) { sb.Append("."); sb.Append(NextDigits(parts[1].Length - 1)); sb.Append(NextDigit(1, 9)); } sb.Append(e); decimal result; bool parsed = decimal.TryParse(sb.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture, out result); if (parsed) return result; return source; } /// /// Gets the randomized double value with same number of digits that in source value. /// /// The source double value. /// Random double value based on source. public double RandomizeDouble(double source) { return (double)RandomizeDecimal((decimal)source); } /// /// Gets the randomized Int16 value with same number of digits that in source value. /// /// The source Int16 value. /// Random Int16 value based on source. public Int16 RandomizeInt16(Int16 source) { StringBuilder sb = new StringBuilder(); int length = source.ToString(CultureInfo.InvariantCulture).Length; if (source < 0) { sb.Append('-'); length--; } int maxLength = Int16.MaxValue.ToString(CultureInfo.InvariantCulture).Length; if (length < maxLength) { sb.Append(NextDigit(1, 9)); sb.Append(NextDigits(length - 1)); } else // Guarantee a value less than 32 000. { int next = NextDigit(1, 3); sb.Append(next); if (next < 3) sb.Append(NextDigits(maxLength - 1)); else { sb.Append(NextDigit(1)); sb.Append(NextDigits(maxLength - 2)); } } Int16 result; bool parsed = Int16.TryParse(sb.ToString(), out result); if (parsed) return result; return source; } /// /// Gets the randomized Int32 value with same number of digits that in source value. /// /// The source Int32 value. /// Random Int32 value based on source. public Int32 RandomizeInt32(Int32 source) { StringBuilder sb = new StringBuilder(); int length = source.ToString(CultureInfo.InvariantCulture).Length; if (source < 0) { sb.Append('-'); length--; } int maxLength = Int32.MaxValue.ToString(CultureInfo.InvariantCulture).Length; if (length < maxLength) { sb.Append(NextDigit(1, 9)); sb.Append(NextDigits(length - 1)); } else // Guarantee a value less than 2 200 000 000. { int next = NextDigit(1, 2); sb.Append(next); if (next < 2) sb.Append(NextDigits(maxLength - 1)); else { sb.Append(NextDigit(1)); sb.Append(NextDigits(maxLength - 2)); } } Int32 result; bool parsed = Int32.TryParse(sb.ToString(), out result); if (parsed) return result; return source; } /// /// Gets the randomized Int64 value with same number of digits that in source value. /// /// The source Int64 value. /// Random Int64 value based on source. public Int64 RandomizeInt64(Int64 source) { StringBuilder sb = new StringBuilder(); int length = source.ToString(CultureInfo.InvariantCulture).Length; if (source < 0) { sb.Append('-'); length--; } int maxLength = Int64.MaxValue.ToString(CultureInfo.InvariantCulture).Length; if (length < maxLength) { sb.Append(NextDigit(1, 9)); sb.Append(NextDigits(length - 1)); } else // Guarantee a value less than 9 200 000 000 000 000 000. { int next = NextDigit(1, 9); sb.Append(next); if (next < 9) sb.Append(NextDigits(maxLength - 1)); else { sb.Append(NextDigit(1)); sb.Append(NextDigits(maxLength - 2)); } } Int64 result; bool parsed = Int64.TryParse(sb.ToString(), out result); if (parsed) return result; return source; } /// /// Gets the randomized SByte value with same number of digits that in source value. /// /// The source SByte value. /// Random SByte value based on source. public SByte RandomizeSByte(SByte source) { StringBuilder sb = new StringBuilder(); int length = source.ToString(CultureInfo.InvariantCulture).Length; if (source < 0) { sb.Append('-'); length--; } int maxLength = SByte.MaxValue.ToString(CultureInfo.InvariantCulture).Length; if (length < maxLength) sb.Append(NextDigits(length)); else // Guarantee a value less than 128. { int next = NextDigit(1); sb.Append(next); if (next < 1) sb.Append(NextDigits(maxLength - 1)); else { sb.Append(NextDigit(2)); sb.Append(NextDigit(7)); } } SByte result; bool parsed = SByte.TryParse(sb.ToString(), out result); if (parsed) return result; return source; } /// /// Gets the randomized Single value with same number of digits that in source value. /// /// The source Single value. /// Random Single value based on source. public float RandomizeFloat(float source) { return (float)RandomizeDecimal((decimal)source); } /// /// Gets the randomized string with same length and same whitespaces that in source string. /// /// The source string. /// Random string based on source string. public string RandomizeString(string source) { StringBuilder sb = new StringBuilder(); foreach (char c in source) { if (char.IsWhiteSpace(c)) sb.Append(c); else if (char.IsLetter(c)) sb.Append(NextLetter(c)); else if (char.IsDigit(c)) sb.Append(NextDigit()); else sb.Append(c); } return sb.ToString(); } /// /// Gets the randomized UInt16 value with same number of digits that in source value. /// /// The source UInt16 value. /// Random UInt16 value based on source. public UInt16 RandomizeUInt16(UInt16 source) { StringBuilder sb = new StringBuilder(); int length = source.ToString(CultureInfo.InvariantCulture).Length; int maxLength = UInt16.MaxValue.ToString(CultureInfo.InvariantCulture).Length; if (length < maxLength) { sb.Append(NextDigit(1, 9)); sb.Append(NextDigits(length - 1)); } else // Guarantee a value less than 65 000. { int next = NextDigit(1, 6); sb.Append(next); if (next < 6) sb.Append(NextDigits(maxLength - 1)); else { sb.Append(NextDigit(4)); sb.Append(NextDigits(maxLength - 2)); } } UInt16 result; bool parsed = UInt16.TryParse(sb.ToString(), out result); if (parsed) return result; return source; } /// /// Gets the randomized UInt32 value with same number of digits that in source value. /// /// The source UInt32 value. /// Random UInt32 value based on source. public UInt32 RandomizeUInt32(UInt32 source) { StringBuilder sb = new StringBuilder(); int length = source.ToString(CultureInfo.InvariantCulture).Length; int maxLength = UInt32.MaxValue.ToString(CultureInfo.InvariantCulture).Length; if (length < maxLength) { sb.Append(NextDigit(1, 9)); sb.Append(NextDigits(length - 1)); } else // Guarantee a value less than 4 200 000 000. { int next = NextDigit(1, 4); sb.Append(next); if (next < 4) sb.Append(NextDigits(maxLength - 1)); else { sb.Append(NextDigit(1)); sb.Append(NextDigits(maxLength - 2)); } } UInt32 result; bool parsed = UInt32.TryParse(sb.ToString(), out result); if (parsed) return result; return source; } /// /// Gets the randomized UInt64 value with same number of digits that in source value. /// /// The source UInt64 value. /// Random UInt64 value based on source. public UInt64 RandomizeUInt64(UInt64 source) { StringBuilder sb = new StringBuilder(); int length = source.ToString(CultureInfo.InvariantCulture).Length; int maxLength = UInt64.MaxValue.ToString(CultureInfo.InvariantCulture).Length; if (length < maxLength) { sb.Append(NextDigit(1, 9)); sb.Append(NextDigits(length - 1)); } else // Guarantee a value less than 18 400 000 000 000 000 000. { sb.Append(1); int next = NextDigit(8); sb.Append(next); if (next < 8) sb.Append(NextDigits(maxLength - 2)); else { sb.Append(NextDigit(3)); sb.Append(NextDigits(maxLength - 3)); } } UInt64 result; bool parsed = UInt64.TryParse(sb.ToString(), out result); if (parsed) return result; return source; } /// /// Gets randomized object based on the source object. /// /// The source object. /// The type of object. /// Random object based on source. public object GetRandomObject(object source, Type type) { try { if (type == typeof(string)) return RandomizeString((string)source); else if (type == typeof(Int32)) return RandomizeInt32((Int32)source); else if (type == typeof(double)) return RandomizeDouble((double)source); else if (type == typeof(DateTime)) return NextDay(new DateTime(1990, 1, 1)); else if (type == typeof(Int64)) return RandomizeInt64((Int64)source); else if (type == typeof(decimal)) return RandomizeDecimal((decimal)source); else if (type == typeof(Int16)) return RandomizeInt16((Int16)source); else if (type == typeof(float)) return RandomizeFloat((float)source); else if (type == typeof(char)) return NextChar(); else if (type == typeof(byte)) return NextByte(); else if (type == typeof(UInt32)) return RandomizeUInt32((UInt32)source); else if (type == typeof(UInt64)) return RandomizeUInt64((UInt64)source); else if (type == typeof(UInt16)) return RandomizeUInt16((UInt16)source); else if (type == typeof(byte[])) return NextBytes(((byte[])source).Length); else if (type == typeof(SByte)) return RandomizeSByte((sbyte)source); else if (type == typeof(TimeSpan)) return NextTimeSpanBetweenHours(0, 24); } catch { return source; } return source; } /// /// Randomizes datasources. /// /// Collection of datasources. public void RandomizeDataSources(DataSourceCollection datasources) { Dictionary uniquesAndRelations = new Dictionary(); // Get list of related columns and columns with unique values with their type and length. foreach (DataSourceBase datasource in datasources) { if (datasource is TableDataSource) { DataTable table = (datasource as TableDataSource).Table; DataSet ds = table.DataSet; int length = table.Rows.Count; for (int c = 0; c < table.Columns.Count; c++) foreach (DataColumn column in table.Columns) { if (column.Unique) { if (!uniquesAndRelations.ContainsKey(column.ColumnName)) uniquesAndRelations.Add(column.ColumnName, new FRColumnInfo(column.DataType, length)); } } foreach (DataRelation dr in ds.Relations) { foreach (DataColumn dc in dr.ParentColumns) { if (!uniquesAndRelations.ContainsKey(dc.ColumnName)) uniquesAndRelations.Add(dc.ColumnName, new FRColumnInfo(dc.DataType, length)); } foreach (DataColumn dc in dr.ChildColumns) { if (!uniquesAndRelations.ContainsKey(dc.ColumnName)) uniquesAndRelations.Add(dc.ColumnName, new FRColumnInfo(dc.DataType, length)); } } } } Dictionary dict = new Dictionary(); foreach (KeyValuePair pair in uniquesAndRelations) { dict.Add(pair.Key, new FRRandomFieldValueCollection()); } // Get values for related columns and columns with unique values. foreach (DataSourceBase datasource in datasources) { if (datasource is TableDataSource) { DataTable table = (datasource as TableDataSource).Table; for (int c = 0; c < table.Columns.Count; c++) { DataColumn column = table.Columns[c]; if (!uniquesAndRelations.ContainsKey(column.ColumnName)) continue; Type type = uniquesAndRelations[column.ColumnName].Type; for (int r = 0; r < table.Rows.Count; r++) { object val = table.Rows[r][c]; if (val != null && !(val is System.DBNull) && !dict[column.ColumnName].ContainsOrigin(val)) { object randomVal; do { randomVal = GetRandomObject(val, type); } while (dict[column.ColumnName].ContainsRandom(new FRRandomFieldValue(val, randomVal))); dict[column.ColumnName].Add(new FRRandomFieldValue(val, randomVal)); } } } } } // Randomize all table datasources. foreach (DataSourceBase datasource in datasources) { if (datasource is TableDataSource) { (datasource as TableDataSource).StoreData = true; DataTable table = (datasource as TableDataSource).Table; for (int c = 0; c < table.Columns.Count; c++) { if (table.Columns[c].ReadOnly) continue; Type type = table.Columns[c].DataType; for (int r = 0; r < table.Rows.Count; r++) { object val = table.Rows[r][c]; if (val != null && !(val is System.DBNull)) { if (uniquesAndRelations.ContainsKey(table.Columns[c].ColumnName)) table.Rows[r][c] = dict[table.Columns[c].ColumnName].GetRandom(val); else table.Rows[r][c] = GetRandomObject(val, type); } } } } } } #endregion Public Methods #region Constructors /// /// Initializes a new instance of the class. /// public FRRandom() { random = new Random(); } #endregion Constructors } /// /// Represents information about column. /// public class FRColumnInfo { #region Fields private Type type; private int length; #endregion Fields #region Properties /// /// Gets or sets the type of column. /// public Type Type { get { return type; } set { type = value; } } /// /// Gets or sets the length of column. /// public int Length { get { return length; } set { length = value; } } #endregion Properties #region Constructors /// /// Initializes a new instance of the class. /// /// The type of column. /// The lenght of column. public FRColumnInfo(Type type, int length) { this.type = type; this.length = length; } #endregion Constructors } /// /// Represents random value of field. /// public class FRRandomFieldValue { #region Fields private object origin; private object random; #endregion Fields #region Properties /// /// Gets or sets the original value of field. /// public object Origin { get { return origin; } set { origin = value; } } /// /// Gets or sets the random value of field. /// public object Random { get { return random; } set { random = value; } } #endregion Properties #region Constructors /// /// Initializes a new instance of the class. /// /// The original value of field. /// The random value of field. public FRRandomFieldValue(object origin, object random) { this.origin = origin; this.random = random; } #endregion Constructors } /// /// Represents collection of random values of field. /// public class FRRandomFieldValueCollection { #region Fields private readonly List list; #endregion Fields #region Properties #endregion Properties #region Constructors /// /// Initializes a new instance of the class. /// public FRRandomFieldValueCollection() { list = new List(); } #endregion Constructors #region Public Methods /// /// Adds an object to the end of this collection. /// /// Object to add. public void Add(FRRandomFieldValue value) { list.Add(value); } /// /// Determines whether an element with the same origin value is in the collection. /// /// The object to locate in the collection. /// true if object is found in the collection; otherwise, false. public bool ContainsOrigin(object origin) { foreach (FRRandomFieldValue value in list) { if (value.Origin == origin) return true; } return false; } /// /// Determines whether an element with the same random value is in the collection. /// /// The object to locate in the collection. /// true if object is found in the collection; otherwise, false. public bool ContainsRandom(object random) { foreach (FRRandomFieldValue value in list) { if (value.Random == random) return true; } return false; } /// /// Gets the random value for specified origin. /// /// The origin value. /// The random value. public object GetRandom(object origin) { foreach (FRRandomFieldValue value in list) { if (value.Origin == origin) return value.Random; } return origin; } #endregion Public Methods } }