net.iaeste.iws.core.transformers.CSVTransformer.java Source code

Java tutorial

Introduction

Here is the source code for net.iaeste.iws.core.transformers.CSVTransformer.java

Source

/*
 * Licensed to IAESTE A.s.b.l. (IAESTE) under one or more contributor
 * license agreements.  See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership. The Authors
 * (See the AUTHORS file distributed with this work) licenses this file to
 * You under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a
 * copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.iaeste.iws.core.transformers;

import static net.iaeste.iws.api.constants.IWSConstants.DATE_FORMAT;
import static net.iaeste.iws.api.constants.IWSConstants.DEFAULT_LOCALE;
import static net.iaeste.iws.api.enums.exchange.OfferFields.STUDY_COMPLETED_BEGINNING;
import static net.iaeste.iws.api.enums.exchange.OfferFields.STUDY_COMPLETED_END;
import static net.iaeste.iws.api.enums.exchange.OfferFields.STUDY_COMPLETED_MIDDLE;
import static net.iaeste.iws.common.utils.StringUtils.toLower;

import net.iaeste.iws.api.constants.IWSErrors;
import net.iaeste.iws.api.enums.Descriptable;
import net.iaeste.iws.api.enums.exchange.OfferFields;
import net.iaeste.iws.api.enums.exchange.StudyLevel;
import net.iaeste.iws.api.enums.exchange.TypeOfWork;
import net.iaeste.iws.api.exceptions.IWSException;
import net.iaeste.iws.api.util.Date;
import net.iaeste.iws.api.util.DatePeriod;
import net.iaeste.iws.api.util.EnumUtil;
import net.iaeste.iws.api.util.Verifiable;
import org.apache.commons.csv.CSVRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * @author  Kim Jensen / last $Author:$
 * @version $Revision:$ / $Date:$
 * @since   IWS 1.1
 */
final class CSVTransformer {

    private static final Logger LOG = LoggerFactory.getLogger(CSVTransformer.class);

    private static final Pattern PATTERN_UNWANTED_CHARACTERS = Pattern.compile("[_\\t\\r\\n\\u00a0]");

    /**
     * Private Constructor, this is a utility class.
     */
    private CSVTransformer() {
    }

    static void transformString(final Map<String, String> errors, final Verifiable obj, final OfferFields field,
            final CSVRecord record) {
        invokeMethodOnObject(errors, obj, field, record.get(field.getField()));
    }

    static void transformBoolean(final Map<String, String> errors, final Verifiable obj, final OfferFields field,
            final CSVRecord record) {
        final String value = record.get(field.getField());

        if ((value != null) && !value.isEmpty()) {
            invokeMethodOnObject(errors, obj, field, convertBoolean(value));
        }
    }

    static void transformInteger(final Map<String, String> errors, final Verifiable obj, final OfferFields field,
            final CSVRecord record) {
        final String value = record.get(field.getField());

        if ((value != null) && !value.isEmpty()) {
            try {
                final Integer number = Integer.valueOf(value);
                invokeMethodOnObject(errors, obj, field, number);
            } catch (NumberFormatException e) {
                LOG.debug(e.getMessage(), e);
                errors.put(field.getField(), e.getMessage());
            }
        }
    }

    static void transformBigDecimal(final Map<String, String> errors, final Verifiable obj, final OfferFields field,
            final CSVRecord record) {
        final String value = record.get(field.getField());

        if ((value != null) && !value.isEmpty()) {
            try {
                final BigDecimal number = new BigDecimal(value);
                invokeMethodOnObject(errors, obj, field, number);
            } catch (NumberFormatException e) {
                LOG.debug(e.getMessage(), e);
                errors.put(field.getField(), e.getMessage());
            }
        }
    }

    static void transformFloat(final Map<String, String> errors, final Verifiable obj, final OfferFields field,
            final CSVRecord record) {
        final String value = record.get(field.getField());

        if ((value != null) && !value.isEmpty()) {
            try {
                final Float number = Float.valueOf(value);
                invokeMethodOnObject(errors, obj, field, number);
            } catch (NumberFormatException e) {
                LOG.debug(e.getMessage(), e);
                errors.put(field.getField(), e.getMessage());
            }
        }
    }

