org.marketcetera.ors.OptionRootUnderlyingMap.java Source code

Java tutorial

Introduction

Here is the source code for org.marketcetera.ors.OptionRootUnderlyingMap.java

Source

package org.marketcetera.ors;

import org.marketcetera.util.misc.ClassVersion;
import org.marketcetera.util.unicode.UnicodeFileReader;
import org.marketcetera.util.file.CloseableRegistry;
import org.springframework.beans.factory.InitializingBean;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.*;

import com.google.common.collect.*;

/* $License$ */
/**
 * A class that provides a mapping between option roots and underlying
 * symbols based on the
 * <a href="http://www.optionsclearing.com/market/listed_products/default.jsp">
 * mapping</a> provided by the Options Clearing Corporation.
 * <p>
 * This class can be safely used concurrently from multiple threads.
 *
 * @author anshul@marketcetera.com
 * @version $Id$
 * @since 2.0.0
 */
@ClassVersion("$Id$")
public class OptionRootUnderlyingMap implements InitializingBean {
    /**
     * Creates an instance.
     */
    public OptionRootUnderlyingMap() {
        sInstance = this;
    }

    /**
     * Gets the underlying symbol, given the option root symbol.
     *
     * @param inOptionRoot the option root symbol.
     *
     * @return the underlying symbol, if a mapping is found. null otherwise.
     */
    public String getUnderlying(String inOptionRoot) {
        if (inOptionRoot == null) {
            return null;
        }
        final Map<String, String> map = mRootToUnderlying;
        return map == null ? null : map.get(inOptionRoot);
    }

    /**
     * Returns the collection of option roots for the underlying symbol.
     *
     * @param inUnderlying the underlying symbol.
     *
     * @return the sorted collection of option roots for the underlying symbol.
     * If no mapping is found, a null value is returned. The returned collection
     * is not modifiable.
     */
    public Collection<String> getOptionRoots(String inUnderlying) {
        if (inUnderlying == null) {
            return null;
        }
        final Map<String, Collection<String>> map = mUnderlyingToRoots;
        return map == null ? null : map.get(inUnderlying);
    }

    /**
     * The name of the file from which the mappings should be read.
     *
     * @param inFilename the file name.
     */
    public void setFilename(String inFilename) {
        mFilename = inFilename;
    }

    /**
     * Sets the type of records to include.
     *
     * @param inIncludeTypes the type of records to include. Should not be null.
     */
    public void setIncludeTypes(String[] inIncludeTypes) {
        if (inIncludeTypes == null) {
            throw new NullPointerException();
        }
        mIncludeTypes = inIncludeTypes;
    }

    /**
     * Returns the singleton instance if initialized, null otherwise.
     *
     * @return the singleton instance.
     */
    public static OptionRootUnderlyingMap getInstance() {
        return sInstance;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        loadFromFile();
    }

    /**
     * Returns the number of option root mappings.
     * <p>
     * This method provided to help with unit testing purposes.
     *
     * @return number of option root mappings.
     */
    int getNumOptionRoots() {
        return mRootToUnderlying == null ? 0 : mRootToUnderlying.size();
    }

    /**
     * The number of underlying mappings.
     * <p>
     * This method provided to help with unit testing purposes.
     *
     * @return number of underlying mappings.
     */
    int getNumUnderlyings() {
        return mUnderlyingToRoots == null ? 0 : mUnderlyingToRoots.size();
    }

    /**
     * Loads the records from the file.
     */
    private void loadFromFile() {
        final String filename = mFilename;
        if (filename != null) {
            BufferedReader reader;
            Map<String, String> rootToUnderlying = null;
            SortedSetMultimap<String, String> underlyingToRoots = null;
            CloseableRegistry registry = new CloseableRegistry();
            try {
                reader = new BufferedReader(new UnicodeFileReader(filename));
                registry.register(reader);
                Set<String> includeTypes = new HashSet<String>(Arrays.asList(mIncludeTypes));
                String line, root, underlying, type;
                rootToUnderlying = new HashMap<String, String>();
                underlyingToRoots = TreeMultimap.create();
                while ((line = reader.readLine()) != null) {
                    root = extract(line, ROOT_START_IDX, ROOT_END_IDX);
                    underlying = extract(line, UNDERLYING_START_IDX, UNDERLYING_END_IDX);
                    type = extract(line, TYPE_START_IDX, TYPE_END_IDX);
                    if (root != null && underlying != null && type != null && includeTypes.contains(type)) {
                        rootToUnderlying.put(root, underlying);
                        underlyingToRoots.put(underlying, root);
                    }
                }
            } catch (IOException e) {
                Messages.ORUM_LOG_ERROR_LOADING_FILE.error(this, e, filename);
            } finally {
                registry.close();
            }
            //Assign the values to volatile variables after the maps have been
            //initialized to prevent concurrency issues.
            mRootToUnderlying = rootToUnderlying == null ? null : Collections.unmodifiableMap(rootToUnderlying);
            mUnderlyingToRoots = underlyingToRoots == null ? null
                    : Multimaps.unmodifiableSortedSetMultimap(underlyingToRoots).asMap();
        } else {
            Messages.ORUM_LOG_SKIP_LOAD_FILE.info(this);
        }
    }

    /**
     * Extracts the field value from the supplied record.
     *
     * @param inValue the current record to extract the field from
     * @param inStartIdx the start index of the field
     * @param inEndIdx the end index of the field
     *
     * @return the field value, if the field was found in the record.
     */
    private String extract(String inValue, int inStartIdx, int inEndIdx) {
        if (inStartIdx >= inValue.length()) {
            return null;
        }
        inEndIdx = Math.min(inEndIdx, inValue.length());
        if (inEndIdx <= inStartIdx) {
            return null;
        }
        return inValue.substring(inStartIdx, inEndIdx).trim();
    }

    private volatile Map<String, String> mRootToUnderlying;
    private volatile Map<String, Collection<String>> mUnderlyingToRoots;
    private volatile String[] mIncludeTypes = { "EU", "EL" }; //$NON-NLS-1$ $NON-NLS-2$
    private volatile String mFilename;
    private static volatile OptionRootUnderlyingMap sInstance;
    private static final int ROOT_START_IDX = 0;
    private static final int ROOT_END_IDX = 6;
    private static final int UNDERLYING_START_IDX = 7;
    private static final int UNDERLYING_END_IDX = 13;
    private static final int TYPE_START_IDX = 80;
    private static final int TYPE_END_IDX = 82;
}