package hirondelle.stocks.quotes; import java.util.*; import java.io.*; import hirondelle.stocks.util.Consts; import hirondelle.stocks.util.FileUtil; /** * Enumeration for all exchanges used by the Yahoo quote system. * * <P>Warning: this is an example of an older-style enumeration. A more modern style * would use an 'enum', instead of a class. * * <P>The full name of the <tt>Exchange</tt> is used as an identifier. All other items * are attached to the full name. * * <P> This type-safe enumeration is unusual in that each element is not * declared or exported individually; rather, the {@link #VALUES} field includes * all elements in one <tt>Collection</tt>. Thus, the caller cannot refer to * specific <tt>Exchange</tt> objects. */ public final class Exchange implements Comparable<Exchange> { /* * Implementation Note: * This class is unusual in that its data is read in from an associated text file, * which is placed in the same directory as this class. This text file must be available * at runtime. The text file is read when this class is loaded. * * The format of the text file is shown by these example lines: * # Blah comment * NYSE Stock Exchanges (NYS) N/A * Amsterdam Stock Exchange (AEX) .AS * ..etc * * This class is not implemented as an enum class, since it relies on an * underlying file for its data, and cannot declare enumeration members in the * usual way. */ /** * Return the suffix representing the <tt>Exchange</tt> (excluding dot). * * <P>These suffixes are defined by Yahoo, and are appended to tickers in order * to provide an exact identification of a traded entity. For example, the Toronto * Stock Exchange is assigned the TO suffix. Yahoo will append .TO to all * tickers defined by the Toronto Stock Exchange. * * <P>In this system, some exchanges (for example, the NYSE) do not have * a suffix. In this case, an empty <tt>String</tt> is returned. * * <P>The suffix is not intended for display to the end user; rather, these * suffixes should be an internal detail hidden from them. */ public String getTickerSuffix(){ return fTickerSuffix; } /** * Return the full name of this <tt>Exchange</tt>, suitable for presentation to the * end user. * * <P>The suffix is not included in the return value. */ @Override public String toString() { return fName; } /** * Convert <tt>aText</tt> into its corresponding <tt>Exchange</tt> object, * if possible. * * @param aText possibly-null text which may map to an Exchange. * @return null if <tt>aText</tt> is null, else try to * match to the <tt>Exchange</tt> whose {@link #toString} equals <tt>aText</tt>. * @throws IllegalArgumentException if a non-null <tt>aText</tt> * cannot be matched to a known enumeration element. */ public static Exchange valueFrom(String aText) { if (aText == null) return null; for(Exchange exchange : VALUES){ if ( aText.equals( exchange.toString() ) ) { return exchange; } } throw new IllegalArgumentException("Cannot parse into Exchange object:" + aText); } @Override public int compareTo(Exchange that) { return fOrdinal - that.fOrdinal; } private final String fName; private final String fTickerSuffix; private static int fNextOrdinal = 0; private final int fOrdinal = fNextOrdinal++; /** Text file name */ private static final String TEXT_FILE_NAME = "exchanges.txt"; /** * The text file resource uses this char to denote comment lines. */ private static final String COMMENT_CHAR = "#"; /** * The text file resource uses this String to denote absent data. * For example, the NYSE has no associated suffix, whose absence is * denoted by this String. */ private static final String NOT_AVAILABLE = "N/A"; /** * Populated when this class is loaded, this field contains the * parsed result of reading in the text resource file. */ private static List<Exchange> fValues; /** * Private constructor is needed to disallow the caller from constructing * these objects. */ private Exchange (String aName, String aTickerSuffix) { fName = aName; fTickerSuffix = aTickerSuffix; } /** * Parse the text resource file and store the result in a field. */ static { fValues = new ArrayList<>(); parseExchangesFromTextFile(); } /** * Parse each line not starting with a fCOMMENT_CHAR into an Exchange object. * Add each Exchange object to fValues, in the same order as in the text file. */ private static void parseExchangesFromTextFile() { List<String> exchangesText = FileUtil.asLines(TEXT_FILE_NAME, Exchange.class); for(String line: exchangesText){ if ( ! line.startsWith(COMMENT_CHAR) ){ parseExchange(line); } } } static private void parseExchange(String aLine){ StringTokenizer parser = new StringTokenizer(aLine, Consts.TAB); String fullName = parser.nextToken(); //ignore the abbreviation: parser.nextToken(); String suffix = getSuffix( parser.nextToken() ); Exchange exchange = new Exchange(fullName, suffix); fValues.add(exchange); } static private String getSuffix(String aRawSuffix){ if ( aRawSuffix.equals(NOT_AVAILABLE) ) { return Consts.EMPTY_STRING; } else { return removeLeadingDot(aRawSuffix); } } static private String removeLeadingDot(String aRawSuffix){ return aRawSuffix.substring(1); } /** * Allows caller to iterate over all elements of the enumeration. */ public static final List<Exchange> VALUES = Collections.unmodifiableList(fValues); }