Package com.coherentlogic.fred.client.core.builders

Source Code of com.coherentlogic.fred.client.core.builders.QueryBuilder

package com.coherentlogic.fred.client.core.builders;

import static com.coherentlogic.fred.client.core.util.Constants.CATEGORIES;
import static com.coherentlogic.fred.client.core.util.Constants.CATEGORY;
import static com.coherentlogic.fred.client.core.util.Constants.CHILDREN;
import static com.coherentlogic.fred.client.core.util.Constants.DATES;
import static com.coherentlogic.fred.client.core.util.Constants.DATE_FORMAT;
import static com.coherentlogic.fred.client.core.util.Constants.DATE_PATTERN;
import static com.coherentlogic.fred.client.core.util.Constants.OBSERVATIONS;
import static com.coherentlogic.fred.client.core.util.Constants.RELATED;
import static com.coherentlogic.fred.client.core.util.Constants.RELEASE;
import static com.coherentlogic.fred.client.core.util.Constants.RELEASES;
import static com.coherentlogic.fred.client.core.util.Constants.SEARCH;
import static com.coherentlogic.fred.client.core.util.Constants.SERIES;
import static com.coherentlogic.fred.client.core.util.Constants.SOURCE;
import static com.coherentlogic.fred.client.core.util.Constants.SOURCES;
import static com.coherentlogic.fred.client.core.util.Constants.TAGS;
import static com.coherentlogic.fred.client.core.util.Constants.TAG_NAMES;
import static com.coherentlogic.fred.client.core.util.Constants.TAG_GROUP_ID;
import static com.coherentlogic.fred.client.core.util.Constants.TAG_SEARCH_TEXT;
import static com.coherentlogic.fred.client.core.util.Constants.UPDATES;
import static com.coherentlogic.fred.client.core.util.Constants.SERIES_SEARCH_TEXT;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.regex.Matcher;

import javax.ws.rs.core.UriBuilder;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestTemplate;

import com.coherentlogic.coherent.data.model.core.builders.AbstractQueryBuilder;
import com.coherentlogic.fred.client.core.domain.AggregationMethod;
import com.coherentlogic.fred.client.core.domain.FileType;
import com.coherentlogic.fred.client.core.domain.FilterValue;
import com.coherentlogic.fred.client.core.domain.FilterVariable;
import com.coherentlogic.fred.client.core.domain.Frequency;
import com.coherentlogic.fred.client.core.domain.OrderBy;
import com.coherentlogic.fred.client.core.domain.OutputType;
import com.coherentlogic.fred.client.core.domain.SearchType;
import com.coherentlogic.fred.client.core.domain.SortOrder;
import com.coherentlogic.fred.client.core.domain.TagGroupId;
import com.coherentlogic.fred.client.core.domain.Unit;
import com.coherentlogic.fred.client.core.exceptions.DateOutOfBoundsException;
import com.coherentlogic.fred.client.core.exceptions.InvalidDateFormatException;
import com.coherentlogic.fred.client.core.exceptions.InvalidParameterValue;
import com.coherentlogic.fred.client.core.exceptions.LimitOutOfBoundsException;
import com.coherentlogic.fred.client.core.exceptions.OffsetOutOfBoundsException;

/**
* Class that allows the developer to construct and execute a query to the
* Federal Reserve Bank of St. Louis' FRED web services.
* <p>
* Note that this class is <b>not</b> thread-safe and cannot be used as a member
* -level property -- if this is required, use the
* {@link com.coherentlogic.fred.client.core.builders#RequestBuilderFactory
* QueryBuilderFactory} class.
* <p>
* In order to facilitate method-chaining each setter method returns a reference
* to this object.
* <p>
* For examples, refer to the {@link
* com.coherentlogic.fred.client.core.builders#QueryBuilderTest
* QueryBuilderTest} class.
*
* @author <a href="support@coherentlogic.com">Support</a>
*/
public class QueryBuilder extends AbstractQueryBuilder {

    private static final Logger log =
        LoggerFactory.getLogger(QueryBuilder.class);

