Package DisplayProject

Source Code of DisplayProject.FormattedNumericDocumentFilter

package DisplayProject;

import java.text.AttributedCharacterIterator;
import java.text.ParseException;
import java.text.AttributedCharacterIterator.Attribute;
import java.text.NumberFormat.Field;
import java.util.Map;

import javax.swing.JFormattedTextField;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;

import Framework.DoubleData;
import Framework.NullAwareNumberFormat;
import Framework.NumericData;

/**
* For numeric types which are formatted, we need to provide a document filter to get the same behaviour as Forte.
* For example, if the field has a value of $1,234.56 and we have the cursor after the decimal place and before the
* 5 and the user hits backspace, we want to remove the 4, not the decimal point. This class handles this custom
* document manipulation.
* <p>
* It is assumed that this class will be used in conjunction with a FormattedNumericNavigationFilter
* @author Tim
*/
public class FormattedNumericDocumentFilter extends DocumentFilter {
  /**
   * A flag which prevents this class from going into an infinite loop.
   */
  private boolean preventReentrant = false;
 
  /**
   * The decimal format underlying this formatted field
   */
  protected NullAwareNumberFormat format;

  /**
   * The JFormattedTextField to which we are bound
   */
  protected JFormattedTextField field;
 
  /**
   * On formatted data fields, the user should be able to enter say "-" followed by "6" if the value is
   * zero to get the value -6. However, if we're zero we don't have a set value to store the sign, so we
   * have to explicitly remember it.
   */
  private enum PendingSign {NEGATIVE, POSITIVE, UNSET};
  private PendingSign pendingSign = PendingSign.UNSET;
 
  /**
   * Create a new document filter with the passed format and the passed field. Note that this method
   * does not attach the document filter onto the field.
   * @param format
   * @param field
   */
  public FormattedNumericDocumentFilter(NullAwareNumberFormat format, JFormattedTextField field) {
    this.format = format;
    this.field = field;
  }

  /**
   * @deprecated -- this constructor is provided to allow the clone helper to make a copy of this class
   */
  public FormattedNumericDocumentFilter() {
  }
 
  /**
   * Determine the offset from the start of the string where two strings co-incide. That is, it will return the largest
   * index i such that pOriginalText.substring(i) = pNewText(j) where j = pNewText.length() - (pOriginalText.length() - i)
   * <p>
   * For example, consider $12,345,678.90 and $5,556.90. In this case, the return value would be where the decimal point is,
   * ie 11
   * <p>
   * If the 2 strings are never co-incident, pOriginalText.length() is returned.
   * @param pOriginalText
   * @param pNewText
   * @return
   */
  private int getReplaceToOffset(String pOriginalText, String pNewText) {
    int originalLength = pOriginalText.length();
    int newLength = pNewText.length();
   
    for (int i = 1; i <= originalLength; i++) {
      if (newLength - i < 0) {
        // The old string is longer, but apart from that they are right-equal.
        return originalLength - newLength;
      }
      else if (pOriginalText.charAt(originalLength - i) != pNewText.charAt(newLength - i)) {
        return originalLength - i + 1;
      }
    }
    return 0;
  }

  /**
   * Return the maximum number of decimal places that this format, for this value, can have. There
   * is a difference to standard java -- a normal decimal format can have only a single number of
   * decimal places, but in Forte, because there are multiple distinct masks, positive, negative,
   * zero and null, we can have different numbers of decimal places based on the passed value
   * @param value
   * @param isNull
   * @return
   */
  protected int getMaximumDecimalPlaces(double value, boolean isNull) {
    int maximumDecimalPlaces;
    // For null aware formatters, the number of decimal places can vary based on the mask...
    if (isNull) {
      maximumDecimalPlaces = ((NullAwareNumberFormat)this.format).getMaximumFractionDigits((Double)null);
    }
    else {
      maximumDecimalPlaces = ((NullAwareNumberFormat)this.format).getMaximumFractionDigits(value);
    }
    return maximumDecimalPlaces;
  }
 
