Package net.helipilot50.stocktrade.displayproject

Source Code of net.helipilot50.stocktrade.displayproject.FormattedNumericDocumentFilter

/*
Copyright (c) 2003-2009 ITerative Consulting Pty Ltd. All Rights Reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:

o Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
 
o Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the distribution.
   
o This jcTOOL Helper Class software, whether in binary or source form may not be used within,
or to derive, any other product without the specific prior written permission of the copyright holder

 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


*/
package net.helipilot50.stocktrade.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 net.helipilot50.stocktrade.framework.DoubleData;
import net.helipilot50.stocktrade.framework.NullAwareNumberFormat;
import net.helipilot50.stocktrade.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 net.helipilot50.stocktrade.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.