This fixes some Swing text formatters.

This makes the last tests in the currently available Intel tests pass, except:

- Bugs in Sun's impl that I don't want to mimic.
- Bugs outside Swing, especially java.text.*.

Most Mauve failures for this package also fall into these categories.

2006-08-29  Roman Kennke  <[EMAIL PROTECTED]>

        * javax/swing/text/InternationalFormatter.java
        (stringToValue): Fixed bounds check.
        * javax/swing/text/MaskFormatter.java
        (MaskFormatter): Don't explicitly set allosInvalid property.
        (convertStringToValue): New helper method.
        (convertValueToString): New helper method.
        (convertValue): Removed. Replaced by the 2 convert* methods
        above.
        (getPadCharAt): Removed.
        (isCharValid): Removed.
        (pad): Removed.
        (stringToValue): Fixed stringToValue conversion.
        (stripLiterals): Removed.
        (valueToString): Fixed valueToString conversion.
        * javax/swing/text/DefaultFormatter.java
        (DefaultFormatter): Default to commitsOnValidEdit = false.

/Roman
Index: javax/swing/text/DefaultFormatter.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/DefaultFormatter.java,v
retrieving revision 1.8
diff -u -1 -2 -r1.8 DefaultFormatter.java
--- javax/swing/text/DefaultFormatter.java	28 Feb 2006 20:26:34 -0000	1.8
+++ javax/swing/text/DefaultFormatter.java	29 Aug 2006 19:49:55 -0000
@@ -207,25 +207,25 @@
   boolean allowsInvalid;
 
   /**
    * The class that is used for values.
    */
   Class valueClass;
 
   /**
    * Creates a new instance of <code>DefaultFormatter</code>.
    */
   public DefaultFormatter()
   {
-    commitsOnValidEdit = true;
+    commitsOnValidEdit = false;
     overwriteMode = true;
     allowsInvalid = true;
   }
 
   /**
    * Installs the formatter on the specified [EMAIL PROTECTED] JFormattedTextField}.
    *
    * This method does the following things:
    * <ul>
    * <li>Display the value of #valueToString in the
    *  <code>JFormattedTextField</code></li>
    * <li>Install the Actions from #getActions on the <code>JTextField</code>
Index: javax/swing/text/InternationalFormatter.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/InternationalFormatter.java,v
retrieving revision 1.8
diff -u -1 -2 -r1.8 InternationalFormatter.java
--- javax/swing/text/InternationalFormatter.java	28 Mar 2006 10:58:36 -0000	1.8
+++ javax/swing/text/InternationalFormatter.java	29 Aug 2006 19:49:55 -0000
@@ -276,25 +276,25 @@
         Object o = format.parseObject(string);
 
         // If a value class has been set, call super in order to get
         // the class right. That is what the JDK API docs suggest, so we do
         // it that way.
         if (valueClass != null)
           o = super.stringToValue(o.toString());
 
         // Check for minimum and maximum bounds
         if (minimum != null && minimum.compareTo(o) > 0)
           throw new ParseException("The value may not be less than the"
                                     + " specified minimum", 0);
-        if (maximum != null && minimum.compareTo(o) < 0)
+        if (maximum != null && maximum.compareTo(o) < 0)
           throw new ParseException("The value may not be greater than the"
                                     + " specified maximum", 0);
         return o;
       }
     else
       return super.stringToValue(string);
   }
 
   /**
    * Returns the [EMAIL PROTECTED] Format.Field} constants that are associated with
    * the specified position in the text.
    *
Index: javax/swing/text/MaskFormatter.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/MaskFormatter.java,v
retrieving revision 1.3
diff -u -1 -2 -r1.3 MaskFormatter.java
--- javax/swing/text/MaskFormatter.java	22 Nov 2005 20:10:23 -0000	1.3
+++ javax/swing/text/MaskFormatter.java	29 Aug 2006 19:49:55 -0000
@@ -101,27 +101,25 @@
     setAllowsInvalid(false);
   }
   
   /**
    * Creates a MaskFormatter with the specified mask.
    * @specnote doesn't actually throw a ParseException although it 
    * is declared to do so
    * @param mask
    * @throws java.text.ParseException
    */
   public MaskFormatter (String mask) throws java.text.ParseException
   {
-    // Override super's default behaviour, in MaskFormatter the default
-    // is not to allow invalid values
-    setAllowsInvalid(false);
+    this();
     setMask (mask);
   }
   
   /**
    * Returns the mask used in this MaskFormatter.
    * @return the mask used in this MaskFormatter.
    */
   public String getMask()
   {
     return mask;
   }
   