    /**
     * @todo Move this message so that it appears in the AbstractQueryBuilder.
     */
    static {
        log.warn("***********************************************************");
        log.warn("*** Welcome to the Coherent Logic FRED Client v. 0.9.9. ***");
        log.warn("***                                                     ***");
        log.warn("***    Please take a moment to follow us on Twitter:    ***");
        log.warn("***                                                     ***");
        log.warn("***         https://twitter.com/CoherentMktData         ***");
        log.warn("***                                                     ***");
        log.warn("***********************************************************");
    }

    public static final String
        API_KEY = "api_key",
        CATEGORY_ID = "category_id",
        REALTIME_START = "realtime_start",
        REALTIME_END = "realtime_end",
        LIMIT = "limit",
        OFFSET = "offset",
        ORDER_BY = "order_by",
        SORT_ORDER = "sort_order",
        FILTER_VARIABLE = "filter_variable",
        FILTER_VALUE = "filter_value",
        INCLUDE_RELEASE_DATES_WITH_NO_DATA
            = "include_release_dates_with_no_data",
        RELEASE_ID = "release_id",
        OBSERVATION_START = "observation_start",
        OBSERVATION_END = "observation_end",
        UNITS = "units",
        FREQUENCY = "frequency",
        AGGREGATION_METHOD = "aggregation_method",
        OUTPUT_TYPE = "output_type",
        FILE_TYPE = "file_type",
        VINTAGE_DATES = "vintagedates",
        VINTAGE_DATES_PARAM = "vintage_dates",
        SERIES_ID = "series_id",
        SOURCE_ID = "source_id",
        SEARCH_TEXT = "search_text",
        SEARCH_TYPE = "search_type",
        SEMICOLON = ";";

    private static final Calendar MIN_DATE_CALENDAR, MAX_DATE_CALENDAR;

    static {
        MIN_DATE_CALENDAR = Calendar.getInstance();
        MIN_DATE_CALENDAR.set(Calendar.YEAR, 1776);
        MIN_DATE_CALENDAR.set(Calendar.MONTH, Calendar.JULY);
        MIN_DATE_CALENDAR.set(Calendar.DAY_OF_MONTH, 04);

        MAX_DATE_CALENDAR = Calendar.getInstance();
        MAX_DATE_CALENDAR.set(Calendar.YEAR, 9999);
        MAX_DATE_CALENDAR.set(Calendar.MONTH, Calendar.DECEMBER);
        MAX_DATE_CALENDAR.set(Calendar.DAY_OF_MONTH, 31);
    }

    public QueryBuilder (
        RestTemplate restTemplate,
        String uri
    ) {
        super (restTemplate, uri);
    }

    public QueryBuilder (
        RestTemplate restTemplate,
        UriBuilder uriBuilder
    ) {
        super (restTemplate, uriBuilder);
    }

    /**
     * Extends the path to include series -- for example:
     *
     * http://api.stlouisfed.org/fred/series
     */
    public QueryBuilder series () {
        extendPathWith(SERIES);

        return this;
    }

    /**
     * Extends the path to include categories -- for example:
     *
     * http://api.stlouisfed.org/fred/categories
     */
    public QueryBuilder categories () {
        extendPathWith(CATEGORIES);

        return this;
    }

    /**
     * Extends the path to include category -- for example:
     *
     * http://api.stlouisfed.org/fred/category
     */
    public QueryBuilder category () {
        extendPathWith(CATEGORY);

        return this;
    }

    /**
     * Extends the path to include observations -- for example:
     *
     * http://api.stlouisfed.org/fred/observations
     */
    public QueryBuilder observations () {
        extendPathWith(OBSERVATIONS);

        return this;
    }

    /**
     * Extends the path to include releases -- for example:
     *
     * http://api.stlouisfed.org/fred/releases
     */
    public QueryBuilder releases () {
        extendPathWith(RELEASES);

        return this;
    }

    /**
     * Extends the path to include release -- for example:
     *
     * http://api.stlouisfed.org/fred/release
     */
    public QueryBuilder release () {
        extendPathWith(RELEASE);

        return this;
    }

    /**
     * Extends the path to include updates -- for example:
     *
     * http://api.stlouisfed.org/fred/updates
     */
    public QueryBuilder updates () {
        extendPathWith(UPDATES);

        return this;
    }

