#region Usings using System; using System.IO; using System.Text; using System.Text.RegularExpressions; #endregion namespace AT.MIN { public static class Tools { #region Public Methods #region IsNumericType /// /// Determines whether the specified value is of numeric type. /// /// The object to check. /// /// true if o is a numeric type; otherwise, false. /// public static bool IsNumericType(object o) { return (o is byte || o is sbyte || o is short || o is ushort || o is int || o is uint || o is long || o is ulong || o is float || o is double || o is decimal); } #endregion #region IsPositive /// /// Determines whether the specified value is positive. /// /// The value. /// if set to true treats 0 as positive. /// /// true if the specified value is positive; otherwise, false. /// public static bool IsPositive(object Value, bool ZeroIsPositive) { switch (Type.GetTypeCode(Value.GetType())) { case TypeCode.SByte: return (ZeroIsPositive ? (sbyte)Value >= 0 : (sbyte)Value > 0); case TypeCode.Int16: return (ZeroIsPositive ? (short)Value >= 0 : (short)Value > 0); case TypeCode.Int32: return (ZeroIsPositive ? (int)Value >= 0 : (int)Value > 0); case TypeCode.Int64: return (ZeroIsPositive ? (long)Value >= 0 : (long)Value > 0); case TypeCode.Single: return (ZeroIsPositive ? (float)Value >= 0 : (float)Value > 0); case TypeCode.Double: return (ZeroIsPositive ? (double)Value >= 0 : (double)Value > 0); case TypeCode.Decimal: return (ZeroIsPositive ? (decimal)Value >= 0 : (decimal)Value > 0); case TypeCode.Byte: return (ZeroIsPositive ? true : (byte)Value > 0); case TypeCode.UInt16: return (ZeroIsPositive ? true : (ushort)Value > 0); case TypeCode.UInt32: return (ZeroIsPositive ? true : (uint)Value > 0); case TypeCode.UInt64: return (ZeroIsPositive ? true : (ulong)Value > 0); case TypeCode.Char: return (ZeroIsPositive ? true : (char)Value != '\0'); default: return false; } } #endregion #region ToUnsigned /// /// Converts the specified values boxed type to its correpsonding unsigned /// type. /// /// The value. /// A boxed numeric object whos type is unsigned. public static object ToUnsigned(object Value) { switch (Type.GetTypeCode(Value.GetType())) { case TypeCode.SByte: return (byte)((sbyte)Value); case TypeCode.Int16: return (ushort)((short)Value); case TypeCode.Int32: return (uint)((int)Value); case TypeCode.Int64: return (ulong)((long)Value); case TypeCode.Byte: return Value; case TypeCode.UInt16: return Value; case TypeCode.UInt32: return Value; case TypeCode.UInt64: return Value; case TypeCode.Single: return (UInt32)((float)Value); case TypeCode.Double: return (ulong)((double)Value); case TypeCode.Decimal: return (ulong)((decimal)Value); default: return null; } } #endregion #region ToInteger /// /// Converts the specified values boxed type to its correpsonding integer /// type. /// /// The value. /// A boxed numeric object whos type is an integer type. public static object ToInteger(object Value, bool Round) { switch (Type.GetTypeCode(Value.GetType())) { case TypeCode.SByte: return Value; case TypeCode.Int16: return Value; case TypeCode.Int32: return Value; case TypeCode.Int64: return Value; case TypeCode.Byte: return Value; case TypeCode.UInt16: return Value; case TypeCode.UInt32: return Value; case TypeCode.UInt64: return Value; case TypeCode.Single: return (Round ? (int)Math.Round((float)Value) : (int)((float)Value)); case TypeCode.Double: return (Round ? (long)Math.Round((double)Value) : (long)((double)Value)); case TypeCode.Decimal: return (Round ? Math.Round((decimal)Value) : (decimal)Value); default: return null; } } #endregion #region UnboxToLong public static long UnboxToLong(object Value, bool Round) { switch (Type.GetTypeCode(Value.GetType())) { case TypeCode.SByte: return (long)((sbyte)Value); case TypeCode.Int16: return (long)((short)Value); case TypeCode.Int32: return (long)((int)Value); case TypeCode.Int64: return (long)Value; case TypeCode.Byte: return (long)((byte)Value); case TypeCode.UInt16: return (long)((ushort)Value); case TypeCode.UInt32: return (long)((uint)Value); case TypeCode.UInt64: return (long)((ulong)Value); case TypeCode.Single: return (Round ? (long)Math.Round((float)Value) : (long)((float)Value)); case TypeCode.Double: return (Round ? (long)Math.Round((double)Value) : (long)((double)Value)); case TypeCode.Decimal: return (Round ? (long)Math.Round((decimal)Value) : (long)((decimal)Value)); default: return 0; } } #endregion #region ReplaceMetaChars /// /// Replaces the string representations of meta chars with their corresponding /// character values. /// /// The input. /// A string with all string meta chars are replaced public static string ReplaceMetaChars(string input) { return Regex.Replace(input, @"(\\)(\d{3}|[^\d])?", new MatchEvaluator(ReplaceMetaCharsMatch)); } private static string ReplaceMetaCharsMatch(Match m) { // convert octal quotes (like \040) if (m.Groups[2].Length == 3) return Convert.ToChar(Convert.ToByte(m.Groups[2].Value, 8)).ToString(); else { // convert all other special meta characters //TODO: \xhhh hex and possible dec !! switch (m.Groups[2].Value) { case "0": // null return "\0"; case "a": // alert (beep) return "\a"; case "b": // BS return "\b"; case "f": // FF return "\f"; case "v": // vertical tab return "\v"; case "r": // CR return "\r"; case "n": // LF return "\n"; case "t": // Tab return "\t"; default: // if neither an octal quote nor a special meta character // so just remove the backslash return m.Groups[2].Value; } } } #endregion #region printf public static void printf(string Format, params object[] Parameters) { Console.Write(Tools.sprintf(Format, Parameters)); } #endregion #region fprintf public static void fprintf(TextWriter Destination, string Format, params object[] Parameters) { Destination.Write(Tools.sprintf(Format, Parameters)); } #endregion #region sprintf public static string sprintf(string Format, params object[] Parameters) { #region Variables StringBuilder f = new StringBuilder(); Regex r = new Regex(@"\%(\d*\$)?([\'\#\-\+ ]*)(\d*)(?:\.(\d+))?([hl])?([dioxXucsfeEgGpn%])"); //"%[parameter][flags][width][.precision][length]type" Match m = null; string w = String.Empty; int defaultParamIx = 0; int paramIx; object o = null; bool flagLeft2Right = false; bool flagAlternate = false; bool flagPositiveSign = false; bool flagPositiveSpace = false; bool flagZeroPadding = false; bool flagGroupThousands = false; int fieldLength = 0; int fieldPrecision = 0; char shortLongIndicator = '\0'; char formatSpecifier = '\0'; char paddingCharacter = ' '; #endregion // find all format parameters in format string f.Append(Format); m = r.Match(f.ToString()); while (m.Success) { #region parameter index paramIx = defaultParamIx; if (m.Groups[1] != null && m.Groups[1].Value.Length > 0) { string val = m.Groups[1].Value.Substring(0, m.Groups[1].Value.Length - 1); paramIx = Convert.ToInt32(val) - 1; }; #endregion #region format flags // extract format flags flagAlternate = false; flagLeft2Right = false; flagPositiveSign = false; flagPositiveSpace = false; flagZeroPadding = false; flagGroupThousands = false; if (m.Groups[2] != null && m.Groups[2].Value.Length > 0) { string flags = m.Groups[2].Value; flagAlternate = (flags.IndexOf('#') >= 0); flagLeft2Right = (flags.IndexOf('-') >= 0); flagPositiveSign = (flags.IndexOf('+') >= 0); flagPositiveSpace = (flags.IndexOf(' ') >= 0); flagGroupThousands = (flags.IndexOf('\'') >= 0); // positive + indicator overrides a // positive space character if (flagPositiveSign && flagPositiveSpace) flagPositiveSpace = false; } #endregion #region field length // extract field length and // pading character paddingCharacter = ' '; fieldLength = int.MinValue; if (m.Groups[3] != null && m.Groups[3].Value.Length > 0) { fieldLength = Convert.ToInt32(m.Groups[3].Value); flagZeroPadding = (m.Groups[3].Value[0] == '0'); } #endregion if (flagZeroPadding) paddingCharacter = '0'; // left2right allignment overrides zero padding if (flagLeft2Right && flagZeroPadding) { flagZeroPadding = false; paddingCharacter = ' '; } #region field precision // extract field precision fieldPrecision = int.MinValue; if (m.Groups[4] != null && m.Groups[4].Value.Length > 0) fieldPrecision = Convert.ToInt32(m.Groups[4].Value); #endregion #region short / long indicator // extract short / long indicator shortLongIndicator = Char.MinValue; if (m.Groups[5] != null && m.Groups[5].Value.Length > 0) shortLongIndicator = m.Groups[5].Value[0]; #endregion #region format specifier // extract format formatSpecifier = Char.MinValue; if (m.Groups[6] != null && m.Groups[6].Value.Length > 0) formatSpecifier = m.Groups[6].Value[0]; #endregion // default precision is 6 digits if none is specified except if (fieldPrecision == int.MinValue && formatSpecifier != 's' && formatSpecifier != 'c' && Char.ToUpper(formatSpecifier) != 'X' && formatSpecifier != 'o') fieldPrecision = 6; #region get next value parameter // get next value parameter and convert value parameter depending on short / long indicator if (Parameters == null || paramIx >= Parameters.Length) o = null; else { o = Parameters[paramIx]; if (shortLongIndicator == 'h') { if (o is int) o = (short)((int)o); else if (o is long) o = (short)((long)o); else if (o is uint) o = (ushort)((uint)o); else if (o is ulong) o = (ushort)((ulong)o); } else if (shortLongIndicator == 'l') { if (o is short) o = (long)((short)o); else if (o is int) o = (long)((int)o); else if (o is ushort) o = (ulong)((ushort)o); else if (o is uint) o = (ulong)((uint)o); } } #endregion // convert value parameters to a string depending on the formatSpecifier w = String.Empty; switch (formatSpecifier) { #region % - character case '%': // % character w = "%"; break; #endregion #region d - integer case 'd': // integer w = FormatNumber((flagGroupThousands ? "n" : "d"), flagAlternate, fieldLength, int.MinValue, flagLeft2Right, flagPositiveSign, flagPositiveSpace, paddingCharacter, o); defaultParamIx++; break; #endregion #region i - integer case 'i': // integer goto case 'd'; #endregion #region o - octal integer case 'o': // octal integer - no leading zero w = FormatOct("o", flagAlternate, fieldLength, int.MinValue, flagLeft2Right, paddingCharacter, o); defaultParamIx++; break; #endregion #region x - hex integer case 'x': // hex integer - no leading zero w = FormatHex("x", flagAlternate, fieldLength, fieldPrecision, flagLeft2Right, paddingCharacter, o); defaultParamIx++; break; #endregion #region X - hex integer case 'X': // same as x but with capital hex characters w = FormatHex("X", flagAlternate, fieldLength, fieldPrecision, flagLeft2Right, paddingCharacter, o); defaultParamIx++; break; #endregion #region u - unsigned integer case 'u': // unsigned integer w = FormatNumber((flagGroupThousands ? "n" : "d"), flagAlternate, fieldLength, int.MinValue, flagLeft2Right, false, false, paddingCharacter, ToUnsigned(o)); defaultParamIx++; break; #endregion #region c - character case 'c': // character if (IsNumericType(o)) w = Convert.ToChar(o).ToString(); else if (o is char) w = ((char)o).ToString(); else if (o is string && ((string)o).Length > 0) w = ((string)o)[0].ToString(); defaultParamIx++; break; #endregion #region s - string case 's': // string string t = "{0" + (fieldLength != int.MinValue ? "," + (flagLeft2Right ? "-" : String.Empty) + fieldLength.ToString() : String.Empty) + ":s}"; w = o.ToString(); if (fieldPrecision >= 0) w = w.Substring(0, fieldPrecision); if (fieldLength != int.MinValue) if (flagLeft2Right) w = w.PadRight(fieldLength, paddingCharacter); else w = w.PadLeft(fieldLength, paddingCharacter); defaultParamIx++; break; #endregion #region f - double number case 'f': // double w = FormatNumber((flagGroupThousands ? "n" : "f"), flagAlternate, fieldLength, fieldPrecision, flagLeft2Right, flagPositiveSign, flagPositiveSpace, paddingCharacter, o); defaultParamIx++; break; #endregion #region e - exponent number case 'e': // double / exponent w = FormatNumber("e", flagAlternate, fieldLength, fieldPrecision, flagLeft2Right, flagPositiveSign, flagPositiveSpace, paddingCharacter, o); defaultParamIx++; break; #endregion #region E - exponent number case 'E': // double / exponent w = FormatNumber("E", flagAlternate, fieldLength, fieldPrecision, flagLeft2Right, flagPositiveSign, flagPositiveSpace, paddingCharacter, o); defaultParamIx++; break; #endregion #region g - general number case 'g': // double / exponent w = FormatNumber("g", flagAlternate, fieldLength, fieldPrecision, flagLeft2Right, flagPositiveSign, flagPositiveSpace, paddingCharacter, o); defaultParamIx++; break; #endregion #region G - general number case 'G': // double / exponent w = FormatNumber("G", flagAlternate, fieldLength, fieldPrecision, flagLeft2Right, flagPositiveSign, flagPositiveSpace, paddingCharacter, o); defaultParamIx++; break; #endregion #region p - pointer case 'p': // pointer if (o is IntPtr) w = "0x" + ((IntPtr)o).ToString("x"); defaultParamIx++; break; #endregion #region n - number of processed chars so far case 'n': // number of characters so far w = FormatNumber("d", flagAlternate, fieldLength, int.MinValue, flagLeft2Right, flagPositiveSign, flagPositiveSpace, paddingCharacter, m.Index); break; #endregion default: w = String.Empty; defaultParamIx++; break; } // replace format parameter with parameter value // and start searching for the next format parameter // AFTER the position of the current inserted value // to prohibit recursive matches if the value also // includes a format specifier f.Remove(m.Index, m.Length); f.Insert(m.Index, w); m = r.Match(f.ToString(), m.Index + w.Length); } return f.ToString(); } #endregion #endregion #region Private Methods #region FormatOCT private static string FormatOct(string NativeFormat, bool Alternate, int FieldLength, int FieldPrecision, bool Left2Right, char Padding, object Value) { string w = String.Empty; string lengthFormat = "{0" + (FieldLength != int.MinValue ? "," + (Left2Right ? "-" : String.Empty) + FieldLength.ToString() : String.Empty) + "}"; if (IsNumericType(Value)) { w = Convert.ToString(UnboxToLong(Value, true), 8); if (Left2Right || Padding == ' ') { if (Alternate && w != "0") w = "0" + w; w = String.Format(lengthFormat, w); } else { if (FieldLength != int.MinValue) w = w.PadLeft(FieldLength - (Alternate && w != "0" ? 1 : 0), Padding); if (Alternate && w != "0") w = "0" + w; } } return w; } #endregion #region FormatHEX private static string FormatHex(string NativeFormat, bool Alternate, int FieldLength, int FieldPrecision, bool Left2Right, char Padding, object Value) { string w = String.Empty; string lengthFormat = "{0" + (FieldLength != int.MinValue ? "," + (Left2Right ? "-" : String.Empty) + FieldLength.ToString() : String.Empty) + "}"; string numberFormat = "{0:" + NativeFormat + (FieldPrecision != int.MinValue ? FieldPrecision.ToString() : String.Empty) + "}"; if (IsNumericType(Value)) { w = String.Format(numberFormat, Value); if (Left2Right || Padding == ' ') { if (Alternate) w = (NativeFormat == "x" ? "0x" : "0X") + w; w = String.Format(lengthFormat, w); } else { if (FieldLength != int.MinValue) w = w.PadLeft(FieldLength - (Alternate ? 2 : 0), Padding); if (Alternate) w = (NativeFormat == "x" ? "0x" : "0X") + w; } } return w; } #endregion #region FormatNumber private static string FormatNumber(string NativeFormat, bool Alternate, int FieldLength, int FieldPrecision, bool Left2Right, bool PositiveSign, bool PositiveSpace, char Padding, object Value) { string w = String.Empty; string lengthFormat = "{0" + (FieldLength != int.MinValue ? "," + (Left2Right ? "-" : String.Empty) + FieldLength.ToString() : String.Empty) + "}"; string numberFormat = "{0:" + NativeFormat + (FieldPrecision != int.MinValue ? FieldPrecision.ToString() : "0") + "}"; if (IsNumericType(Value)) { w = String.Format(numberFormat, Value); if (Left2Right || Padding == ' ') { if (IsPositive(Value, true)) w = (PositiveSign ? "+" : (PositiveSpace ? " " : String.Empty)) + w; w = String.Format(lengthFormat, w); } else { if (w.StartsWith("-")) w = w.Substring(1); if (FieldLength != int.MinValue) w = w.PadLeft(FieldLength - 1, Padding); if (IsPositive(Value, true)) w = (PositiveSign ? "+" : (PositiveSpace ? " " : (FieldLength != int.MinValue ? Padding.ToString() : String.Empty))) + w; else w = "-" + w; } } return w; } #endregion #endregion } }