ftp.nice.ch/pub/next/developer/resources/classes/MOKit.1.0.0.s.tar.gz#/MOKit_1.0.0/Documentation/Classes/MORegexFormCell.rtf

This is MORegexFormCell.rtf in view mode; [Download] [Up]

Version 1.0  Copyright ©1992, 1993, 1994 by Mike Ferris.  All Rights Reserved.
Mike Ferris  -  October 2nd, 1993



MORegexFormCell 






Inherits From:	FormCell : ActionCell : Cell : Object

Declared In:	MOKit/MORegexFormCell.h





Class Description

MORegexFormCell adds entry checking by regular expression to FormCell.  The cell keeps a list of regular expression pattern strings.  Ending editing in the cell causes the entry to be checked sequentially against the cell's patterns.  If a match is found the entry is accepted and editing ends.  If a match is not found with any of the patterns, the entry is not accepted and the attempt to end editing is aborted.  The cell beeps and reselects the text remaining the first responder.  If multiple patterns would match, the first matching pattern in the list is the one that IS matched.  This can matter for the -pieceAt: method.

The regular expression routines are those of Henry Spencer (the only change was to make the functions all static).  See the source code and README files for information on those routines.

A call to endEditing explicitly may force the editing to end.  Use this snippet instead (taken from the Window class documentation):

if ([myWindow makeFirstResponder:myWindow]) {
    [myWindow endEditingFor:nil];
    . . .
}

This should allow the form to keep editing if the format is incorrect. 

The cell's entry is checked by the method - (BOOL)isEntryAcceptable:(const char *)aString.  Whenever this method is called, if it finds a match, it does a little extra work.  It remembers the substrings of aString which matched the subexpressions of the pattern that it matched.  The cell can be queried for any of these substrings.  The method -€(const char *)pieceAt:(int)index gives back a pointer to the substring that matched subexpression index in the pattern that was last matched.  Piece number zero is the substring that matched the entire pattern (this may not be the whole contents of the cell).

The cell has a delegate which is called upon to further validate an entry if it passes the regular expression tests of the cell.  The - finishValidation: for: method is used to accomplish this.

Note:  I am not completely satisfied with the way the regular expression stuff is getting incorporated into the object, but my overriding concern was that two objects in two different palettes might want to use these same routines.  I wanted to avoid cluttering the global namespace, and so I made the functions all static then #included the c file in my own source file.  This is yucky.




Useful Regular Expressions

The following regular expressions are provided because they check for common data types.

Integer:

Subexpression 1 = the actual number part suitable for passing to atoi or whatever.

^[ ]*([+-]?[0-9]+)[ ]*$

Floating point number:

There are a couple of problems with this one that might be solved by making multiple expressions for cases like no decimal part or no exponent part.

Subexpression 1 = the actual number part suitable for passing to atof or whatever.
Subexpression 2 = the integral part.
Subexpression 3 = the decimal part.
Subexpression 4 = the exponent.

^[ ]*(([+-]?[0-9]*)\.?([0-9]*)[eE]?([0-9]*))[ ]*$

American style Dates:

Three cases are needed to deal with dates somewhat intelligently.  Still it can't check days in a month, or leap year type stuff.  With a lot more expressions it might be possible to check days in a month, but you need math for leap years.  The first pattern is for no separators in the format mmddyy or mmddyyyy.  The second is for dates like 1/2/92 or 01/12/1992.  The third is for named months like January 3, 1992 or Jan 5 92.

Subexpression 1 = the date.
Subexpression 2 = the month part.
Subexpression 3 = the day part.
Subexpression 4 = the year part.