    /**
     * Extends the path to include vintagedates -- for example:
     *
     * http://api.stlouisfed.org/fred/vintagedates
     */
    public QueryBuilder vintageDates () {
        extendPathWith(VINTAGE_DATES);

        return this;
    }

    /**
     * Extends the path to include search -- for example:
     *
     * http://api.stlouisfed.org/fred/search
     */
    public QueryBuilder search () {
        extendPathWith(SEARCH);

        return this;
    }

    /**
     * Extends the path to include children -- for example:
     *
     * http://api.stlouisfed.org/fred/children
     */
    public QueryBuilder children () {
        extendPathWith(CHILDREN);

        return this;
    }

    /**
     * Extends the path to include related -- for example:
     *
     * http://api.stlouisfed.org/fred/related
     */
    public QueryBuilder related () {
        extendPathWith(RELATED);

        return this;
    }

    /**
     * Extends the path to include sources -- for example:
     *
     * http://api.stlouisfed.org/fred/sources
     */
    public QueryBuilder sources () {
        extendPathWith(SOURCES);

        return this;
    }

    /**
     * Extends the path to include source -- for example:
     *
     * http://api.stlouisfed.org/fred/source
     */
    public QueryBuilder source () {
        extendPathWith(SOURCE);

        return this;
    }

    /**
     * Extends the path to include dates -- for example:
     *
     * http://api.stlouisfed.org/fred/dates
     */
    public QueryBuilder dates () {
        extendPathWith(DATES);

        return this;
    }

    /**
     * Extends the path to include tags -- for example:
     *
     * http://api.stlouisfed.org/fred/tags
     */
    public QueryBuilder tags () {
        extendPathWith(TAGS);

        return this;
    }

    /**
     * Setter method for the API key parameter. Note the API key is requires by
     * every FRED web service.
     *
     * Register for an API key <a href="http://api.stlouisfed.org/api_key.html">
     * here</a>.
     *
     * @param apiKey For example, "abcdefghijklmnopqrstuvwxyz123456".
     */
    public QueryBuilder setApiKey (String apiKey) {

        addParameter(API_KEY, apiKey);

        return this;
    }

    /**
     * Setter method for the series id parameter.
     *
     * @param seriesId For example, "GNPCA".
     */
    public QueryBuilder setSeriesId (String seriesId) {

        put(SERIES_ID, seriesId);

        return this;
    }

    /**
     * Setter method for the release id parameter.
     *
     * @param releaseId For example, 53L.
     */
    public QueryBuilder setReleaseId (long releaseId) {

        put(RELEASE_ID, Long.toString(releaseId));

        return this;
    }

    /**
     * Setter method for the category id parameter.
     */
    public QueryBuilder setCategoryId (long categoryId) {

        put(CATEGORY_ID, Long.toString(categoryId));

        return this;
    }

    /**
     * Setter method for the source id parameter.
     */
    public QueryBuilder setSourceId (long sourceId) {

        put(SOURCE_ID, Long.toString(sourceId));

        return this;
    }

    /**
     * Setter method for the real-time start date parameter.
     */
    public QueryBuilder setRealtimeStart (Date realtimeStart) {

        boolean between = isBetween (realtimeStart);

        if (!between)
            throw new DateOutOfBoundsException(realtimeStart);

        DateFormat dateFormat = new SimpleDateFormat (DATE_FORMAT);

        String realtimeStartText = dateFormat.format(realtimeStart);

        put(REALTIME_START, realtimeStartText);

        return this;
    }

    /**
     * Setter method for the real-time start date parameter.
     *
     * @throws InvalidDateFormatException When the date does not conform to the
     *  expected format (ie. yyyy-MM-dd / 2012-06-21).
     */
    public QueryBuilder setRealtimeStart (String realtimeStart) {

        assertDateFormatIsValid ("realtimeStart", realtimeStart);

        put(REALTIME_START, realtimeStart);

        return this;
    }

