com.quinsoft.opencuas.RevenueDomain.java Source code

Java tutorial

Introduction

Here is the source code for com.quinsoft.opencuas.RevenueDomain.java

Source

/**
This file is part of the Zeidon Java Object Engine (Zeidon JOE).
    
Zeidon JOE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
Zeidon JOE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.
    
You should have received a copy of the GNU Lesser General Public License
along with Zeidon JOE.  If not, see <http://www.gnu.org/licenses/>.
    
Copyright 2009-2015 QuinSoft
 */

package com.quinsoft.opencuas;

import java.text.DecimalFormat;
import java.text.ParsePosition;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;

import com.quinsoft.zeidon.Application;
import com.quinsoft.zeidon.AttributeInstance;
import com.quinsoft.zeidon.InvalidAttributeValueException;
import com.quinsoft.zeidon.Task;
import com.quinsoft.zeidon.domains.AbstractNumericDomain;
import com.quinsoft.zeidon.domains.BaseDomainContext;
import com.quinsoft.zeidon.domains.Domain;
import com.quinsoft.zeidon.domains.DomainContext;
import com.quinsoft.zeidon.objectdefinition.AttributeDef;
import com.quinsoft.zeidon.utils.PortableFileReader;

/**
 * Decimal domain, which stores values as Double.
 *
 * Uses DecimalFormat for formatting values to string.  See
 * http://java.sun.com/docs/books/tutorial/i18n/format/decimalFormat.html
 * Also has code for java edit string TextForCheckAmount and for a java edit string containing "CR" which shows CR when
 * number is negative instead of "-".
 *
 * @author DG/KJS
 *
 */
public class RevenueDomain extends AbstractNumericDomain {
    private final DecimalFormat parser = new DecimalFormat("#,###.#");

    public RevenueDomain(Application application, Map<String, Object> domainProperties, Task task) {
        super(application, domainProperties, task);
        //        if ( name.equals( "DecimalFormat" ) )  Decimal format not needed.  See contexts.
    }

    @Override
    public Object convertExternalValue(Task task, AttributeInstance attributeInstance, AttributeDef attributeDef,
            String contextName, Object externalValue) {
        // If external value is an AttributeInstance then get *its* internal value.
        if (externalValue instanceof AttributeInstance)
            externalValue = ((AttributeInstance) externalValue).getValue();

        // KJS - Added 01/28/11 the following two returns.
        if (externalValue == null)
            return null;

        if (externalValue instanceof Number)
            return ((Number) externalValue).doubleValue();

        if (externalValue instanceof CharSequence) {
            String str = externalValue.toString();

            // VML uses "" as a synonym for null.
            if (StringUtils.isBlank(str))
                return null;

            ParsePosition ps = new ParsePosition(0);
            Double d;
            synchronized (parser) {
                d = parser.parse(str, ps).doubleValue();
            }

            int idx = Math.max(ps.getErrorIndex(), ps.getIndex());
            if (idx != str.length()) {
                throw new InvalidAttributeValueException(attributeDef, str,
                        "Error parsing '" + str + "' at position " + (idx + 1));
            }

            return d;
        }

        throw new InvalidAttributeValueException(attributeDef, externalValue, "Can't convert '%s' to Double",
                externalValue.getClass().getName());
    }

    @Override
    public void validateInternalValue(Task task, AttributeDef attributeDef, Object internalValue)
            throws InvalidAttributeValueException {
        if (!(internalValue instanceof Double))
            throw new InvalidAttributeValueException(attributeDef, internalValue,
                    "'%s' is an invalid Object for RevenueDomain", internalValue.getClass().getName());

        super.validateInternalValue(task, attributeDef, internalValue);
    }

    @Override
    public Object addToAttribute(Task task, AttributeInstance attributeInstance, AttributeDef attributeDef,
            Object currentValue, Object operand) {
        Double num = (Double) convertExternalValue(task, attributeInstance, attributeDef, null, operand);
        Double value = (Double) currentValue;
        return value + num;
    }

    @Override
    public Object multiplyAttribute(Task task, AttributeInstance attributeInstance, AttributeDef attributeDef,
            Object currentValue, Object operand) {
        Double num = (Double) convertExternalValue(task, attributeInstance, attributeDef, null, operand);
        Double value = (Double) currentValue;
        return value * num;
    }

    @Override
    public String convertToString(Task task, AttributeDef attributeDef, Object internalValue, String contextName) {
        return getContext(task, contextName).convertToString(task, attributeDef, internalValue);
    }

    @Override
    public DomainContext newContext(Task task) {
        return new DoubleContext(this);
    }

    private static class DoubleContext extends BaseDomainContext {
        private DecimalFormat format = null;
        private String formatPattern = null;

        /**
         * @param domain
         */
        public DoubleContext(Domain domain) {
            super(domain);
        }

