All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.quinsoft.zeidon.domains.DoubleDomain Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
/**
    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 .

    Copyright 2009-2015 QuinSoft
 */

package com.quinsoft.zeidon.domains;

import java.text.DecimalFormat;
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 adhocContexts;

    public DoubleDomain( Application application, Map 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;

        if ( externalValue instanceof Double )
        {
            Double d = (Double) externalValue;
            if ( d.isNaN() )
                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 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;

                Double d;
                try
                {
                    d = Double.parseDouble( str );
                }
                catch ( Exception e )
                {
                    throw new InvalidAttributeValueException( attributeDef, str, e.getMessage() )
                            .setCause( e );
                }

                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 );
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy