home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programming Languages Suite
/
ProgLangD.iso
/
VCAFE.3.0A
/
Main.bin
/
MaskEngine.java
< prev
next >
Wrap
Text File
|
1998-12-11
|
26KB
|
617 lines
/*
Engine class for support of masked input in IRAD.
Created 3/26/98 by Paul Lancaster.
*/
package com.symantec.itools.swing;
import java.awt.event.KeyEvent;
import java.awt.*;
public class MaskEngine {
// These constants are used to specify the data type information needed by
// the engine to specialize mask behaviour.
public static final int texttype = 0;
public static final int numbtype = 1;
public static final int datetype = 2;
public static final int timetype = 3;
public MaskEngine(String mask, int datatype) { // ctor
init();
setMask(mask);
setDatatype(datatype);
}
public MaskEngine() { // default constructor
init();
}
public void setMask(String mask) {
//vasu start
if(mask.trim().equals("")) return;
//vasu end
_mask = mask;
_maskLength = mask.length();
// Analyze filter and initialize supporting data structures
_filterCount = 0;
resetMaskScan();
// First pass: count filters and total displayable mask characters.
// Also mark first and last filter positions.
for (_maskCount = 0; 0 != getNextMaskChar(); _maskCount++) {
if (_curMaskType == filtertype) {
_filterCount++; // count filters
_lastFilterPos = _curMaskPos; // new "last" filter pos
}
if (1 == _filterCount)
_firstFilterPos = _curMaskPos; // note first filter pos
}
if (_maskCount * _filterCount == 0)
return; // error: no displayable characters or no filters in mask
// Second pass: fill filter position array.
_filterPositions = new int[_filterCount];
_commandCorrections = new int[_filterCount];
resetMaskScan();
for (int i = 0, j = 0; i < _maskCount; i++) {
getNextMaskChar();
if (_curMaskType == filtertype) {
_filterPositions[j] = _curMaskPos;
_commandCorrections[j++] = _commandCorrection;
}
}
}
public void setDatatype(int datatype) { _datatype = datatype; }
public String getMask() { return _mask ; }
public int getDatatype() { return _datatype ; }
/* Called to initialize the display of the masked data.
The first parameter is the current data in the field.
The second parameter holds the string that should be displayed.
The return value is the initial caret position.
*/
public int initDisplay(String data, StringBuffer newData) {
//vasu start
if (_filterCount == 0){ // no filters?
newData.append(data);
return 0; // return first position
}
//vasu end
int datalen = data.length();
int inpos = 0; // track input position
boolean zerofill = false;
boolean dataRightOfDecimal = false;
boolean maskRightOfDecimal = false;
resetMaskScan();
char c;
for (int i = 0; (c = getNextMaskChar()) != 0; i++) { // mask drives output
if (_curMaskType == filtertype) { // we are on a filter?
char z = zerofill ? '0' : '_';
if (inpos < datalen) { // consumed all data yet?
c = data.charAt(inpos++); // if not, get next data char
if (numbtype == _datatype) { // special handling for numerics
if (_decimalPoint == c) {
c = z;
dataRightOfDecimal = true;
if (maskRightOfDecimal && inpos < datalen)
c = data.charAt(inpos++);
} else if (dataRightOfDecimal && !maskRightOfDecimal) {
c = z;
inpos--; // can not use this fractional digit yet
} else if (!dataRightOfDecimal && maskRightOfDecimal) {
c = z;
while (inpos < datalen) // scan data for dec. pt.
if (_decimalPoint == data.charAt(inpos++)) {
if (inpos < datalen)
c = data.charAt(inpos++);
dataRightOfDecimal = true;
break;
}
}
} // if numeric field
} else // we have consumed all input data
c = z;
} else if (_curMaskChar == _decimalPoint && numbtype == _datatype) {
maskRightOfDecimal = true;
if (0 == datalen) { // for empty numeric fields, start zero filling
zerofill = true;
newData.setCharAt(newData.length() - 1, '0'); // zero units digit
}
else if (inpos < datalen && data.charAt(inpos) == _decimalPoint) {
inpos++; // skip decimal point in input
dataRightOfDecimal = true;
}
}
newData.append(c);
}
// Zero units digit of empty numeric fields with no decimal.
if (numbtype == _datatype && 0 == datalen && !zerofill && _maskLength > 0)
newData.setCharAt(newData.length() - 1, '0');
return _filterPositions[0] - _commandCorrections[0];
}
/* This is the main workhorse method.
It is called for every key stroke corresponding to displayable
characters once editing begins.
The 1st parameter is the user keystroke event object.
The 2nd parameter is the current cursor position (zero based).
The 3rd parameter is the current text from the component.
The 4th parameter is output and is what should be displayed in the component.
The 5th and 6th parameters are the selection start and end, respectively.
The return value is the new cursor position within the "newData"
parameter (zero based), unless it is negative, in which case:
-1 means the input is inconsistent (position not in range or not on a filter.
-2 means the input keystroke is not accepted by the relevant filter.
*/
public int processKey(java.awt.event.KeyEvent e, int pos, String data,
StringBuffer newData, int selStart, int selEnd) {
newData.append(data); // init output to input
int keyCode = e.getKeyCode();
if (!setMaskPos(pos) && !isNavKey(keyCode))
return -1; // cursor pos not in bounds
char key = e.getKeyChar();
if (key == _decimalPoint && _datatype == numbtype) { // decimal?
// Move caret to first filter to the right of first decimal point
// that is to the right of current caret position.
while (getNextMaskChar() != 0)
if (_curMaskChar == _decimalPoint)
return nextFilterPos();
return pos; // could not find rightward decimal point
} else if (filtertype != _curMaskType) { // not on filter position?
if (0 == pos && 0 == selStart && selEnd > _firstFilterPos)
setMaskPos(pos = _firstFilterPos);
else if (!isNavKey(keyCode)) {
if (pos > _lastFilterPos)
return -1; // beyond last filter
nextFilterPos(); // advance to next filter
pos = _curMaskPos - _commandCorrection;
}
}
switch (keyCode) { // handle control keys
case e.VK_BACK_SPACE:
if (selStart < selEnd) { // if selected text exists
if (pos != selStart || selEnd - selStart > 1) {
clearSelectedText(selStart, selEnd, newData);
return Math.max(selStart, _firstFilterPos);
}
}
int newPos = prevFilterPos();
if (pos > 0 && newPos != -1)
newData.setCharAt(newPos, '_');
return newPos != -1 ? newPos : pos;
case e.VK_DELETE:
if (selStart < selEnd) { // if selected text exists
clearSelectedText(selStart, selEnd, newData);
return Math.max(selStart, _firstFilterPos);
}
newData.setCharAt(pos, '_');
return pos;
case e.VK_LEFT:
return prevFilterPos();
case e.VK_RIGHT:
return nextFilterPos();
case e.VK_HOME:
return _filterPositions[0] - _commandCorrections[0];
case e.VK_END:
return _filterPositions[_filterCount-1] - _commandCorrections[_filterCount-1];
}
if (matchFilter(key)) {
clearSelectedText(selStart, selEnd, newData);
if (eShiftLower == _shiftState)
key = Character.toLowerCase(key);
else if (eShiftUpper == _shiftState)
key = Character.toUpperCase(key);
newData.setCharAt(pos, key);
int newpos = nextFilterPos();; // match: advance to next filter
return newpos == -1 ? _lastFilterPos - 999 : newpos;
} else // no match: return with no changes
return -2;
}
public boolean stripMask(String data, StringBuffer newData) {
if (_maskLength == 0){
newData.append(data);
return true;
}
resetMaskScan();
boolean retval = true;
for (int i = 0; getNextMaskChar() != 0; i++) {
char c = data.charAt(i);
if (_curMaskType == filtertype) {
if (c != '_')
newData.append(c);
else if (isMandatory()) // empty filter position
retval = false; // missing mandatory data
} else if (_curMaskChar == _decimalPoint && _datatype == numbtype) {
newData.append(_decimalPoint);
}
}
return retval;
}
// Return true iff the engine handles the given key stroke.
public boolean isHandledKey(java.awt.event.KeyEvent e) {
if (0 == _maskLength) // if no mask, let component handle all keys
return false;
char c = e.getKeyChar();
if (Character.isISOControl(c)) {
int k = e.getKeyCode();
switch (k) {
case e.VK_LEFT:
case e.VK_RIGHT:
case e.VK_HOME:
case e.VK_END:
return !e.isShiftDown();
}
switch (c) {
case e.VK_BACK_SPACE:
case e.VK_DELETE:
return true;
default:
return false;
}
}
return true;
}
public String cut(String data, int selStart, int selEnd, StringBuffer newData) {
newData.append(data);
setMaskPos(selStart);
clearSelectedText(selStart, selEnd, newData);
return data.substring(selStart, selEnd);
}
public int paste(String data, String pasteData, int pos, StringBuffer newData, int selStart, int selEnd) {
boolean hasSelection = selStart < selEnd;
if (hasSelection)
pos = selStart;
if (!setMaskPos(pos))
return -1;
StringBuffer sb = new StringBuffer(data);
int len = pasteData.length();
KeyEvent e = new KeyEvent(new TextField(), 0, 0, 0, 0);
String s = new String(sb.toString());
int newpos = pos;
for (int i = 0; i < len; i++) {
char c = pasteData.charAt(i);
e.setKeyChar(c);
e.setKeyCode(c);
sb = new StringBuffer();
int selstart = 0, selend = 0;
if (hasSelection && newpos >= selStart && newpos < selEnd) {
if (i == 0) {
selstart = selStart;
selend = selEnd;
}
else {
selstart = newpos;
selend = newpos + 1;
}
}
newpos = processKey(e, newpos, s, sb, selstart, selend);
if (newpos == -1 || newpos == -2) {
break; // next paste character was rejected by mask
}
s = sb.toString();
}
newData.append(s);
return newpos;
}
//fix for 68889
public String getMatchedText ( String t ) {
if ( debug ) {
System.out.println ( "Input text : " + t ) ;
}
//Initailize the return stringbuffer
StringBuffer retString = new StringBuffer ( );
if ( t != null && t.length() > 0 ) {//there is some text
//if mask is "", then do not do anything
String mask = getMask() ;
if ( mask.length() > 0 ) {//there is some mask
int textCharCounter = 0;
java.text.StringCharacterIterator maskIterator = new java.text.StringCharacterIterator( mask );
//iterate for each mask character - also means that if the text provided
//is more the extra char will be neglected
//used do while iteration so that the substatement is executed
//atleast once
do {
char textChar = '_';
char maskChar = maskIterator.current();
if ( textCharCounter < t.length () ) {//sufficient text
//we are still within the text
textChar = t.charAt ( textCharCounter ) ;
if ( isFilter ( maskChar ) ) { //if the mask char is a filter
// if it matches with the filter, type accept it
if ( matchFilter ( textChar , maskChar ) ) {
retString.append ( textChar );
}
else {
// otherwise use '_'
retString.append ( '_' );
}
}
else if (isCommand ( maskChar ) ) {//if the mask char is a command
//handle case conversion and escape command
//in all these cases, the current mask is just a command
//that is applied to the next mask filter. so get it.
maskIterator.next();
if ( maskChar == '>' ) {//command : convert to upper case
// if it matches with the filter, type accept it
if ( matchFilter ( textChar , maskIterator.current()) ) {
retString.append ( Character.toUpperCase( textChar ));
}
else {
// otherwise use '_'
retString.append ( '_' );
}
}
else if ( maskChar == '<' ){//command : convert to lower case
// if it matches with the filter, type accept it
if ( matchFilter ( textChar , maskIterator.current()) ) {
retString.append ( Character.toLowerCase( textChar ));
}
else {
// otherwise use '_'
retString.append ( '_' );
}
}
else {//escape character - so append mask char as a literal
retString.append ( maskIterator.current() );
}
}
else {//if the mask char is a literal
//otherwise neglect the text char. append the masks char
//because it is a mask literal
retString.append ( maskChar );
}
}
else {//insufficient text - text length < mask length
// otherwise use '_'
retString.append ( '_' );
}
textCharCounter ++;//increment text counter
} while ( maskIterator.next() != maskIterator.DONE ) ;
}
else {//there is no mask - append the input string as it is.
retString.append ( t ) ;
}
}
else {//there is no text or it is null
//do nothing
}
if ( debug ) {
System.out.println ( "Output text : " + retString ) ;
}
return retString.toString() ;
}
boolean clearSelectedText(int selStart, int selEnd, StringBuffer newData) {
if (selEnd > selStart) { // only if selected text exists
// save mask scan state
int curMaskPos = _curMaskPos;
int nextMaskPos = _nextMaskPos;
int curMaskType = _curMaskType;
char curMaskChar = _curMaskChar;
char lastMaskChar = _lastMaskChar;
char decimalPoint = _decimalPoint;
int commandCorrection = _commandCorrection;
int shiftState = _shiftState;
setMaskPos(selStart);
int outpos = selStart; // track input position
char c = _curMaskChar;
while (outpos < selEnd && c != 0) {
if (_curMaskType == filtertype)
c = '_';
newData.setCharAt(outpos++, c);
c = getNextMaskChar();
}
// restore mask scan state
_curMaskPos = curMaskPos;
_nextMaskPos = nextMaskPos;
_curMaskType = curMaskType;
_curMaskChar = curMaskChar;
_lastMaskChar = lastMaskChar;
_decimalPoint = decimalPoint;
_commandCorrection = commandCorrection;
_shiftState = shiftState;
return true;
}
return false;
}
// Set new position in mask. Return false iff it is out of bounds.
boolean setMaskPos(int pos) {
if (pos < 0)
return false;
resetMaskScan();
_inRange = true;
while (pos-- >= 0)
if (getNextMaskChar() == 0)
return _inRange = false;
return true;
}
// Returns the position in the mask of the first filter that follows the
// current position.
int nextFilterPos() {
while (getNextMaskChar() != 0)
if (_curMaskType == filtertype)
return _curMaskPos - _commandCorrection;
return -1;
}
// Returns the position in the mask of the filter that precedes the
// current position. Return of -1 means no previous filter was found.
int prevFilterPos() {
int oldPos = _curMaskPos;
resetMaskScan();
int prevFilterPos = -1;
int prevCommandCorrection = 0;
while (nextFilterPos() != -1) {
if (_curMaskPos >= oldPos) { // scanned past previous filter
if (!_inRange)
return _curMaskPos - _commandCorrection;
break;
}
prevFilterPos = _curMaskPos; // save pos info on this filter
prevCommandCorrection = _commandCorrection;
}
return prevFilterPos - prevCommandCorrection;
}
// Return true iff key is accepted by current filter
boolean matchFilter(char key) {
return matchFilter ( key , _curMaskChar ) ;
}
// Return true iff key is accepted by current filter
boolean matchFilter(char key , char maskChar ) {
switch ( maskChar ) {
case '0': // any digit
return Character.isDigit(key);
case '9': // any digit or space
return Character.isDigit(key) || Character.isSpaceChar(key);
case '#': // digit, space or sign
return Character.isDigit(key) || Character.isSpaceChar(key) ||
key == '+' || key == '-';
case 'L': // letter
case '?':
return Character.isLetter(key);
case 'A': // letter or digit
case 'a':
return Character.isLetterOrDigit(key);
case '&': // any character
case 'C':
//some ignorable characters cause problems while
//auto-repeating back-space and other keys. so filter them out.
return ( !Character.isIdentifierIgnorable ( key ));
default:
return false;
}
}
// Prepare mask to be scanned from the beginning.
void resetMaskScan() {
_curMaskPos = 0;
_nextMaskPos = 0; // set first char to get
_lastMaskChar = 0;
_commandCorrection = 0;
_curMaskType = 0;
_shiftState = eShiftNul;
}
char getNextMaskChar() {
while (true) { // loop through command chars
if (_nextMaskPos >= _maskLength)
return 0;
_lastMaskChar = _curMaskChar;
_curMaskChar = _mask.charAt(_nextMaskPos++);
if (isCommand()) {
_commandCorrection++;
if ('<' == _curMaskChar)
_shiftState = eShiftLower;
else if ('>' == _curMaskChar)
_shiftState = eShiftUpper;
} else
break;
}
_curMaskType = literaltype;
if (_lastMaskChar != '\\' && isFilter())
_curMaskType = filtertype;
_curMaskPos = _nextMaskPos - 1;
return _curMaskChar;
}
// Return true iff key is a cursor navigation key.
boolean isNavKey(int key) {
switch (key) {
default:
return false;
case java.awt.event.KeyEvent.VK_LEFT:
case java.awt.event.KeyEvent.VK_RIGHT:
case java.awt.event.KeyEvent.VK_BACK_SPACE:
case java.awt.event.KeyEvent.VK_HOME:
case java.awt.event.KeyEvent.VK_END:
}
return true;
}
boolean isFilter() { return isFilter(_curMaskChar); }
boolean isCommand() { return isCommand(_curMaskChar); }
boolean isMandatory() { return isMandatory(_curMaskChar); }
//add other utility methods
boolean isFilter( char c ) { return -1 != "09#aAL?&C".indexOf( c ); }
boolean isCommand( char c ) { return -1 != "<>\\" .indexOf( c ); }
boolean isMandatory( char c ) { return -1 != "0LA&" .indexOf( c ); }
// Called by all ctors
void init() {}
// Variables
String _mask = "" ; // current mask
int _maskLength = 0 ; // its length
int _datatype = texttype; // data type of masked field
int _filterCount = 0 ; // count of filters in mask
int _maskCount = 0 ; // count of displayable characters
int _firstFilterPos = 0 ;
int _lastFilterPos = 0 ;
int[] _filterPositions ; // track all filter positions
int[] _commandCorrections ; // track all command corrections
boolean _inRange = true ; // true if last mask position request is in range
// State variables for scanning the mask
int _curMaskPos = 0; // position of current mask character
int _nextMaskPos = 0; // position of next mask character
int _curMaskType = nulltype; // type of current mask character
char _curMaskChar = 0;
char _lastMaskChar = 0;
int _commandCorrection = 0;
int _shiftState = eShiftNul;
char _decimalPoint = '.';
// Internal mask command type codes
static final int nulltype = 0;
static final int filtertype = 1;
static final int literaltype = 2;
// Internal shift states
static final int eShiftNul = 0;
static final int eShiftUpper = 1;
static final int eShiftLower = 2;
//debug flag
private boolean debug = false;
}