HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

"JMaskField"
Volume: 4 Issue: 1, p. 14

Download files assoicated with this article

	

Listing 1.
 
public class MaskToken { protected int pos; protected String text; 
public MaskToken(int pos, String text) 
{ this.pos = pos; this.text = text; } 
public boolean equals(char chr) 
{ if (text.length() == 1) return chr == text.charAt(0); 
else return false; } 
public boolean equals(String test) 
{ return text.equals(test); } public String toString() 
{ return "token(" + pos + "," + '"' + text + "' + ")"; } }


Listing 2.

public class MaskTokenizer
{
  protected Vector tokens = new Vector();
  protected String include, exclude;
  protected int pos = 0;

  public MaskTokenizer(String include, String exclude)
  {
    this.include = include;
    this.exclude = exclude;
  }

  public void tokenize(String text)
  {
    int prev = 0;
    tokens.removeAllElements();
    StringBuffer buffer = new StringBuffer();
    for (int i = 0; i < text.length(); i++)
    {
      if (include.indexOf(text.charAt(i)) > -1)
      {
        if (buffer.length() > 0) tokens.addElement(
          new MaskToken(prev, buffer.toString()));
        tokens.addElement(
          new MaskToken(i, "" + text.charAt(i)));
        buffer.setLength(0);
        prev = i + 1;
      }
      else if (exclude.indexOf(text.charAt(i)) > -1)
      {
        if (buffer.length() > 0) tokens.addElement(
          new MaskToken(prev, buffer.toString()));
        buffer.setLength(0);
        prev = i + 1;
      }
      else buffer.append(text.charAt(i));
    }
    if (buffer.length() > 0) tokens.addElement(
      new MaskToken(prev, buffer.toString()));
  }

  public boolean hasMoreTokens()
  {
    return pos < tokens.size();
  }

  public MaskToken nextToken()
  {
    return (MaskToken)tokens.elementAt(pos++);
  }

  public void ignoreToken()
  {
    if (pos > 0) pos--;
  }
}

Listing 3.

public class MaskException extends RuntimeException
{
  public MaskException(String description)
  {
    super(description);
  }
}

Listing 4.

public interface MaskElement
{
  public String toString();
  public boolean match(char chr);
}

Listing 5.

public class MaskLiteral implements MaskElement
{
  protected char chr;

  public MaskLiteral(char chr)
  {
    this.chr = chr;
  }

  public String toString()
  {
    return "literal('" + chr + "')";
  }

  public boolean match(char chr)
  {
    return this.chr == chr;
  }
}

Listing 6.

public class MaskSet implements MaskElement
{
  protected boolean negate;
  protected String set;

  public MaskSet(boolean negate, String set)
  {
    this.negate = negate;
    this.set = set;
  }

  public String toString()
  {
    return (negate ? "not(" : "set(") + set +
")";
  }

  public boolean match(char chr)
  {
    boolean member = set.indexOf(chr) > -1;
    if (negate) return !member;
    else return member;
  }
}

Listing 7.

public class MaskExpression implements MaskElement
{
  protected MaskElement element;

  public MaskExpression(MaskElement element)
  {
    this.element = element;
  }

  public String toString()
  {
    return "expression(" + element.toString() + ")";
  }

  public boolean match(char chr)
  {
    return element.match(chr);
  }
}

Listing 8.

public class MaskCondition implements MaskElement
{
  public static final boolean AND = true;
  public static final boolean OR = false;

  protected boolean and;
  protected MaskElement left, right;

  public MaskCondition(boolean and,
    MaskElement left, MaskElement right)
  {
    this.and = and;
    this.left = left;
    this.right = right;
  }

  public String toString()
  {
    return "rule(" + left.toString() +
      (and ? " and " : " or ") +
      right.toString() + ")";
  }

  public boolean match(char chr)
  {
    if (and) return left.match(chr) && right.match(chr);
    else return left.match(chr) || right.match(chr);
  }
}

Listing 9.

public class MaskParser
{
  public MaskElement parseMacro(String text)
  {
    MaskTokenizer tokenizer =
      new MaskTokenizer("&|![]() ", "");
    tokenizer.tokenize(text);
    return parseCondition(tokenizer);
  }