        @Override
        public String convertToString(Task task, AttributeDef attributeDef, Object internalValue) {
            if (internalValue == null)
                return null;

            Double d = (Double) internalValue;

            // KJS 10/13/11 - In Zencas we have a revenue context "RevenueText" which renders the double amount as number text
            // in the format for a check amount (Three Thousand Nine Hundred Thirty and 00 / 100 Dollars).
            // I decided to create a Java Edit Format of "TextForCheckAmount".  Perhaps this is a bad way to do this
            // but I will let DG tell me...
            if (formatPattern.equals("TextForCheckAmount")) {
                String tmpStr = EnglishNumberToWords.convert(d);
                return tmpStr;

            }
            // KJS 10/13/11 - In Zencas we have a revenue context "RevenueWithDollarSignCR" renders the decimal with a dollar sign and if
            // the decimal is < 0, a "CR" is concatenated instead of having a "-" negative sign.
            if (formatPattern.indexOf("CR") > 0) {
                String cr = "";
                String mask = formatPattern.substring(0, formatPattern.indexOf("CR"));
                DecimalFormat df = new DecimalFormat(mask);
                if (d < 0) {
                    d = d * -1;
                    cr = "CR";
                }
                String tmpStr = df.format(d);
                tmpStr = tmpStr + cr;
                return tmpStr;

            }
            synchronized (format) {
                return format.format(d);
            }
        }

        @Override
        public void setAttribute(PortableFileReader reader) {
            String name = reader.getAttributeName();
            if (name.equals("DecimalFormat")) {
                int scale = Integer.parseInt(reader.getAttributeValue());
                formatPattern = scale == 0 ? "#" : "#." + StringUtils.repeat("#", scale);
                format = new DecimalFormat(formatPattern);
            } else if (name.equals("JavaEditString")) {
                formatPattern = reader.getAttributeValue();
                format = new DecimalFormat(formatPattern);
            } else
                super.setAttribute(reader);
        }
    }

    private static class EnglishNumberToWords {
        private static final String[] tensNames = { "", " Ten", " Twenty", " Thirty", " Forty", " Fifty", " Sixty",
                " Seventy", " Eighty", " Ninety" };

        private static final String[] numNames = { "", " One", " Two", " Three", " Four", " Five", " Six", " Seven",
                " Eight", " Nine", " Ten", " Eleven", " Twelve", " Thirteen", " Fourteen", " Fifteen", " Sixteen",
                " Seventeen", " Eighteen", " Nineteen" };

        private static String convertLessThanOneThousand(int number) {
            String soFar;

            if (number % 100 < 20) {
                soFar = numNames[number % 100];
                number /= 100;
            } else {
                soFar = numNames[number % 10];
                number /= 10;

                soFar = tensNames[number % 10] + soFar;
                number /= 10;
            }
            if (number == 0)
                return soFar;
            return numNames[number] + " Hundred" + soFar;
        }

        public static String convert(Double number) {
            // 0 to 999 999 999 999
            if (number == 0) {
                return "Zero";
            }

            // Right now I'm going to assume that we want two precision decimal.
            String mask = "0.00";
            DecimalFormat df = new DecimalFormat(mask);
            String snumber = df.format(number);
            String sdecimal = snumber.substring(snumber.indexOf(".") + 1);
            snumber = snumber.substring(0, snumber.indexOf("."));

            // pad with "0"
            mask = "000000000000";
            df = new DecimalFormat(mask);
            snumber = df.format(number);

            // XXXnnnnnnnnn
            int billions = Integer.parseInt(snumber.substring(0, 3));
            // nnnXXXnnnnnn
            int millions = Integer.parseInt(snumber.substring(3, 6));
            // nnnnnnXXXnnn
            int hundredThousands = Integer.parseInt(snumber.substring(6, 9));
            // nnnnnnnnnXXX
            int thousands = Integer.parseInt(snumber.substring(9, 12));

            String tradBillions;
            switch (billions) {
            case 0:
                tradBillions = "";
                break;
            case 1:
                tradBillions = convertLessThanOneThousand(billions) + " Billion ";
                break;
            default:
                tradBillions = convertLessThanOneThousand(billions) + " Billion ";
            }
            String result = tradBillions;

            String tradMillions;
            switch (millions) {
            case 0:
                tradMillions = "";
                break;
            case 1:
                tradMillions = convertLessThanOneThousand(millions) + " Million ";
                break;
            default:
                tradMillions = convertLessThanOneThousand(millions) + " Million ";
            }
            result = result + tradMillions;

            String tradHundredThousands;
            switch (hundredThousands) {
            case 0:
                tradHundredThousands = "";
                break;
            case 1:
                tradHundredThousands = "One Thousand ";
                break;
            default:
                tradHundredThousands = convertLessThanOneThousand(hundredThousands) + " Thousand ";
            }
            result = result + tradHundredThousands;

            String tradThousand;
            tradThousand = convertLessThanOneThousand(thousands);
            result = result + tradThousand;

            if (!sdecimal.isEmpty()) {
                result = result + " and " + sdecimal + " / 100 Dollars";
            }

            // remove extra spaces!
            return result.replaceAll("^\\s+", "").replaceAll("\\b\\s{2,}\\b", " ");
        }
    }

}