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

Java tutorial

Introduction

Here is the source code for com.quinsoft.zeidon.domains.DateTimeDomain.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.util.Date;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import com.quinsoft.zeidon.Application;
import com.quinsoft.zeidon.AttributeInstance;
import com.quinsoft.zeidon.EntityInstance;
import com.quinsoft.zeidon.InvalidAttributeValueException;
import com.quinsoft.zeidon.ObjectEngine;
import com.quinsoft.zeidon.Task;
import com.quinsoft.zeidon.ZeidonException;
import com.quinsoft.zeidon.objectdefinition.AttributeDef;
import com.quinsoft.zeidon.utils.JoeUtils;

/**
 * This domain handles any differences needed to deal with dates+time.
 */
public class DateTimeDomain extends AbstractDomain {
    protected DateTimeFormatter defaultDateTimeFormatter = DateTimeFormat
            .forPattern(ObjectEngine.INTERNAL_DATE_STRING_FORMAT + "HHmmss");

    public DateTimeDomain(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();

        // KJS - Added 01/27/11 because of line 2836 in lTrnscpt_Object.java
        // OrderEntityForView( lTrnscpt, "StudentMajorDegreeTrack", "wPrimarySortOrder A GraduationDate A" );
        // value = null so we are getting to the exception.  Will try returning null, see what happens.
        if (externalValue == null)
            return null;

        if (externalValue instanceof DateTime)
            return externalValue;

        if (externalValue instanceof Date)
            return new DateTime(externalValue);

        // VML operations use "" as synonymous with null.
        if (externalValue instanceof String && StringUtils.isBlank((String) externalValue))
            return null;

        if (externalValue instanceof CharSequence) {
            // If string is "NOW" then we'll use current datetime.
            if (externalValue.toString().equals("NOW"))
                return new DateTime();

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

        throw new InvalidAttributeValueException(attributeDef, externalValue,
                "Invalid object: Domain %s cannot convert value for context %s.", this.getClass().getName(),
                contextName);
    }

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

        if (internalValue.toString().isEmpty())
            return internalValue.toString();

        return defaultDateTimeFormatter.print((DateTime) internalValue);
    }

    /**
     * Attempt to get the context.  This differs from the normal getContext() by attempting to create
     * a SimpleDateFormatter using the contextName as the format if a context is not found by name.
     */
    @Override
    public DomainContext getContext(Task task, String contextName) {
        DomainContext context = getContext(contextName);
        if (context != null)
            return context; // We found one by name.

        if (StringUtils.isBlank(contextName))
            throw new ZeidonException("Domain '%s' does not have a default context defined.", getName());

        // Create a temporary new one and set its edit string to the context name.
        DateContext dateContext = new DateContext(this);
        dateContext.setName(contextName);
        dateContext.setEditString(contextName);
        return dateContext;
    }

    @Override
    public void validateInternalValue(Task task, AttributeDef attributeDef, Object internalValue)
            throws InvalidAttributeValueException {
        if (internalValue instanceof DateTime)
            return;

        throw new InvalidAttributeValueException(attributeDef, internalValue,
                "Internal value must be Joda DateTime, not %s", internalValue.getClass().getName());
    }

    /**
     * Adds milliseconds to the datetime value.
     */
    @Override
    public Object addToAttribute(Task task, AttributeInstance attributeInstance, AttributeDef attributeDef,
            Object currentValue, Object addValue) {
        DateTime date1 = (DateTime) convertExternalValue(task, attributeInstance, attributeDef, null, currentValue);
        if (date1 == null)
            throw new ZeidonException("Target attribute for add is NULL").prependAttributeDef(attributeDef);

        if (addValue == null)
            return date1;

        if (addValue instanceof Number) {
            int millis = ((Number) addValue).intValue();
            return date1.plusMillis(millis);
        }

        throw new ZeidonException("Value type of %s not supported for add to DateDomain",
                addValue.getClass().getName());
    }

    @Override
    public Object addToAttribute(Task task, AttributeInstance attributeInstance, AttributeDef attributeDef,
            Object currentValue, Object operand, String contextName) {
        if (!StringUtils.isBlank(contextName)) {
            return addWithContext(task, attributeInstance, attributeDef, currentValue, operand, contextName);
        }

        return addToAttribute(task, attributeInstance, attributeDef, currentValue, operand);
    }