    /**
     * Invoked prior to insertion of text into the
     * specified Document. Subclasses that want to conditionally allow
     * insertion should override this and only call supers implementation as
     * necessary, or call directly into the FilterBypass.
     *
     * @param fb FilterBypass that can be used to mutate Document
     * @param offset  the offset into the document to insert the content >= 0.
     *    All positions that track change at or after the given location
     *    will move. 
     * @param string the string to insert
     * @param attr      the attributes to associate with the inserted
     *   content.  This may be null if there are no attributes.
     * @exception BadLocationException  the given insert position is not a
     *   valid position within the document
     */
  @Override
  public void insertString(javax.swing.text.DocumentFilter.FilterBypass fb, int offset, String string,
      AttributeSet attr) throws BadLocationException {
    if (preventReentrant) {
      super.insertString(fb, offset, string, attr);
    }
    else {
      PendingSign pendingSignToUse = PendingSign.UNSET;
     
      boolean isSignChar = (string.length() == 1 && (string.charAt(0) == '+' || string.charAt(0) =='-'));
     
      // Strip out any non-digits in the input string
      int insertStringLength = string.length();
      // TF:9/4/08:Changed to StringBuilder for efficiency.
      StringBuilder realValueToInsert = new StringBuilder(insertStringLength);
      for (int i = 0; i < insertStringLength; i++) {
        if (Character.isDigit(string.charAt(i))) {
          realValueToInsert.append(string.charAt(i));
        }
      }
     
      if (realValueToInsert.length() == 0 && !isSignChar) {
        return;
      }
     
        String text = fb.getDocument().getText(0, fb.getDocument().getLength());
       
        // get the current value as a number
        int maximumDecimalPlaces = 0;
        boolean isNegative = false;
        Object o = null;
      try {
        o = this.field.getFormatter().stringToValue(text);
        double value = 0.0;
        if (o instanceof Number) {
          value = ((Number)o).doubleValue();
        }
        else if (o instanceof NumericData) {
          value = ((NumericData)o).doubleValue();
        }
        isNegative = (value < 0.0);
        maximumDecimalPlaces = this.getMaximumDecimalPlaces(value, o == null);
      }
      catch (ParseException e) {
        e.printStackTrace();
      }
      // TF:9/4/08: Changed this to a StringBuilder for performance reasons
        StringBuilder stringVal = new StringBuilder(fb.getDocument().getLength()+string.length());
        int decimalPlaceLocation = -1;
        int count = 0;
        int insertLocationInNewString = -1;
        //int originalDPLoc = -1;
          AttributedCharacterIterator iterator = format.formatToCharacterIterator(o);
          if (iterator != null) {
            for (char c = iterator.first(); c != AttributedCharacterIterator.DONE; c = iterator.next()) {
              Map<Attribute, Object> map = iterator.getAttributes();
              if (count == offset) {
                insertLocationInNewString = stringVal.length();
              }
              if (map.containsKey(Field.GROUPING_SEPARATOR)) {
                // Do nothing. Needed as the Grouping separator is also an integer!
              }
              else if (map.containsKey(Field.DECIMAL_SEPARATOR)) {
              decimalPlaceLocation = stringVal.length();
              //originalDPLoc = count;
              }
              else if (map.containsKey(Field.INTEGER) || map.containsKey(Field.FRACTION)) {
              stringVal.append(c);
            }
              count++;
            }
          }
         
          // Special case: if they use a format like "#,##0.##" we need to end in a ".". However,
          // the java masks don't do this, so we explicitly force it. Unfortunately, the iterator
          // cannot be altered, so we need to cater for it here...
          boolean endsWithDecimalPoint = false;
          // TF:29 sept. 2008:Fixed this to use an international character separator
          if (text.endsWith(DoubleData.getDecimalSeparatorStr()) && decimalPlaceLocation < 0) {
            decimalPlaceLocation = stringVal.length();
            endsWithDecimalPoint = true;
            //originalDPLoc = text.length();
          }
         
          // CraigM:23/07/2008 - If they have a blank as zero template, ignore the 0 value
          if (this.format.isBlankForZeroTemplate() && stringVal.length() == 1 && stringVal.charAt(0) == '0') {
            stringVal.delete(0, 1);
          }

          // TF:10/04/2008: We need to see if there are any trailing 0's to add back onto the character
          // string, in case of masks like #,##0.#### and attempting to enter say 0.001
//          if (originalDPLoc < 0 && text.lastIndexOf('.') > 0) {
//            originalDPLoc = text.lastIndexOf('.');
//          }
//          System.out.println("originalDPloc = " + originalDPLoc);
//          System.out.println("text.length = " + text.length());
//          System.out.println("stringval.length = " + stringVal.length());
//          System.out.println("decimalplaceLocation = " + decimalPlaceLocation);
//          System.out.println("maximumDecimalPlaces = " + maximumDecimalPlaces);
//          if (originalDPLoc > 0) {
//            boolean skipLoop = false;
//            if (decimalPlaceLocation < 0) {
//              if (text.lastIndexOf('0') > originalDPLoc) {
//                // There's no decimal place, but it looks like we need it.
//                decimalPlaceLocation = stringVal.length();
//                endsWithDecimalPoint = false;
//              }
//              else {
//                skipLoop = true;
//              }
//            }
//            if (!skipLoop) {
//              for (int i = text.length() - 1; i > originalDPLoc && stringVal.length() - decimalPlaceLocation < maximumDecimalPlaces; i--) {
//System.out.println("text[" + i + "] = " + text.charAt(i));
//                if (text.charAt(i) == '0') {
//                  stringVal.append('0');
//                }
//                else if (Character.isDigit(text.charAt(i))) {
//                  break;
//                }
//              }
//            }
//          }
//          System.out.println("StringVal after loop: " + stringVal);
          // If we haven't encountered the place to insert this string yet, we need to do so at the end of the string
          boolean insertingAtEndOfString = false;
          if (insertLocationInNewString == -1) {
            insertLocationInNewString = stringVal.length();
            insertingAtEndOfString = true;
          }
          String newValue = "";
          stringVal.insert(insertLocationInNewString, realValueToInsert);
          if (decimalPlaceLocation >= 0) {
            int len = realValueToInsert.length();
            if (insertLocationInNewString > decimalPlaceLocation && decimalPlaceLocation >= 0 && len > 0) {
             
              // Inserting the text after the decimal point
              // TF:7/4/08:Corrected this logic. It used to work in all cases except where multiple
              // characters were being inserted after the decimal point.
              int placesAfterDecimalPoint = stringVal.length() - decimalPlaceLocation;
              int placesToMove = placesAfterDecimalPoint - maximumDecimalPlaces;
              if (placesToMove < 0) {
                placesToMove = 0;
              }
              decimalPlaceLocation += placesToMove;
            }
            else if (endsWithDecimalPoint && insertingAtEndOfString) {
              // We're inserting at the decimal point location. This is a special case -- the
              // decimal place stays still unless there's not enough characters after the decimal point
              int minLocation = stringVal.length() - maximumDecimalPlaces;
              if (decimalPlaceLocation < minLocation) {
                decimalPlaceLocation = minLocation;
              }
            }
            else {
              // Got to move the decimal point by the length of the string to insert
              decimalPlaceLocation += len;
            }
         
            // TF:29 sept. 2008:We must leave this as a decimal point, as this is going to be formatted
            // as a decimal, not displayed as a decimal
            stringVal.insert(decimalPlaceLocation, '.');
            Double d = Double.valueOf(stringVal.toString());
            if (isSignChar) {
              if ((d < 0 && string.charAt(0) =='+') || (d > 0 && string.charAt(0) == '-')) {
                d = -d;
              }
              else if (d == 0) {
                // Set the pending sign marker
                pendingSignToUse = string.charAt(0) == '+' ? PendingSign.POSITIVE : PendingSign.NEGATIVE;
              }
            }
            else if (isNegative && d > 0) {
            d = -d;
          }
            if (!isSignChar && this.pendingSign != PendingSign.UNSET) {
              if ((d < 0 && this.pendingSign == PendingSign.POSITIVE) || (d > 0 && this.pendingSign == PendingSign.NEGATIVE)) {
                d = -d;
              }
            }
            try {
          int newMaximumDecimalPlaces = this.getMaximumDecimalPlaces(d, false);
          // If the number of decimal places has changes, we're changing between masks, eg going
          // from a zero mask to a positive mask. This can only happen if we're replacing all the
          // characters, so we want to shift what we're inserting so the digit(s) we've entered
          // are the least significant in the decimal places.
          if (newMaximumDecimalPlaces != maximumDecimalPlaces && !isSignChar) {
            while (newMaximumDecimalPlaces > maximumDecimalPlaces) {
              d /= 10;
              newMaximumDecimalPlaces--;
            }
            while (newMaximumDecimalPlaces < maximumDecimalPlaces) {
              d *= 10;
              newMaximumDecimalPlaces++;
            }
            newValue = this.field.getFormatter().valueToString(d);
          }
          else {
            // TF:8/4/08:Need to separate this out to allow trailing zeros
            newValue = this.field.getFormatter().valueToString(d);
//System.out.println("newValue = " + newValue);
//System.out.println("StringValue = " + stringVal);
//            int dpIndex = stringVal.toString().lastIndexOf('.');
//            int length = stringVal.length();
//            int currentDecimalPlaces = length - dpIndex + 1;
//System.out.println("dpIndex = " + dpIndex);
//System.out.println("length = " + length);
//System.out.println("currentDcimalPlaces = " + currentDecimalPlaces);
//            for (int i = 0; stringVal.charAt(length - i - 1) == '0' && length - i > dpIndex && currentDecimalPlaces < maximumDecimalPlaces; i++) {
//              newValue = newValue.concat("0");
//              currentDecimalPlaces++;
//System.out.println("newValue = " + newValue + ", currentDecimalPlaces = " + currentDecimalPlaces);
//            }
          }
        }
        catch (ParseException e) {
          // This should never happen, just ignore it.
          e.printStackTrace();
        }
           
          }
          else {
            Long l = Long.valueOf(stringVal.toString());
            if (isSignChar) {
              if ((l < 0 && string.charAt(0) =='+') || (l > 0 && string.charAt(0) == '-')) {
                l = -l;
              }
              else if (l == 0) {
                // Set the pending sign marker
                pendingSignToUse = string.charAt(0) == '+' ? PendingSign.POSITIVE : PendingSign.NEGATIVE;
              }
            }
            else if (isNegative && l > 0) {
            l = -l;
          }
            if (!isSignChar && this.pendingSign != PendingSign.UNSET) {
              if ((l < 0 && this.pendingSign == PendingSign.POSITIVE) || (l > 0 && this.pendingSign == PendingSign.NEGATIVE)) {
                l = -l;
              }
            }
            try {
          int newMaximumDecimalPlaces = this.getMaximumDecimalPlaces(l, false);
          // If the number of decimal places has changes, we're changing between masks, eg going
          // from a zero mask to a positive mask. This can only happen if we're replacing all the
          // characters, so we want to shift what we're inserting so the digit(s) we've entered
          // are the least significant in the decimal places.
          if (newMaximumDecimalPlaces != maximumDecimalPlaces && !isSignChar) {
            double d = (double)l;
            while (newMaximumDecimalPlaces > maximumDecimalPlaces) {
              d /= 10;
              newMaximumDecimalPlaces--;
            }
            while (newMaximumDecimalPlaces < maximumDecimalPlaces) {
              d *= 10;
              newMaximumDecimalPlaces++;
            }
            newValue = this.field.getFormatter().valueToString(d);
          }
          else {
            newValue = this.field.getFormatter().valueToString(l);
          }
        }
        catch (ParseException e) {
          // This should never happen, just ignore it.
          e.printStackTrace();
        }
          }
         
          // Now StringVal should contain the new number to format.
          preventReentrant = true;
          int replaceToOffset = getReplaceToOffset(text, newValue);
          int distanceFromOldEnd = text.length() - replaceToOffset;
          int replaceLength = newValue.length() - distanceFromOldEnd;
          String newString = newValue.substring(0, replaceLength);
          super.replace(fb, 0, replaceToOffset, newString, attr);
          preventReentrant = false;
          this.pendingSign = pendingSignToUse;
    }
  }

