I have extended a togglebutton control in Silverlight 5 and have created a Dependency property to Hold PathGeometry data. Expectedly the PathGeometry data is not being read. I'm not sure if its the converter or the setup. Here is all my code.
ExtentedRadTogglebutton.cs
public static readonly DependencyProperty ButtonIconPathDataProperty = DependencyProperty.Register(
"ButtonIconPathData",
typeof(PathGeometry),
typeof(ExtendedRadToggleButton),
new PropertyMetadata(new PathGeometry(), null));
Customised_RadToggleButtonControl.cs
if (!string.IsNullOrEmpty(settings.CusNav_L2_HomeButton_IconPathData))
{
StringToPathGeometryConverter cvSTGeo = new StringToPathGeometryConverter();
var iconPathData = cvSTGeo.Convert(settings.CusNav_L2_HomeButton_IconPathData,
null, null, null);
_radToggleButton.SetValue(ExtendedRadToggleButton.ButtonIconPathDataProperty, iconPathData);
}
XAML. resource
<Path x:Name="arrow"
Data="{TemplateBinding ButtonIconPathData}"/>
Converter
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Globalization;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace MyClub.SilverlightClient.Converter
{
public class StringToPathGeometryConverter : IValueConverter
{
#region Const & Private Variables
const bool AllowSign = true;
const bool AllowComma = true;
const bool IsFilled = true;
const bool IsClosed = true;
IFormatProvider _formatProvider;
PathFigure _figure = null; // Figure object, which will accept parsed segments
string _pathString; // Input string to be parsed
int _pathLength;
int _curIndex; // Location to read next character from
bool _figureStarted; // StartFigure is effective
Point _lastStart; // Last figure starting point
Point _lastPoint; // Last point
Point _secondLastPoint; // The point before last point
char _token; // Non whitespace character returned by ReadToken
#endregion
#region Public Functionality
/// <summary>
/// Main conversion routine - converts string path data definition to PathGeometry object
/// </summary>
/// <param name="path">String with path data definition</param>
/// <returns>PathGeometry object created from string definition</returns>
public PathGeometry Convert(string path)
{
if (null == path)
throw new ArgumentException("Path string cannot be null!");
if (path.Length == 0)
throw new ArgumentException("Path string cannot be empty!");
return parse(path);
}
/// <summary>
/// Main back conversion routine - converts PathGeometry object to its string equivalent
/// </summary>
/// <param name="geometry">Path Geometry object</param>
/// <returns>String equivalent to PathGeometry contents</returns>
public string ConvertBack(PathGeometry geometry)
{
if (null == geometry)
throw new ArgumentException("Path Geometry cannot be null!");
return parseBack(geometry);
}
#endregion
#region Private Functionality
/// <summary>
/// Main parser routine, which loops over each char in received string, and performs actions according to command/parameter being passed
/// </summary>
/// <param name="path">String with path data definition</param>
/// <returns>PathGeometry object created from string definition</returns>
private PathGeometry parse(string path)
{
PathGeometry _pathGeometry = null;
_formatProvider = CultureInfo.InvariantCulture;
_pathString = path;
_pathLength = path.Length;
_curIndex = 0;
_secondLastPoint = new Point(0, 0);
_lastPoint = new Point(0, 0);
_lastStart = new Point(0, 0);
_figureStarted = false;
bool first = true;
char last_cmd = ' ';
while (ReadToken()) // Empty path is allowed in XAML
{
char cmd = _token;
if (first)
{
if ((cmd != 'M') && (cmd != 'm') && (cmd != 'f') && (cmd != 'F')) // Path starts with M|m
{
ThrowBadToken();
}
first = false;
}
switch (cmd)
{
case 'f':
case 'F':
_pathGeometry = new PathGeometry();
double _num = ReadNumber(!AllowComma);
_pathGeometry.FillRule = _num == 0 ? FillRule.EvenOdd : FillRule.Nonzero;
break;
case 'm':
case 'M':
// XAML allows multiple points after M/m
_lastPoint = ReadPoint(cmd, !AllowComma);
_figure = new PathFigure();
_figure.StartPoint = _lastPoint;
_figure.IsFilled = IsFilled;
_figure.IsClosed = !IsClosed;
//context.BeginFigure(_lastPoint, IsFilled, !IsClosed);
_figureStarted = true;
_lastStart = _lastPoint;
last_cmd = 'M';
while (IsNumber(AllowComma))
{
_lastPoint = ReadPoint(cmd, !AllowComma);
LineSegment _lineSegment = new LineSegment();
_lineSegment.Point = _lastPoint;
_figure.Segments.Add(_lineSegment);
//context.LineTo(_lastPoint, IsStroked, !IsSmoothJoin);
last_cmd = 'L';
}
break;
case 'l':
case 'L':
case 'h':
case 'H':
case 'v':
case 'V':
EnsureFigure();
do
{
switch (cmd)
{
case 'l': _lastPoint = ReadPoint(cmd, !AllowComma); break;
case 'L': _lastPoint = ReadPoint(cmd, !AllowComma); break;
case 'h': _lastPoint.X += ReadNumber(!AllowComma); break;
case 'H': _lastPoint.X = ReadNumber(!AllowComma); break;
case 'v': _lastPoint.Y += ReadNumber(!AllowComma); break;
case 'V': _lastPoint.Y = ReadNumber(!AllowComma); break;
}
LineSegment _lineSegment = new LineSegment();
_lineSegment.Point = _lastPoint;
_figure.Segments.Add(_lineSegment);
//context.LineTo(_lastPoint, IsStroked, !IsSmoothJoin);
}
while (IsNumber(AllowComma));
last_cmd = 'L';
break;
case 'c':
case 'C': // cubic Bezier
case 's':
case 'S': // smooth cublic Bezier
EnsureFigure();
do
{
Point p;
if ((cmd == 's') || (cmd == 'S'))
{
if (last_cmd == 'C')
{
p = Reflect();
}
else
{
p = _lastPoint;
}
_secondLastPoint = ReadPoint(cmd, !AllowComma);
}
else
{
p = ReadPoint(cmd, !AllowComma);
_secondLastPoint = ReadPoint(cmd, AllowComma);
}
_lastPoint = ReadPoint(cmd, AllowComma);
BezierSegment _bizierSegment = new BezierSegment();
_bizierSegment.Point1 = p;
_bizierSegment.Point2 = _secondLastPoint;
_bizierSegment.Point3 = _lastPoint;
_figure.Segments.Add(_bizierSegment);
//context.BezierTo(p, _secondLastPoint, _lastPoint, IsStroked, !IsSmoothJoin);
last_cmd = 'C';
}
while (IsNumber(AllowComma));
break;
case 'q':
case 'Q': // quadratic Bezier
case 't':
case 'T': // smooth quadratic Bezier
EnsureFigure();
do
{
if ((cmd == 't') || (cmd == 'T'))
{
if (last_cmd == 'Q')
{
_secondLastPoint = Reflect();
}
else
{
_secondLastPoint = _lastPoint;
}
_lastPoint = ReadPoint(cmd, !AllowComma);
}
else
{
_secondLastPoint = ReadPoint(cmd, !AllowComma);
_lastPoint = ReadPoint(cmd, AllowComma);
}
QuadraticBezierSegment _quadraticBezierSegment = new QuadraticBezierSegment();
_quadraticBezierSegment.Point1 = _secondLastPoint;
_quadraticBezierSegment.Point2 = _lastPoint;
_figure.Segments.Add(_quadraticBezierSegment);
//context.QuadraticBezierTo(_secondLastPoint, _lastPoint, IsStroked, !IsSmoothJoin);
last_cmd = 'Q';
}
while (IsNumber(AllowComma));
break;
case 'a':
case 'A':
EnsureFigure();
do
{
// A 3,4 5, 0, 0, 6,7
double w = ReadNumber(!AllowComma);
double h = ReadNumber(AllowComma);
double rotation = ReadNumber(AllowComma);
bool large = ReadBool();
bool sweep = ReadBool();
_lastPoint = ReadPoint(cmd, AllowComma);
ArcSegment _arcSegment = new ArcSegment();
_arcSegment.Point = _lastPoint;
_arcSegment.Size = new Size(w, h);
_arcSegment.RotationAngle = rotation;
_arcSegment.IsLargeArc = large;
_arcSegment.SweepDirection = sweep ? SweepDirection.Clockwise : SweepDirection.Counterclockwise;
_figure.Segments.Add(_arcSegment);
//context.ArcTo(
// _lastPoint,
// new Size(w, h),
// rotation,
// large,
// sweep ? SweepDirection.Clockwise : SweepDirection.Counterclockwise,
// IsStroked,
// !IsSmoothJoin
// );
}
while (IsNumber(AllowComma));
last_cmd = 'A';
break;
case 'z':
case 'Z':
EnsureFigure();
_figure.IsClosed = IsClosed;
//context.SetClosedState(IsClosed);
_figureStarted = false;
last_cmd = 'Z';
_lastPoint = _lastStart; // Set reference point to be first point of current figure
break;
default:
ThrowBadToken();
break;
}
if (null != _figure)
{
if (_figure.IsClosed)
{
if (null == _pathGeometry)
_pathGeometry = new PathGeometry();
_pathGeometry.Figures.Add(_figure);
_figure = null;
first = true;
}
}
}
if (null != _figure)
{
if (null == _pathGeometry)
_pathGeometry = new PathGeometry();
if (!_pathGeometry.Figures.Contains(_figure))
_pathGeometry.Figures.Add(_figure);
}
return _pathGeometry;
}
void SkipDigits(bool signAllowed)
{
// Allow for a sign
if (signAllowed && More() && ((_pathString[_curIndex] == '-') || _pathString[_curIndex] == '+'))
{
_curIndex++;
}
while (More() && (_pathString[_curIndex] >= '0') && (_pathString[_curIndex] <= '9'))
{
_curIndex++;
}
}
bool ReadBool()
{
SkipWhiteSpace(AllowComma);
if (More())
{
_token = _pathString[_curIndex++];
if (_token == '0')
{
return false;
}
else if (_token == '1')
{
return true;
}
}
ThrowBadToken();
return false;
}
private Point Reflect()
{
return new Point(2 * _lastPoint.X - _secondLastPoint.X,
2 * _lastPoint.Y - _secondLastPoint.Y);
}
private void EnsureFigure()
{
if (!_figureStarted)
{
_figure = new PathFigure();
_figure.StartPoint = _lastStart;
//_context.BeginFigure(_lastStart, IsFilled, !IsClosed);
_figureStarted = true;
}
}
double ReadNumber(bool allowComma)
{
if (!IsNumber(allowComma))
{
ThrowBadToken();
}
bool simple = true;
int start = _curIndex;
//
// Allow for a sign
//
// There are numbers that cannot be preceded with a sign, for instance, -NaN, but it's
// fine to ignore that at this point, since the CLR parser will catch this later.
//
if (More() && ((_pathString[_curIndex] == '-') || _pathString[_curIndex] == '+'))
{
_curIndex++;
}
// Check for Infinity (or -Infinity).
if (More() && (_pathString[_curIndex] == 'I'))
{
//
// Don't bother reading the characters, as the CLR parser will
// do this for us later.
//
_curIndex = Math.Min(_curIndex + 8, _pathLength); // "Infinity" has 8 characters
simple = false;
}
// Check for NaN
else if (More() && (_pathString[_curIndex] == 'N'))
{
//
// Don't bother reading the characters, as the CLR parser will
// do this for us later.
//
_curIndex = Math.Min(_curIndex + 3, _pathLength); // "NaN" has 3 characters
simple = false;
}
else
{
SkipDigits(!AllowSign);
// Optional period, followed by more digits
if (More() && (_pathString[_curIndex] == '.'))
{
simple = false;
_curIndex++;
SkipDigits(!AllowSign);
}
// Exponent
if (More() && ((_pathString[_curIndex] == 'E') || (_pathString[_curIndex] == 'e')))
{
simple = false;
_curIndex++;
SkipDigits(AllowSign);
}
}
if (simple && (_curIndex <= (start + 8))) // 32-bit integer
{
int sign = 1;
if (_pathString[start] == '+')
{
start++;
}
else if (_pathString[start] == '-')
{
start++;
sign = -1;
}
int value = 0;
while (start < _curIndex)
{
value = value * 10 + (_pathString[start] - '0');
start++;
}
return value * sign;
}
else
{
string subString = _pathString.Substring(start, _curIndex - start);
try
{
return System.Convert.ToDouble(subString, _formatProvider);
}
catch (FormatException except)
{
throw new FormatException(string.Format("Unexpected character in path '{0}' at position {1}", _pathString, _curIndex - 1), except);
}
}
}
private bool IsNumber(bool allowComma)
{
bool commaMet = SkipWhiteSpace(allowComma);
if (More())
{
_token = _pathString[_curIndex];
// Valid start of a number
if ((_token == '.') || (_token == '-') || (_token == '+') || ((_token >= '0') && (_token <= '9'))
|| (_token == 'I') // Infinity
|| (_token == 'N')) // NaN
{
return true;
}
}
if (commaMet) // Only allowed between numbers
{
ThrowBadToken();
}
return false;
}
private Point ReadPoint(char cmd, bool allowcomma)
{
double x = ReadNumber(allowcomma);
double y = ReadNumber(AllowComma);
if (cmd >= 'a') // 'A' < 'a'. lower case for relative
{
x += _lastPoint.X;
y += _lastPoint.Y;
}
return new Point(x, y);
}
private bool ReadToken()
{
SkipWhiteSpace(!AllowComma);
// Check for end of string
if (More())
{
_token = _pathString[_curIndex++];
return true;
}
else
{
return false;
}
}
bool More()
{
return _curIndex < _pathLength;
}
// Skip white space, one comma if allowed
private bool SkipWhiteSpace(bool allowComma)
{
bool commaMet = false;
while (More())
{
char ch = _pathString[_curIndex];
switch (ch)
{
case ' ':
case '\n':
case '\r':
case '\t': // SVG whitespace
break;
case ',':
if (allowComma)
{
commaMet = true;
allowComma = false; // one comma only
}
else
{
ThrowBadToken();
}
break;
default:
// Avoid calling IsWhiteSpace for ch in (' ' .. 'z']
if (((ch > ' ') && (ch <= 'z')) || !Char.IsWhiteSpace(ch))
{
return commaMet;
}
break;
}
_curIndex++;
}
return commaMet;
}
private void ThrowBadToken()
{
throw new FormatException(string.Format("Unexpected character in path '{0}' at position {1}", _pathString, _curIndex - 1));
}
static internal char GetNumericListSeparator(IFormatProvider provider)
{
char numericSeparator = ',';
// Get the NumberFormatInfo out of the provider, if possible
// If the IFormatProvider doesn't not contain a NumberFormatInfo, then
// this method returns the current culture's NumberFormatInfo.
NumberFormatInfo numberFormat = NumberFormatInfo.GetInstance(provider);
// Is the decimal separator is the same as the list separator?
// If so, we use the ";".
if ((numberFormat.NumberDecimalSeparator.Length > 0) && (numericSeparator == numberFormat.NumberDecimalSeparator[0]))
{
numericSeparator = ';';
}
return numericSeparator;
}
private string parseBack(PathGeometry geometry)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
IFormatProvider provider = new System.Globalization.CultureInfo("en-us");
string format = null;
sb.Append("F" + (geometry.FillRule == FillRule.EvenOdd ? "0" : "1") + " ");
foreach (PathFigure figure in geometry.Figures)
{
sb.Append("M " + ((IFormattable)figure.StartPoint).ToString(format, provider) + " ");
foreach (PathSegment segment in figure.Segments)
{
char separator = GetNumericListSeparator(provider);
if (segment.GetType() == typeof(LineSegment))
{
LineSegment _lineSegment = segment as LineSegment;
sb.Append("L " + ((IFormattable)_lineSegment.Point).ToString(format, provider) + " ");
}
else if (segment.GetType() == typeof(BezierSegment))
{
BezierSegment _bezierSegment = segment as BezierSegment;
sb.Append(String.Format(provider,
"C{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "} ",
separator,
_bezierSegment.Point1,
_bezierSegment.Point2,
_bezierSegment.Point3
));
}
else if (segment.GetType() == typeof(QuadraticBezierSegment))
{
QuadraticBezierSegment _quadraticBezierSegment = segment as QuadraticBezierSegment;
sb.Append(String.Format(provider,
"Q{1:" + format + "}{0}{2:" + format + "} ",
separator,
_quadraticBezierSegment.Point1,
_quadraticBezierSegment.Point2));
}
else if (segment.GetType() == typeof(ArcSegment))
{
ArcSegment _arcSegment = segment as ArcSegment;
sb.Append(String.Format(provider,
"A{1:" + format + "}{0}{2:" + format + "}{0}{3}{0}{4}{0}{5:" + format + "} ",
separator,
_arcSegment.Size,
_arcSegment.RotationAngle,
_arcSegment.IsLargeArc ? "1" : "0",
_arcSegment.SweepDirection == SweepDirection.Clockwise ? "1" : "0",
_arcSegment.Point));
}
}
if (figure.IsClosed)
sb.Append("Z");
}
return sb.ToString();
}
#endregion
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string path = value as string;
if (null != path)
return Convert(path);
else
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
PathGeometry geometry = value as PathGeometry;
if (null != geometry)
return ConvertBack(geometry);
else
return default(string);
}
#endregion
}
}
Related
I know I sound like a bad programmer right now - but I'm new and I can't figure out how to use this reference thing and pass parameters, I mean I know how to do it - but at the same time - this isn't working and I don't know why.
static void Main(string[] args) {
DealCard(ref card);
Console.WriteLine();
Console.ReadLine();
}
private static void DealCard(string card) {
string finalNum = "";
string finalSuite = "";
bool diffCard = false;
do {
Random cardPicker = new Random();
int cardSuite = cardPicker.Next(1, 5);
if (cardSuite == 1) {
finalSuite = "Hearts";
} else if (cardSuite == 2) {
finalSuite = "Spades";
} else if (cardSuite == 3) {
finalSuite = "Clubs";
} else if (cardSuite == 4) {
finalSuite = "Diamonds";
}
int cardNum = cardPicker.Next(1, 14);
if (cardNum == 1) {
finalNum = "Ace";
} else if (cardNum == 2) {
finalNum = "Two";
} else if (cardNum == 3) {
finalNum = "Thre";
} else if (cardNum == 4) {
finalNum = "Four";
} else if (cardNum == 5) {
finalNum = "Five";
} else if (cardNum == 6) {
finalNum = "Six";
} else if (cardNum == 7) {
finalNum = "Seven";
} else if (cardNum == 8) {
finalNum = "Eight";
} else if (cardNum == 9) {
finalNum = "Nine";
} else if (cardNum == 10) {
finalNum = "Ten";
} else if (cardNum == 11) {
finalNum = "Jack";
} else if (cardNum == 12) {
finalNum = "Queen";
} else if (cardNum == 13) {
finalNum = "King";
}
string newCard = finalNum + " of " + finalSuite;
if (newCard != card) {
card = finalNum + " of " + finalSuite;
diffCard = true;
} else {
}
card = newCard;
} while (diffCard == false);
}
Yes I know that massive 'if' is an eyesore.
Yes I know I could accomplish this in less than half the lines.
Yes I know it's a simple question.
Yes I know I'm bad, but I'd like to humbly request that anyone helps me to stop losing hair over this.
You have to declare your method like this:
private static void DealCard(ref string card)
Basically the method has to accept a ref parameter.
Here is documentation to support the answer:
Value Type Parameters
Reference Type Parameters
Your code can be like this
public class Program
{
public static void Main(string[] args) {
string card = "";
DealCard(ref card);
}
private static void DealCard(ref string card)
{
string finalNum = "";
string finalSuite = "";
bool diffCard = false;
do {
Random cardPicker = new Random();
int cardSuite = cardPicker.Next(1, 5);
string[] suite = new String[]{"Hearts","Spades", "Clubs", "Diaminds"};
int cardNum = cardPicker.Next(1, 3);
string[] numbers = new String[]{"one","two","three", "four"};
string newCard = numbers[cardNum] + " of " + suite[cardSuite];
if (newCard != card) {
card = finalNum + " of " + finalSuite;
diffCard = true;
} else {
}
card = newCard;
Console.WriteLine(newCard);
} while (diffCard == false);
}
}
I have some code like:
new ValidationFailure<AddSystemUserDto>
This is in various places in my application service layer, I want to find all the different "Dto" types that have been used when newing up a ValidationFailure across the code, is this possible using reflection? Without having to run each application service method?
Yes.
I use a little helper class for these kinds of tasks, that I conveniently call Decompiler. Basically it transforms a byte[] of .NET code to opcodes:
public class ILInstruction
{
public ILInstruction(int offset, OpCode code, object operand)
{
this.Offset = offset;
this.Code = code;
this.Operand = operand;
}
// Fields
public int Offset { get; private set; }
public OpCode Code { get; private set; }
public object Operand { get; private set; }
}
internal class Decompiler
{
private Decompiler() { }
static Decompiler()
{
InitDecompiler();
}
private static OpCode[] singleByteOpcodes;
private static OpCode[] multiByteOpcodes;
private static void InitDecompiler()
{
singleByteOpcodes = new OpCode[0x100];
multiByteOpcodes = new OpCode[0x100];
FieldInfo[] infoArray1 = typeof(OpCodes).GetFields();
for (int num1 = 0; num1 < infoArray1.Length; num1++)
{
FieldInfo info1 = infoArray1[num1];
if (info1.FieldType == typeof(OpCode))
{
OpCode code1 = (OpCode)info1.GetValue(null);
ushort num2 = (ushort)code1.Value;
if (num2 < 0x100)
{
singleByteOpcodes[(int)num2] = code1;
}
else
{
if ((num2 & 0xff00) != 0xfe00)
{
throw new Exception("Invalid opcode: " + num2.ToString());
}
multiByteOpcodes[num2 & 0xff] = code1;
}
}
}
}
public static IEnumerable<ILInstruction> Decompile(MethodBase mi, byte[] ildata)
{
Module module = mi.Module;
ByteReader reader = new ByteReader(ildata);
while (!reader.Eof)
{
OpCode code = OpCodes.Nop;
int offset = reader.Position;
ushort b = reader.ReadByte();
if (b != 0xfe)
{
code = singleByteOpcodes[b];
}
else
{
b = reader.ReadByte();
code = multiByteOpcodes[b];
b |= (ushort)(0xfe00);
}
object operand = null;
switch (code.OperandType)
{
case OperandType.InlineBrTarget:
operand = reader.ReadInt32() + reader.Position;
break;
case OperandType.InlineField:
if (mi is ConstructorInfo)
{
operand = module.ResolveField(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), Type.EmptyTypes);
}
else
{
operand = module.ResolveField(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments());
}
break;
case OperandType.InlineI:
operand = reader.ReadInt32();
break;
case OperandType.InlineI8:
operand = reader.ReadInt64();
break;
case OperandType.InlineMethod:
try
{
if (mi is ConstructorInfo)
{
operand = module.ResolveMember(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), Type.EmptyTypes);
}
else
{
operand = module.ResolveMember(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments());
}
}
catch
{
operand = null;
}
break;
case OperandType.InlineNone:
break;
case OperandType.InlineR:
operand = reader.ReadDouble();
break;
case OperandType.InlineSig:
operand = module.ResolveSignature(reader.ReadInt32());
break;
case OperandType.InlineString:
operand = module.ResolveString(reader.ReadInt32());
break;
case OperandType.InlineSwitch:
int count = reader.ReadInt32();
int[] targetOffsets = new int[count];
for (int i = 0; i < count; ++i)
{
targetOffsets[i] = reader.ReadInt32();
}
int pos = reader.Position;
for (int i = 0; i < count; ++i)
{
targetOffsets[i] += pos;
}
operand = targetOffsets;
break;
case OperandType.InlineTok:
case OperandType.InlineType:
try
{
if (mi is ConstructorInfo)
{
operand = module.ResolveMember(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), Type.EmptyTypes);
}
else
{
operand = module.ResolveMember(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments());
}
}
catch
{
operand = null;
}
break;
case OperandType.InlineVar:
operand = reader.ReadUInt16();
break;
case OperandType.ShortInlineBrTarget:
operand = reader.ReadSByte() + reader.Position;
break;
case OperandType.ShortInlineI:
operand = reader.ReadSByte();
break;
case OperandType.ShortInlineR:
operand = reader.ReadSingle();
break;
case OperandType.ShortInlineVar:
operand = reader.ReadByte();
break;
default:
throw new Exception("Unknown instruction operand; cannot continue. Operand type: " + code.OperandType);
}
yield return new ILInstruction(offset, code, operand);
}
}
}
From this point, the rest is a matter of finding all types that you use when calling constructors (Note: untested code, there's probably something wrong :-):
IEnumerable<Type> FindDtoTypes()
{
foreach (var item in GetType().Assembly.GetTypes())
{
foreach (var member in type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))
{
var meth = member as MethodBase;
if (meth != null && meth.GetMethodBody() != null)
{
var code = meth.GetMethodBody().GetILAsByteArray();
foreach (var instr in Decompile(meth, code))
{
var oper = instr.Operand as MethodBase;
if (oper != null && oper.IsConstructor &&
oper.DeclaringType.IsGenericType)
{
var dtoType = mb.DeclaringType.GetGenericArguments().First();
if (dtoType.IsAssignableFrom(oper.DeclaringType))
yield return oper.DeclaringType;
}
}
}
}
}
}
I have Xamarin iOS app with in-app products. I get base64 encoded app receipt:
NSUrl receiptURL = NSBundle.MainBundle.AppStoreReceiptUrl;
String receiptData = receipt.GetBase64EncodedString(0);
According to Apple docs 7.0+ app receipt is packed in PKCS7 container using ASN1. When I send it to apple server it returns receipt in JSON. But I want to parse it locally to know what iaps user does already have. I don't need validation, as Apple does it remotely, I just need to get receipts for purchased inaps.
So far, as an investigation, I've parsed it in php with phpseclib, manually (no idea how to do this programatically) located receipt and parsed it as well.
$asn_parser = new File_ASN1();
//parse the receipt binary string
$pkcs7 = $asn_parser->decodeBER(base64_decode($f));
//print_r($pkcs7);
$payload_sequence = $pkcs7[0]['content'][1]['content'][0]['content'][2]['content'];
$pld = $asn_parser->decodeBER($payload_sequence[1]['content'][0]['content']);
//print_r($pld);
$prd = $asn_parser->decodeBER($pld[0]['content'][21]['content'][2]['content']);
print_r($prd);
But even this way I've got a mess of attributes, each looks like:
Array
(
[start] => 271
[headerlength] => 2
[type] => 4
[content] => 2016-08-22T13:22:00Z
[length] => 24
)
It is not human readable, I need something like (output with print_r of returned by Apple) :
[receipt] => Array
(
[receipt_type] => ProductionSandbox
[adam_id] => 0
[app_item_id] => 0
[bundle_id] => com.my.test.app.iOS
...
[in_app] => Array
(
[0] => Array
(
[quantity] => 1
[product_id] => test_iap_1
[transaction_id] => 1000000230806171
...
[is_trial_period] => false
)
)
)
Things seem too complicated, I hardly believe unpacking receipt is so complex. Does anybody know how to manage this? I've found this post but library is written in objective-C which is not applicable to my current environment. I'd say sources of this lib are frightened me: so much complex code just to unpack standartized container. I mean working with json, bson, etc. is very easy, but not asn1.
Finally I've unpacked it using Liping Dai LCLib (lipingshare.com). Asn1Parser returns DOM-like tree with root node - very handy lib.
public class AppleAppReceipt
{
public class AppleInAppPurchaseReceipt
{
public int Quantity;
public string ProductIdentifier;
public string TransactionIdentifier;
public DateTime PurchaseDate;
public string OriginalTransactionIdentifier;
public DateTime OriginalPurchaseDate;
public DateTime SubscriptionExpirationDate;
public DateTime CancellationDate;
public int WebOrderLineItemID;
}
const int AppReceiptASN1TypeBundleIdentifier = 2;
const int AppReceiptASN1TypeAppVersion = 3;
const int AppReceiptASN1TypeOpaqueValue = 4;
const int AppReceiptASN1TypeHash = 5;
const int AppReceiptASN1TypeReceiptCreationDate = 12;
const int AppReceiptASN1TypeInAppPurchaseReceipt = 17;
const int AppReceiptASN1TypeOriginalAppVersion = 19;
const int AppReceiptASN1TypeReceiptExpirationDate = 21;
const int AppReceiptASN1TypeQuantity = 1701;
const int AppReceiptASN1TypeProductIdentifier = 1702;
const int AppReceiptASN1TypeTransactionIdentifier = 1703;
const int AppReceiptASN1TypePurchaseDate = 1704;
const int AppReceiptASN1TypeOriginalTransactionIdentifier = 1705;
const int AppReceiptASN1TypeOriginalPurchaseDate = 1706;
const int AppReceiptASN1TypeSubscriptionExpirationDate = 1708;
const int AppReceiptASN1TypeWebOrderLineItemID = 1711;
const int AppReceiptASN1TypeCancellationDate = 1712;
public string BundleIdentifier;
public string AppVersion;
public string OriginalAppVersion; //какую покупали
public DateTime ReceiptCreationDate;
public Dictionary<string, AppleInAppPurchaseReceipt> PurchaseReceipts;
public bool parseAsn1Data(byte[] val)
{
if (val == null)
return false;
Asn1Parser p = new Asn1Parser();
var stream = new MemoryStream(val);
try
{
p.LoadData(stream);
}
catch (Exception e)
{
return false;
}
Asn1Node root = p.RootNode;
if (root == null)
return false;
PurchaseReceipts = new Dictionary<string, AppleInAppPurchaseReceipt>();
parseNodeRecursive(root);
return !string.IsNullOrEmpty(BundleIdentifier);
}
private static string getStringFromSubNode(Asn1Node nn)
{
string dataStr = null;
if ((nn.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.OCTET_STRING && nn.ChildNodeCount > 0)
{
Asn1Node n = nn.GetChildNode(0);
switch (n.Tag & Asn1Tag.TAG_MASK)
{
case Asn1Tag.PRINTABLE_STRING:
case Asn1Tag.IA5_STRING:
case Asn1Tag.UNIVERSAL_STRING:
case Asn1Tag.VISIBLE_STRING:
case Asn1Tag.NUMERIC_STRING:
case Asn1Tag.UTC_TIME:
case Asn1Tag.UTF8_STRING:
case Asn1Tag.BMPSTRING:
case Asn1Tag.GENERAL_STRING:
case Asn1Tag.GENERALIZED_TIME:
{
if ((n.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.UTF8_STRING)
{
UTF8Encoding unicode = new UTF8Encoding();
dataStr = unicode.GetString(n.Data);
}
else
{
dataStr = Asn1Util.BytesToString(n.Data);
}
}
break;
}
}
return dataStr;
}
private static DateTime getDateTimeFromSubNode(Asn1Node nn)
{
string dataStr = getStringFromSubNode(nn);
if (string.IsNullOrEmpty(dataStr))
return DateTime.MinValue;
DateTime retval = DateTime.MaxValue;
try
{
retval = DateTime.Parse(dataStr);
}
catch (Exception e)
{
}
return retval;
}
private static int getIntegerFromSubNode(Asn1Node nn)
{
int retval = -1;
if ((nn.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.OCTET_STRING && nn.ChildNodeCount > 0)
{
Asn1Node n = nn.GetChildNode(0);
if ((n.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.INTEGER)
retval = (int)Asn1Util.BytesToLong(n.Data);
}
return retval;
}
private void parseNodeRecursive(Asn1Node tNode)
{
bool processed_node = false;
if ((tNode.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.SEQUENCE && tNode.ChildNodeCount == 3)
{
Asn1Node node1 = tNode.GetChildNode(0);
Asn1Node node2 = tNode.GetChildNode(1);
Asn1Node node3 = tNode.GetChildNode(2);
if ((node1.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.INTEGER && (node2.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.INTEGER &&
(node3.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.OCTET_STRING)
{
processed_node = true;
int type = (int)Asn1Util.BytesToLong(node1.Data);
switch (type)
{
case AppReceiptASN1TypeBundleIdentifier:
BundleIdentifier = getStringFromSubNode(node3);
break;
case AppReceiptASN1TypeAppVersion:
AppVersion = getStringFromSubNode(node3);
break;
case AppReceiptASN1TypeOpaqueValue:
break;
case AppReceiptASN1TypeHash:
break;
case AppReceiptASN1TypeOriginalAppVersion:
OriginalAppVersion = getStringFromSubNode(node3);
break;
case AppReceiptASN1TypeReceiptExpirationDate:
break;
case AppReceiptASN1TypeReceiptCreationDate:
ReceiptCreationDate = getDateTimeFromSubNode(node3);
break;
case AppReceiptASN1TypeInAppPurchaseReceipt:
{
if (node3.ChildNodeCount > 0)
{
Asn1Node node31 = node3.GetChildNode(0);
if ((node31.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.SET && node31.ChildNodeCount > 0)
{
AppleInAppPurchaseReceipt receipt = new AppleInAppPurchaseReceipt();
for (int i = 0; i < node31.ChildNodeCount; i++)
{
Asn1Node node311 = node31.GetChildNode(i);
if ((node311.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.SEQUENCE && node311.ChildNodeCount == 3)
{
Asn1Node node3111 = node311.GetChildNode(0);
Asn1Node node3112 = node311.GetChildNode(1);
Asn1Node node3113 = node311.GetChildNode(2);
if ((node3111.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.INTEGER && (node3112.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.INTEGER &&
(node3113.Tag & Asn1Tag.TAG_MASK) == Asn1Tag.OCTET_STRING)
{
int type1 = (int)Asn1Util.BytesToLong(node3111.Data);
switch (type1)
{
case AppReceiptASN1TypeQuantity:
receipt.Quantity = getIntegerFromSubNode(node3113);
break;
case AppReceiptASN1TypeProductIdentifier:
receipt.ProductIdentifier = getStringFromSubNode(node3113);
break;
case AppReceiptASN1TypeTransactionIdentifier:
receipt.TransactionIdentifier = getStringFromSubNode(node3113);
break;
case AppReceiptASN1TypePurchaseDate:
receipt.PurchaseDate = getDateTimeFromSubNode(node3113);
break;
case AppReceiptASN1TypeOriginalTransactionIdentifier:
receipt.OriginalTransactionIdentifier = getStringFromSubNode(node3113);
break;
case AppReceiptASN1TypeOriginalPurchaseDate:
receipt.OriginalPurchaseDate = getDateTimeFromSubNode(node3113);
break;
case AppReceiptASN1TypeSubscriptionExpirationDate:
receipt.SubscriptionExpirationDate = getDateTimeFromSubNode(node3113);
break;
case AppReceiptASN1TypeWebOrderLineItemID:
receipt.WebOrderLineItemID = getIntegerFromSubNode(node3113);
break;
case AppReceiptASN1TypeCancellationDate:
receipt.CancellationDate = getDateTimeFromSubNode(node3113);
break;
}
}
}
}
if (!string.IsNullOrEmpty(receipt.ProductIdentifier))
PurchaseReceipts.Add(receipt.ProductIdentifier, receipt);
}
}
}
break;
default:
processed_node = false;
break;
}
}
}
if (!processed_node)
{
for (int i = 0; i < tNode.ChildNodeCount; i++)
{
Asn1Node chld = tNode.GetChildNode(i);
if (chld != null)
parseNodeRecursive(chld);
}
}
}
}
And usage:
public void printAppReceipt()
{
NSUrl receiptURL = NSBundle.MainBundle.AppStoreReceiptUrl;
if (receiptURL != null)
{
Console.WriteLine("receiptUrl='" + receiptURL + "'");
NSData receipt = NSData.FromUrl(receiptURL);
if (receipt != null)
{
byte[] rbytes = receipt.ToArray();
AppleAppReceipt apprec = new AppleAppReceipt();
if (apprec.parseAsn1Data(rbytes))
{
Console.WriteLine("Received receipt for " + apprec.BundleIdentifier + " with " + apprec.PurchaseReceipts.Count +
" products");
Console.WriteLine(JsonConvert.SerializeObject(apprec,Formatting.Indented));
}
}
else
Console.WriteLine("receipt == null");
}
}
I am working on a logic that decreases the value of an alphanumeric List<char>. For example, A10 becomes A9, BBA becomes BAZ, 123 becomes 122. And yes, if the value entered is the last one(like A or 0), then I should return -
An additional overhead is that there is a List<char> variable which is maintained by the user. It has characters which are to be skipped. For example, if the list contains A in it, the value GHB should become GGZ and not GHA.
The base of this logic is a very simple usage of decreasing the char but with these conditions, I am finding it very difficult.
My project is in Silverlight, the language is C#. Following is my code that I have been trying to do in the 3 methods:
List<char> lstGetDecrName(List<char> lstVal)//entry point of the value that returns decreased value
{
List<char> lstTmp = lstVal;
subCheckEmpty(ref lstTmp);
switch (lstTmp.Count)
{
case 0:
lstTmp.Add('-');
return lstTmp;
case 1:
if (lstTmp[0] == '-')
{
return lstTmp;
}
break;
case 2:
if (lstTmp[1] == '0')
{
if (lstTmp[0] == '1')
{
lstTmp.Clear();
lstTmp.Add('9');
return lstTmp;
}
if (lstTmp[0] == 'A')
{
lstTmp.Clear();
lstTmp.Add('-');
return lstTmp;
}
}
if (lstTmp[1] == 'A')
{
if (lstTmp[0] == 'A')
{
lstTmp.Clear();
lstTmp.Add('Z');
return lstTmp;
}
}
break;
}
return lstGetDecrValue(lstTmp,lstVal);
}
List<char> lstGetDecrValue(List<char> lstTmp,List<char> lstVal)
{
List<char> lstValue = new List<char>();
switch (lstTmp.Last())
{
case 'A':
lstValue = lstGetDecrTemp('Z', lstTmp, lstVal);
break;
case 'a':
lstValue = lstGetDecrTemp('z', lstTmp, lstVal);
break;
case '0':
lstValue = lstGetDecrTemp('9', lstTmp, lstVal);
break;
default:
char tmp = (char)(lstTmp.Last() - 1);
lstTmp.RemoveAt(lstTmp.Count - 1);
lstTmp.Add(tmp);
lstValue = lstTmp;
break;
}
return lstValue;
}
List<char> lstGetDecrTemp(char chrTemp, List<char> lstTmp, List<char> lstVal)//shifting places eg unit to ten,etc.
{
if (lstTmp.Count == 1)
{
lstTmp.Clear();
lstTmp.Add('-');
return lstTmp;
}
lstTmp.RemoveAt(lstTmp.Count - 1);
lstVal = lstGetDecrName(lstTmp);
lstVal.Insert(lstVal.Count, chrTemp);
return lstVal;
}
I seriously need help for this. Please help me out crack through this.
The problem you are trying to solve is actually how to decrement discreet sections of a sequence of characters, each with it's own counting system, where each section is separated by a change between Alpha and Numeric. The rest of the problem is easy once you identify this.
The skipping of unwanted characters is simply a matter of repeating the decrement if you get an unwanted character in the result.
One difficultly is the ambiguous definition of the sequences. e.g. what to do when you get down to say A00, what is next? "A" or "-". For the sake of argument I am assuming a practical implementation based loosely on Excel cell names (i.e. each section operates independently of the others).
The code below does 95% of what you wanted, however there is a bug in the exclusions code. e.g. "ABB" becomes "AAY". I feel the exclusions need to be applied at a higher level (e.g. repeat decrement until no character is in the exclusions list), but I don't have time to finish it now. Also it is resulting in a blank string when it counts down to nothing, rather than the "-" you wanted, but that is trivial to add at the end of the process.
Part 1 (divide the problem into sections):
public static string DecreaseName( string name, string exclusions )
{
if (string.IsNullOrEmpty(name))
{
return name;
}
// Split the problem into sections (reverse order)
List<StringBuilder> sections = new List<StringBuilder>();
StringBuilder result = new StringBuilder(name.Length);
bool isNumeric = char.IsNumber(name[0]);
StringBuilder sb = new StringBuilder();
sections.Add(sb);
foreach (char c in name)
{
// If we change between alpha and number, start new string.
if (char.IsNumber(c) != isNumeric)
{
isNumeric = char.IsNumber(c);
sb = new StringBuilder();
sections.Insert(0, sb);
}
sb.Append(c);
}
// Now process each section
bool cascadeToNext = true;
foreach (StringBuilder section in sections)
{
if (cascadeToNext)
{
result.Insert(0, DecrementString(section, exclusions, out cascadeToNext));
}
else
{
result.Insert(0, section);
}
}
return result.ToString().Replace(" ", "");
}
Part2 (decrement a given string):
private static string DecrementString(StringBuilder section, string exclusions, out bool cascadeToNext)
{
bool exclusionsExist = false;
do
{
exclusionsExist = false;
cascadeToNext = true;
// Process characters in reverse
for (int i = section.Length - 1; i >= 0 && cascadeToNext; i--)
{
char c = section[i];
switch (c)
{
case 'A':
c = (i > 0) ? 'Z' : ' ';
cascadeToNext = (i > 0);
break;
case 'a':
c = (i > 0) ? 'z' : ' ';
cascadeToNext = (i > 0);
break;
case '0':
c = (i > 0) ? '9' : ' ';
cascadeToNext = (i > 0);
break;
case ' ':
cascadeToNext = false;
break;
default:
c = (char)(((int)c) - 1);
if (i == 0 && c == '0')
{
c = ' ';
}
cascadeToNext = false;
break;
}
section[i] = c;
if (exclusions.Contains(c.ToString()))
{
exclusionsExist = true;
}
}
} while (exclusionsExist);
return section.ToString();
}
The dividing can of course be done more efficiently, just passing start and end indexes to the DecrementString, but this is easier to write & follow and not much slower in practical terms.
do a check if its a number if so then do a minus math of the number, if its a string then change it to char codes and then the char code minus 1
I couldn't stop thinking about this yesterday, so here's an idea. Note, this is just pseudo-code, and not tested, but I think the idea is valid and should work (with a few modifications).
The main point is to define your "alphabet" directly, and specify which characters in it are illegal and should be skipped, then use a list or array of positions in this alphabet to define the word you start with.
I can't spend any more time on this right now, but please let me know if you decide to use it and get it to work!
string[] alphabet = {a, b, c, d, e};
string[] illegal = {c, d};
public string ReduceString(string s){
// Create a list of the alphabet-positions for each letter:
int[] positionList = s.getCharsAsPosNrsInAlphabet();
int[] reducedPositionList = ReduceChar(positionList, positionList.length);
string result = "";
foreach(int pos in reducedPositionList){
result += alphabet[pos];
}
return result;
}
public string ReduceChar(string[] positionList, posToReduce){
int reducedCharPosition = ReduceToNextLegalChar(positionList[posToReduce]);
// put reduced char back in place:
positionList[posToReduce] = reducedCharPosition;
if(reducedCharPosition < 0){
if(posToReduce <= 0){
// Reached the end, reduced everything, return empty array!:
return new string[]();
}
// move to back of alphabet again (ie, like the 9 in "11 - 2 = 09"):
reducedCharPosition += alphabet.length;
// Recur and reduce next position (ie, like the 0 in "11 - 2 = 09"):
return ReduceChar(positionList, posToReduce-1);
}
return positionList;
}
public int ReduceToNextLegalChar(int pos){
int nextPos = pos--;
return (isLegalChar(nextPos) ? nextPos : ReduceToNextLegalChar(nextPos));
}
public boolean IsLegalChar(int pos){
return (! illegal.contains(alphabet[pos]));
}
enter code here
Without writing all your code for you, here's a suggestion as to how you can break this down:
char DecrementAlphaNumericChar(char input, out bool hadToWrap)
{
if (input == 'A')
{
hadToWrap = true;
return 'Z';
}
else if (input == '0')
{
hadToWrap = true;
return '9';
}
else if ((input > 'A' && input <= 'Z') || (input > '0' && input <= '9'))
{
hadToWrap = false;
return (char)((int)input - 1);
}
throw new ArgumentException(
"Characters must be digits or capital letters",
"input");
}
char DecrementAvoidingProhibited(
char input, List<char> prohibited, out bool hadToWrap)
{
var potential = DecrementAlphaNumericChar(input, out hadToWrap);
while (prohibited.Contains(potential))
{
bool temp;
potential = DecrementAlphaNumericChar(potential, out temp);
if (potential == input)
{
throw new ArgumentException(
"A whole class of characters was prohibited",
"prohibited");
}
hadToWrap |= temp;
}
return potential;
}
string DecrementString(string input, List<char> prohibited)
{
char[] chrs = input.ToCharArray();
for (int i = chrs.Length - 1; i >= 0; i--)
{
bool wrapped;
chrs[i] = DecrementAvoidingProhibited(
chrs[i], prohibited, out wrapped);
if (!wrapped)
return new string(chrs);
}
return "-";
}
The only issue here is that it will reduce e.g. A10 to A09 not A9. I actually prefer this myself, but it should be simple to write a final pass that removes the extra zeroes.
For a little more performance, replace the List<char>s with Hashset<char>s, they should allow a faster Contains lookup.
I found solution to my own answer with some other workarounds.
The calling function:
MyFunction()
{
//stuff I do before
strValue = lstGetDecrName(strValue.ToList());//decrease value here
if (strValue.Contains('-'))
{
strValue = "-";
}
//stuff I do after
}
In all there are 4 functions. 2 Main functions and 2 helper functions.
List<char> lstGetDecrName(List<char> lstVal)//entry point, returns decreased value
{
if (lstVal.Contains('-'))
{
return "-".ToList();
}
List<char> lstTmp = lstVal;
subCheckEmpty(ref lstTmp);
switch (lstTmp.Count)
{
case 0:
lstTmp.Add('-');
return lstTmp;
case 1:
if (lstTmp[0] == '-')
{
return lstTmp;
}
break;
case 2:
if (lstTmp[1] == '0')
{
if (lstTmp[0] == '1')
{
lstTmp.Clear();
lstTmp.Add('9');
return lstTmp;
}
if (lstTmp[0] == 'A')
{
lstTmp.Clear();
lstTmp.Add('-');
return lstTmp;
}
}
if (lstTmp[1] == 'A')
{
if (lstTmp[0] == 'A')
{
lstTmp.Clear();
lstTmp.Add('Z');
return lstTmp;
}
}
break;
}
List<char> lstValue = new List<char>();
switch (lstTmp.Last())
{
case 'A':
lstValue = lstGetDecrTemp('Z', lstTmp, lstVal);
break;
case 'a':
lstValue = lstGetDecrTemp('z', lstTmp, lstVal);
break;
case '0':
lstValue = lstGetDecrTemp('9', lstTmp, lstVal);
break;
default:
char tmp = (char)(lstTmp.Last() - 1);
lstTmp.RemoveAt(lstTmp.Count - 1);
lstTmp.Add(tmp);
subCheckEmpty(ref lstTmp);
lstValue = lstTmp;
break;
}
lstGetDecrSkipValue(lstValue);
return lstValue;
}
List<char> lstGetDecrSkipValue(List<char> lstValue)
{
bool blnSkip = false;
foreach (char tmpChar in lstValue)
{
if (lstChars.Contains(tmpChar))
{
blnSkip = true;
break;
}
}
if (blnSkip)
{
lstValue = lstGetDecrName(lstValue);
}
return lstValue;
}
void subCheckEmpty(ref List<char> lstTmp)
{
bool blnFirst = true;
int i = -1;
foreach (char tmpChar in lstTmp)
{
if (char.IsDigit(tmpChar) && blnFirst)
{
i = tmpChar == '0' ? lstTmp.IndexOf(tmpChar) : -1;
if (tmpChar == '0')
{
i = lstTmp.IndexOf(tmpChar);
}
blnFirst = false;
}
}
if (!blnFirst && i != -1)
{
lstTmp.RemoveAt(i);
subCheckEmpty(ref lstTmp);
}
}
List<char> lstGetDecrTemp(char chrTemp, List<char> lstTmp, List<char> lstVal)//shifting places eg unit to ten,etc.
{
if (lstTmp.Count == 1)
{
lstTmp.Clear();
lstTmp.Add('-');
return lstTmp;
}
lstTmp.RemoveAt(lstTmp.Count - 1);
lstVal = lstGetDecrName(lstTmp);
lstVal.Insert(lstVal.Count, chrTemp);
subCheckEmpty(ref lstVal);
return lstVal;
}
I'm looking for a fast .NET class/library that has a StringComparer that supports wildcard (*) AND incase-sensitivity.
Any Ideas?
You could use Regex with RegexOptions.IgnoreCase, then compare with the IsMatch method.
var wordRegex = new Regex( "^" + prefix + ".*" + suffix + "$", RegexOptions.IgnoreCase );
if (wordRegex.IsMatch( testWord ))
{
...
}
This would match prefix*suffix. You might also consider using StartsWith or EndsWith as alternatives.
Alternatively you can use these extended functions:
public static bool CompareWildcards(this string WildString, string Mask, bool IgnoreCase)
{
int i = 0;
if (String.IsNullOrEmpty(Mask))
return false;
if (Mask == "*")
return true;
while (i != Mask.Length)
{
if (CompareWildcard(WildString, Mask.Substring(i), IgnoreCase))
return true;
while (i != Mask.Length && Mask[i] != ';')
i += 1;
if (i != Mask.Length && Mask[i] == ';')
{
i += 1;
while (i != Mask.Length && Mask[i] == ' ')
i += 1;
}
}
return false;
}
public static bool CompareWildcard(this string WildString, string Mask, bool IgnoreCase)
{
int i = 0, k = 0;
while (k != WildString.Length)
{
if (i > Mask.Length - 1)
return false;
switch (Mask[i])
{
case '*':
if ((i + 1) == Mask.Length)
return true;
while (k != WildString.Length)
{
if (CompareWildcard(WildString.Substring(k + 1), Mask.Substring(i + 1), IgnoreCase))
return true;
k += 1;
}
return false;
case '?':
break;
default:
if (IgnoreCase == false && WildString[k] != Mask[i])
return false;
if (IgnoreCase && Char.ToLower(WildString[k]) != Char.ToLower(Mask[i]))
return false;
break;
}
i += 1;
k += 1;
}
if (k == WildString.Length)
{
if (i == Mask.Length || Mask[i] == ';' || Mask[i] == '*')
return true;
}
return false;
}
CompareWildcards compares a string against multiple wildcard patterns, and CompareWildcard compares a string against a single wildcard pattern.
Example usage:
if (Path.CompareWildcards("*txt;*.zip;", true) == true)
{
// Path matches wildcard
}
alternatively you can try following
class Wildcard : Regex
{
public Wildcard() { }
public Wildcard(string pattern) : base(WildcardToRegex(pattern)) { }
public Wildcard(string pattern, RegexOptions options) : base(WildcardToRegex(pattern), options) { }
public static string WildcardToRegex(string pattern)
{
return "^" + Regex.Escape(pattern).
Replace("\\*", ".*").
Replace("\\?", ".") + "$";
}
}