    /**
     * Setter method for the real-time end date parameter.
     */
    public QueryBuilder setRealtimeEnd (Date realtimeEnd) {

        boolean between = isBetween (realtimeEnd);

        if (!between)
            throw new DateOutOfBoundsException(realtimeEnd);

        DateFormat dateFormat = new SimpleDateFormat (DATE_FORMAT);

        String realtimeEndText = dateFormat.format(realtimeEnd);

        put(REALTIME_END, realtimeEndText);

        return this;
    }

    /**
     * Setter method for the real-time end date parameter.
     *
     * @throws InvalidDateFormatException When the date does not conform to the
     *  expected format (ie. yyyy-MM-dd / 2012-06-21).
     */
    public QueryBuilder setRealtimeEnd (String realtimeEnd) {

        assertDateFormatIsValid ("realtimeEnd", realtimeEnd);

        put(REALTIME_END, realtimeEnd);

        return this;
    }

    /**
     * Setter method for the limit parameter.
     *
     * @param limit A value between 1 and 100000 (inclusive).
     */
    public QueryBuilder setLimit (long limit) {

        if (!(1 <= limit && limit <= 100000))
            throw new LimitOutOfBoundsException(limit);

        put(LIMIT, Long.toString(limit));

        return this;
    }

    /**
     * Setter method for the offset parameter.
     *
     * @param offset A non-negative integer.
     *
     * @throws OffsetOutOfBoundsException if the value of the offset is less
     *  than zero.
     */
    public QueryBuilder setOffset (int offset) {

        if (offset < 0)
            throw new OffsetOutOfBoundsException (offset);

        put(OFFSET, Integer.toString(offset));

        return this;
    }

    /**
     * Setter method for the order-by parameter.
     *
     * @see {@link OrderBy}
     */
    public QueryBuilder setOrderBy (OrderBy orderBy) {

        put(ORDER_BY, orderBy.toString());

        return this;
    }

    /**
     * Setter method for the sort order parameter.
     *
     * @see {@link SortOrder}
     */
    public QueryBuilder setSortOrder (SortOrder sortOrder) {

        put(SORT_ORDER, sortOrder.toString());

        return this;
    }

    /**
     * Setter method for the filter variable parameter.
     *
     * @see {@link FilterVariable}
     */
    public QueryBuilder setFilterVariable (FilterVariable filterVariable) {

        put(FILTER_VARIABLE, filterVariable.toString());

        return this;
    }

    /**
     * Setter method for the filter value parameter.
     *
     * @see {@link FilterValue}
     */
    public QueryBuilder setFilterValue (FilterValue filterValue) {

        put(FILTER_VALUE, filterValue.toString());

        return this;
    }

    /**
     * Setter method for the include-release-dates-with-no-data flag parameter.
     */
    public QueryBuilder setIncludeReleaseDatesWithNoData (boolean value) {

        put(INCLUDE_RELEASE_DATES_WITH_NO_DATA, Boolean.toString(value));

        return this;
    }

    /**
     * Setter method for the observation start date parameter.
     */
    public QueryBuilder setObservationStart (Date observationStart) {

        boolean between = isBetween (observationStart);

        if (!between)
            throw new DateOutOfBoundsException(observationStart);

        DateFormat dateFormat = new SimpleDateFormat (DATE_FORMAT);

        String observationStartText = dateFormat.format(observationStart);

        put(OBSERVATION_START, observationStartText);

        return this;
    }

    /**
     * Setter method for the observation start date parameter.
     *
     * @throws InvalidDateFormatException When the date does not conform to the
     *  expected format (ie. yyyy-MM-dd / 2012-06-21).
     */
    public QueryBuilder setObservationStart (String observationStart) {

        assertDateFormatIsValid ("observationStart", observationStart);

        put(OBSERVATION_START, observationStart);

        return this;
    }

    /**
     * Setter method for the observation end date parameter.
     *
     * @param observationEnd
     */
    public QueryBuilder setObservationEnd (Date observationEnd) {

        boolean between = isBetween (observationEnd);

        if (!between)
            throw new DateOutOfBoundsException(observationEnd);

        DateFormat dateFormat = new SimpleDateFormat (DATE_FORMAT);

        String observationEndText = dateFormat.format(observationEnd);

        put(OBSERVATION_END, observationEndText);

        return this;
    }