    private Object addWithContext(Task task, AttributeInstance attributeInstance, AttributeDef attributeDef,
            Object currentValue, Object operand, String contextName) {
        assert !StringUtils.isBlank(contextName);

        if (operand instanceof AttributeInstance)
            operand = ((AttributeInstance) operand).getValue();

        if (!(operand instanceof Integer) && !(operand instanceof Long)) {
            throw new ZeidonException(
                    "When adding to DateTime with a context, operand must be integer or long value.  "
                            + "Type of operand = %s",
                    operand.getClass().getName()).prependAttributeDef(attributeDef);
        }

        int value = ((Number) operand).intValue();
        DateTime dt = (DateTime) currentValue;

        switch (contextName.toLowerCase()) {
        case "day":
        case "days":
            return dt.plusDays(value);

        case "hour":
        case "hours":
            return dt.plusHours(value);

        case "minute":
        case "minutes":
            return dt.plusMinutes(value);

        case "milli":
        case "millis":
        case "millisecond":
        case "milliseconds":
            return dt.plus(((Number) operand).longValue());

        case "month":
        case "months":
            return dt.plusMonths(value);

        case "second":
        case "seconds":
            return dt.plusSeconds(value);

        case "week":
        case "weeks":
            return dt.plusWeeks(value);

        case "year":
        case "years":
            return dt.plusYears(value);
        }

        // TODO Auto-generated method stub
        throw new ZeidonException("Unknown context name '%s' for DateTime domain", contextName)
                .prependAttributeDef(attributeDef);
    }

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

    /**
     * Generate a random test value for this domain.  This is used by test code to create random
     * test data.
     *
     * @param task current task
     * @param attributeDef def of the attribute.
     * @param entityInstance if not null this is the EntityInstance that will store the test data.
     *
     * @return random test data for this domain.
     */
    @Override
    public Object generateRandomTestValue(Task task, AttributeDef attributeDef, EntityInstance entityInstance) {
        DomainContext context = getDefaultContext();
        return context.convertToString(task, attributeDef, new DateTime());
    }

    private class DateContext extends BaseDomainContext {
        /**
         * URL for Java SimpleDateFormat help.
         */
        private static final String JAVA_DATE_FORMATTING_URL = "http://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html";

        public DateContext(Domain domain) {
            super(domain);
        }

        private DateTimeFormatter formatter;

        @Override
        public String convertToString(Task task, AttributeDef attributeDef, Object internalValue)
                throws ZeidonException {
            if (internalValue == null)
                return StringDomain.checkNullString(task.getApplication(), null);

            if (internalValue.toString().isEmpty())
                return internalValue.toString();

            if (formatter == null)
                throw new ZeidonException("JavaEditString is not set for context %s", this.toString());

            return formatter.print((DateTime) internalValue);
        }

        /**
         * Assumes a string.
         */
        @Override
        public Object convertExternalValue(Task task, AttributeDef attributeDef, Object value)
                throws InvalidAttributeValueException {
            if (value == null)
                return null;

            // If external value is an AttributeInstance then get *its* internal value.
            if (value instanceof AttributeInstance)
                value = ((AttributeInstance) value).getValue();

            String s = (String) value;

            // VML operations use "" as synonymous with null.
            if (StringUtils.isBlank(s))
                return null;

            // A common internal date/time format is the DefaultTimeFormat.  Let's try
            // a quick check to see if 's' is the same length and only digits.
            if (s.length() >= ObjectEngine.INTERNAL_DATE_STRING_FORMAT.length() && JoeUtils.onlyDigits(s))
                return JoeUtils.parseStandardDateString(s);

            if (formatter == null)
                throw new ZeidonException("JavaEditString is not set for context %s", this.toString());

            try {
                return formatter.parseDateTime(s);
            } catch (Exception e) {
                throw new InvalidAttributeValueException(attributeDef, s, e.getMessage())
                        .appendMessage("Format string = %s", getEditString())
                        .appendMessage("See %s for help on Java Date formatting", JAVA_DATE_FORMATTING_URL)
                        .setCause(e);
            }
        }

        @Override
        protected void setEditString(String editString) {
            super.setEditString(editString);
            formatter = JoeUtils.createDateFormatterFromEditString(editString);
        }
    }
}