    /**
     * Invoked prior to removal of the specified region in the
     * specified Document. Subclasses that want to conditionally allow
     * removal should override this and only call supers implementation as
     * necessary, or call directly into the <code>FilterBypass</code> as
     * necessary.
     *
     * @param fb FilterBypass that can be used to mutate Document
     * @param offset the offset from the beginning >= 0
     * @param length the number of characters to remove >= 0
     * @exception BadLocationException  some portion of the removal range
     *   was not a valid part of the document.  The location in the exception
     *   is the first bad position encountered.
     */
  @Override
  public void remove(javax.swing.text.DocumentFilter.FilterBypass fb, int offset, int length)
      throws BadLocationException {
    if (preventReentrant) {
      super.remove(fb, offset, length);
    }
    else {
      this.pendingSign = PendingSign.UNSET;
      // System.out.println("remove(" + offset + ", " + length + ")");
         
        if (length > 0) {
          String text = fb.getDocument().getText(0, fb.getDocument().getLength());
          // System.out.println("Initial text = " + text);
//          String charsToRemove;
          // If the user is trying to backspace over a special character, like a comma, we don't
          // allow it, but instead move to the previous number. This only applies if the dot and mark
          // of the filter are the same (ie, they don't have a highlighted selection)
          JFormattedTextField comp = this.field;
          if (length == 1 && comp.getCaret().getDot() == comp.getCaret().getMark()) {
            while (offset >= 0 && !Character.isDigit(text.charAt(offset))) {
              offset--;
            }
            if (offset < 0) {
              // Nothing to do
              return;
            }
//            else {
//              charsToRemove = text.substring(offset, offset + length);
//            }
          }
//          else {
//             charsToRemove = text.substring(offset, offset + length);
//          }
          // System.out.println("Chars to remove = " + charsToRemove);
          try {
          Object o = comp.getFormatter().stringToValue(text);
          String value = text;
          // TF:11/04/2008: Check for null coming back in case it's a widget mapped to
          // a nullable field with a null value
          if (o != null) {
            value = o.toString();
            // System.out.println(o.toString() + " - " + o.getClass().toString());
            char[] chars = text.toCharArray();
            int srcIndex = offset-1;
            int destIndex = offset + length-1;
            while (destIndex >= 0) {
              if (Character.isDigit(chars[destIndex])) {
                //need to replace this digit with another one
                while (srcIndex >= 0 && !Character.isDigit(chars[srcIndex])) {
                  srcIndex--;
                }
                if (srcIndex >= 0) {
                  // Replace this index with the source digit
                  chars[destIndex] = chars[srcIndex];
                  // Now drop the source index back one
                  srcIndex--;
                }
                else {
                  // No character, must replace with 0
                  chars[destIndex] = '0';
                }
              }
              destIndex--;
            }
            // System.out.println("Current dot at:" + this.field.getCaret().getDot());
            // System.out.println("Current mark at:" + this.field.getCaret().getMark());
            // System.out.println("New value = " + new String(chars));
            o = comp.getFormatter().stringToValue(new String(chars));
          }
          value = comp.getFormatter().valueToString(o);
              int lengthToReplace = getReplaceToOffset(text, value);
             
              lengthToReplace = Math.max(length + offset, lengthToReplace);
              // System.out.println("new value = " + value);
              // System.out.println("replacement length(" + text + ", " + value + ") = " + lengthToReplace);
              this.preventReentrant = true;
              int substrLength = value.length() - (text.length() - lengthToReplace);
              super.replace(fb, 0, lengthToReplace, value.substring(0, substrLength), null);
              this.preventReentrant = false;
              return;
        }
        catch (ParseException e) {
            super.remove(fb, offset, length);
        }
        }
        super.remove(fb, offset, length);
    }
  }
 