^[ ]*(([0][1-9]|[1][0-2])([0][1-9]|[1-2][0-9]|[3][0-1])([0-9]?[0-9]?[0-9][0-9]))[ ]*$
^[ ]*(([0]?[1-9]|[1][0-2])[-/]([0]?[1-9]|[1-2][0-9]|[3][0-1])[-/]([0-9]?[0-9]?[0-9][0-9]))[ ]*$
^[ ]*(([jJ][aA][nN]?[uU]?[aA]?[rR]?[yY]?|[fF][eE]?[bB]?[rR]?[uU]?[aA]?[rR]?[yY]?|[mM][aA][rR][cC]?[hH]?|[aA][pP][rR]?[iI]?[lL]?|[mM][aA][yY]|[jJ][uU][nN][eE]?|[jJ][uU][lL][yY]?|[aA][uU][gG]?[uU]?[sS]?[tT]?|[sS][eE]?[pP]?[tT]?[eE]?[mM]?[bB]?[eE]?[rR]?|[oO][cC]?[tT]?[oO]?[bB]?[eE]?[rR]?|[nN][oO]?[vV]?[eE]?[mM]?[bB]?[eE]?[rR]?|[dD][eE]?[cC]?[eE]?[mM]?[bB]?[eE]?[rR]?)[ ]*([0]?[1-9]|[1-2][0-9]|[3][0-1])[ ]*[,]?[ ]*([0-9]?[0-9]?[0-9][0-9]))[ ]*$

European style Dates:

Three cases are needed to deal with dates somewhat intelligently.  Still it can't check days in a month, or leap year type stuff.  With a lot more expressions it might be possible to check days in a month, but you need math for leap years.  For pretty obvious reasons it is not always possible to distinguish between American and European dates lexically, so you pretty much have to pick one or the other.  The first pattern is for no separators in the format ddmmyy or ddmmyyyy.  The second is for dates like 1/2/92 or 01/12/1992.  The third is for named months like 3 December, 1992 or 5 Nov 93.

Subexpression 1 = the date.
Subexpression 2 = the day part.
Subexpression 3 = the month part.
Subexpression 4 = the year part.

^[ ]*(([0][1-9]|[1-2][0-9]|[3][0-1])([0][1-9]|[1][0-2])([0-9]?[0-9]?[0-9][0-9]))[ ]*$
^[ ]*(([0]?[1-9]|[1-2][0-9]|[3][0-1])[-/]([0]?[1-9]|[1][0-2])[-/]([0-9]?[0-9]?[0-9][0-9]))[ ]*$
^[ ]*(([0]?[1-9]|[1-2][0-9]|[3][0-1])[ ]*[,]?[ ]*([jJ][aA][nN]?[uU]?[aA]?[rR]?[yY]?|[fF][eE]?[bB]?[rR]?[uU]?[aA]?[rR]?[yY]?|[mM][aA][rR][cC]?[hH]?|[aA][pP][rR]?[iI]?[lL]?|[mM][aA][yY]|[jJ][uU][nN][eE]?|[jJ][uU][lL][yY]?|[aA][uU][gG]?[uU]?[sS]?[tT]?|[sS][eE]?[pP]?[tT]?[eE]?[mM]?[bB]?[eE]?[rR]?|[oO][cC]?[tT]?[oO]?[bB]?[eE]?[rR]?|[nN][oO]?[vV]?[eE]?[mM]?[bB]?[eE]?[rR]?|[dD][eE]?[cC]?[eE]?[mM]?[bB]?[eE]?[rR]?)[ ]*[,]?[ ]*([0-9]?[0-9]?[0-9][0-9]))[ ]*$

Times (with seconds):

A couple of situations are required.  One for AM/PM dates and one for 24-hour dates.  The first pattern is for times like 7:34:12 PM.  The second is for times like 19:34:12

Subexpression 1 = the time
Subexpression 2 = the hours
Subexpression 3 = the minutes
Subexpression 4 = the seconds
Subexpression 5 = the AM/PM if there is a fifth subexpression

^[ ]*(([0]?[0-9]|[1][0-2]):([0-5][0-9]):([0-5][0-9])[ ]*([aA][mM]?|[pP][mM]?))[ ]*$
^[ ]*(([0]?[0-9]|[1][0-9]|[2][0-3]):([0-5][0-9]):([0-5][0-9]))[ ]*$