    static void transformDate(final Map<String, String> errors, final Verifiable obj, final OfferFields field,
            final CSVRecord record) {
        final Date date = convertDate(errors, field, record);
        invokeMethodOnObject(errors, obj, field, date);
    }

    static void transformDatePeriod(final Map<String, String> errors, final Verifiable obj,
            final OfferFields startField, final OfferFields endField, final CSVRecord record) {
        final Date from = convertDate(errors, startField, record);
        final Date to = convertDate(errors, endField, record);

        if ((from != null) && (to != null)) {
            try {
                final DatePeriod period = new DatePeriod(from, to);
                invokeMethodOnObject(errors, obj, startField, period);
            } catch (IllegalArgumentException e) {
                LOG.debug(e.getMessage(), e);
                errors.put(startField.getField(), e.getMessage());
            }
        }
    }

    static <T extends Enum<T>> void transformEnum(final Map<String, String> errors, final Verifiable obj,
            final OfferFields field, final CSVRecord record, final Class<T> enumType) {
        final String value = record.get(field.getField());

        if ((value != null) && !value.isEmpty()) {
            try {
                final T theEnum = Enum.valueOf(enumType, value.toUpperCase(DEFAULT_LOCALE));
                invokeMethodOnObject(errors, obj, field, theEnum);
            } catch (IllegalArgumentException e) {
                LOG.debug(e.getMessage(), e);
                errors.put(field.getField(), e.getMessage());
            }
        }
    }

    static <E extends Enum<E> & Descriptable<E>> void transformDescriptableEnumSet(final Map<String, String> errors,
            final Verifiable obj, final OfferFields field, final CSVRecord record, final Class<E> enumType) {
        final String value = record.get(field.getField());

        if ((value != null) && !value.isEmpty()) {
            try {
                final Set<E> set = CollectionTransformer.explodeEnumSet(enumType, value);
                invokeMethodOnObject(errors, obj, field, set);
            } catch (IllegalArgumentException e) {
                LOG.debug(e.getMessage(), e);
                errors.put(field.getField(), e.getMessage());
            }
        }
    }

    static <E extends Enum<E> & Descriptable<E>> void transformDescriptableEnum(final Map<String, String> errors,
            final Verifiable obj, final OfferFields field, final CSVRecord record, final Class<E> enumType) {
        final String value = record.get(field.getField());

        if ((value != null) && !value.isEmpty()) {
            try {
                final E theEnum = EnumUtil.valueOf(enumType, value);
                invokeMethodOnObject(errors, obj, field, theEnum);
            } catch (IllegalArgumentException e) {
                LOG.debug(e.getMessage(), e);
                errors.put(field.getField(), e.getMessage());
            }
        }
    }

    static void transformStringSet(final Map<String, String> errors, final Verifiable obj, final OfferFields field,
            final CSVRecord record) {
        final String value = record.get(field.getField());
        final String parsedValue = PATTERN_UNWANTED_CHARACTERS.matcher(value).replaceAll(" ").trim();

        final Set<String> set = CollectionTransformer.explodeStringSet(parsedValue);
        invokeMethodOnObject(errors, obj, field, set);
    }

    static void transformTypeOfWork(final Map<String, String> errors, final Verifiable obj, final OfferFields field,
            final CSVRecord record) {
        final Boolean typeR = convertBoolean(record.get(OfferFields.WORK_TYPE_P.getField()));
        final Boolean typeO = convertBoolean(record.get(OfferFields.WORK_TYPE_R.getField()));
        final Boolean typeF = convertBoolean(record.get(OfferFields.WORK_TYPE_W.getField()));

        if (convertBoolean(record.get(OfferFields.WORK_TYPE_N.getField()))) {
            LOG.info("Ignoring the TypeOfWork 'N'.");
        }

        // Using the Boolean comparison as it reduces the NPath complexity
        final int sum = Boolean.compare(typeR, false) + Boolean.compare(typeO, false)
                + Boolean.compare(typeF, false);

        if (sum > 1) {
            errors.put(field.getField(), "Multiple TypeOfWork is set, only one is allowed.");
        } else if (sum == 0) {
            errors.put(field.getField(), "No TypeOfWork defined.");
        } else {
            final TypeOfWork value;
            if (typeR) {
                value = TypeOfWork.R;
            } else if (typeO) {
                value = TypeOfWork.O;
            } else {
                value = TypeOfWork.F;
            }

            invokeMethodOnObject(errors, obj, field, value);
        }
    }