    /**
     * Invoked prior to replacing a region of text in the
     * specified Document. Subclasses that want to conditionally allow
     * replace should override this and only call supers implementation as
     * necessary, or call directly into the FilterBypass.
     *
     * @param fb FilterBypass that can be used to mutate Document
     * @param offset Location in Document
     * @param length Length of text to delete
     * @param text Text to insert, null indicates no text to insert
     * @param attrs AttributeSet indicating attributes of inserted text,
     *              null is legal.
     * @exception BadLocationException  the given insert position is not a
     *   valid position within the document
     */
  @Override
  public void replace(javax.swing.text.DocumentFilter.FilterBypass fb, int offset, int length, String string,
      AttributeSet attrs) throws BadLocationException {
    if (preventReentrant) {
      super.replace(fb, offset, length, string, attrs);
    }
    else if (length == 0) {
      // This is just an insert, call insert. This shouldn't happen
      this.insertString(fb, offset, string, attrs);
    }
    else if (string.length() == 0) {
      // This is just a remove, call remove. This shouldn't happen
      this.remove(fb, offset, length);
    }
    else {
      // System.out.println("replaceString(" + offset + ", " + length +", '" + string + "', " + attrs + ")");

      boolean isSignChar = (string.length() == 1 && (string.charAt(0) == '+' || string.charAt(0) =='-'));
      if (isSignChar) {
        // The sign character will be squashed in the insert anyway and just change the sign of the number
          this.insertString(fb, offset, string, attrs);
      }
      else {
        // Most of the time, replace is equivalent to remove() followed by insert. However, not all the
        // time. Consider a mask of $#,##0.00 and a value of 123. This is formatted as $1.23. If all the
        // text is selected and the character "1" used to replace it, then the delete formats this as "$0.00"
        // then the insert puts the 1 after the dollar sign, so we end up with "$10.00", instead of "$0.01"
        // Strip out any non-digits in the input string
        int insertStringLength = string.length();
        StringBuilder realValueToInsert = new StringBuilder(insertStringLength);
        for (int i = 0; i < insertStringLength; i++) {
          if (Character.isDigit(string.charAt(i))) {
            realValueToInsert.append(string.charAt(i));
          }
        }
       
        if (realValueToInsert.length() == 0) {
          return;
        }
       
          String text = fb.getDocument().getText(0, fb.getDocument().getLength());
          // System.out.println("Initial text = " + text);
         
          // get the current value as a number
          int maximumDecimalPlaces = 0;
          boolean isNegative = false;
          Object o = null;
        try {
          o = this.field.getFormatter().stringToValue(text);
          double value = 0.0;
          if (o instanceof Number) {
            value = ((Number)o).doubleValue();
          }
          else if (o instanceof NumericData) {
            value = ((NumericData)o).doubleValue();
          }
          isNegative = (value < 0.0);
          maximumDecimalPlaces = this.getMaximumDecimalPlaces(value, o == null);
        }
        catch (ParseException e) {
          e.printStackTrace();
        }
          StringBuilder stringVal = new StringBuilder(fb.getDocument().getLength()+string.length());
          int decimalPlaceLocation = -1;
          int count = 0;
          int startOffset = Math.min(offset, offset + length);
          int endOffset = Math.max(offset, offset + length);
          int startInsertLocationInNewString = -1;
          int endInsertLocationInNewString = -1;
          int digitCharsToRemove = 0;
         
            AttributedCharacterIterator iterator = format.formatToCharacterIterator(o);
            if (iterator != null) {
              for (char c = iterator.first(); c != AttributedCharacterIterator.DONE; c = iterator.next()) {
                Map<Attribute, Object> map = iterator.getAttributes();
                if (count == startOffset) {
                  startInsertLocationInNewString = stringVal.length();
                }
                if (count == endOffset) {
                  endInsertLocationInNewString = stringVal.length();
                }
                if (map.containsKey(Field.GROUPING_SEPARATOR)) {
                  // Do nothing. Needed as the Grouping separator is also an integer!
                }
                else if (map.containsKey(Field.DECIMAL_SEPARATOR)) {
                decimalPlaceLocation = stringVal.length();
                }
                else if (map.containsKey(Field.INTEGER) || map.containsKey(Field.FRACTION)) {
                stringVal.append(c);
                if (startInsertLocationInNewString >= 0 && endInsertLocationInNewString < 0) {
                  // We're going to be removing this digit, need to count it
                  digitCharsToRemove++;
                }
              }
                count++;
              }
            }           
            // If we haven't encountered the place to insert this string yet, we need to do so at the end of the string
            if (startInsertLocationInNewString == -1) {
              startInsertLocationInNewString = stringVal.length();
            }

            // TF:29 sept. 2008:Fixed this to use an international character separator
            if (text.endsWith(DoubleData.getDecimalSeparatorStr()) && decimalPlaceLocation < 0) {
              decimalPlaceLocation = stringVal.length();
            }
            // If we haven't encountered the place to insert this string yet, we need to do so at the end of the string
            if (endInsertLocationInNewString == -1) {
              endInsertLocationInNewString = stringVal.length();
            }

            String newValue = "";
            int originalLength = stringVal.length();
            stringVal.replace(startInsertLocationInNewString, endInsertLocationInNewString, realValueToInsert.toString());
            if (decimalPlaceLocation >= 0) {
              // For a replace, we normally don't move the decimal place location relative to the end of the string
              // except if the end insert location is at the end of the string.
              if (endInsertLocationInNewString == originalLength && startInsertLocationInNewString > decimalPlaceLocation) {
                // The decimal point location doesn't change unless we exceed the maximum
                if (stringVal.length() - decimalPlaceLocation > maximumDecimalPlaces) {
                  decimalPlaceLocation = stringVal.length() - maximumDecimalPlaces;
                }
              }
              else {
                decimalPlaceLocation = stringVal.length() - (originalLength - decimalPlaceLocation);
                while (decimalPlaceLocation < 0) {
                  stringVal.insert(0, '0');
                  decimalPlaceLocation++;
                }
              }
              // TF:29 sept. 2008:We must leave this as a decimal point, as this is going to be formatted
              // as a decimal, not displayed as a decimal
              stringVal.insert(decimalPlaceLocation, '.');
              Double d = Double.valueOf(stringVal.toString());
              if (isNegative && d > 0) {
              d = -d;
            }
              try {
            int newMaximumDecimalPlaces = this.getMaximumDecimalPlaces(d, false);
            // If the number of decimal places has changes, we're changing between masks, eg going
            // from a zero mask to a positive mask. This can only happen if we're replacing all the
            // characters, so we want to shift what we're inserting so the digit(s) we've entered
            // are the least significant in the decimal places.
            if (newMaximumDecimalPlaces != maximumDecimalPlaces) {
              while (newMaximumDecimalPlaces > maximumDecimalPlaces) {
                d /= 10;
                newMaximumDecimalPlaces--;
              }
              while (newMaximumDecimalPlaces < maximumDecimalPlaces) {
                d *= 10;
                newMaximumDecimalPlaces++;
              }
            }
            newValue = this.field.getFormatter().valueToString(d);
          }
          catch (ParseException e) {
            // This should never happen just ignore it.
            e.printStackTrace();
          }
            }
            else {
              Long l = Long.valueOf(stringVal.toString());
              if (isNegative && l > 0) {
              l = -l;
            }
              try {
            int newMaximumDecimalPlaces = this.getMaximumDecimalPlaces(l, false);
            // If the number of decimal places has changes, we're changing between masks, eg going
            // from a zero mask to a positive mask. This can only happen if we're replacing all the
            // characters, so we want to shift what we're inserting so the digit(s) we've entered
            // are the least significant in the decimal places.
            if (newMaximumDecimalPlaces != maximumDecimalPlaces) {
              double d = (double)l;
              while (newMaximumDecimalPlaces > maximumDecimalPlaces) {
                d /= 10;
                newMaximumDecimalPlaces--;
              }
              while (newMaximumDecimalPlaces < maximumDecimalPlaces) {
                d *= 10;
                newMaximumDecimalPlaces++;
              }
              newValue = this.field.getFormatter().valueToString(d);
            }
            else {
              newValue = this.field.getFormatter().valueToString(l);
            }
          }
          catch (ParseException e) {
            // This should never happen, just ignore it.
            e.printStackTrace();
          }
            }
           
            // System.out.println("replacement length(" + text + ", " + newValue + ") = " + getReplaceToOffset(text, newValue));
            // Now StringVal should contain the new number to format.
            preventReentrant = true;
            int replaceToOffset = getReplaceToOffset(text, newValue);
            int distanceFromOldEnd = text.length() - replaceToOffset;
            int replaceLength = newValue.length() - distanceFromOldEnd;
            super.replace(fb, 0, replaceToOffset, newValue.substring(0, replaceLength), attrs);
            preventReentrant = false;
        this.pendingSign = PendingSign.UNSET;
      }
    }
  }
}
TOP

Related Classes of DisplayProject.FormattedNumericDocumentFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.