com.quinsoft.zeidon.domains.DoubleDomain.java Source code

Java tutorial

Introduction

Here is the source code for com.quinsoft.zeidon.domains.DoubleDomain.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.zeidon.domains;

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

import org.apache.commons.lang3.StringUtils;

import com.google.common.collect.MapMaker;
import com.quinsoft.zeidon.Application;
import com.quinsoft.zeidon.AttributeInstance;
import com.quinsoft.zeidon.InvalidAttributeValueException;
import com.quinsoft.zeidon.Task;
import com.quinsoft.zeidon.ZeidonException;
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
 *
 * @author DG
 *
 */
public class DoubleDomain extends AbstractNumericDomain {
    private final DecimalFormat parser = new DecimalFormat("#,###.#");

    /**
     * A map for keeping track of "ad-hoc" contexts so we don't have to keep recreating
     * them.  An ad-hoc context is one that isn't listed in zeidon.xdm but is a Java
     * format string for DecimalFormat.  If a context name is a valid format we'll accept
     * it.
     */
    private ConcurrentMap<String, DomainContext> adhocContexts;

    public DoubleDomain(Application application, Map<String, Object> domainProperties, Task task) {
        super(application, domainProperties, task);
    }

    @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();

        if (externalValue == null)
            return null;

        DomainContext context = getContext(task, contextName);
        return context.convertExternalValue(task, attributeDef, externalValue);
    }

    @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 DoubleDomain", 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;
    }

    private synchronized ConcurrentMap<String, DomainContext> getAdhocContextMap() {
        if (adhocContexts == null)
            adhocContexts = new MapMaker().concurrencyLevel(2).makeMap();

        return adhocContexts;
    }

    @Override
    protected DomainContext getContext(String contextName) {
        DomainContext context = super.getContext(contextName);
        if (context == null) {
            // If we get here then we searched have a context name but we didn't find
            // one.  This probably means that the user is using an "adhoc" context where
            // the context name is a format string. Try creating a context that uses the
            // contextName as a format string.

            // Have we already created one?
            context = getAdhocContextMap().get(contextName);
            if (context == null) {
                // Doesn't exist, so create it.
                try {
                    context = new DoubleContext(this, contextName);
                    getAdhocContextMap().putIfAbsent(contextName, context);
                    if (getAdhocContextMap().size() > 1000) {
                        throw new ZeidonException("Ad-hoc context list is too large for this domain.")
                                .appendMessage("Domain = %s", this.toString());
                    }
                } catch (Exception e) {
                    throw new ZeidonException("Unknown Double context and context is not valid format string.")
                            .setCause(e);
                }
            }
        }

        return context;
    }

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

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

        Double d = (Double) internalValue;
        return d.intValue();
    }

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

    private class DoubleContext extends BaseDomainContext {
        private DecimalFormat format = parser;
        private String formatPattern = null;
        private Integer decimalPlaces;

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

        private DoubleContext(Domain domain, String formatPattern) {
            super(domain);
            setName(formatPattern);
            this.formatPattern = formatPattern;
            format = new DecimalFormat(formatPattern);
        }

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

            Double d = (Double) internalValue;
            synchronized (format) {
                return format.format(d);
            }
        }

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

            if (externalValue instanceof Number) {
                if (decimalPlaces == null)
                    return ((Number) externalValue).doubleValue();

                // KJS 01/10/14 - In the c world, we round our decimals based on
                // the decimal precision value.
                // so I am adding decimal precision here.
                String formatPattern = decimalPlaces == 0 ? "#" : "#." + StringUtils.repeat("#", decimalPlaces);
                DecimalFormat df = new DecimalFormat(formatPattern);
                String formate = df.format(externalValue);
                double finalValue;
                try {
                    // If the externalValue has no decimal values, the df.format
                    // above fails because it
                    // thinks the number is a Long. When this happens, do the
                    // following.
                    finalValue = (Double) df.parse(formate);
                } catch (Exception e) {
                    return ((Number) externalValue).doubleValue();
                }

                return finalValue;
            }

            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;
                try {
                    synchronized (parser) {
                        d = parser.parse(str, ps).doubleValue();
                    }
                } catch (Exception e) {
                    throw new InvalidAttributeValueException(attributeDef, str, e.getMessage()).setCause(e);
                }

                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
        protected void setEditString(String editString) {
            super.setEditString(editString);
            formatPattern = editString;
            format = new DecimalFormat(formatPattern);
        }

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