com.google.code.stackexchange.client.query.impl.BaseStackOverflowApiQuery.java Source code

Java tutorial

Introduction

Here is the source code for com.google.code.stackexchange.client.query.impl.BaseStackOverflowApiQuery.java

Source

/*
 * Copyright 2010 Nabeel Mukhtar 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 *  http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 * 
 */
package com.google.code.stackexchange.client.query.impl;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.code.stackexchange.client.AsyncResponseHandler;
import com.google.code.stackexchange.client.constant.ApplicationConstants;
import com.google.code.stackexchange.client.exception.StackExchangeApiException;
import com.google.code.stackexchange.client.impl.StackExchangeApiGateway;
import com.google.code.stackexchange.client.provider.ApiProvider;
import com.google.code.stackexchange.client.provider.url.ApiUrlBuilder;
import com.google.code.stackexchange.client.query.StackExchangeApiQuery;
import com.google.code.stackexchange.common.PagedArrayList;
import com.google.code.stackexchange.common.PagedList;
import com.google.code.stackexchange.schema.Answer;
import com.google.code.stackexchange.schema.Badge;
import com.google.code.stackexchange.schema.BadgeRank;
import com.google.code.stackexchange.schema.Comment;
import com.google.code.stackexchange.schema.Error;
import com.google.code.stackexchange.schema.PostTimeline;
import com.google.code.stackexchange.schema.PostTimelineType;
import com.google.code.stackexchange.schema.PostType;
import com.google.code.stackexchange.schema.Question;
import com.google.code.stackexchange.schema.Reputation;
import com.google.code.stackexchange.schema.Revision;
import com.google.code.stackexchange.schema.RevisionType;
import com.google.code.stackexchange.schema.SchemaEntity;
import com.google.code.stackexchange.schema.Site;
import com.google.code.stackexchange.schema.SiteState;
import com.google.code.stackexchange.schema.Statistics;
import com.google.code.stackexchange.schema.Tag;
import com.google.code.stackexchange.schema.TagRestriction;
import com.google.code.stackexchange.schema.User;
import com.google.code.stackexchange.schema.UserTimeline;
import com.google.code.stackexchange.schema.UserTimelineType;
import com.google.code.stackexchange.schema.UserType;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;

/**
 * The Class BaseStackOverflowApiQuery.
 */