    /**
     * Setter method for the observation end date parameter parameter.
     *
     * @throws InvalidDateFormatException When the date does not conform to the
     *  expected format (ie. yyyy-MM-dd / 2012-06-21).
     */
    public QueryBuilder setObservationEnd (String observationEnd) {

        assertDateFormatIsValid ("observationEnd", observationEnd);

        put(OBSERVATION_END, observationEnd);

        return this;
    }

    /**
     * Setter method for the units parameter.
     *
     * @see {@link #Unit}
     */
    public QueryBuilder setUnits (Unit unit) {

        put(UNITS, unit.toString());

        return this;
    }

    /**
     * Setter method for the frequency parameter.
     *
     * @see {@link Frequency}
     */
    public QueryBuilder setFrequency (Frequency frequency) {

        put(FREQUENCY, frequency.toString());

        return this;
    }

    /**
     * Setter method for the aggregation method parameter.
     *
     * @see {@link AggregationMethod}
     */
    public QueryBuilder setAggregationMethod (
        AggregationMethod aggregationMethod) {

        put(AGGREGATION_METHOD, aggregationMethod.toString());

        return this;
    }

    /**
     * Setter method for the output type parameter.
     *
     * @see {@link OutputType}
     */
    public QueryBuilder setOutputType (OutputType outputType) {

        put(OUTPUT_TYPE, outputType.toString());

        return this;
    }

    /**
     * Setter method for the file type parameter.
     *
     * @see {@link FileType}
     */
    public QueryBuilder setFileType (FileType fileType) {

        put(FILE_TYPE, fileType.toString());

        return this;
    }

    /**
     * Setter method for the vinatage dates parameter, which can be a single
     * date, or a list of dates separated by a comma.
     *
     * Note that the format of the dates is not checked client-side -- if there
     * is an invalid date, the server will reject the call.
     */
    public QueryBuilder setVintageDates (String vintageDates) {

        put(VINTAGE_DATES_PARAM, vintageDates);

        return this;
    }

    /**
     * Setter method for the array of vintage dates parameter.
     *
     * @throws InvalidDateFormatException If any of the dates are not of the
     *  format yyyy-MM-dd.
     */
    public QueryBuilder setVintageDates (String... vintageDates) {

        String value = convertDates ("setVintageDates", ",", vintageDates);

        put(VINTAGE_DATES_PARAM, value);

        return this;
    }

    /**
     * Setter method for the search text  parameter which consists of the words
     * to match against economic data series.
     */
    public QueryBuilder setSearchText (String searchText) {

        put(SEARCH_TEXT, searchText);

        return this;
    }

    /**
     * Setter method for the series search text  parameter which consists of the
     * words to match against economic data series -- for example
     * "monetary service index".
     */
    public QueryBuilder setSeriesSearchText (String seriesSearchText) {

        put(SERIES_SEARCH_TEXT, seriesSearchText);

        return this;
    }

    /**
     * Setter method for the search type parameter.
     *
     * @see {@link SearchType}
     */
    public QueryBuilder setSearchType (SearchType searchType) {

        put(SEARCH_TYPE, searchType.toString());

        return this;
    }

    /**
     * Method takes a single string, such as "slovenia;food;oecd", and set this
     * as the tagName; this value filters results to match either tag
     * "slovenia", "food", or "oecd".
     *
     * @see http://api.stlouisfed.org/docs/fred/series_search_related_tags.html
     */
    public QueryBuilder setTagNames (String tagNames) {

        put(TAG_NAMES, tagNames);

        return this;
    }

    /**
     * An array of tags to filter results by -- optional, no filtering by tags
     * by default.
     *
     * For example: 'm1;m2' -- this value filters results to match either tag
     * 'm1' or tag 'm2'.
     *
     * This method takes an array of strings, such as "slovenia", "food", "oecd"
     * and creates a single aggregated string with each value separated by a
     * semicolon (ie. "slovenia;food;oecd").
     *
     * @see http://api.stlouisfed.org/docs/fred/series_search_related_tags.html
     */
    public QueryBuilder setTagNames (String... tagNames) {

        String result = combine (SEMICOLON, tagNames);

        return setTagNames (result);
    }