@@ -298,286 +296,261 @@
    * Parses the text using the mask, valid characters, and invalid characters
    * to determine the appropriate Object to return.  This strips the literal
    * characters if necessary and invokes super.stringToValue.  If the paramter
    * is invalid for the current mask and valid/invalid character sets this 
    * method will throw a ParseException.
    * 
    * @param value the String to parse
    * @throws ParseException if value doesn't match the mask and valid/invalid
    * character sets
    */
   public Object stringToValue (String value) throws ParseException
   {
-    int vLength = value.length();
-    
-    // For value to be a valid it must be the same length as the mask
-    // note this doesn't take into account symbols that occupy more than 
-    // one character, this is something we may possibly need to fix.
-    if (maskLength != vLength)
-      throw new ParseException ("stringToValue passed invalid value", vLength);
-    
-    // Check if the value is valid according to the mask and valid/invalid 
-    // sets.
-    try
-    {
-      convertValue(value, false);      
-    }
-    catch (ParseException pe)
-    {
-      throw new ParseException("stringToValue passed invalid value",
-                                 pe.getErrorOffset());
-    }
-    
-    if (!getValueContainsLiteralCharacters())
-      value = stripLiterals(value);
-    return super.stringToValue(value);
+    return super.stringToValue(convertStringToValue(value));
   }
   
-  /**
-   * Strips the literal characters from the given String.
-   * @param value the String to strip
-   * @return the stripped String
-   */
-  String stripLiterals(String value)
+  private String convertStringToValue(String value)
+    throws ParseException
   {
     StringBuffer result = new StringBuffer();
-    for (int i = 0; i < value.length(); i++)
+    char valueChar;
+    boolean isPlaceHolder;
+
+    int length = mask.length();
+    for (int i = 0, j = 0; j < length; j++)
       {
-        // Only append the characters that don't correspond to literal
-        // characters in the mask.
-        switch (mask.charAt(i))
+        char maskChar = mask.charAt(j);
+
+        if (i < value.length())
+          {
+            isPlaceHolder = false;
+            valueChar = value.charAt(i);
+            if (maskChar != ESCAPE_CHAR && maskChar != valueChar)
+              {
+                if (invalidChars != null
+                    && invalidChars.indexOf(valueChar) != -1)
+                  throw new ParseException("Invalid character: " + valueChar, i);
+                if (validChars != null
+                    && validChars.indexOf(valueChar) == -1)
+                  throw new ParseException("Invalid character: " + valueChar, i);
+              }
+          }
+        else if (placeHolder != null && i < placeHolder.length())
+          {
+            isPlaceHolder = true;
+            valueChar = placeHolder.charAt(i);
+          }
+        else
+          {
+            isPlaceHolder = true;
+            valueChar = placeHolderChar;
+          }
+
+        // This switch block on the mask character checks that the character 
+        // within <code>value</code> at that point is valid according to the
+        // mask and also converts to upper/lowercase as needed.
+        switch (maskChar)
           {
           case NUM_CHAR:
+            if (! Character.isDigit(valueChar))
+              throw new ParseException("Number expected: " + valueChar, i);
+            result.append(valueChar);
+            i++;
+            break;
           case UPPERCASE_CHAR:
+            if (! Character.isLetter(valueChar))
+              throw new ParseException("Letter expected", i);
+            result.append(Character.toUpperCase(valueChar));
+            i++;
+            break;
           case LOWERCASE_CHAR:
+            if (! Character.isLetter(valueChar))
+              throw new ParseException("Letter expected", i);
+            result.append(Character.toLowerCase(valueChar));
+            i++;
+            break;
           case ALPHANUM_CHAR:
+            if (! Character.isLetterOrDigit(valueChar))
+              throw new ParseException("Letter or number expected", i);
+            result.append(valueChar);
+            i++;
+            break;
           case LETTER_CHAR:
+            if (! Character.isLetter(valueChar))
+              throw new ParseException("Letter expected", i);
+            result.append(valueChar);
+            i++;
+            break;
           case HEX_CHAR:
+            if (hexString.indexOf(valueChar) == -1 && ! isPlaceHolder)
+              throw new ParseException("Hexadecimal character expected", i);
+            result.append(valueChar);
+            i++;
+            break;
           case ANYTHING_CHAR:
-            result.append(value.charAt(i));
+            result.append(valueChar);
+            i++;
+            break;
+          case ESCAPE_CHAR:
+            // Escape character, check the next character to make sure that 
+            // the literals match
+            j++;
+            if (j < length)
+              {
+                maskChar = mask.charAt(j);
+                if (! isPlaceHolder && getValueContainsLiteralCharacters()
+                    && valueChar != maskChar)
+                  throw new ParseException ("Invalid character: "+ valueChar, i);
+                if (getValueContainsLiteralCharacters())
+                  {
+                    result.append(maskChar);
+                  }
+                i++;
+              }
+            else if (! isPlaceHolder)
+              throw new ParseException("Bad match at trailing escape: ", i);
             break;
           default:
+            if (! isPlaceHolder && getValueContainsLiteralCharacters()
+                && valueChar != maskChar)
+              throw new ParseException ("Invalid character: "+ valueChar, i);
+            if (getValueContainsLiteralCharacters())
+              {
+                result.append(maskChar);
+              }
+            i++;
           }
       }
     return result.toString();
   }