Times (without seconds):

A couple of situations are required.  One for AM/PM dates and one for 24-hour dates.  The first pattern is for times like 7:34 PM.  The second is for times like 19:34

Subexpression 1 = the time
Subexpression 2 = the hours
Subexpression 3 = the minutes
Subexpression 4 = the AM/PM (might be null)

^[ ]*(([0]?[0-9]|[1][0-2]):([0-5][0-9])[ ]*([aA][mM]?|[pP][mM]?))[ ]*$
^[ ]*(([0]?[0-9]|[1][0-9]|[2][0-3]):([0-5][0-9])())[ ]*$

Phone Numbers:

This could probably be narrowed down to be more stringent than it is.

Subexpression 1 = the whole number
Subexpression 2 = the area code (might be null)
Subexpression 3 = the number

^[ ]*(\(([0-9][0-9][0-9])\)[ ]*([0-9][0-9][0-9][-\./]?[0-9][0-9][0-9][0-9]))[ ]*$
^[ ]*(([0-9][0-9][0-9])[-\./]?([0-9][0-9][0-9][-\./]?[0-9][0-9][0-9][0-9]))[ ]*$
^[ ]*(()[-\./]?([0-9][0-9][0-9][-\./]?[0-9][0-9][0-9][0-9]))[ ]*$

Social Security Numbers:

Subexpression 1 = the SS number

^[ ]*([0-9][0-9][0-9][-]?[0-9][0-9][-]?[0-9][0-9][0-9][0-9])[ ]*$




Instance Variables

id regexStrs;
BOOL allowEmptyString;
int pieceCount;
char *pieces[10];
int matchedIndex;
id delegate;



regexStrs 	A List object containing MOString objects.  Each MOString object holds a regular expression pattern.

allowEmptyString 	YES if an empty entry should be allowed, NO if it should only be allowed if one of the patterns matches an empty string.

pieceCount 	The number of subexpressions in the last matched pattern.

pieces 	An array of substrings of the string last matched that correspond to the subexpressions of the pattern that matched it.

matchedIndex 	The index of the last pattern string that was matched.  Can become invalid as expressions are added to or removed from the pattern list.

delegate 	The cell's delegate which is responsible for further validation of new entries.





Method Types

Initializing the class	+initialize

Creating and freeing instances	- initTextCell:
- initTextCell: withRegex:
- copyFromZone:
- free

Checking regex validity	+ isValidRegex:

Setting the cell's value	- setDoubleValue:
- setFloatValue:
- setIntValue:
- setStringValue:
- setStringValueNoCopy:
- setStringValueNoCopy: shouldFree:

Handling empty strings	- doesAllowEmptyString
- setAllowEmptyString:

Managing the patterns	- regexStr
- setRegexStr:
- regexStrAt:
- addRegexStr:
- removeRegexStrAt:
- regexStrCount
- regexStrList

Getting at the pieces	- pieceCount
- pieceAt:
- matchedPatternIndex

Validating the entry	- isEntryAcceptable:

Setting the delegate	- delegate
- setDelegate:

Archiving	- awake
- read:
- write:






Class Methods

initialize
+ initialize

Initializes the class instance doing things like setting the version number.




isValidRegex:
+ isValidRegex:

Given a potential regular expression string, this method attempts to compile it.  If the compilation succeeds (ie it was a valid regular expression string) this returns YES, else NO.  This can be used, for instance, by the inspector class in the palette for this object to validate regular expressions before adding them to an actual instance.





Instance Methods

addRegexStr:
- addRegexStr:(const char *)re

Adds the given string to the cell's list of patterns to check.  The string should be a regular expression.

See also:   ±€setRegexStr:, ±€regexStr, ±€regexStrAt:, ±€removeRegexStrAt:




awake
- awake

Checks the cell's validity after archiving.  An error is written to stderr if the cell awakes with an incompatible stringValue and set of patterns.

