2010-04-06 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System / System.ComponentModel / MaskedTextProvider.cs
blob483d25df765998f84370369701855b0a4ab9b56e
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 //
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2007 Novell, Inc. (http://www.novell.com)
22 // Authors:
23 // Rolf Bjarne Kvinge <RKvinge@novell.com>
29 Mask language:
31 0 Digit, required. This element will accept any single digit between 0 and 9.
32 9 Digit or space, optional.
33 # Digit or space, optional. If this position is blank in the mask, it will be rendered as a space in the Text property. Plus (+) and minus (-) signs are allowed.
34 L Letter, required. Restricts input to the ASCII letters a-z and A-Z. This mask element is equivalent to [a-zA-Z] in regular expressions.
35 ? Letter, optional. Restricts input to the ASCII letters a-z and A-Z. This mask element is equivalent to [a-zA-Z]? in regular expressions.
36 & Character, required. If the AsciiOnly property is set to true, this element behaves like the "L" element.
37 C Character, optional. Any non-control character. If the AsciiOnly property is set to true, this element behaves like the "?" element.
38 * LAMESPEC: A is REQUIRED, not optional.
39 A Alphanumeric, optional. If the AsciiOnly property is set to true, the only characters it will accept are the ASCII letters a-z and A-Z.
40 a Alphanumeric, optional. If the AsciiOnly property is set to true, the only characters it will accept are the ASCII letters a-z and A-Z.
41 . Decimal placeholder. The actual display character used will be the decimal symbol appropriate to the format provider, as determined by the control's FormatProvider property.
42 , Thousands placeholder. The actual display character used will be the thousands placeholder appropriate to the format provider, as determined by the control's FormatProvider property.
43 : Time separator. The actual display character used will be the time symbol appropriate to the format provider, as determined by the control's FormatProvider property.
44 / Date separator. The actual display character used will be the date symbol appropriate to the format provider, as determined by the control's FormatProvider property.
45 $ Currency symbol. The actual character displayed will be the currency symbol appropriate to the format provider, as determined by the control's FormatProvider property.
46 < Shift down. Converts all characters that follow to lowercase.
47 > Shift up. Converts all characters that follow to uppercase.
48 | Disable a previous shift up or shift down.
49 \ Escape. Escapes a mask character, turning it into a literal. "\\" is the escape sequence for a backslash.
51 * */
53 #if NET_2_0
54 using System.Globalization;
55 using System.Collections;
56 using System.Diagnostics;
57 using System.Text;
59 namespace System.ComponentModel {
60 public class MaskedTextProvider : ICloneable
62 #region Private fields
63 private bool allow_prompt_as_input;
64 private bool ascii_only;
65 private CultureInfo culture;
66 private bool include_literals;
67 private bool include_prompt;
68 private bool is_password;
69 private string mask;
70 private char password_char;
71 private char prompt_char;
72 private bool reset_on_prompt;
73 private bool reset_on_space;
74 private bool skip_literals;
76 private EditPosition [] edit_positions;
78 private static char default_prompt_char = '_';
79 private static char default_password_char = char.MinValue;
81 #endregion
83 #region Private classes
84 private enum EditState {
85 None,
86 UpperCase,
87 LowerCase
89 private enum EditType {
90 DigitRequired, // 0
91 DigitOrSpaceOptional, // 9
92 DigitOrSpaceOptional_Blank, // #
93 LetterRequired, // L
94 LetterOptional, // ?
95 CharacterRequired, // &
96 CharacterOptional, // C
97 AlphanumericRequired, // A
98 AlphanumericOptional, // a
99 DecimalPlaceholder, // .
100 ThousandsPlaceholder, // ,
101 TimeSeparator, // :
102 DateSeparator, // /
103 CurrencySymbol, // $
104 Literal
107 private class EditPosition {
108 public MaskedTextProvider Parent;
109 public EditType Type;
110 public EditState State;
111 public char MaskCharacter;
112 public char input;
114 public void Reset ()
116 input = char.MinValue;
119 internal EditPosition Clone () {
120 EditPosition result = new EditPosition ();
121 result.Parent = Parent;
122 result.Type = Type;
123 result.State = State;
124 result.MaskCharacter = MaskCharacter;
125 result.input = input;
126 return result;
129 public char Input {
130 get {
131 return input;
133 set {
134 switch (State) {
135 case EditState.LowerCase:
136 input = char.ToLower (value, Parent.Culture);
137 break;
138 case EditState.UpperCase:
139 input = char.ToUpper (value, Parent.Culture);
140 break;
141 default:// case EditState.None:
142 input = value;
143 break;
148 public bool IsAscii (char c)
150 return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
153 public bool Match (char c, out MaskedTextResultHint resultHint, bool only_test)
155 if (!MaskedTextProvider.IsValidInputChar (c)) {
156 resultHint = MaskedTextResultHint.InvalidInput;
157 return false;
160 if (Parent.ResetOnSpace && c == ' ' && Editable) {
161 resultHint = MaskedTextResultHint.CharacterEscaped;
162 if (FilledIn) {
163 resultHint = MaskedTextResultHint.Success;
164 if (!only_test && input != ' ') {
165 switch (Type) {
166 case EditType.AlphanumericOptional:
167 case EditType.AlphanumericRequired:
168 case EditType.CharacterOptional:
169 case EditType.CharacterRequired:
170 Input = c;
171 break;
172 default:
173 Input = char.MinValue;
174 break;
178 return true;
181 if (Type == EditType.Literal && MaskCharacter == c && Parent.SkipLiterals) {
182 resultHint = MaskedTextResultHint.Success;
183 return true;
186 if (!Editable) {
187 resultHint = MaskedTextResultHint.NonEditPosition;
188 return false;
191 switch (Type) {
192 case EditType.AlphanumericOptional:
193 case EditType.AlphanumericRequired:
194 if (char.IsLetterOrDigit (c)) {
195 if (Parent.AsciiOnly && !IsAscii (c)) {
196 resultHint = MaskedTextResultHint.AsciiCharacterExpected;
197 return false;
198 } else {
199 if (!only_test) {
200 Input = c;
202 resultHint = MaskedTextResultHint.Success;
203 return true;
205 } else {
206 resultHint = MaskedTextResultHint.AlphanumericCharacterExpected;
207 return false;
209 case EditType.CharacterOptional:
210 case EditType.CharacterRequired:
211 if (Parent.AsciiOnly && !IsAscii (c)) {
212 resultHint = MaskedTextResultHint.LetterExpected;
213 return false;
214 } else if (!char.IsControl (c)) {
215 if (!only_test) {
216 Input = c;
218 resultHint = MaskedTextResultHint.Success;
219 return true;
220 } else {
221 resultHint = MaskedTextResultHint.LetterExpected;
222 return false;
224 case EditType.DigitOrSpaceOptional:
225 case EditType.DigitOrSpaceOptional_Blank:
226 if (char.IsDigit (c) || c == ' ') {
227 if (!only_test) {
228 Input = c;
230 resultHint = MaskedTextResultHint.Success;
231 return true;
232 } else {
233 resultHint = MaskedTextResultHint.DigitExpected;
234 return false;
236 case EditType.DigitRequired:
237 if (char.IsDigit (c)) {
238 if (!only_test) {
239 Input = c;
241 resultHint = MaskedTextResultHint.Success;
242 return true;
243 } else {
244 resultHint = MaskedTextResultHint.DigitExpected;
245 return false;
247 case EditType.LetterOptional:
248 case EditType.LetterRequired:
249 if (!char.IsLetter (c)) {
250 resultHint = MaskedTextResultHint.LetterExpected;
251 return false;
252 } else if (Parent.AsciiOnly && !IsAscii (c)) {
253 resultHint = MaskedTextResultHint.LetterExpected;
254 return false;
255 } else {
256 if (!only_test) {
257 Input = c;
259 resultHint = MaskedTextResultHint.Success;
260 return true;
262 default:
263 resultHint = MaskedTextResultHint.Unknown;
264 return false;
268 public bool FilledIn {
269 get {
270 return Input != char.MinValue;
274 public bool Required {
275 get {
276 switch (MaskCharacter) {
277 case '0':
278 case 'L':
279 case '&':
280 case 'A':
281 return true;
282 default:
283 return false;
288 public bool Editable {
289 get {
290 switch (MaskCharacter) {
291 case '0':
292 case '9':
293 case '#':
294 case 'L':
295 case '?':
296 case '&':
297 case 'C':
298 case 'A':
299 case 'a':
300 return true;
301 default:
302 return false;
307 public bool Visible {
308 get {
309 switch (MaskCharacter) {
310 case '|':
311 case '<':
312 case '>':
313 return false;
314 default:
315 return true;
319 public string Text {
320 get {
321 if (Type == EditType.Literal) {
322 return MaskCharacter.ToString ();
324 switch (MaskCharacter) {
325 case '.': return Parent.Culture.NumberFormat.NumberDecimalSeparator;
326 case ',': return Parent.Culture.NumberFormat.NumberGroupSeparator;
327 case ':': return Parent.Culture.DateTimeFormat.TimeSeparator;
328 case '/': return Parent.Culture.DateTimeFormat.DateSeparator;
329 case '$': return Parent.Culture.NumberFormat.CurrencySymbol;
330 default:
331 return FilledIn ? Input.ToString () : Parent.PromptChar.ToString ();
336 private EditPosition ()
340 public EditPosition (MaskedTextProvider Parent, EditType Type, EditState State, char MaskCharacter)
342 this.Type = Type;
343 this.Parent = Parent;
344 this.State = State;
345 this.MaskCharacter = MaskCharacter;
349 #endregion
351 #region Private methods & properties
352 private void SetMask (string mask)
354 if (mask == null || mask == string.Empty)
355 throw new ArgumentException ("The Mask value cannot be null or empty.\r\nParameter name: mask");
357 this.mask = mask;
359 System.Collections.Generic.List <EditPosition> list = new System.Collections.Generic.List<EditPosition> (mask.Length);
361 EditState State = EditState.None;
362 bool escaped = false;
363 for (int i = 0; i < mask.Length; i++) {
364 if (escaped) {
365 list.Add (new EditPosition (this, EditType.Literal, State, mask [i]));
366 escaped = false;
367 continue;
370 switch (mask [i]) {
371 case '\\':
372 escaped = true;
373 break;
374 case '0':
375 list.Add (new EditPosition (this, EditType.DigitRequired, State, mask [i]));
376 break;
377 case '9':
378 list.Add (new EditPosition (this, EditType.DigitOrSpaceOptional, State, mask [i]));
379 break;
380 case '#':
381 list.Add (new EditPosition (this, EditType.DigitOrSpaceOptional_Blank, State, mask [i]));
382 break;
383 case 'L':
384 list.Add (new EditPosition (this, EditType.LetterRequired, State, mask [i]));
385 break;
386 case '?':
387 list.Add (new EditPosition (this, EditType.LetterOptional, State, mask [i]));
388 break;
389 case '&':
390 list.Add (new EditPosition (this, EditType.CharacterRequired, State, mask [i]));
391 break;
392 case 'C':
393 list.Add (new EditPosition (this, EditType.CharacterOptional, State, mask [i]));
394 break;
395 case 'A':
396 list.Add (new EditPosition (this, EditType.AlphanumericRequired, State, mask [i]));
397 break;
398 case 'a':
399 list.Add (new EditPosition (this, EditType.AlphanumericOptional, State, mask [i]));
400 break;
401 case '.':
402 list.Add (new EditPosition (this, EditType.DecimalPlaceholder, State, mask [i]));
403 break;
404 case ',':
405 list.Add (new EditPosition (this, EditType.ThousandsPlaceholder, State, mask [i]));
406 break;
407 case ':':
408 list.Add (new EditPosition (this, EditType.TimeSeparator, State, mask [i]));
409 break;
410 case '/':
411 list.Add (new EditPosition (this, EditType.DateSeparator, State, mask [i]));
412 break;
413 case '$':
414 list.Add (new EditPosition (this, EditType.CurrencySymbol, State, mask [i]));
415 break;
416 case '<':
417 State = EditState.LowerCase;
418 break;
419 case '>':
420 State = EditState.UpperCase;
421 break;
422 case '|':
423 State = EditState.None;
424 break;
425 default:
426 list.Add (new EditPosition (this, EditType.Literal, State, mask [i]));
427 break;
430 edit_positions = list.ToArray ();
433 private EditPosition [] ClonePositions ()
435 EditPosition [] result = new EditPosition [edit_positions.Length];
436 for (int i = 0; i < result.Length; i++) {
437 result [i] = edit_positions [i].Clone ();
439 return result;
442 private bool AddInternal (string str_input, out int testPosition, out MaskedTextResultHint resultHint, bool only_test)
444 EditPosition [] edit_positions;
446 if (only_test) {
447 edit_positions = ClonePositions ();
448 } else {
449 edit_positions = this.edit_positions;
452 if (str_input == null)
453 throw new ArgumentNullException ("input");
455 if (str_input.Length == 0) {
456 resultHint = MaskedTextResultHint.NoEffect;
457 testPosition = LastAssignedPosition + 1;
458 return true;
461 resultHint = MaskedTextResultHint.Unknown;
462 testPosition = 0;
464 int next_position = LastAssignedPosition;
465 MaskedTextResultHint tmpResult = MaskedTextResultHint.Unknown;
467 if (next_position >= edit_positions.Length) {
468 testPosition = next_position;
469 resultHint = MaskedTextResultHint.UnavailableEditPosition;
470 return false;
473 for (int i = 0; i < str_input.Length; i++) {
474 char input = str_input [i];
475 next_position++;
476 testPosition = next_position;
478 if (tmpResult > resultHint) {
479 resultHint = tmpResult;
482 if (VerifyEscapeChar (input, next_position)) {
483 tmpResult = MaskedTextResultHint.CharacterEscaped;
484 continue;
487 next_position = FindEditPositionFrom (next_position, true);
488 testPosition = next_position;
490 if (next_position == InvalidIndex) {
491 testPosition = edit_positions.Length;
492 resultHint = MaskedTextResultHint.UnavailableEditPosition;
493 return false;
496 if (!IsValidInputChar (input)) {
497 testPosition = next_position;
498 resultHint = MaskedTextResultHint.InvalidInput;
499 return false;
502 if (!edit_positions [next_position].Match (input, out tmpResult, false)) {
503 testPosition = next_position;
504 resultHint = tmpResult;
505 return false;
509 if (tmpResult > resultHint) {
510 resultHint = tmpResult;
513 return true;
516 private bool AddInternal (char input, out int testPosition, out MaskedTextResultHint resultHint, bool check_available_positions_first, bool check_escape_char_first)
519 * check_available_positions_first: when adding a char, MS first seems to check if there are any available edit positions, then if there are any
520 * they will try to add the char (meaning that if you add a char matching a literal char in the mask with SkipLiterals and there are no
521 * more available edit positions, it will fail).
523 * check_escape_char_first: When adding a char, MS doesn't check for escape char, they directly search for the first edit position.
524 * When adding a string, they check first for escape char, then if not successful they find the first edit position.
527 int new_position;
528 testPosition = 0;
530 new_position = LastAssignedPosition + 1;
532 if (check_available_positions_first) {
533 int tmp = new_position;
534 bool any_available = false;
535 while (tmp < edit_positions.Length) {
536 if (edit_positions [tmp].Editable) {
537 any_available = true;
538 break;
540 tmp++;
542 if (!any_available) {
543 testPosition = tmp;
544 resultHint = MaskedTextResultHint.UnavailableEditPosition;
545 return GetOperationResultFromHint (resultHint);
549 if (check_escape_char_first) {
550 if (VerifyEscapeChar (input, new_position)) {
551 testPosition = new_position;
552 resultHint = MaskedTextResultHint.CharacterEscaped;
553 return true;
557 new_position = FindEditPositionFrom (new_position, true);
559 if (new_position > edit_positions.Length - 1 ||new_position == InvalidIndex) {
560 testPosition = new_position;
561 resultHint = MaskedTextResultHint.UnavailableEditPosition;
562 return GetOperationResultFromHint (resultHint);
565 if (!IsValidInputChar (input)) {
566 testPosition = new_position;
567 resultHint = MaskedTextResultHint.InvalidInput;
568 return GetOperationResultFromHint (resultHint);
571 if (!edit_positions [new_position].Match (input, out resultHint, false)) {
572 testPosition = new_position;
573 return GetOperationResultFromHint (resultHint);
576 testPosition = new_position;
578 return GetOperationResultFromHint (resultHint);
581 private bool VerifyStringInternal (string input, out int testPosition, out MaskedTextResultHint resultHint, int startIndex, bool only_test)
583 int previous_position = startIndex;
584 int current_position;
585 resultHint = MaskedTextResultHint.Unknown;
587 // Replace existing characters
588 for (int i = 0; i < input.Length; i++) {
589 MaskedTextResultHint tmpResult;
590 current_position = FindEditPositionFrom (previous_position, true);
591 if (current_position == InvalidIndex) {
592 testPosition = edit_positions.Length;
593 resultHint = MaskedTextResultHint.UnavailableEditPosition;
594 return false;
597 if (!VerifyCharInternal (input [i], current_position, out tmpResult, only_test)) {
598 testPosition = current_position;
599 resultHint = tmpResult;
600 return false;
602 if (tmpResult > resultHint) {
603 resultHint = tmpResult;
605 previous_position = current_position + 1;
608 // Remove characters not in the input.
609 if (!only_test) {
610 previous_position = FindEditPositionFrom (previous_position, true);
611 while (previous_position != InvalidIndex) {
612 if (edit_positions [previous_position].FilledIn) {
613 edit_positions [previous_position].Reset ();
614 if (resultHint != MaskedTextResultHint.NoEffect) {
615 resultHint = MaskedTextResultHint.Success;
618 previous_position = FindEditPositionFrom (previous_position + 1, true);
621 if (input.Length > 0) {
622 testPosition = startIndex + input.Length - 1;
623 } else {
624 testPosition = startIndex;
625 if (resultHint < MaskedTextResultHint.NoEffect) {
626 resultHint = MaskedTextResultHint.NoEffect;
630 return true;
633 private bool VerifyCharInternal (char input, int position, out MaskedTextResultHint hint, bool only_test)
635 hint = MaskedTextResultHint.Unknown;
637 if (position < 0 || position >= edit_positions.Length) {
638 hint = MaskedTextResultHint.PositionOutOfRange;
639 return false;
642 if (!IsValidInputChar (input)) {
643 hint = MaskedTextResultHint.InvalidInput;
644 return false;
647 if (input == ' ' && ResetOnSpace && edit_positions [position].Editable && edit_positions [position].FilledIn) {
648 if (!only_test) {
649 edit_positions [position].Reset ();
651 hint = MaskedTextResultHint.SideEffect;
652 return true;
655 if (edit_positions [position].Editable && edit_positions [position].FilledIn && edit_positions [position].input == input) {
656 hint = MaskedTextResultHint.NoEffect;
657 return true;
660 if (SkipLiterals && !edit_positions [position].Editable && edit_positions [position].Text == input.ToString ()) {
661 hint = MaskedTextResultHint.CharacterEscaped;
662 return true;
665 return edit_positions [position].Match (input, out hint, only_test);
668 // Test to see if the input string can be inserted at the specified position.
669 // Does not try to move characters.
670 private bool IsInsertableString (string str_input, int position, out int testPosition, out MaskedTextResultHint resultHint)
672 int current_position = position;
673 int test_position;
675 resultHint = MaskedTextResultHint.UnavailableEditPosition;
676 testPosition = InvalidIndex;
678 for (int i = 0; i < str_input.Length; i++) {
679 char ch = str_input [i];
681 test_position = FindEditPositionFrom (current_position, true);
683 if (test_position != InvalidIndex && VerifyEscapeChar (ch, test_position)) {
684 current_position = test_position + 1;
685 continue;
688 if (VerifyEscapeChar (ch, current_position)) {
689 current_position = current_position + 1;
690 continue;
694 if (test_position == InvalidIndex) {
695 resultHint = MaskedTextResultHint.UnavailableEditPosition;
696 testPosition = edit_positions.Length;
697 return false;
700 testPosition = test_position;
702 if (!edit_positions [test_position].Match (ch, out resultHint, true)) {
703 return false;
706 current_position = test_position + 1;
708 resultHint = MaskedTextResultHint.Success;
710 return true;
713 private bool ShiftPositionsRight (EditPosition [] edit_positions, int start, out int testPosition, out MaskedTextResultHint resultHint)
715 int index = start;
716 int last_assigned_index = FindAssignedEditPositionFrom (edit_positions.Length, false);
717 int endindex = FindUnassignedEditPositionFrom (last_assigned_index, true); // Find the first non-assigned edit position
719 testPosition = start;
720 resultHint = MaskedTextResultHint.Unknown;
722 if (endindex == InvalidIndex) {
723 // No more free edit positions.
724 testPosition = edit_positions.Length;
725 resultHint = MaskedTextResultHint.UnavailableEditPosition;
726 return false;
729 while (endindex > index) {
730 char char_to_assign;
731 int index_to_move;
733 index_to_move = FindEditPositionFrom (endindex - 1, false);
734 char_to_assign = edit_positions [index_to_move].input;
736 if (char_to_assign == char.MinValue) {
737 edit_positions [endindex].input = char_to_assign;
738 } else {
739 if (!edit_positions [endindex].Match (char_to_assign, out resultHint, false)) {
740 testPosition = endindex;
741 return false;
744 endindex = index_to_move;
747 if (endindex != InvalidIndex) {
748 edit_positions [endindex].Reset ();
751 return true;
754 private bool ReplaceInternal (string input, int startPosition, int endPosition, out int testPosition, out MaskedTextResultHint resultHint, bool only_test, bool dont_remove_at_end)
756 EditPosition [] edit_positions;
757 resultHint = MaskedTextResultHint.Unknown;
759 if (only_test) {
760 edit_positions = ClonePositions ();
761 } else {
762 edit_positions = this.edit_positions;
765 if (input == null)
766 throw new ArgumentNullException ("input");
768 if (endPosition >= edit_positions.Length) {
769 testPosition = endPosition;
770 resultHint = MaskedTextResultHint.PositionOutOfRange;
771 return false;
774 if (startPosition < 0) {
775 testPosition = startPosition;
776 resultHint = MaskedTextResultHint.PositionOutOfRange;
777 return false;
780 if (startPosition >= edit_positions.Length) {
781 testPosition = startPosition;
782 resultHint = MaskedTextResultHint.PositionOutOfRange;
783 return false;
786 if (startPosition > endPosition) {
787 testPosition = startPosition;
788 resultHint = MaskedTextResultHint.PositionOutOfRange;
789 return false;
793 if (input.Length == 0) {
794 return RemoveAtInternal (startPosition, endPosition, out testPosition, out resultHint, only_test);
797 int previous_position = startPosition;
798 int current_position = previous_position;
799 MaskedTextResultHint tmpResult = MaskedTextResultHint.Unknown;
800 testPosition = InvalidIndex;
802 for (int i = 0; i < input.Length; i++) {
803 char current_input = input [i];
805 current_position = previous_position;
807 if (VerifyEscapeChar (current_input, current_position)) {
808 if (edit_positions [current_position].FilledIn &&
809 edit_positions [current_position].Editable &&
810 (current_input == ' ' && ResetOnSpace) || (current_input == PromptChar && ResetOnPrompt)) {
811 edit_positions [current_position].Reset ();
812 tmpResult = MaskedTextResultHint.SideEffect;
813 } else {
814 tmpResult = MaskedTextResultHint.CharacterEscaped;
816 } else if (current_position < edit_positions.Length && !edit_positions [current_position].Editable && FindAssignedEditPositionInRange (current_position, endPosition, true) == InvalidIndex) {
817 // If replacing over a literal, the replacement character is INSERTED at the next
818 // available edit position. Weird, huh?
819 current_position = FindEditPositionFrom (current_position, true);
820 if (current_position == InvalidIndex) {
821 resultHint = MaskedTextResultHint.UnavailableEditPosition;
822 testPosition = edit_positions.Length;
823 return false;
825 if (!InsertAtInternal (current_input.ToString (), current_position, out testPosition, out tmpResult, only_test)) {
826 resultHint = tmpResult;
827 return false;
829 } else {
831 current_position = FindEditPositionFrom (current_position, true);
833 if (current_position == InvalidIndex) {
834 testPosition = edit_positions.Length;
835 resultHint = MaskedTextResultHint.UnavailableEditPosition;
836 return false;
839 if (!IsValidInputChar (current_input)) {
840 testPosition = current_position;
841 resultHint = MaskedTextResultHint.InvalidInput;
842 return false;
845 if (!ReplaceInternal (edit_positions, current_input, current_position, out testPosition, out tmpResult, false)) {
846 resultHint = tmpResult;
847 return false;
851 if (tmpResult > resultHint) {
852 resultHint = tmpResult;
855 previous_position = current_position + 1;
858 testPosition = current_position;
860 int tmpPosition;
861 if (!dont_remove_at_end && previous_position <= endPosition) {
862 if (!RemoveAtInternal (previous_position, endPosition, out tmpPosition, out tmpResult, only_test)) {
863 testPosition = tmpPosition;
864 resultHint = tmpResult;
865 return false;
868 if (tmpResult == MaskedTextResultHint.Success && resultHint < MaskedTextResultHint.SideEffect) {
869 resultHint = MaskedTextResultHint.SideEffect;
872 return true;
875 private bool ReplaceInternal (EditPosition [] edit_positions, char input, int position, out int testPosition, out MaskedTextResultHint resultHint, bool only_test)
877 testPosition = position;
879 if (!IsValidInputChar (input)) {
880 resultHint = MaskedTextResultHint.InvalidInput;
881 return false;
884 if (VerifyEscapeChar (input, position)) {
885 if (edit_positions [position].FilledIn && edit_positions [position].Editable && (input == ' ' && ResetOnSpace) || (input == PromptChar && ResetOnPrompt)) {
886 edit_positions [position].Reset ();
887 resultHint = MaskedTextResultHint.SideEffect;
888 } else {
889 resultHint = MaskedTextResultHint.CharacterEscaped;
891 testPosition = position;
892 return true;
895 if (!edit_positions [position].Editable) {
896 resultHint = MaskedTextResultHint.NonEditPosition;
897 return false;
900 bool is_filled = edit_positions [position].FilledIn;
901 if (is_filled && edit_positions [position].input == input) {
902 if (VerifyEscapeChar (input, position)) {
903 resultHint = MaskedTextResultHint.CharacterEscaped;
904 } else {
905 resultHint = MaskedTextResultHint.NoEffect;
907 } else if (input == ' ' && this.ResetOnSpace) {
908 if (is_filled) {
909 resultHint = MaskedTextResultHint.SideEffect;
910 edit_positions [position].Reset ();
911 } else {
912 resultHint = MaskedTextResultHint.CharacterEscaped;
914 return true;
915 } else if (VerifyEscapeChar (input, position)) {
916 resultHint = MaskedTextResultHint.SideEffect;
917 } else {
918 resultHint = MaskedTextResultHint.Success;
920 MaskedTextResultHint tmpResult;
921 if (!edit_positions [position].Match (input, out tmpResult, false)) {
922 resultHint = tmpResult;
923 return false;
926 return true;
929 private bool RemoveAtInternal (int startPosition, int endPosition, out int testPosition, out MaskedTextResultHint resultHint, bool only_testing)
931 EditPosition [] edit_positions;
932 testPosition = -1;
933 resultHint = MaskedTextResultHint.Unknown;
935 if (only_testing) {
936 edit_positions = ClonePositions ();
937 } else {
938 edit_positions = this.edit_positions;
941 if (endPosition < 0 || endPosition >= edit_positions.Length) {
942 testPosition = endPosition;
943 resultHint = MaskedTextResultHint.PositionOutOfRange;
944 return false;
947 if (startPosition < 0 || startPosition >= edit_positions.Length) {
948 testPosition = startPosition;
949 resultHint = MaskedTextResultHint.PositionOutOfRange;
950 return false;
954 if (startPosition > endPosition) {
955 testPosition = startPosition;
956 resultHint = MaskedTextResultHint.PositionOutOfRange;
957 return false;
959 int edit_positions_in_range = 0;
960 for (int i = startPosition; i <= endPosition; i++) {
961 if (edit_positions [i].Editable) {
962 edit_positions_in_range++;
966 if (edit_positions_in_range == 0) {
967 testPosition = startPosition;
968 resultHint = MaskedTextResultHint.NoEffect;
969 return true;
972 int current_edit_position = FindEditPositionFrom (startPosition, true);
973 while (current_edit_position != InvalidIndex) {
974 // Find the edit position that will reach the current position.
975 int next_index = FindEditPositionFrom (current_edit_position + 1, true);
976 for (int j = 1; j < edit_positions_in_range && next_index != InvalidIndex; j++) {
977 next_index = FindEditPositionFrom (next_index + 1, true);
980 if (next_index == InvalidIndex) {
981 if (edit_positions [current_edit_position].FilledIn) {
982 edit_positions [current_edit_position].Reset ();
983 resultHint = MaskedTextResultHint.Success;
984 } else {
985 if (resultHint < MaskedTextResultHint.NoEffect) {
986 resultHint = MaskedTextResultHint.NoEffect;
989 } else {
990 if (!edit_positions [next_index].FilledIn) {
991 if (edit_positions [current_edit_position].FilledIn) {
992 edit_positions [current_edit_position].Reset ();
993 resultHint = MaskedTextResultHint.Success;
994 } else {
995 if (resultHint < MaskedTextResultHint.NoEffect) {
996 resultHint = MaskedTextResultHint.NoEffect;
999 } else {
1000 MaskedTextResultHint tmpResult = MaskedTextResultHint.Unknown;
1001 if (edit_positions [current_edit_position].FilledIn) {
1002 resultHint = MaskedTextResultHint.Success;
1003 } else if (resultHint < MaskedTextResultHint.SideEffect) {
1004 resultHint = MaskedTextResultHint.SideEffect;
1006 if (!edit_positions [current_edit_position].Match (edit_positions [next_index].input, out tmpResult, false)) {
1007 resultHint = tmpResult;
1008 testPosition = current_edit_position;
1009 return false;
1012 edit_positions [next_index].Reset ();
1014 current_edit_position = FindEditPositionFrom (current_edit_position + 1, true);
1017 if (resultHint == MaskedTextResultHint.Unknown) {
1018 resultHint = MaskedTextResultHint.NoEffect;
1021 testPosition = startPosition;
1023 return true;
1026 private bool InsertAtInternal (string str_input, int position, out int testPosition, out MaskedTextResultHint resultHint, bool only_testing)
1028 EditPosition [] edit_positions;
1029 testPosition = -1;
1030 resultHint = MaskedTextResultHint.Unknown;
1032 if (only_testing) {
1033 edit_positions = ClonePositions ();
1034 } else {
1035 edit_positions = this.edit_positions;
1038 if (position < 0 || position >= edit_positions.Length) {
1039 testPosition = 0;
1040 resultHint = MaskedTextResultHint.PositionOutOfRange;
1041 return false;
1044 if (!IsInsertableString (str_input, position, out testPosition, out resultHint)) {
1045 return false;
1048 resultHint = MaskedTextResultHint.Unknown;
1050 int next_position = position;
1051 for (int i = 0; i < str_input.Length; i++) {
1052 char input = str_input [i];
1053 int index = FindEditPositionFrom (next_position, true); // Find the first edit position (here the input will go)
1054 int endindex = FindUnassignedEditPositionFrom (next_position, true); // Find the first non-assigned edit position
1055 bool escaped = false;
1057 if (VerifyEscapeChar (input, next_position)) {
1058 escaped = true;
1060 if (input.ToString () == edit_positions [next_position].Text) {
1061 if (FindAssignedEditPositionInRange (0, next_position - 1, true) != InvalidIndex && endindex == InvalidIndex) {
1062 resultHint = MaskedTextResultHint.UnavailableEditPosition;
1063 testPosition = edit_positions.Length;
1064 return false;
1066 resultHint = MaskedTextResultHint.CharacterEscaped;
1067 testPosition = next_position;
1068 next_position++;
1069 continue;
1073 if (!escaped && index == InvalidIndex) {
1074 // No edit positions left at all in the string
1075 testPosition = edit_positions.Length;
1076 resultHint = MaskedTextResultHint.UnavailableEditPosition;
1077 return false;
1080 if (index == InvalidIndex) {
1081 index = next_position;
1084 bool was_filled = edit_positions [index].FilledIn;
1085 bool shift = was_filled;
1086 if (shift) {
1087 if (!ShiftPositionsRight (edit_positions, index, out testPosition, out resultHint)) {
1088 return false;
1092 testPosition = index;
1093 if (escaped) {
1094 if (was_filled) {
1095 resultHint = MaskedTextResultHint.Success;
1096 } else if (!edit_positions [index].Editable && input.ToString () == edit_positions [index].Text) {
1097 resultHint = MaskedTextResultHint.CharacterEscaped;
1098 testPosition = next_position;
1099 } else {
1100 int first_edit_position = FindEditPositionFrom (index, true);
1101 if (first_edit_position == InvalidIndex) {
1102 resultHint = MaskedTextResultHint.UnavailableEditPosition;
1103 testPosition = edit_positions.Length;
1104 return false;
1107 resultHint = MaskedTextResultHint.CharacterEscaped;
1108 if (input.ToString () == edit_positions [next_position].Text) {
1109 testPosition = next_position;
1112 } else {
1113 MaskedTextResultHint tmpResult;
1114 if (!edit_positions [index].Match (input, out tmpResult, false)) {
1115 resultHint = tmpResult;
1116 return false;
1118 if (resultHint < tmpResult) {
1119 resultHint = tmpResult;
1122 next_position = index + 1;
1126 return true;
1128 #endregion
1129 #region Public constructors
1130 static MaskedTextProvider()
1134 public MaskedTextProvider(string mask)
1135 : this (mask, null, true, default_prompt_char, default_password_char, false)
1139 public MaskedTextProvider (string mask, bool restrictToAscii)
1140 : this (mask, null, true, default_prompt_char, default_password_char, restrictToAscii)
1144 public MaskedTextProvider (string mask, CultureInfo culture)
1145 : this (mask, culture, true, default_prompt_char, default_password_char, false)
1149 public MaskedTextProvider (string mask, char passwordChar, bool allowPromptAsInput)
1150 : this (mask, null, allowPromptAsInput, default_prompt_char, passwordChar, false)
1154 public MaskedTextProvider (string mask, CultureInfo culture, bool restrictToAscii)
1155 : this (mask, culture, true, default_prompt_char, default_password_char, restrictToAscii)
1159 public MaskedTextProvider(string mask, CultureInfo culture, char passwordChar, bool allowPromptAsInput)
1160 : this (mask, culture, allowPromptAsInput, default_prompt_char, passwordChar, false)
1164 public MaskedTextProvider(string mask, CultureInfo culture, bool allowPromptAsInput, char promptChar, char passwordChar, bool restrictToAscii)
1166 SetMask (mask);
1168 if (culture == null)
1169 this.culture = Threading.Thread.CurrentThread.CurrentCulture;
1170 else
1171 this.culture = culture;
1173 this.allow_prompt_as_input = allowPromptAsInput;
1175 this.PromptChar = promptChar;
1176 this.PasswordChar = passwordChar;
1177 this.ascii_only = restrictToAscii;
1179 include_literals = true;
1180 reset_on_prompt = true;
1181 reset_on_space = true;
1182 skip_literals = true;
1184 #endregion
1186 #region Public methods
1187 public bool Add(char input)
1189 int testPosition;
1190 MaskedTextResultHint resultHint;
1191 return Add (input, out testPosition, out resultHint);
1194 public bool Add (string input)
1196 int testPosition;
1197 MaskedTextResultHint resultHint;
1198 return Add (input, out testPosition, out resultHint);
1201 public bool Add (char input, out int testPosition, out MaskedTextResultHint resultHint)
1203 return AddInternal (input, out testPosition, out resultHint, true, false);
1206 public bool Add (string input, out int testPosition, out MaskedTextResultHint resultHint)
1208 bool result;
1210 result = AddInternal (input, out testPosition, out resultHint, true);
1212 if (result) {
1213 result = AddInternal (input, out testPosition, out resultHint, false);
1216 return result;
1219 public void Clear ()
1221 MaskedTextResultHint resultHint;
1222 Clear (out resultHint);
1225 public void Clear (out MaskedTextResultHint resultHint)
1227 resultHint = MaskedTextResultHint.NoEffect;
1228 for (int i = 0; i < edit_positions.Length; i++) {
1229 if (edit_positions [i].Editable && edit_positions [i].FilledIn) {
1230 edit_positions [i].Reset ();
1231 resultHint = MaskedTextResultHint.Success;
1236 public object Clone ()
1238 MaskedTextProvider result = new MaskedTextProvider (mask);
1240 result.allow_prompt_as_input = allow_prompt_as_input;
1241 result.ascii_only = ascii_only;
1242 result.culture = culture;
1243 result.edit_positions = ClonePositions ();
1244 result.include_literals = include_literals;
1245 result.include_prompt = include_prompt;
1246 result.is_password = is_password;
1247 result.mask = mask;
1248 result.password_char = password_char;
1249 result.prompt_char = prompt_char;
1250 result.reset_on_prompt = reset_on_prompt;
1251 result.reset_on_space = reset_on_space;
1252 result.skip_literals = skip_literals;
1254 return result;
1257 public int FindAssignedEditPositionFrom (int position, bool direction)
1259 if (direction) {
1260 return FindAssignedEditPositionInRange (position, edit_positions.Length - 1, direction);
1261 } else {
1262 return FindAssignedEditPositionInRange (0, position, direction);
1266 public int FindAssignedEditPositionInRange (int startPosition, int endPosition, bool direction)
1268 int step;
1269 int start, end;
1271 if (startPosition < 0)
1272 startPosition = 0;
1273 if (endPosition >= edit_positions.Length)
1274 endPosition = edit_positions.Length - 1;
1276 if (startPosition > endPosition)
1277 return InvalidIndex;
1279 step = direction ? 1 : -1;
1280 start = direction ? startPosition : endPosition;
1281 end = (direction ? endPosition: startPosition) + step;
1283 for (int i = start; i != end; i+=step) {
1284 if (edit_positions [i].Editable && edit_positions [i].FilledIn)
1285 return i;
1287 return InvalidIndex;
1290 public int FindEditPositionFrom (int position, bool direction)
1292 if (direction) {
1293 return FindEditPositionInRange (position, edit_positions.Length - 1, direction);
1294 } else {
1295 return FindEditPositionInRange (0, position, direction);
1299 public int FindEditPositionInRange (int startPosition, int endPosition, bool direction)
1301 int step;
1302 int start, end;
1304 if (startPosition < 0)
1305 startPosition = 0;
1306 if (endPosition >= edit_positions.Length)
1307 endPosition = edit_positions.Length - 1;
1309 if (startPosition > endPosition)
1310 return InvalidIndex;
1312 step = direction ? 1 : -1;
1313 start = direction ? startPosition : endPosition;
1314 end = (direction ? endPosition : startPosition) + step;
1316 for (int i = start; i != end; i += step) {
1317 if (edit_positions [i].Editable)
1318 return i;
1320 return InvalidIndex;
1323 public int FindNonEditPositionFrom (int position, bool direction)
1325 if (direction) {
1326 return FindNonEditPositionInRange (position, edit_positions.Length - 1, direction);
1327 } else {
1328 return FindNonEditPositionInRange (0, position, direction);
1332 public int FindNonEditPositionInRange (int startPosition, int endPosition, bool direction)
1334 int step;
1335 int start, end;
1337 if (startPosition < 0)
1338 startPosition = 0;
1339 if (endPosition >= edit_positions.Length)
1340 endPosition = edit_positions.Length - 1;
1342 if (startPosition > endPosition)
1343 return InvalidIndex;
1345 step = direction ? 1 : -1;
1346 start = direction ? startPosition : endPosition;
1347 end = (direction ? endPosition : startPosition) + step;
1349 for (int i = start; i != end; i += step) {
1350 if (!edit_positions [i].Editable)
1351 return i;
1353 return InvalidIndex;
1356 public int FindUnassignedEditPositionFrom (int position, bool direction)
1358 if (direction) {
1359 return FindUnassignedEditPositionInRange (position, edit_positions.Length - 1, direction);
1360 } else {
1361 return FindUnassignedEditPositionInRange (0, position, direction);
1365 public int FindUnassignedEditPositionInRange (int startPosition, int endPosition, bool direction)
1367 int step;
1368 int start, end;
1370 if (startPosition < 0)
1371 startPosition = 0;
1372 if (endPosition >= edit_positions.Length)
1373 endPosition = edit_positions.Length - 1;
1375 if (startPosition > endPosition)
1376 return InvalidIndex;
1378 step = direction ? 1 : -1;
1379 start = direction ? startPosition : endPosition;
1380 end = (direction ? endPosition : startPosition) + step;
1382 for (int i = start; i != end; i += step) {
1383 if (edit_positions [i].Editable && !edit_positions [i].FilledIn)
1384 return i;
1386 return InvalidIndex;
1389 public static bool GetOperationResultFromHint (MaskedTextResultHint hint)
1391 return (hint == MaskedTextResultHint.CharacterEscaped ||
1392 hint == MaskedTextResultHint.NoEffect ||
1393 hint == MaskedTextResultHint.SideEffect ||
1394 hint == MaskedTextResultHint.Success);
1397 public bool InsertAt (char input, int position)
1399 int testPosition;
1400 MaskedTextResultHint resultHint;
1401 return InsertAt (input, position, out testPosition, out resultHint);
1404 public bool InsertAt (string input, int position)
1406 int testPosition;
1407 MaskedTextResultHint resultHint;
1408 return InsertAt (input, position, out testPosition, out resultHint);
1411 public bool InsertAt (char input, int position, out int testPosition, out MaskedTextResultHint resultHint)
1413 return InsertAt (input.ToString (), position, out testPosition, out resultHint);
1416 public bool InsertAt (string input, int position, out int testPosition, out MaskedTextResultHint resultHint)
1418 if (input == null)
1419 throw new ArgumentNullException ("input");
1421 if (position >= edit_positions.Length) {
1422 testPosition = position;
1423 resultHint = MaskedTextResultHint.PositionOutOfRange;
1424 return false;
1427 if (input == string.Empty) {
1428 testPosition = position;
1429 resultHint = MaskedTextResultHint.NoEffect;
1430 return true;
1433 bool result;
1435 result = InsertAtInternal (input, position, out testPosition, out resultHint, true);
1437 if (result) {
1438 result = InsertAtInternal (input, position, out testPosition, out resultHint, false);
1441 return result;
1444 public bool IsAvailablePosition (int position)
1446 if (position < 0 || position >= edit_positions.Length)
1447 return false;
1449 return edit_positions [position].Editable && !edit_positions [position].FilledIn;
1452 public bool IsEditPosition (int position)
1454 if (position < 0 || position >= edit_positions.Length)
1455 return false;
1457 return edit_positions [position].Editable;
1460 public static bool IsValidInputChar (char c)
1462 return char.IsLetterOrDigit (c) || char.IsPunctuation (c) || char.IsSymbol (c) || c == ' ';
1465 public static bool IsValidMaskChar (char c)
1467 return char.IsLetterOrDigit (c) || char.IsPunctuation (c) || char.IsSymbol (c) || c == ' ';
1470 public static bool IsValidPasswordChar (char c)
1472 return char.IsLetterOrDigit (c) || char.IsPunctuation (c) || char.IsSymbol (c) || c == ' ' || c == char.MinValue;
1475 public bool Remove ()
1477 int testPosition;
1478 MaskedTextResultHint resultHint;
1479 return Remove (out testPosition, out resultHint);
1482 public bool Remove (out int testPosition, out MaskedTextResultHint resultHint)
1484 if (LastAssignedPosition == InvalidIndex) {
1485 resultHint = MaskedTextResultHint.NoEffect;
1486 testPosition = 0;
1487 return true;
1490 testPosition = LastAssignedPosition;
1491 resultHint = MaskedTextResultHint.Success;
1492 edit_positions [LastAssignedPosition].input = char.MinValue;
1494 return true;
1497 public bool RemoveAt (int position)
1499 return RemoveAt (position, position);
1502 public bool RemoveAt (int startPosition, int endPosition)
1504 int testPosition;
1505 MaskedTextResultHint resultHint;
1506 return RemoveAt (startPosition, endPosition, out testPosition, out resultHint);
1509 public bool RemoveAt (int startPosition, int endPosition, out int testPosition, out MaskedTextResultHint resultHint)
1511 bool result;
1513 result = RemoveAtInternal (startPosition, endPosition, out testPosition, out resultHint, true);
1515 if (result) {
1516 result = RemoveAtInternal (startPosition, endPosition, out testPosition, out resultHint, false);
1519 return result;
1522 public bool Replace (char input, int position)
1524 int testPosition;
1525 MaskedTextResultHint resultHint;
1526 return Replace (input, position, out testPosition, out resultHint);
1529 public bool Replace (string input, int position)
1531 int testPosition;
1532 MaskedTextResultHint resultHint;
1533 return Replace (input, position, out testPosition, out resultHint);
1536 public bool Replace (char input, int position, out int testPosition, out MaskedTextResultHint resultHint)
1538 if (position < 0 || position >= edit_positions.Length) {
1539 testPosition = position;
1540 resultHint = MaskedTextResultHint.PositionOutOfRange;
1541 return false;
1544 if (VerifyEscapeChar (input, position)) {
1545 if (edit_positions [position].FilledIn && edit_positions [position].Editable && (input == ' ' && ResetOnSpace) || (input == PromptChar && ResetOnPrompt)) {
1546 edit_positions [position].Reset ();
1547 resultHint = MaskedTextResultHint.SideEffect;
1548 } else {
1549 resultHint = MaskedTextResultHint.CharacterEscaped;
1551 testPosition = position;
1552 return true;
1555 int current_edit_position;
1556 current_edit_position = FindEditPositionFrom (position, true);
1558 if (current_edit_position == InvalidIndex) {
1559 testPosition = position;
1560 resultHint = MaskedTextResultHint.UnavailableEditPosition;
1561 return false;
1564 if (!IsValidInputChar (input)) {
1565 testPosition = current_edit_position;
1566 resultHint = MaskedTextResultHint.InvalidInput;
1567 return false;
1570 return ReplaceInternal (edit_positions, input, current_edit_position, out testPosition, out resultHint, false);
1573 public bool Replace (string input, int position, out int testPosition, out MaskedTextResultHint resultHint)
1575 if (input == null)
1576 throw new ArgumentNullException ("input");
1578 if (position < 0 || position >= edit_positions.Length) {
1579 testPosition = position;
1580 resultHint = MaskedTextResultHint.PositionOutOfRange;
1581 return false;
1584 if (input.Length == 0) {
1585 return RemoveAt (position, position, out testPosition, out resultHint);
1588 bool result;
1590 result = ReplaceInternal (input, position, edit_positions.Length - 1, out testPosition, out resultHint, true, true);
1592 if (result) {
1593 result = ReplaceInternal (input, position, edit_positions.Length - 1, out testPosition, out resultHint, false, true);
1596 return result;
1599 public bool Replace (char input, int startPosition, int endPosition, out int testPosition, out MaskedTextResultHint resultHint)
1602 if (endPosition >= edit_positions.Length) {
1603 testPosition = endPosition;
1604 resultHint = MaskedTextResultHint.PositionOutOfRange;
1605 return false;
1608 if (startPosition < 0) {
1609 testPosition = startPosition;
1610 resultHint = MaskedTextResultHint.PositionOutOfRange;
1611 return false;
1614 if (startPosition > endPosition) {
1615 testPosition = startPosition;
1616 resultHint = MaskedTextResultHint.PositionOutOfRange;
1617 return false;
1620 if (startPosition == endPosition) {
1621 return ReplaceInternal (edit_positions, input, startPosition, out testPosition, out resultHint, false);
1624 return Replace (input.ToString (), startPosition, endPosition, out testPosition, out resultHint);
1627 public bool Replace (string input, int startPosition, int endPosition, out int testPosition, out MaskedTextResultHint resultHint)
1629 bool result;
1631 result = ReplaceInternal (input, startPosition, endPosition, out testPosition, out resultHint, true, false);
1633 if (result) {
1634 result = ReplaceInternal (input, startPosition, endPosition, out testPosition, out resultHint, false, false);
1636 return result;
1639 public bool Set (string input)
1641 int testPosition;
1642 MaskedTextResultHint resultHint;
1643 return Set (input, out testPosition, out resultHint);
1646 public bool Set (string input, out int testPosition, out MaskedTextResultHint resultHint)
1648 bool result;
1650 if (input == null) {
1651 throw new ArgumentNullException ("input");
1654 result = VerifyStringInternal (input, out testPosition, out resultHint, 0, true);
1656 if (result) {
1657 result = VerifyStringInternal (input, out testPosition, out resultHint, 0, false);
1660 return result;
1663 public string ToDisplayString ()
1665 return ToString (false, true, true, 0, Length);
1668 public override string ToString ()
1670 return ToString (true, IncludePrompt, IncludeLiterals, 0, Length);
1673 public string ToString (bool ignorePasswordChar)
1675 return ToString (ignorePasswordChar, IncludePrompt, IncludeLiterals, 0, Length);
1678 public string ToString (bool includePrompt, bool includeLiterals)
1680 return ToString (true, includePrompt, includeLiterals, 0, Length);
1683 public string ToString (int startPosition, int length)
1685 return ToString (true, IncludePrompt, IncludeLiterals, startPosition, length);
1688 public string ToString (bool ignorePasswordChar, int startPosition, int length)
1690 return ToString (ignorePasswordChar, IncludePrompt, IncludeLiterals, startPosition, length);
1693 public string ToString (bool includePrompt, bool includeLiterals, int startPosition, int length)
1695 return ToString (true, includePrompt, includeLiterals, startPosition, length);
1698 public string ToString (bool ignorePasswordChar, bool includePrompt, bool includeLiterals, int startPosition, int length)
1700 if (startPosition < 0)
1701 startPosition = 0;
1703 if (length <= 0)
1704 return string.Empty;
1706 StringBuilder result = new StringBuilder ();
1707 int start = startPosition;
1708 int end = startPosition + length - 1;
1710 if (end >= edit_positions.Length) {
1711 end = edit_positions.Length - 1;
1713 int last_assigned_position = FindAssignedEditPositionInRange (start, end, false);
1715 // Find the last position in the mask to check for
1716 if (!includePrompt) {
1717 int last_non_edit_position;
1719 last_non_edit_position = FindNonEditPositionInRange (start, end, false);
1721 if (includeLiterals) {
1722 end = last_assigned_position > last_non_edit_position ? last_assigned_position : last_non_edit_position;
1723 } else {
1724 end = last_assigned_position;
1728 for (int i = start; i <= end; i++) {
1729 EditPosition ed = edit_positions [i];
1731 if (ed.Type == EditType.Literal) {
1732 if (includeLiterals) {
1733 result.Append (ed.Text);
1735 } else if (ed.Editable) {
1736 if (IsPassword) {
1737 if (!ed.FilledIn) { // Nothing to hide or show.
1738 if (includePrompt)
1739 result.Append (PromptChar);
1740 else
1741 result.Append (" ");
1742 } else if (ignorePasswordChar)
1743 result.Append (ed.Input);
1744 else
1745 result.Append (PasswordChar);
1746 } else if (!ed.FilledIn) {
1747 if (includePrompt) {
1748 result.Append (PromptChar);
1749 } else if (includeLiterals) {
1750 result.Append (" ");
1751 } else if (last_assigned_position != InvalidIndex && last_assigned_position > i) {
1752 result.Append (" ");
1754 } else {
1755 result.Append (ed.Text);
1757 } else {
1758 if (includeLiterals)
1759 result.Append (ed.Text);
1763 return result.ToString ();
1766 public bool VerifyChar (char input, int position, out MaskedTextResultHint hint)
1768 return VerifyCharInternal (input, position, out hint, true);
1771 public bool VerifyEscapeChar (char input, int position)
1773 if (position >= edit_positions.Length || position < 0) {
1774 return false;
1777 if (!edit_positions [position].Editable) {
1778 if (SkipLiterals) {
1779 return input.ToString () == edit_positions [position].Text;
1780 } else {
1781 return false;
1785 if (ResetOnSpace && input == ' ') {
1786 return true;
1787 } else if (ResetOnPrompt && input == PromptChar) {
1788 return true;
1789 } else {
1790 return false;
1794 public bool VerifyString (string input)
1796 int testPosition;
1797 MaskedTextResultHint resultHint;
1798 return VerifyString (input, out testPosition, out resultHint);
1801 public bool VerifyString (string input, out int testPosition, out MaskedTextResultHint resultHint)
1803 if (input == null || input.Length == 0) {
1804 testPosition = 0;
1805 resultHint = MaskedTextResultHint.NoEffect;
1806 return true;
1809 return VerifyStringInternal (input, out testPosition, out resultHint, 0, true);
1811 #endregion
1813 #region Public properties
1814 public bool AllowPromptAsInput {
1815 get {
1816 return allow_prompt_as_input;
1820 public bool AsciiOnly {
1821 get {
1822 return ascii_only;
1826 public int AssignedEditPositionCount {
1827 get {
1828 int result = 0;
1829 for (int i = 0; i < edit_positions.Length; i++) {
1830 if (edit_positions [i].FilledIn) {
1831 result++;
1834 return result;
1838 public int AvailableEditPositionCount {
1839 get {
1840 int result = 0;
1841 foreach (EditPosition edit in edit_positions) {
1842 if (!edit.FilledIn && edit.Editable) {
1843 result++;
1846 return result;
1850 public CultureInfo Culture {
1851 get {
1852 return culture;
1856 public static char DefaultPasswordChar {
1857 get {
1858 return '*';
1862 public int EditPositionCount {
1863 get {
1864 int result = 0;
1865 foreach (EditPosition edit in edit_positions) {
1866 if (edit.Editable) {
1867 result++;
1871 return result;
1875 public IEnumerator EditPositions {
1876 get {
1877 System.Collections.Generic.List <int> result = new System.Collections.Generic.List<int> ();
1878 for (int i = 0; i < edit_positions.Length; i++) {
1879 if (edit_positions [i].Editable) {
1880 result.Add (i);
1883 return result.GetEnumerator ();
1887 public bool IncludeLiterals {
1888 get {
1889 return include_literals;
1891 set {
1892 include_literals = value;
1896 public bool IncludePrompt {
1897 get {
1898 return include_prompt;
1900 set {
1901 include_prompt = value;
1905 public static int InvalidIndex {
1906 get {
1907 return -1;
1911 public bool IsPassword {
1912 get {
1913 return password_char != char.MinValue;
1915 set {
1916 password_char = value ? DefaultPasswordChar : char.MinValue;
1920 public char this [int index] {
1921 get {
1922 if (index < 0 || index >= Length) {
1923 throw new IndexOutOfRangeException (index.ToString ());
1926 return ToString (true, true, true, 0, edit_positions.Length) [index];
1930 public int LastAssignedPosition {
1931 get {
1932 return FindAssignedEditPositionFrom (edit_positions.Length - 1, false);
1936 public int Length {
1937 get {
1938 int result = 0;
1939 for (int i = 0; i < edit_positions.Length; i++) {
1940 if (edit_positions [i].Visible)
1941 result++;
1943 return result;
1947 public string Mask {
1948 get {
1949 return mask;
1953 public bool MaskCompleted {
1954 get {
1955 for (int i = 0; i < edit_positions.Length; i++)
1956 if (edit_positions [i].Required && !edit_positions [i].FilledIn)
1957 return false;
1958 return true;
1962 public bool MaskFull {
1963 get {
1964 for (int i = 0; i < edit_positions.Length; i++)
1965 if (edit_positions [i].Editable && !edit_positions [i].FilledIn)
1966 return false;
1967 return true;
1971 public char PasswordChar {
1972 get {
1973 return password_char;
1975 set {
1976 password_char = value;
1980 public char PromptChar {
1981 get {
1982 return prompt_char;
1984 set {
1985 prompt_char = value;
1989 public bool ResetOnPrompt {
1990 get {
1991 return reset_on_prompt;
1993 set {
1994 reset_on_prompt = value;
1998 public bool ResetOnSpace {
1999 get {
2000 return reset_on_space;
2002 set {
2003 reset_on_space = value;
2007 public bool SkipLiterals {
2008 get {
2009 return skip_literals;
2011 set {
2012 skip_literals = value;
2015 #endregion
2019 #endif