-  
+
   /**
    * Returns a String representation of the Object value based on the mask.
    * 
    * @param value the value to convert
    * @throws ParseException if value is invalid for this mask and valid/invalid
    * character sets
    */
-  public String valueToString (Object value) throws ParseException
+  public String valueToString(Object value) throws ParseException
   {
-    String result = super.valueToString(value);
-    int rLength = result.length();
-    
-    // If value is longer than the mask, truncate it.  Note we may need to 
-    // account for symbols that are more than one character long.
-    if (rLength > maskLength)
-      result = result.substring(0, maskLength);
-    
-    // Verify the validity and convert to upper/lowercase as needed.
-    result = convertValue(result, true);        
-    if (rLength < maskLength)
-      return pad(result, rLength);    
-    return result;
+    String string = value != null ? value.toString() : "";
+    return convertValueToString(string);
   }
   
   /**
    * This method takes in a String and runs it through the mask to make
    * sure that it is valid.  If <code>convert</code> is true, it also
    * converts letters to upper/lowercase as required by the mask.
    * @param value the String to convert
-   * @param convert true if we should convert letters to upper/lowercase
    * @return the converted String
    * @throws ParseException if the given String isn't valid for the mask
    */
-  String convertValue(String value, boolean convert) throws ParseException
+  private String convertValueToString(String value)
+    throws ParseException
   {
-    StringBuffer result = new StringBuffer(value);
-    char markChar;
-    char resultChar;
-    boolean literal;
-
-    // this boolean is specifically to avoid calling the isCharValid method
-    // when neither invalidChars or validChars has been set
-    boolean checkCharSets = (invalidChars != null || validChars != null);
+    StringBuffer result = new StringBuffer();
+    char valueChar;
+    boolean isPlaceHolder;
 
-    for (int i = 0, j = 0; i < value.length(); i++, j++)
+    int length = mask.length();
+    for (int i = 0, j = 0; j < length; j++)
       {
-        literal = false;
-        resultChar = result.charAt(i);
+        char maskChar = mask.charAt(j);
+        if (i < value.length())
+          {
+            isPlaceHolder = false;
+            valueChar = value.charAt(i);
+            if (maskChar != ESCAPE_CHAR && valueChar != maskChar)
+              {
+                if (invalidChars != null
+                    && invalidChars.indexOf(valueChar) != -1)
+                  throw new ParseException("Invalid character: " + valueChar,
+                                           i);
+                if (validChars != null && validChars.indexOf(valueChar) == -1)
+                  throw new ParseException("Invalid character: " + valueChar +" maskChar: " + maskChar,
+                                           i);
+              }
+          }
+        else if (placeHolder != null && i < placeHolder.length())
+          {
+            isPlaceHolder = true;
+            valueChar = placeHolder.charAt(i);
+          }
+        else
+          {
+            isPlaceHolder = true;
+            valueChar = placeHolderChar;
+          }
+
         // This switch block on the mask character checks that the character 
         // within <code>value</code> at that point is valid according to the
         // mask and also converts to upper/lowercase as needed.
-        switch (mask.charAt(j))
+        switch (maskChar)
           {
           case NUM_CHAR:
-            if (!Character.isDigit(resultChar))
-              throw new ParseException("Number expected", i);
+            if ( ! isPlaceHolder && ! Character.isDigit(valueChar))
+              throw new ParseException("Number expected: " + valueChar, i);
+            result.append(valueChar);
+            i++;
             break;
           case UPPERCASE_CHAR:
-            if (!Character.isLetter(resultChar))
+            if (! Character.isLetter(valueChar))
               throw new ParseException("Letter expected", i);
-            if (convert)
-              result.setCharAt(i, Character.toUpperCase(resultChar));
+            result.append(Character.toUpperCase(valueChar));
+            i++;
             break;
           case LOWERCASE_CHAR:
-            if (!Character.isLetter(resultChar))
+            if (! Character.isLetter(valueChar))
               throw new ParseException("Letter expected", i);
-            if (convert)
-              result.setCharAt(i, Character.toLowerCase(resultChar));
+            result.append(Character.toLowerCase(valueChar));
+            i++;
             break;
           case ALPHANUM_CHAR:
-            if (!Character.isLetterOrDigit(resultChar))
+            if (! Character.isLetterOrDigit(valueChar))
               throw new ParseException("Letter or number expected", i);
+            result.append(valueChar);
+            i++;
             break;
           case LETTER_CHAR:
-            if (!Character.isLetter(resultChar))
+            if (! Character.isLetter(valueChar))
               throw new ParseException("Letter expected", i);
+            result.append(valueChar);
+            i++;
             break;
           case HEX_CHAR:
-            if (hexString.indexOf(resultChar) == -1)
+            if (hexString.indexOf(valueChar) == -1 && ! isPlaceHolder)
               throw new ParseException("Hexadecimal character expected", i);
+            result.append(valueChar);
+            i++;
             break;
           case ANYTHING_CHAR:
+            result.append(valueChar);
+            i++;
             break;
           case ESCAPE_CHAR:
             // Escape character, check the next character to make sure that 
             // the literals match
             j++;
-            literal = true;
-            if (resultChar != mask.charAt(j))
-              throw new ParseException ("Invalid character: "+resultChar, i);
+            if (j < length)
+              {
+                maskChar = mask.charAt(j);
+                if (! isPlaceHolder && getValueContainsLiteralCharacters()
+                    && valueChar != maskChar)
+                  throw new ParseException ("Invalid character: "+ valueChar, i);
+                if (getValueContainsLiteralCharacters())
+                  i++;
+                result.append(maskChar);
+              }
             break;
           default:
-            literal = true;
-            if (!getValueContainsLiteralCharacters() && convert)
-              throw new ParseException ("Invalid character: "+resultChar, i);
-            else if (resultChar != mask.charAt(j))
-              throw new ParseException ("Invalid character: "+resultChar, i);
+            if (! isPlaceHolder && getValueContainsLiteralCharacters()
+                && valueChar != maskChar)
+              throw new ParseException ("Invalid character: "+ valueChar, i);
+            if (getValueContainsLiteralCharacters())
+              i++;
+            result.append(maskChar);
           }
-        // If necessary, check if the character is valid.
-        if (!literal && checkCharSets && !isCharValid(resultChar))
-          throw new ParseException("invalid character: "+resultChar, i);
-
-      }
-    return result.toString();
-  }
-  
-  /**
-   * Convenience method used by many other methods to check if a character is 
-   * valid according to the mask, the validChars, and the invalidChars.  To
-   * be valid a character must:
-   * 1. be allowed by the mask
-   * 2. be present in any non-null validChars String
-   * 3. not be present in any non-null invalidChars String
-   * @param testChar the character to test
-   * @return true if the character is valid
-   */
-  boolean isCharValid(char testChar)
-  {
-    char lower = Character.toLowerCase(testChar);
-    char upper = Character.toUpperCase(testChar);
-    // If validChars isn't null, the character must appear in it.
-    if (validChars != null)
-      if (validChars.indexOf(lower) == -1 && validChars.indexOf(upper) == -1)
-        return false;
-    // If invalidChars isn't null, the character must not appear in it.
-    if (invalidChars != null)
-      if (invalidChars.indexOf(lower) != -1
-          || invalidChars.indexOf(upper) != -1)
-        return false;
-    return true;
-  }
-  
-  /**
-   * Pads the value with literals, the placeholder String and/or placeholder
-   * character as appropriate.
-   * @param value the value to pad
-   * @param currLength the current length of the value
-   * @return the padded String
-   */
-  String pad (String value, int currLength)
-  {
-    StringBuffer result = new StringBuffer(value);
-    int index = currLength;
-    while (result.length() < maskLength)
-      {
-        // The character used to pad may be a literal, a character from the 
-        // place holder string, or the place holder character.  getPadCharAt
-        // will find the proper one for us.
-        result.append (getPadCharAt(index));
-        index++;
       }
     return result.toString();
   }
 