See also:   ±€read:, ±€write:




copyFromZone:
- copyFromZone:(NXZone *)zone

Makes a copy of the cell.  This method needs to make distinct copies of the regular expression patterns and needs to reset the pieces array so that the old and new instance don't share any memory that each one thinks it has exclusive rights to.

See also:   ±€initTextCell:, ±€initTextCell: withRegex:, ±€free




doesAllowEmptyString
- (BOOL)doesAllowEmptyString

Returns YES if the cell allows empty strings even if none of its patterns do.  NO otherwise.

See also:   ±€setAllowEmptyString




free
- free

Frees the memory we may have allocated for various things.

See also:   ±€initTextCell:, ±initTextCell: withRegex:, ±€copyFromZone:




initTextCell:
- initTextCell:(const char *)aString

Initializes the cell to have no patterns, allow empty strings and have stringValue aString.  This overrides the designated initializer for FormCell.

See also:   ±€initTextCell: withRegex:, ±€free, ±€copyFromZone:




initTextCell: withRegex:
- initTextCell:(const char *)aString withRegex:(const char *)re

Initializes the cell to have one pattern equal to re, allow empty strings and have stringValue aString.  This overrides the designated initializer for FormCell.

See also:   ±€initTextCell:, ±free, ±€copyFromZone:




isEntryAcceptable
- (BOOL)isEntryAcceptable:(const char *)aString

Returns YES if aString matches one of the cell's patterns.  If allowEmptyString is set then this returns YES when the string is empty regardless of whether it matches any of the patterns.  Returns NO otherwise.  If a match is found the substrings matching the subexpressions of the matched pattern are remembered.

See also:   ±€matchedPatternIndex, ±pieceCount, ±pieceAt:




matchedPatternIndex
- (int)matchedPatternIndex

Returns the index of the pattern that was matched last time isEntryAcceptable: was called or -1 if no match was made.

See also:   ±€isEntryAcceptable:, ±pieceCount, ±pieceAt:




pieceAt:
- (const char *)pieceAt:(int)n

Returns the substring corresponding to the nth subexpression of the last pattern matched.

See also:   ±€isEntryAcceptable:, ±€matchedPatternIndex, ±pieceCount




pieceCount
- (int)pieceCount

Returns the number of subexpressions in the last pattern matched or -1 if no pattern was matched.

See also:   ±€isEntryAcceptable:, ±€matchedPatternIndex, ±pieceAt:




read
- read:(NXTypedStream *)strm

Reads the object from the typed stream.  Overriden to read in the cell's patterns and other settings.

See also:   ±€awake, ±€write:




regexStr
- (const char *)regexStr

Returns the first pattern string of this cell.  This call is provided as a convenient shorthand for regexStrAt:0 and should (for style reasons) only be used when the cell has one pattern.

See also:  ±€addRegexStr:, ±€setRegexStr:, ±€regexStrAt:, ±€removeRegexStrAt:




regexStrAt:
- (const char *)regexStrAt:(int)index

Returns the pattern string at index of this cell.

See also:  ±€addRegexStr:, ±€setRegexStr:, ±€regexStr, ±€removeRegexStrAt:




regexStrCount
- (int)regexStrCount

Returns the number of patterns in this cell.

See also:   ±€regexStrList




regexStrList
- regexStrList

Returns the List object used to store the strings.  Some might want to modify this directly.  I don't recommend it highly.

See also:   ±€regexStrCount




removeRegexStrAt:
- removeRegexStrAt:(int)index

Removes the pattern string at index of this cell.

See also:  ±€addRegexStr:, ±€setRegexStr:, ±€regexStr, ±€regexStrAt:




setAllowEmptyString:
- setAllowEmptyString:(BOOL)flag

Sets whether of not this cell allows the empty string regaardless of whether it matches any of the cell's patterns.

See also:   ±€doesAllowEmptyString




setDoubleValue:
- setDoubleValue:(double)aDouble

Overrides the standard method to perform validation on the new value.  The new value is accepted regardless of whether it passes validation, but the regular expression pieces are extracted as part of the validation, so the ± pieceAt: method can be called directly after setting the cell's value.

See also:   ±€setFloatValue:, ±€setIntValue:, ±€setStringValue:, ±€setStringValueNoCopy:, ±€setStringValueNoCopy: shouldFree:




setFloatValue:
- setFloatValue:(float)aFloat

Overrides the standard method to perform validation on the new value.  The new value is accepted regardless of whether it passes validation, but the regular expression pieces are extracted as part of the validation, so the ± pieceAt: method can be called directly after setting the cell's value.

See also:   ±€setDoubleValue:, ±€setIntValue:, ±€setStringValue:, ±€setStringValueNoCopy:, ±€setStringValueNoCopy: shouldFree:




setIntValue:
- setIntValue:(int)anInt

Overrides the standard method to perform validation on the new value.  The new value is accepted regardless of whether it passes validation, but the regular expression pieces are extracted as part of the validation, so the ± pieceAt: method can be called directly after setting the cell's value.

See also:   ±€setDoubleValue:, ±€setFloatValue:, ±€setStringValue:, ±€setStringValueNoCopy:, ±€setStringValueNoCopy: shouldFree:




setRegexStr:
- setRegexStr:(const char *)re

Sets this form to match only one pattern: the one given.  First removes all other patterns, then adds re.

See also:  ±€addRegexStr:, ±€regexStr, ±€regexStrAt:, ±€removeRegexStrAt:




setStringValue:
- setStringValue:(const char *)aString

Overrides the standard method to perform validation on the new value.  The new value is accepted regardless of whether it passes validation, but the regular expression pieces are extracted as part of the validation, so the ± pieceAt: method can be called directly after setting the cell's value.

See also:   ±€setDoubleValue:, ±€setFloatValue:, ±€setIntValue:, ±€setStringValueNoCopy:, ±€setStringValueNoCopy: shouldFree:




setStringValueNoCopy:
- setStringValueNoCopy:(char *)aString

Overrides the standard method to perform validation on the new value.  The new value is accepted regardless of whether it passes validation, but the regular expression pieces are extracted as part of the validation, so the ± pieceAt: method can be called directly after setting the cell's value.

See also:   ±€setDoubleValue:, ±€setFloatValue:, ±€setIntValue:, ±€setStringValue:, ±€setStringValueNoCopy: shouldFree:




setStringValueNoCopy: shouldFree:
- setStringValueNoCopy:(char *)aString shouldFree:(BOOL)flag

Overrides the standard method to perform validation on the new value.  The new value is accepted regardless of whether it passes validation, but the regular expression pieces are extracted as part of the validation, so the ± pieceAt: method can be called directly after setting the cell's value.

See also:   ±€setDoubleValue:, ±€setFloatValue:, ±€setIntValue:, ±€setStringValue:, ±€setStringValueNoCopy:




write
- write:(NXTypedStream *)strm

Writes the object to the typed stream.  Overriden to write the cell's patterns and other settings.

See also:   ±€read:, ±€awake






Methods Implemented by the Delegate

finishValidating: for:
±€(BOOL)finishValidating:(const char *)aString for:sender

Should make sure that the string is acceptable.  Return YES to finally accept the entry and NO to reject.  Your implementation should do any checking that was not possible with regular expressions.  This method is only called if the string has already passed the cell's own regular expression tests.  You can rely on the string having matched the one of the cell's regular expressions and can use any of the utility methods like ±€matchedPatternIndex or ±€pieceAt: to find out more information.  One entry type that needs checking of this kind is dates (however, dates may eventually be done by a subclass...).  The example date matching expressions in the CommonPatterns file just makes sure the day is between 1 and 31.  It doesn't make sure that if the month is November, the day isn't 31.  A delegate would have to be used to finish validating a date.





These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.