    static void transformStudyLevels(final Map<String, String> errors, final Verifiable obj,
            final OfferFields field, final CSVRecord record) {
        final Set<StudyLevel> value = EnumSet.noneOf(StudyLevel.class);
        final Boolean beginning = convertBoolean(record.get(STUDY_COMPLETED_BEGINNING.getField()));
        final Boolean middle = convertBoolean(record.get(STUDY_COMPLETED_MIDDLE.getField()));
        final Boolean end = convertBoolean(record.get(STUDY_COMPLETED_END.getField()));

        if (!beginning && !middle && !end) {
            errors.put(field.getField(), "No StudyLevel defined.");
        } else {
            if (beginning) {
                value.add(StudyLevel.B);
            }
            if (middle) {
                value.add(StudyLevel.M);
            }
            if (end) {
                value.add(StudyLevel.E);
            }
        }

        invokeMethodOnObject(errors, obj, field, value);
    }

    // =========================================================================
    // Common internal functionality
    // =========================================================================

    private static Boolean convertBoolean(final String value) {
        return "yes".equals(toLower(value));
    }

    private static Date convertDate(final Map<String, String> errors, final OfferFields field,
            final CSVRecord record) {
        final String value = record.get(field.getField());
        Date result = null;

        if ((value != null) && !value.isEmpty()) {
            try {
                final DateFormat formatter = new SimpleDateFormat(DATE_FORMAT, DEFAULT_LOCALE);
                result = new Date(formatter.parse(value));
            } catch (ParseException e) {
                LOG.debug(e.getMessage(), e);
                errors.put(field.getField(), e.getMessage());
            }
        }

        return result;
    }

    /**
     * <p>Reflective invocation of the Object Setter methods. To enforce the
     * Setter Validation checks on the values. Required to avoid that illegal
     * values is being processed.</p>
     *
     * <p>The method will also catch any thrown IllegalArgument Exceptions and
     * add the result to the error map given.</p>
     *
     * @param errors   Validation Error Map
     * @param obj      The Object to invoke the Setter on
     * @param field    The Object field to be set
     * @param argument Argument to the Setter
     * @throws IWSException If a Reflection Error occurred.
     */
    private static <O extends Verifiable> void invokeMethodOnObject(final Map<String, String> errors, final O obj,
            final OfferFields field, final Object argument) {
        if ((field.getMethod() != null) && field.useField(OfferFields.Type.DOMESTIC)) {
            try {
                final Method implementation = obj.getClass().getMethod(field.getMethod(), field.getArgumentClass());
                implementation.invoke(obj, argument);
            } catch (InvocationTargetException e) {
                // The Reflection Framework is wrapping all caught Exceptions
                // inside the Invocation Target Exception. Since our setters
                // is throwing an IllegalArgument Exception, we have to see if
                // this occurs.
                if (e.getTargetException() instanceof IllegalArgumentException) {
                    LOG.debug("Setter {} Invocation Error: {}", field.getMethod(), e.getTargetException(),
                            e.getTargetException());
                    errors.put(field.getField(), e.getTargetException().getMessage());
                } else {
                    LOG.debug("Setter {} Invocation Error: {}", field.getMethod(), e.getMessage(), e);
                    errors.put(field.getField(), e.getMessage());
                }
            } catch (SecurityException | NoSuchMethodException | IllegalArgumentException
                    | IllegalAccessException e) {
                // The Reflection framework forces a check for the
                // NoSuchMethodException. Additionally, if the Java Security
                // Manager is used, we may also see a SecurityException.
                //   Invoking the method may also result in either an
                // IllegalArgumentException or IllegalAccessException.
                LOG.error(e.getMessage(), e);
                throw new IWSException(IWSErrors.FATAL, e.getMessage(), e);
            }
        } else {
            LOG.warn("Cannot set field {}, as there is no method associated with it.", field.getField());
        }
    }
}