  public MaskElement parseCondition(MaskTokenizer tokenizer)
  {
    MaskElement node = parseExpression(tokenizer);
    if (tokenizer.hasMoreTokens())
    {
      MaskToken next = tokenizer.nextToken();
      if (next.equals('|'))
      {
        return new MaskCondition(MaskCondition.OR,
          node, parseCondition(tokenizer));
      }
      if (next.equals('&'))
      {
        return new MaskCondition(MaskCondition.AND,
          node, parseCondition(tokenizer));
      }
    }
    tokenizer.ignoreToken();
    return node;
  }

  private MaskElement parseExpression(MaskTokenizer tokenizer)
  {
    MaskToken token = tokenizer.nextToken();
    if (token.equals('('))
    {
      MaskElement node = parseCondition(tokenizer);
      expect(tokenizer.nextToken(), ')');
      return new MaskExpression(node);
    }
    tokenizer.ignoreToken();
    return parseSet(tokenizer);
  }

  private MaskElement parseSet(MaskTokenizer tokenizer)
  {
    expect(tokenizer.nextToken(), '[');
    MaskToken token = tokenizer.nextToken();
    boolean negate = token.equals('!');
    if (negate) token = tokenizer.nextToken();
    expect(tokenizer.nextToken(), ']');
    return new MaskSet(negate, expandSet(token.text));
  }

  private String expandSet(String text)
  {
    int i = 0;
    StringBuffer buffer = new StringBuffer();
    while (i < text.length())
    {
      if (i < text.length() - 2 && text.charAt(i + 1) == '-')
      {
        int from = (int)text.charAt(i);
        int to = (int)text.charAt(i + 2);
        if (from > to)
        {
          int temp = from;
          from = to;
          to = temp;
        }
        for (int c = from; c <= to; c++)
        {
          buffer.append((char)c);
        }
        i += 3;
      }
      else
      {
        buffer.append(text.charAt(i));
        i++;
      }
    }
    return buffer.toString();
  }

  public static void expect(MaskToken token, char chr)
  {
    if (!token.equals(chr)) throw new MaskException(
      "Syntax error: '" + chr + "' expected at " +
token.pos);
  }
}

Listing 10.

public class MaskMacros
{
  protected Hashtable table;
  protected MaskParser parser = new MaskParser();

  public MaskMacros()
  {
    table = new Hashtable();
  }

  public void addMacro(char key, String macro)
  {
    MaskElement element = parser.parseMacro(macro);
    table.put(new Character(key), element);
  }

  public void removeMacro(char key)
  {
    table.remove(new Character(key));
  }

  public MaskElement getMacro(char key)
  {
    return (MaskElement)table.get(new Character(key));
  }

  public boolean containsMacro(char key)
  {
    return table.containsKey(new Character(key));
  }

  public String toString()
  {
    StringBuffer buffer = new StringBuffer();
    buffer.append("macros\n{\n");
    Enumeration keys = table.keys();
    Enumeration enum = table.elements();
    Character key;
    MaskElement element;
    while (keys.hasMoreElements())
    {
      key = (Character)keys.nextElement();
      element = (MaskElement)enum.nextElement();
      buffer.append(" " + key.charValue() + "=");
      buffer.append(element.toString() + "\n");
    }
    buffer.append("}\n");
    return buffer.toString();
  }
}

Listing 11.

public class MaskDocument extends PlainDocument
{
  protected char templateChar;
  protected MaskMacros macros;
  protected MaskTokenizer tokenizer;
  protected Vector pattern = new Vector();
  protected MaskParser parser = new MaskParser();

  public MaskDocument(String mask,
    MaskMacros macros, char templateChar)
  {
    this.templateChar = templateChar;
    this.macros = macros;
    parse(mask);
  }

  public void parse(String text)
  {
    MaskTokenizer tokenizer =
      new MaskTokenizer("&|![](){} ", "");
    pattern.removeAllElements();
    tokenizer.tokenize(text);
    while (tokenizer.hasMoreTokens())
    {
      parseElement(tokenizer);
    }
  }

  private void parseElement(MaskTokenizer tokenizer)
  {
    MaskToken next = tokenizer.nextToken();
    if (next.equals('{'))
    {
      pattern.addElement(parser.parseCondition(tokenizer));
      MaskParser.expect(tokenizer.nextToken(), '}');
    }
    else
    {
      String text = next.text;
      for (int i = 0; i < text.length(); i++)
      {
        pattern.addElement(new MaskLiteral(text.charAt(i)));
      }
    }
  }

  public MaskElement getRule(int index)
  {
    return (MaskElement)pattern.elementAt(index);
  }

  public String template()
  {
    int length = pattern.size();
    StringBuffer buffer = new StringBuffer();
    for (int i = 0; i < length; i++)
    {

     buffer.append(template(i));
    }
    return buffer.toString();
  }