    /**
     * A tag group id to filter tags by type.
     *
     * String, optional, no filtering by tag group by default.
     * One of the following: 'freq', 'gen', 'geo', 'geot', 'rls', 'seas', 'src'.
     *
     * freq = Frequency
     * gen = General or Concept
     * geo = Geography
     * geot = Geography Type
     * rls = Release
     * seas = Seasonal Adjustment
     * src = Source
     */
    public QueryBuilder setTagGroupId (TagGroupId tagGroupId) {

        put (TAG_GROUP_ID, tagGroupId.toString());

        return this;
    }

    /**
     * The words to find matching tags with -- optional, no filtering by search
     * words by default.
     */
    public QueryBuilder setTagSearchText (String tagSearchText) {

        put (TAG_SEARCH_TEXT, tagSearchText);

        return this;
    }

    /**
     * Method adds a name-value pair to the internal list of name-value pairs.
     *
     * @param name The name of the parameter.
     * @param value The parameter value.
     */
    private void put (String name, String value) {
        addParameter(name, value);
    }

    static String combine (String seperator, String... values) {

        assertNotNullOrEmpty ("values", values);

        String result = "";

        int ctr = values.length;

        for (String next : values) {
            result += next;

            if (1 < ctr--) {
                result += seperator;
            }
        }
        return result;
    }

    /**
     * @todo Move this method to the parent package.
     */
    static void assertNotNullOrEmpty (String variableName, Object[] values) {
        if (values == null || values.length == 0)
            throw new InvalidParameterValue ("The variable named '" +
                variableName + "' is either null or empty (" +
                ToStringBuilder.reflectionToString(values) + ").");
    }

    /**
     * Method checks that the actual date is between the
     * {@link #MIN_DATE_CALENDAR} and {@link #MAX_DATE_CALENDAR} dates.
     *
     * @param actual The actual date.
     *
     * @return True if the actual date is between the {@link #MIN_DATE_CALENDAR}
     *  and {@link #MAX_DATE_CALENDAR} dates.
     */
    private boolean isBetween (Date actual) {

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(actual);

        int minValue = calendar.compareTo(MIN_DATE_CALENDAR);
        int maxValue = calendar.compareTo(MAX_DATE_CALENDAR);

        boolean result = (0 <= minValue) && (maxValue <= 0);

        return result;
    }

    /**
     * Method converts the <i>dates</i> into a single String, with each date
     * separated by a comma.
     *
     * @param method The name of the method calling this method; this is used
     *  when an exception is thrown because one of the dates is in an invalid
     *  format.
     *
     * @param seperator The character which separates the dates.
     *
     * @param dates An array of dates. Note that each date must be in the format
     *  yyyy-MM-dd.
     *
     * @return A single string consisting of all dates separated by the
     *  separator.
     */
    static String convertDates (
        String method,
        String seperator,
        String... dates
    ) {
        StringBuffer buffer = new StringBuffer();

        if (dates != null && 0 < dates.length) {

            for (int ctr = 0; ctr < dates.length; ctr++) {

                assertDateFormatIsValid (method, dates[ctr]);

                buffer.append(dates[ctr]);

                if (ctr < dates.length - 1)
                    buffer.append(seperator);
            }
        }

        String result = (buffer.length() == 0) ? null : buffer.toString();

        return result;
    }

    /**
     * Method simply checks that the date format is valid and throws an
     * exception if it isn't.
     *
     * @param method The method that was invoked.
     *
     * @param date For example 2001-10-20, which is valid, or X001-1-2, which
     *  is invalid.
     *
     * @throws InvalidDateFormatException Whenever the date is invalid.
     */
    static void assertDateFormatIsValid (String method, String date) {
        Matcher matcher = DATE_PATTERN.matcher(date);

        if (!matcher.matches())
            throw new InvalidDateFormatException (
                "The date parameter " + date +" passed to the method " +
                method + " is invalid.");
    }
}
TOP

Related Classes of com.coherentlogic.fred.client.core.builders.QueryBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.