public abstract class BaseStackOverflowApiQuery<T> extends StackExchangeApiGateway
        implements StackExchangeApiQuery<T> {

    /** The api url builder. */
    protected ApiUrlBuilder apiUrlBuilder;

    protected static final Charset UTF_8_CHAR_SET = Charset.forName(ApplicationConstants.CONTENT_ENCODING);

    /** The parser. */
    private final JsonParser parser = new JsonParser();

    private static final Map<Class<? extends SchemaEntity>, String> LIST_PLACE_HOLDERS = new HashMap<Class<? extends SchemaEntity>, String>();

    static {
        LIST_PLACE_HOLDERS.put(Answer.class, "answers");
        LIST_PLACE_HOLDERS.put(Badge.class, "badges");
        LIST_PLACE_HOLDERS.put(Comment.class, "comments");
        LIST_PLACE_HOLDERS.put(Question.class, "questions");
        LIST_PLACE_HOLDERS.put(PostTimeline.class, "post_timelines");
        LIST_PLACE_HOLDERS.put(Reputation.class, "rep_changes");
        LIST_PLACE_HOLDERS.put(Statistics.class, "statistics");
        LIST_PLACE_HOLDERS.put(Tag.class, "tags");
        LIST_PLACE_HOLDERS.put(User.class, "users");
        LIST_PLACE_HOLDERS.put(UserTimeline.class, "user_timelines");
        LIST_PLACE_HOLDERS.put(Revision.class, "revisions");
        LIST_PLACE_HOLDERS.put(Site.class, "api_sites");
    }

    /** The handlers. */
    private List<AsyncResponseHandler<PagedList<T>>> handlers = new ArrayList<AsyncResponseHandler<PagedList<T>>>();

    /**
     * Instantiates a new base stack overflow api query.
     * 
     * @param applicationId the application id
     */
    public BaseStackOverflowApiQuery(String applicationId) {
        super.setApplicationKey(applicationId);
        requestHeaders = new HashMap<String, String>();

        // by default we compress contents
        requestHeaders.put("Accept-Encoding", "gzip, deflate");
        this.reset();
    }

    @Override
    public void setApiProvider(ApiProvider apiProvider) {
        super.setApiProvider(apiProvider);
        this.reset();
    }

    /**
     * Instantiates a new base stack overflow api query.
     * 
     * @param applicationId the application id
     * @param apiVersion the api version
     */
    public BaseStackOverflowApiQuery(String applicationId, String apiVersion) {
        this(applicationId);
        super.setApiVersion(apiVersion);
    }

    /* (non-Javadoc)
     * @see com.google.code.stackexchange.client.query.StackOverflowApiQuery#list()
     */
    @Override
    public PagedList<T> list() {
        InputStream jsonContent = null;
        try {
            jsonContent = callApiMethod(apiUrlBuilder.buildUrl());
            JsonElement response = parser.parse(new InputStreamReader(jsonContent, UTF_8_CHAR_SET));
            if (response.isJsonObject()) {
                PagedList<T> responseList = unmarshall(response.getAsJsonObject());
                notifyObservers(responseList);
                return responseList;
            }
            throw new StackExchangeApiException("Unknown content found in response:" + response.toString());
        } catch (Exception e) {
            throw new StackExchangeApiException(e);
        } finally {
            closeStream(jsonContent);
        }
    }

    /* (non-Javadoc)
     * @see com.google.code.stackexchange.client.query.StackOverflowApiQuery#singleResult()
     */
    @Override
    public T singleResult() {
        InputStream jsonContent = null;
        try {
            jsonContent = callApiMethod(apiUrlBuilder.buildUrl());
            JsonElement response = parser.parse(new InputStreamReader(jsonContent, UTF_8_CHAR_SET));
            if (response.isJsonObject()) {
                PagedList<T> responseList = unmarshall(response.getAsJsonObject());
                notifyObservers(responseList);
                return getFirstElement(responseList);
            }
            throw new StackExchangeApiException("Unknown content found in response:" + response.toString());
        } catch (Exception e) {
            throw new StackExchangeApiException(e);
        } finally {
            closeStream(jsonContent);
        }
    }

    /**
     * Notify observers.
     * 
     * @param response the response
     */
    protected void notifyObservers(PagedList<T> response) {
        for (AsyncResponseHandler<PagedList<T>> handler : handlers) {
            handler.handleResponse(response);
        }
    }

    /* (non-Javadoc)
     * @see com.google.code.stackexchange.client.query.StackExchangeApiQuery#addResonseHandler(com.google.code.stackexchange.client.AsyncResponseHandler)
     */
    public void addResonseHandler(AsyncResponseHandler<PagedList<T>> handler) {
        handlers.add(handler);
    }

    protected <A> A unmarshallObject(Class<A> clazz, InputStream jsonContent) {
        if (clazz.equals(Error.class)) {
            try {
                JsonElement response = parser.parse(new InputStreamReader(jsonContent, UTF_8_CHAR_SET));
                if (response.isJsonObject()) {
                    JsonObject adaptee = response.getAsJsonObject();
                    Gson gson = getGsonBuilder().create();
                    return gson.fromJson(adaptee.get("error"), clazz);
                }
            } catch (Exception e) {
                throw new StackExchangeApiException(e);
            }
        }
        return null;
    }

    protected <A> PagedList<A> unmarshallList(Class<A> clazz, InputStream jsonContent) {
        try {
            JsonElement response = parser.parse(new InputStreamReader(jsonContent, UTF_8_CHAR_SET));
            if (response.isJsonObject()) {
                JsonObject adaptee = response.getAsJsonObject();
                return unmarshallList(clazz, adaptee);
            }
            throw new StackExchangeApiException("Unknown content found in response:" + response.toString());
        } catch (Exception e) {
            throw new StackExchangeApiException(e);
        }
    }

    /**
     * @param <A>
     * @param clazz
     * @param adaptee
     * @return
     */
    protected <A> PagedList<A> unmarshallList(Class<A> clazz, JsonObject adaptee) {
        PagedList<A> list = new PagedArrayList<A>();
        if (adaptee.has("total")) {
            list.setTotal(adaptee.get("total").getAsLong());
        }
        if (adaptee.has("page")) {
            list.setPage(adaptee.get("page").getAsInt());
        }
        if (adaptee.has("pagesize")) {
            list.setPageSize(adaptee.get("pagesize").getAsInt());
        }
        String placeHolder = LIST_PLACE_HOLDERS.get(clazz);
        if (adaptee.has(placeHolder)) {
            JsonArray elements = adaptee.get(placeHolder).getAsJsonArray();
            if (elements != null) {
                Gson gson = getGsonBuilder().create();
                for (JsonElement o : elements) {
                    list.add(gson.fromJson(o, clazz));
                }
            }
        }
        return list;
    }

    /* (non-Javadoc)
     * @see com.google.code.stackexchange.client.impl.StackOverflowApiGateway#marshallObject(java.lang.Object)
     */
    protected String marshallObject(Object element) {
        return null;
    }

    /**
     * Unmarshall.
     * 
     * @param json the json
     * 
     * @return the paged list< t>
     */
    protected abstract PagedList<T> unmarshall(JsonObject json);

    /**
     * Gets the first element.
     * 
     * @param list the list
     * 
     * @return the first element
     */
    private T getFirstElement(List<T> list) {
        if (list.isEmpty()) {
            return null;
        }
        return list.get(0);
    }

    protected GsonBuilder getGsonBuilder() {
        GsonBuilder builder = new GsonBuilder();
        builder.registerTypeAdapter(Date.class, new JsonDeserializer<Date>() {

            @Override
            public Date deserialize(JsonElement source, Type arg1, JsonDeserializationContext arg2)
                    throws JsonParseException {
                return new Date(source.getAsLong() * 1000);
            }

        });
        builder.registerTypeAdapter(BadgeRank.class, new JsonDeserializer<BadgeRank>() {

            @Override
            public BadgeRank deserialize(JsonElement arg0, Type arg1, JsonDeserializationContext arg2)
                    throws JsonParseException {
                return BadgeRank.fromValue(arg0.getAsString());
            }

        });
        builder.registerTypeAdapter(PostType.class, new JsonDeserializer<PostType>() {

            @Override
            public PostType deserialize(JsonElement arg0, Type arg1, JsonDeserializationContext arg2)
                    throws JsonParseException {
                return PostType.fromValue(arg0.getAsString());
            }

        });
        builder.registerTypeAdapter(PostTimelineType.class, new JsonDeserializer<PostTimelineType>() {

            @Override
            public PostTimelineType deserialize(JsonElement arg0, Type arg1, JsonDeserializationContext arg2)
                    throws JsonParseException {
                return PostTimelineType.fromValue(arg0.getAsString());
            }

        });
        builder.registerTypeAdapter(UserTimelineType.class, new JsonDeserializer<UserTimelineType>() {

            @Override
            public UserTimelineType deserialize(JsonElement arg0, Type arg1, JsonDeserializationContext arg2)
                    throws JsonParseException {
                return UserTimelineType.fromValue(arg0.getAsString());
            }

        });
        builder.registerTypeAdapter(UserType.class, new JsonDeserializer<UserType>() {

            @Override
            public UserType deserialize(JsonElement arg0, Type arg1, JsonDeserializationContext arg2)
                    throws JsonParseException {
                return UserType.fromValue(arg0.getAsString());
            }

        });
        builder.registerTypeAdapter(RevisionType.class, new JsonDeserializer<RevisionType>() {

            @Override
            public RevisionType deserialize(JsonElement arg0, Type arg1, JsonDeserializationContext arg2)
                    throws JsonParseException {
                return RevisionType.fromValue(arg0.getAsString());
            }

        });
        builder.registerTypeAdapter(TagRestriction.class, new JsonDeserializer<TagRestriction>() {

            @Override
            public TagRestriction deserialize(JsonElement arg0, Type arg1, JsonDeserializationContext arg2)
                    throws JsonParseException {
                return TagRestriction.fromValue(arg0.getAsString());
            }

        });
        builder.registerTypeAdapter(SiteState.class, new JsonDeserializer<SiteState>() {

            @Override
            public SiteState deserialize(JsonElement arg0, Type arg1, JsonDeserializationContext arg2)
                    throws JsonParseException {
                return SiteState.fromValue(arg0.getAsString());
            }
        });

        builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);

        return builder;
    }
}