  public char template(int pos)
  {
    MaskElement rule = getRule(pos);
    if (rule instanceof MaskLiteral)
    {
      char literal = ((MaskLiteral)rule).chr;
      if (!macros.containsMacro(literal))
      return literal;
    }
    return templateChar;
  }

  public boolean match(int pos, char chr)
  {
   MaskElement element = getRule(pos);
    if (element instanceof MaskLiteral)
    {
      char macro = ((MaskLiteral)element).chr;
      if (macros.containsMacro(macro))
      {
        return macros.getMacro(macro).match(chr);
      }
    }
    return element.match(chr);
  }

  public void insertString(int pos, String text,
    AttributeSet attr) throws BadLocationException
  {
    int len = text.length();
    if (len == 0) return;
    if (len > 1)
    {
     for (int i = pos; i < len; i++)
        insertString(pos, "" + text.charAt(i), attr);
      return;
    }
    else
    {
      if (match(pos, text.charAt(0)))
      {
        super.remove(pos, 1);
        super.insertString(pos, text, attr);
      }
      else
      {
        Toolkit.getDefaultToolkit().beep();
        return;
      }
    }
  }

  public void remove(int pos, int length)
    throws BadLocationException
  {
    if (length > 1)
    {
      for (int i = pos; i < length; i++)
       remove(pos, 1);
      return;
    }
    else
    {
      if (length == 0 && getLength() == 0)
      {
        String template = template();
        super.insertString(pos, template, null);
        return;
      }
      if (pos == getLength()) return;

      String text = "" + template(pos);
      super.remove(pos, 1);
      super.insertString(pos, text, null);
    }
  }
}

Listing 12.

public class JMaskField extends JTextField implements
  DocumentListener, KeyListener, FocusListener
{
  protected MaskMacros macros;
  protected MaskDocument doc;
  protected boolean bspace;
  protected boolean delete;
  protected int pos = -1;

  public JMaskField(String mask)
  {
    this(mask, new MaskMacros(), '_');
  }

  public JMaskField(String mask, MaskMacros macros)
  {
    this(mask, macros, '_');
  }

  public JMaskField(String mask,
    MaskMacros macros, char templateChar)
  {
    setMacros(macros);
    doc = new MaskDocument(mask, macros, templateChar);
    doc.addDocumentListener(this);
    addFocusListener(this);
    addKeyListener(this);
    setDocument(doc);
    setText("");
    setPreferredSize(new Dimension(128, 23));
  }

  public MaskMacros getMacros()
  {
    return macros;
  }

  public void setMacros(MaskMacros macros)
  {
    this.macros = macros;
  }

  private void adjustCaretForward(int pos)
  {
    while (isLiteral(pos)) {pos++;}
    if (pos > doc.getLength()) pos = doc.getLength();
    setCaretPosition(pos);
  }

  private void adjustCaretBackward(int pos)
  {
    while (isLiteral(pos - 1)) {pos--;}
    if (pos <= 0)
    {
      adjustCaretForward(0);
    }
    else setCaretPosition(pos);
  }

 private boolean isLiteral(int pos)
  {
    if (pos < 0 || pos >= doc.pattern.size())
      return false;
    MaskElement rule = doc.getRule(pos);
    if (rule instanceof MaskLiteral)
    {
      char literal = (((MaskLiteral)rule).chr);
      return !doc.macros.containsMacro(literal);
    }
    return false;
  }

  public void focusLost(FocusEvent event)
  {
    pos = getCaretPosition();
  }

  public void focusGained(FocusEvent event)
  {
    if (pos < 0) adjustCaretForward(0);
    else setCaretPosition(pos);
  }

  public void keyTyped(KeyEvent event) {}
  public void keyReleased(KeyEvent event) {}
  public void keyPressed(KeyEvent event)
  {
    bspace = event.getKeyCode() == KeyEvent.VK_BACK_SPACE;
    delete = event.getKeyCode() == KeyEvent.VK_DELETE;
  }

  public void changedUpdate(DocumentEvent event) {}
  public void removeUpdate(DocumentEvent event) {}
  public void insertUpdate(DocumentEvent event)
  {
    int pos = event.getOffset();
    int len = event.getLength();
    if (bspace) adjustCaretBackward(pos);
    else if (delete) setCaretPosition(pos);
    else adjustCaretForward(pos + 1);
  }
}


  
      
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.