Package net.helipilot50.stocktrade.framework

Source Code of net.helipilot50.stocktrade.framework.NumericDisplayFormatter

/*
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.framework;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;

import javax.swing.text.DefaultFormatter;
import javax.swing.text.DocumentFilter;
import javax.swing.text.NavigationFilter;

/**
* Common formatter to manage display and data input into numeric fields - decimals, amounts, percentages etc. 
* The edit formatter handles the formatting while the field is being edited, while the display
* formatter is used strictly for display purposes
*/

@SuppressWarnings("serial")
public abstract class NumericDisplayFormatter extends DefaultFormatter {

    private static Double EMPTY_VALUE = new Double(0.00);
    private DecimalFormat format = null;
    private int scale = 0;
    private BigDecimal scaleFactor = null;
    private Double divisor = null;
    private NavigationFilter navigationFilter = null;

    public NumericDisplayFormatter(String pattern, int decimalPoints, Class<?> mappedType) {
        super();
        format = getFormat();
        format.applyLocalizedPattern(pattern);

        // NOTE: formats support a maximum of 14 significant digits
        // eg 999999999999.99% or $99,999,999,999,999 or 99999999999.999
        format.setMinimumFractionDigits(decimalPoints);
        format.setMaximumFractionDigits(decimalPoints)
        format.setMaximumIntegerDigits(14-decimalPoints)

        scale = decimalPoints;
        scaleFactor = getScaleFactor(scale);
        divisor = getDivisor();
        setCommitsOnValidEdit(true); // format on each keystroke
        setValueClass(mappedType);
    }

    // Subclasses need to implement these methods
    protected abstract DecimalFormat getFormat();
    protected abstract Double getDivisor();
    protected abstract BigDecimal getScaleFactor(int scale);
    protected abstract boolean allowsDecimalPoint();
    public abstract boolean requiresNavigationFilter();

    // Document filter to limit the characters allowed to be typed in
    protected DocumentFilter getDocumentFilter() {
        return new NumericDocumentFilter(this, allowsDecimalPoint());
    }

    // NavigationFilter to manage cursor position
    protected NavigationFilter getNavigationFilter() {
        if(navigationFilter == null) {
            if(requiresNavigationFilter()) {
                navigationFilter = new NumericNavigationFilter();
            }
            else {
                navigationFilter = super.getNavigationFilter();
            }
        }
        return navigationFilter;
    }

    public Object stringToValue(String text) throws ParseException {
        Object val = EMPTY_VALUE;
        // clean any invalid characters and convert to object
        text = clean(text, true);
        if(text != null && text.length() > 0) {

            val = super.stringToValue(text);
            if(val != null) {
                if(val instanceof Double) {
                    double result = ((Double)val).doubleValue() / scaleFactor.doubleValue();
                    val = new Double(result);
                }
                else if (val instanceof Long) {
                    // need to re-convert to Double
                    Double d = new Double(((Long)val).longValue());
                    double result = d.doubleValue() / scaleFactor.doubleValue();
                    val = new Double(result);
                }
                else if (val instanceof Integer) {
                    // need to re-convert to Double
                    Double d = new Double(((Integer)val).intValue());
                    double result = d.doubleValue() / scaleFactor.doubleValue();
                    val = new Double(result);
                }
                else if (val instanceof String) {
                    // need to re-convert to Double
                    Double d = new Double(((String)val));
                    double result = d.doubleValue() / scaleFactor.doubleValue();
                    val = new Double(result);
                }
                else if(getValueClass() != null) {
                    val = convertToMappedType(val);
                }
                else {
                    // just return converted object
                }
            }
        }
        else if(getValueClass() != null) {
            // we need a specific type of empty value
            if(this.getValueClass().getName().equals(DecimalNullable.class.getName())) {
                // MH 06/03/07 create a DecimalNullable set to null
                val = new DecimalNullable();
                ((DecimalNullable)val).setNull();
            }
            else{
                val = super.stringToValue(text);
            }
        }
        return val;
    }

    // special handling for specific types
    private Object convertToMappedType(Object o) throws ParseException {
        Object val = o;
        if(val instanceof DecimalNullable) {
            Class<?> instanceClass = val.getClass();
            if(instanceClass.equals(DecimalNullable.class)) {
                double result = ((DecimalNullable)val).getValue() / scaleFactor.doubleValue();
                // MH 3rd param is resolver which is not used in constructor, 2nd param is scale
                val = new DecimalNullable(result, this.scale, 0);
            }
            else {
                // A subclass of DecimalNullable like CurrencyDomain - do nothing for the moment
            }
        }
        else if(val instanceof DecimalData) {
            // a generic DecimalData object
            double result = ((DecimalData)val).getValue() / scaleFactor.doubleValue();
            // MH 3rd param is resolver which is not used in constructor, 2nd param is scale
            val = new DecimalData(result, this.scale, 0);
        }
        else {
            // some other type, but mapped type specified. not handled yet
            ParseException errorVar = new ParseException("Unexpected data type: "+val.getClass().getName(), 0);
            ErrorMgr.addError(errorVar);
            throw errorVar;
        }
        return val;
    }

    public String valueToString(Object value) throws ParseException {
        String s = null;
        if(value != null) {
            if(value instanceof Double) {
                double result = ((Double)value).doubleValue() / divisor.doubleValue();
                s = format.format(result);
            }
            else if (value instanceof Long) {
                long result = ((Long)value).longValue() / divisor.intValue();
                s = format.format(result);
            }
            else if (value instanceof Integer) {
                long result = ((Integer)value).intValue() / divisor.intValue();
                s = format.format(result);
            }
            else if(value instanceof DecimalData) {
                // Forte-style decimal (eg CurrencyDomain)
                if(((DecimalData)value).getIsNull()){
                    return "N/A";
                }
                else{
                    double result = ((DecimalData)value).getValue() / divisor.doubleValue();
                    s = format.format(result);
                }
            }
            else {
                ParseException errorVar = new ParseException("Unexpected data type: "+value.getClass().getName(), 0);
                ErrorMgr.addError(errorVar);
                throw errorVar;
            }
        }
        // MH handle null objects (as per TextData) i.e. return "N/A"
        else{
            Class<?> vc = this.getValueClass();
            if (vc != null && DecimalNullable.class.isAssignableFrom(vc)) {
                return "N/A";
            }
//          else {
//            return super.valueToString(value);
//          }
        }
        return s;
    }

    // Clean invalid characters, but maintain numeric precision
    protected String clean(String text, boolean rescale) {
        String clean = text;

        if(clean != null){
            // MH 06/03/07 remove "N/A" if it exists - could be present for nullable fields
            if(text.startsWith("N/A")) {
                clean = text.substring(3);
            }

            boolean negative = clean.startsWith("-");
            if (rescale && clean.indexOf(DoubleData.getDecimalSeparatorStr()) != -1) {
                BigDecimal bd = new BigDecimal(clean);

                // rescale if we've lost digits after the decimal point
                if (bd.scale() < scale) {
                    bd = bd.setScale(scale);
                }
                bd = bd.scaleByPowerOfTen(bd.scale());  
                clean = bd.toString();
            }
            // handle negatives
            if(negative) {
                if("-".equals(clean) || "0".equals(clean) || "-0".equals(clean)) {
                    return "-0.0";
                }
            }
        }
        return clean;
    }
}
TOP

Related Classes of net.helipilot50.stocktrade.framework.NumericDisplayFormatter

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.