com.opengamma.strata.collect.io.CsvRow.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.strata.collect.io.CsvRow.java

Source

/**
 * Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
 * 
 * Please see distribution for license.
 */
package com.opengamma.strata.collect.io;

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;

/**
 * A row in a CSV file.
 * <p>
 * Represents a single row in a CSV file, accessed via {@link CsvFile}.
 * Each row object provides access to the data in the row by field index.
 * If the CSV file has headers, the headers can also be used to lookup the fields.
 */
public final class CsvRow {

    /**
     * The header row, ordered as the headers appear in the file.
     */
    private final ImmutableList<String> headers;
    /**
     * The header map, transformed for case-insensitive searching.
     */
    private final ImmutableMap<String, Integer> searchHeaders;
    /**
     * The fields in the row.
     */
    private final ImmutableList<String> fields;

    //------------------------------------------------------------------------
    /**
     * Creates an instance, specifying the headers and row.
     * <p>
     * See {@link CsvFile}.
     * 
     * @param headers  the headers
     * @param fields  the fields
     */
    private CsvRow(ImmutableList<String> headers, ImmutableList<String> fields) {
        this.headers = headers;
        // need to allow duplicate headers and only store the first instance
        Map<String, Integer> searchHeaders = new HashMap<>();
        for (int i = 0; i < headers.size(); i++) {
            String searchHeader = headers.get(i).toLowerCase(Locale.ENGLISH);
            searchHeaders.putIfAbsent(searchHeader, i);
        }
        this.searchHeaders = ImmutableMap.copyOf(searchHeaders);
        this.fields = fields;
    }

    /**
     * Creates an instance, specifying the headers and row.
     * <p>
     * See {@link CsvFile}.
     * 
     * @param headers  the headers
     * @param searchHeaders  the search headers
     * @param fields  the fields
     */
    CsvRow(ImmutableList<String> headers, ImmutableMap<String, Integer> searchHeaders,
            ImmutableList<String> fields) {

        this.headers = headers;
        this.searchHeaders = searchHeaders;
        this.fields = fields;
    }

    //------------------------------------------------------------------------
    /**
     * Gets the header row.
     * <p>
     * If there is no header row, an empty list is returned.
     * 
     * @return the header row
     */
    public ImmutableList<String> headers() {
        return headers;
    }

    /**
     * Gets all fields in the row.
     * 
     * @return the fields
     */
    public ImmutableList<String> fields() {
        return fields;
    }

    /**
     * Gets the number of fields.
     * <p>
     * This will never be less than the number of headers.
     * 
     * @return the number of fields
     */
    public int fieldCount() {
        return Math.max(fields.size(), headers.size());
    }

    /**
     * Gets the specified field.
     * 
     * @param index  the field index
     * @return the field
     * @throws IndexOutOfBoundsException if the field index is invalid
     */
    public String field(int index) {
        if (index >= fields.size() && index < headers.size()) {
            return "";
        }
        return fields.get(index);
    }

    /**
     * Gets a single field value from the row by header.
     * <p>
     * This returns the value of the first column where the header matches the specified header.
     * Matching is case insensitive.
     * 
     * @param header  the column header
     * @return the trimmed field value
     * @throws IllegalArgumentException if the header is not found
     */
    public String getField(String header) {
        return findField(header).orElseThrow(() -> new IllegalArgumentException("Header not found: " + header));
    }

    /**
     * Gets a single field value from the row by header.
     * <p>
     * This returns the value of the first column where the header matches the specified header.
     * Matching is case insensitive.
     * 
     * @param header  the column header
     * @return the trimmed field value, empty if not found
     */
    public Optional<String> findField(String header) {
        return Optional.ofNullable(searchHeaders.get(header.toLowerCase(Locale.ENGLISH))).map(idx -> field(idx));
    }

    /**
     * Gets a single field value from the row by header pattern.
     * <p>
     * This returns the value of the first column where the header matches the specified header pattern.
     * 
     * @param headerPattern  the header pattern to match
     * @return the trimmed field value, empty
     * @throws IllegalArgumentException if the header is not found
     */
    public String getField(Pattern headerPattern) {
        return findField(headerPattern)
                .orElseThrow(() -> new IllegalArgumentException("Header pattern not found: " + headerPattern));
    }

    /**
     * Gets a single field value from the row by header pattern.
     * <p>
     * This returns the value of the first column where the header matches the specified header pattern.
     * 
     * @param headerPattern  the header pattern to match
     * @return the trimmed field value, empty if not found
     */
    public Optional<String> findField(Pattern headerPattern) {
        for (int i = 0; i < headers.size(); i++) {
            if (headerPattern.matcher(headers.get(i)).matches()) {
                return Optional.of(field(i));
            }
        }
        return Optional.empty();
    }

    //-------------------------------------------------------------------------
    /**
     * Obtains a sub-row, containing a selection of fields by index.
     * <p>
     * All fields after the specified index are included.
     * 
     * @param startInclusive  the start index, zero-based, inclusive
     * @return the sub row
     */
    public CsvRow subRow(int startInclusive) {
        return subRow(startInclusive, fields.size());
    }

    /**
     * Obtains a sub-row, containing a selection of fields by index.
     * 
     * @param startInclusive  the start index, zero-based, inclusive
     * @param endExclusive  the end index, zero-based, exclusive
     * @return the sub row
     */
    public CsvRow subRow(int startInclusive, int endExclusive) {
        return new CsvRow(
                headers.subList(Math.min(startInclusive, headers.size()), Math.min(endExclusive, headers.size())),
                fields.subList(startInclusive, endExclusive));
    }

    //-------------------------------------------------------------------------
    /**
     * Checks if this CSV file equals another.
     * <p>
     * The comparison checks the content.
     * 
     * @param obj  the other file, null returns false
     * @return true if equal
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj instanceof CsvRow) {
            CsvRow other = (CsvRow) obj;
            return headers.equals(other.headers) && fields.equals(other.fields);
        }
        return false;
    }

    /**
     * Returns a suitable hash code for the CSV file.
     * 
     * @return the hash code
     */
    @Override
    public int hashCode() {
        return headers.hashCode() ^ fields.hashCode();
    }

    /**
     * Returns a string describing the CSV file.
     * 
     * @return the descriptive string
     */
    @Override
    public String toString() {
        return "CsvRow" + fields.toString();
    }

}