-  /**
-   * Returns the character with which to pad the value at the given index
-   * position.  If the mask has a literal at this position, this is returned
-   * otherwise if the place holder string is initialized and is longer than 
-   * <code>i</code> characters then the character at position <code>i</code>
-   * from this String is returned.  Else, the place holder character is 
-   * returned.
-   * @param i the index at which we want to pad the value
-   * @return the character with which we should pad the value
-   */
-  char getPadCharAt(int i)
-  {
-    boolean escaped = false;
-    int target = i;
-    char maskChar;
-    int holderLength = placeHolder == null ? -1 : placeHolder.length();
-    // We must iterate through the mask from the beginning, because the given
-    // index doesn't account for escaped characters.  For example, with the 
-    // mask "1A'A''A1" index 2 refers to the literalized A, not to the 
-    // single quotation.
-    for (int n = 0; n < mask.length(); n++)
-      {
-        maskChar = mask.charAt(n);
-        if (maskChar == ESCAPE_CHAR && !escaped)
-          {
-            target++;
-            escaped = true;
-          }
-        else if (escaped == true)
-          {
-            // Check if target == n which means we've come to the character
-            // we want to return and since it is a literal (because escaped 
-            // is true), we return it.
-            if (target == n)
-              return maskChar;
-            escaped = false;
-          }
-        if (target == n)
-          {
-            // We've come to the character we want to return.  It wasn't
-            // escaped so if it isn't a literal we should return either
-            // the character from place holder string or the place holder
-            // character, depending on whether or not the place holder
-            // string is long enough.
-            switch (maskChar)
-            {
-            case NUM_CHAR:
-            case UPPERCASE_CHAR:
-            case LOWERCASE_CHAR:
-            case ALPHANUM_CHAR:
-            case LETTER_CHAR:
-            case HEX_CHAR:
-            case ANYTHING_CHAR:
-              if (holderLength > i)
-                return placeHolder.charAt(i);
-              else
-                return placeHolderChar;
-            default:
-              return maskChar;
-            }
-          }
-      }
-    // This shouldn't happen
-    throw new AssertionError("MaskFormatter.getMaskCharAt failed");
-  }
 }

Reply via email to