com.github.r351574nc3.amex.assignment1.csv.DefaultInterpreter.java Source code

Java tutorial

Introduction

Here is the source code for com.github.r351574nc3.amex.assignment1.csv.DefaultInterpreter.java

Source

/*
 * The MIT License (MIT)
 * 
 * Copyright (c) 2015 Leo Przybylski
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.github.r351574nc3.amex.assignment1.csv;

import static com.github.r351574nc3.logging.FormattedLogger.*;

import com.google.inject.Inject;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;

import com.github.r351574nc3.amex.assignment1.model.EmailNotificationTestData;
import com.github.r351574nc3.amex.assignment1.model.TestData;

/**
 * Default implementation that reads {@link CSVRecord} instances from a CSV file and converts them to 
 * {@link TestData} instances.
 *
 * @author Leo Przybylski
 */
public class DefaultInterpreter implements Interpreter<Hashtable> {
    protected static final String TOKEN_REGEX = "\\{PZN[0-9]+\\}";

    /**
     * Convert records to {@link TestData}.
     *
     * @return {@link Hashtable} instance which makes record lookup by name much easier. Records that belong to a given name are indexed
     * within the {@link Hashtable} instance. In case there is more than one instance, the object in the {@link Hashtable} is
     * a {@link LinkedList} which can be quickly iterated
     */
    public Hashtable interpret(final File input) throws IOException {
        final CSVParser parser = CSVParser.parse(input, Charset.defaultCharset(),
                CSVFormat.RFC4180.withDelimiter('|'));

        // Using a {@link Hashtable with the name field on the CSV record as the key. A lower load factor is used to give more
        // priority to the time cost for looking up values. 
        final Hashtable<String, LinkedList<TestData>> index = new Hashtable<String, LinkedList<TestData>>(2, 0.5f);

        for (final CSVRecord record : parser) {
            final EmailNotificationTestData data = toTestData(record);

            LinkedList<TestData> data_ls = index.get(data.getName());
            if (data_ls == null) {
                data_ls = new LinkedList<TestData>();
                index.put(data.getName(), data_ls);
            }
            data_ls.add(data);
        }

        return index;
    }

    protected EmailNotificationTestData toTestData(final CSVRecord record) {
        final EmailNotificationTestData retval = new EmailNotificationTestData();

        retval.setName(record.get(0));
        retval.setMemberSince(Integer.parseInt(record.get(1)));
        retval.setAnnualSpend(new BigDecimal(record.get(2)));
        retval.setEmailAddress(record.get(3));
        retval.setSubject(record.get(4));

        if (record.size() > 6) {
            for (int idx = 6; idx < record.size(); idx++) {
                retval.getAdditionalFields().add(record.get(idx));
            }
        }

        retval.setContent(replaceTokens(record.get(5), retval.getAdditionalFields()));

        return retval;
    }

    /**
     * Handles replacement of special <code>{PZN#}</code> tokens in the content field and replaces them with the personalizable field
     * values in order. This may be different than the expected behavior which is that personalizable fields may be duplicatable in
     * the content as well as being unordered. I realize that is the case, 
     *
     * @param template {@link String} instance containing tokens
     * @param fields are additional fields to replace tokens with.
     * @return evaluated and replaced {@link String} instance.
     */
    protected String replaceTokens(final String template, final List<String> fields) {
        final String[] fieldsArr = fields.toArray(new String[fields.size()]);
        return String.format(template.replaceAll(TOKEN_REGEX, "%s"), fieldsArr);
    }
}