org.eobjects.analyzer.beans.convert.ConvertToDateTransformer.java Source code

Java tutorial

Introduction

Here is the source code for org.eobjects.analyzer.beans.convert.ConvertToDateTransformer.java

Source

/**
 * AnalyzerBeans
 * Copyright (C) 2014 Neopost - Customer Information Management
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program 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 this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.eobjects.analyzer.beans.convert;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.inject.Inject;

import org.eobjects.analyzer.beans.api.Categorized;
import org.eobjects.analyzer.beans.api.Configured;
import org.eobjects.analyzer.beans.api.Description;
import org.eobjects.analyzer.beans.api.Initialize;
import org.eobjects.analyzer.beans.api.OutputColumns;
import org.eobjects.analyzer.beans.api.Transformer;
import org.eobjects.analyzer.beans.api.TransformerBean;
import org.eobjects.analyzer.beans.categories.ConversionCategory;
import org.eobjects.analyzer.beans.categories.DateAndTimeCategory;
import org.eobjects.analyzer.data.InputColumn;
import org.eobjects.analyzer.data.InputRow;
import org.eobjects.analyzer.util.convert.NowDate;
import org.eobjects.analyzer.util.convert.TodayDate;
import org.eobjects.analyzer.util.convert.YesterdayDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

/**
 * Attempts to convert anything to a Date value
 */
@TransformerBean("Convert to date")
@Description("Converts anything to a date (or null).")
@Categorized({ ConversionCategory.class, DateAndTimeCategory.class })
public class ConvertToDateTransformer implements Transformer<Date> {

    private static final String[] prototypePatterns = { "yyyy-MM-dd", "dd-MM-yyyy", "MM-dd-yyyy" };

    private static final DateTimeFormatter NUMBER_BASED_DATE_FORMAT_LONG = DateTimeFormat.forPattern("yyyyMMdd");
    private static final DateTimeFormatter NUMBER_BASED_DATE_FORMAT_SHORT = DateTimeFormat.forPattern("yyMMdd");

    private static ConvertToDateTransformer internalInstance;

    @Inject
    @Configured(order = 1)
    InputColumn<?>[] input;

    @Inject
    @Configured(required = false, order = 2)
    Date nullReplacement;

    @Inject
    @Configured(required = false, order = 3)
    String[] dateMasks;

    private DateTimeFormatter[] _dateTimeFormatters;

    public static ConvertToDateTransformer getInternalInstance() {
        if (internalInstance == null) {
            internalInstance = new ConvertToDateTransformer();
            internalInstance.init();
        }
        return internalInstance;
    }

    public ConvertToDateTransformer() {
        dateMasks = getDefaultDateMasks();
    }

    @Initialize
    public void init() {
        if (dateMasks == null) {
            dateMasks = getDefaultDateMasks();
        }

        _dateTimeFormatters = new DateTimeFormatter[dateMasks.length];
        for (int i = 0; i < dateMasks.length; i++) {
            final String dateMask = dateMasks[i];
            _dateTimeFormatters[i] = DateTimeFormat.forPattern(dateMask);
        }
    }

    @Override
    public OutputColumns getOutputColumns() {
        String[] names = new String[input.length];
        for (int i = 0; i < names.length; i++) {
            names[i] = input[i].getName() + " (as date)";
        }
        return new OutputColumns(names);
    }

    @Override
    public Date[] transform(InputRow inputRow) {
        Date[] result = new Date[input.length];
        for (int i = 0; i < input.length; i++) {
            Object value = inputRow.getValue(input[i]);
            Date d = transformValue(value);
            if (d == null) {
                d = nullReplacement;
            }
            result[i] = d;
        }
        return result;
    }

    public Date transformValue(Object value) {
        Date d = null;
        if (value != null) {
            if (value instanceof Date) {
                d = (Date) value;
            } else if (value instanceof Calendar) {
                d = ((Calendar) value).getTime();
            } else if (value instanceof String) {
                d = convertFromString((String) value);
            } else if (value instanceof Number) {
                d = convertFromNumber((Number) value, true);
            }
        }
        return d;
    }

    protected Date convertFromString(final String value) {
        if ("now()".equalsIgnoreCase(value)) {
            return new NowDate();
        }
        if ("today()".equalsIgnoreCase(value)) {
            return new TodayDate();
        }
        if ("yesterday()".equalsIgnoreCase(value)) {
            return new YesterdayDate();
        }

        for (DateTimeFormatter formatter : _dateTimeFormatters) {
            try {
                return formatter.parseDateTime(value).toDate();
            } catch (Exception e) {
                // proceed to next formatter
            }
        }

        try {
            long longValue = Long.parseLong(value);
            return convertFromNumber(longValue, false);
        } catch (NumberFormatException e) {
            // do nothing, proceed to dateFormat parsing
        }

        // try also with SimpleDateFormat since it is more fault tolerant in
        // millisecond parsing
        final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.S");
        try {
            return format.parse(value);
        } catch (ParseException e) {
            // do nothing
        }

        return null;
    }

    protected Date convertFromNumber(Number value) {
        return convertFromNumber(value, true);
    }

    protected Date convertFromNumber(Number value, boolean tryDateTimeFormatters) {
        Number numberValue = (Number) value;
        long longValue = numberValue.longValue();

        String stringValue = Long.toString(longValue);

        if (tryDateTimeFormatters) {
            for (int i = 0; i < _dateTimeFormatters.length; i++) {
                String dateMask = dateMasks[i];
                boolean isPotentialNumberDateMask = dateMask.indexOf("-") == -1 && dateMask.indexOf(".") == -1
                        && dateMask.indexOf("/") == -1;
                if (isPotentialNumberDateMask) {
                    DateTimeFormatter formatter = _dateTimeFormatters[i];
                    try {
                        return formatter.parseDateTime(stringValue).toDate();
                    } catch (Exception e) {
                        // proceed to next formatter
                    }
                }
            }
        }

        // test if the number is actually a format of the type yyyyMMdd
        if (stringValue.length() == 8 && (stringValue.startsWith("1") || stringValue.startsWith("2"))) {
            try {
                return NUMBER_BASED_DATE_FORMAT_LONG.parseDateTime(stringValue).toDate();
            } catch (Exception e) {
                // do nothing, proceed to next method of conversion
            }
        }

        // test if the number is actually a format of the type yyMMdd
        if (stringValue.length() == 6) {
            try {
                return NUMBER_BASED_DATE_FORMAT_SHORT.parseDateTime(stringValue).toDate();
            } catch (Exception e) {
                // do nothing, proceed to next method of conversion
            }
        }

        if (longValue > 5000000) {
            // this number is most probably amount of milliseconds since
            // 1970
            return new Date(longValue);
        } else {
            // this number is most probably the amount of days since
            // 1970
            return new Date(longValue * 1000 * 60 * 60 * 24);
        }
    }

    private String[] getDefaultDateMasks() {
        final List<String> defaultDateMasks = new ArrayList<String>();

        defaultDateMasks.add("yyyy-MM-dd HH:mm:ss.S");
        defaultDateMasks.add("yyyy-MM-dd HH:mm:ss");
        defaultDateMasks.add("yyyy-MM-dd HH:mm");
        defaultDateMasks.add("yyyyMMddHHmmssZ");
        defaultDateMasks.add("yyMMddHHmmssZ");

        for (String string : prototypePatterns) {
            defaultDateMasks.add(string);
            string = string.replaceAll("\\-", "\\.");
            defaultDateMasks.add(string);
            string = string.replaceAll("\\.", "\\/");
            defaultDateMasks.add(string);
        }

        return defaultDateMasks.toArray(new String[defaultDateMasks.size()]);
    }

    public String[] getDateMasks() {
        return dateMasks;
    }

    public Date getNullReplacement() {
        return nullReplacement;
    }

    public void setNullReplacement(Date nullReplacement) {
        this.nullReplacement = nullReplacement;
    }
}