/*
Copyright (C) 2014 Prasanna Thirumalai
This file is part of StackX.
StackX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
StackX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with StackX. If not, see <http://www.gnu.org/licenses/>.
*/
package com.prasanna.android.stacknetwork.service;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.prasanna.android.http.AbstractHttpException;
import com.prasanna.android.http.ClientException;
import com.prasanna.android.http.HttpContentTypes;
import com.prasanna.android.http.HttpHeaderParams;
import com.prasanna.android.stacknetwork.model.Answer;
import com.prasanna.android.stacknetwork.model.Comment;
import com.prasanna.android.stacknetwork.model.Post.PostType;
import com.prasanna.android.stacknetwork.model.Question;
import com.prasanna.android.stacknetwork.model.SearchCriteria;
import com.prasanna.android.stacknetwork.model.StackXPage;
import com.prasanna.android.stacknetwork.utils.AppUtils;
import com.prasanna.android.stacknetwork.utils.JSONObjectWrapper;
import com.prasanna.android.stacknetwork.utils.JsonFields;
import com.prasanna.android.stacknetwork.utils.OperatingSite;
import com.prasanna.android.stacknetwork.utils.StackUri;
import com.prasanna.android.stacknetwork.utils.StackUri.Order;
import com.prasanna.android.stacknetwork.utils.StringConstants;
import com.prasanna.android.utils.LogWrapper;
public class QuestionServiceHelper extends AbstractBaseServiceHelper {
private static final String TAG = QuestionServiceHelper.class.getSimpleName();
private static final QuestionServiceHelper questionService = new QuestionServiceHelper();
protected QuestionServiceHelper() {
}
public static QuestionServiceHelper getInstance() {
return questionService;
}
public String getQuestionBodyForId(long id, String site) {
String restEndPoint = "questions/" + id;
String questionBody = null;
Map<String, String> queryParams = getDefaultQueryParams(site);
JSONObjectWrapper questionJsonResponse = executeHttpGetRequest(restEndPoint, queryParams);
if (questionJsonResponse != null) {
try {
JSONArray jsonArray = questionJsonResponse.getJSONArray(JsonFields.ITEMS);
if (jsonArray != null && jsonArray.length() == 1) {
JSONObjectWrapper jsonObject = JSONObjectWrapper.wrap(jsonArray.getJSONObject(0));
questionBody = jsonObject.getString(JsonFields.Question.BODY);
}
} catch (JSONException e) {
LogWrapper.d(TAG, e.getMessage());
}
}
return questionBody;
}
public String getQuestionBodyForId(long id) {
return getQuestionBodyForId(id, OperatingSite.getSite().apiSiteParameter);
}
public ArrayList<Answer> getAnswersForQuestion(long id, String site, int page) {
ArrayList<Answer> answers = new ArrayList<Answer>();
String restEndPoint = "questions/" + id + "/answers";
Map<String, String> queryParams = getDefaultQueryParams(site);
queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page));
queryParams.put(StackUri.QueryParams.PAGE_SIZE, String.valueOf(StackUri.QueryParamDefaultValues.ANSWERS_PAGE_SIZE));
queryParams.put(StackUri.QueryParams.SORT, StackUri.Sort.VOTES);
JSONObjectWrapper answersJson = executeHttpGetRequest(restEndPoint, queryParams);
if (answersJson != null) {
try {
JSONArray jsonArray = answersJson.getJSONArray(JsonFields.ITEMS);
if (jsonArray != null) {
for (int i = 0; i < jsonArray.length(); i++) {
JSONObjectWrapper jsonObject = JSONObjectWrapper.wrap(jsonArray.getJSONObject(i));
Answer answer = getSerializedAnswerObject(jsonObject);
if (answer.accepted && answers.size() > 0) answers.add(0, answer);
else answers.add(answer);
}
if (!answers.isEmpty()) getCommentsForAnswers(answers);
}
} catch (JSONException e) {
LogWrapper.d(TAG, e.getMessage());
}
}
return answers;
}
private void getCommentsForAnswers(ArrayList<Answer> answers) {
try {
StringBuilder stringBuilder = new StringBuilder();
HashMap<Long, Answer> idAnswerMap = new HashMap<Long, Answer>();
for (Answer answer : answers) {
stringBuilder.append(answer.id).append(";");
idAnswerMap.put(answer.id, answer);
}
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
getCommensAndUpdateAnswer(stringBuilder.toString(), idAnswerMap);
} catch (AbstractHttpException e) {
LogWrapper.e(TAG, e.getMessage());
}
}
private void getCommensAndUpdateAnswer(String answerIds, HashMap<Long, Answer> idAnswerMap) {
boolean hasMore = true;
int page = 1;
while (hasMore) {
StackXPage<Comment> commentsPage = getComments(StringConstants.ANSWERS, answerIds, page++);
if (commentsPage != null && commentsPage.items != null) {
for (Comment comment : commentsPage.items) {
if (idAnswerMap.containsKey(comment.post_id)) {
if (idAnswerMap.get(comment.post_id).comments == null)
idAnswerMap.get(comment.post_id).comments = new ArrayList<Comment>();
idAnswerMap.get(comment.post_id).comments.add(comment);
}
}
hasMore = commentsPage.hasMore;
} else hasMore = false;
}
}
public StackXPage<Comment> getComments(String parent, String site, String parentIds, int page) {
StackXPage<Comment> commentsPage = null;
String restEndPoint = parent + "/" + parentIds + "/comments";
Map<String, String> queryParams = getDefaultQueryParams(site);
queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page));
queryParams.put(StackUri.QueryParams.SORT, StackUri.Sort.CREATION);
queryParams.put(StackUri.QueryParams.ORDER, Order.ASC);
queryParams.put(StackUri.QueryParams.FILTER, StackUri.QueryParamDefaultValues.COMMENT_FILTER);
JSONObjectWrapper commentsJsonResponse = executeHttpGetRequest(restEndPoint, queryParams);
if (commentsJsonResponse != null) {
try {
getPageInfo(commentsJsonResponse, commentsPage);
JSONArray jsonArray = commentsJsonResponse.getJSONArray(JsonFields.ITEMS);
for (int count = 0; count < jsonArray.length(); count++) {
JSONObject jsonObject = jsonArray.getJSONObject(count);
if (commentsPage == null) {
commentsPage = new StackXPage<Comment>();
commentsPage.items = new ArrayList<Comment>();
}
commentsPage.items.add(Comment.parse(JSONObjectWrapper.wrap(jsonObject)));
}
} catch (JSONException e) {
LogWrapper.d(TAG, e.getMessage());
}
}
return commentsPage;
}
public StackXPage<Comment> getComments(String parent, String parentIds, int page) {
return getComments(parent, OperatingSite.getSite().apiSiteParameter, parentIds, page);
}
public StackXPage<Question> search(String query, int page) {
String restEndPoint = "search";
Map<String, String> queryParams = AppUtils.getDefaultQueryParams();
queryParams.put(StackUri.QueryParams.SITE, OperatingSite.getSite().apiSiteParameter);
queryParams.put(StackUri.QueryParams.IN_TITLE, query);
queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page));
queryParams.put(StackUri.QueryParams.PAGE_SIZE, String.valueOf(StackUri.QueryParamDefaultValues.PAGE_SIZE));
JSONObjectWrapper questionsJsonResponse = executeHttpGetRequest(restEndPoint, queryParams);
return getQuestionModel(questionsJsonResponse);
}
public StackXPage<Question> advancedSearch(SearchCriteria searchCriteria) {
String restEndPoint = "search/advanced";
Map<String, String> queryParams = AppUtils.getDefaultQueryParams();
queryParams.put(StackUri.QueryParams.SITE, OperatingSite.getSite().apiSiteParameter);
queryParams.putAll(searchCriteria.getMap());
return getQuestionModel(executeHttpGetRequest(restEndPoint, queryParams));
}
@Override
protected String getLogTag() {
return TAG;
}
public Question getQuestionFullDetails(long id, String site) {
String restEndPoint = "questions/" + id;
Question question = null;
Map<String, String> queryParams = getDefaultQueryParams(site);
JSONObjectWrapper questionJsonResponse = executeHttpGetRequest(restEndPoint, queryParams);
if (questionJsonResponse != null) {
try {
JSONArray jsonArray = questionJsonResponse.getJSONArray(JsonFields.ITEMS);
if (jsonArray != null && jsonArray.length() == 1) {
JSONObjectWrapper jsonObject = JSONObjectWrapper.wrap(jsonArray.getJSONObject(0));
question = getSerializedQuestionObject(jsonObject);
question.body = jsonObject.getString(JsonFields.Question.BODY);
}
} catch (JSONException e) {
LogWrapper.d(TAG, e.getMessage());
}
}
return question;
}
public Question getQuestionFullDetails(long id) {
return getQuestionFullDetails(id, OperatingSite.getSite().apiSiteParameter);
}
public StackXPage<Question> getAllQuestions(String sort, int page) {
if (sort == null) sort = StackUri.Sort.ACTIVITY;
Map<String, String> queryParams = AppUtils.getDefaultQueryParams();
queryParams.put(StackUri.QueryParams.ORDER, StackUri.QueryParamDefaultValues.ORDER);
queryParams.put(StackUri.QueryParams.SORT, sort);
queryParams.put(StackUri.QueryParams.SITE, OperatingSite.getSite().apiSiteParameter);
queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page));
queryParams.put(StackUri.QueryParams.PAGE_SIZE, String.valueOf(StackUri.QueryParamDefaultValues.PAGE_SIZE));
return getQuestionPage(StringConstants.QUESTIONS, queryParams);
}
public StackXPage<Question> getFeatured(int page) {
Map<String, String> queryParams = AppUtils.getDefaultQueryParams();
queryParams.put(StackUri.QueryParams.ORDER, StackUri.QueryParamDefaultValues.ORDER);
queryParams.put(StackUri.QueryParams.SORT, StackUri.Sort.ACTIVITY);
queryParams.put(StackUri.QueryParams.SITE, OperatingSite.getSite().apiSiteParameter);
queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page));
queryParams.put(StackUri.QueryParams.PAGE_SIZE, String.valueOf(StackUri.QueryParamDefaultValues.PAGE_SIZE));
return getQuestionPage("questions/featured", queryParams);
}
public StackXPage<Question> getRelatedQuestions(long questionId, int page) {
String restEndPoint = "questions/" + questionId + "/related";
Map<String, String> queryParams = AppUtils.getDefaultQueryParams();
queryParams.put(StackUri.QueryParams.ORDER, StackUri.QueryParamDefaultValues.ORDER);
queryParams.put(StackUri.QueryParams.SORT, StackUri.Sort.ACTIVITY);
queryParams.put(StackUri.QueryParams.SITE, OperatingSite.getSite().apiSiteParameter);
queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page));
queryParams.put(StackUri.QueryParams.PAGE_SIZE, String.valueOf(StackUri.QueryParamDefaultValues.PAGE_SIZE));
return getQuestionPage(restEndPoint, queryParams);
}
public StackXPage<Question> getFaqForTag(String tag, int page) {
if (tag != null) {
String restEndPoint = "tags/" + tag + "/faq";
Map<String, String> queryParams = AppUtils.getDefaultQueryParams();
queryParams.put(StackUri.QueryParams.SITE, OperatingSite.getSite().apiSiteParameter);
queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page));
queryParams.put(StackUri.QueryParams.PAGE_SIZE, String.valueOf(StackUri.QueryParamDefaultValues.PAGE_SIZE));
return getQuestionPage(restEndPoint, queryParams);
}
return null;
}
public StackXPage<Question> getQuestionsForTag(String tag, String sort, int page) {
if (sort == null) sort = StackUri.Sort.ACTIVITY;
if (tag != null) {
String restEndPoint = "search/advanced";
Map<String, String> queryParams = AppUtils.getDefaultQueryParams();
queryParams.put(StackUri.QueryParams.SITE, OperatingSite.getSite().apiSiteParameter);
queryParams.put(StackUri.QueryParams.TAGGED, tag);
queryParams.put(StackUri.QueryParams.SORT, sort);
queryParams.put(StackUri.QueryParams.ORDER, StackUri.QueryParamDefaultValues.ORDER);
queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page));
queryParams.put(StackUri.QueryParams.PAGE_SIZE, String.valueOf(StackUri.QueryParamDefaultValues.PAGE_SIZE));
return getQuestionPage(restEndPoint, queryParams);
}
return null;
}
public StackXPage<Question> getSimilar(String title, int page) {
if (title != null) {
String restEndPoint = "similar";
Map<String, String> queryParams = AppUtils.getDefaultQueryParams();
queryParams.put(StackUri.QueryParams.SITE, OperatingSite.getSite().apiSiteParameter);
queryParams.put(StackUri.QueryParams.TITLE, title);
queryParams.put(StackUri.QueryParams.SORT, StackUri.Sort.RELEVANCE);
queryParams.put(StackUri.QueryParams.PAGE, String.valueOf(page));
queryParams.put(StackUri.QueryParams.PAGE_SIZE, String.valueOf(StackUri.QueryParamDefaultValues.PAGE_SIZE));
return getQuestionPage(restEndPoint, queryParams);
}
return null;
}
private StackXPage<Question> getQuestionPage(String restEndPoint, Map<String, String> queryParams) {
JSONObjectWrapper questionsJsonResponse = executeHttpGetRequest(restEndPoint, queryParams);
if (questionsJsonResponse != null) return getQuestionModel(questionsJsonResponse);
return null;
}
public Answer getAnswer(long id) {
return getAnswer(id, OperatingSite.getSite().apiSiteParameter);
}
public Answer getAnswer(long id, String site) {
String restEndPoint = "answers/" + id;
JSONObjectWrapper jsonObjectWrapper = executeHttpGetRequest(restEndPoint, getDefaultQueryParams(site));
if (jsonObjectWrapper != null) {
try {
JSONArray jsonArray = jsonObjectWrapper.getJSONArray(JsonFields.ITEMS);
if (jsonArray != null && jsonArray.length() == 1) {
JSONObjectWrapper jsonObject = JSONObjectWrapper.wrap(jsonArray.getJSONObject(0));
Answer answer = getSerializedAnswerObject(jsonObject);
answer.body = jsonObject.getString(JsonFields.Question.BODY);
return answer;
}
} catch (JSONException e) {
LogWrapper.d(TAG, e.getMessage());
}
}
return null;
}
public int upvote(final PostType postType, final String site, final long id) {
return score(urlEncodedPost(postType.getValue() + "s/" + id + "/upvote"));
}
public int downvote(final PostType postType, final String site, final long id) {
return score(urlEncodedPost(postType.getValue() + "s/" + id + "/downvote"));
}
public int undoUpvote(final PostType postType, final String site, final long id) {
return score(urlEncodedPost(postType.getValue() + "s/" + id + "/upvote/undo"));
}
public int undoDownvote(final PostType postType, final String site, final long id) {
return score(urlEncodedPost(postType.getValue() + "s/" + id + "/downvote/undo"));
}
private int score(JSONObjectWrapper jsonObjectWrapper) {
if (jsonObjectWrapper != null) {
try {
JSONArray jsonArray = jsonObjectWrapper.getJSONArray(JsonFields.ITEMS);
if (jsonArray != null && jsonArray.length() == 1) {
JSONObjectWrapper jsonObject = JSONObjectWrapper.wrap(jsonArray.getJSONObject(0));
return jsonObject.getInt(JsonFields.Answer.SCORE);
}
} catch (JSONException e) {
LogWrapper.d(TAG, e.getMessage());
}
}
return Integer.MIN_VALUE;
}
public void favorite(final String site, final long id) {
urlEncodedPost("questions/" + id + "/favorite");
}
public void undoFavorite(final String site, final long id) {
urlEncodedPost("questions/" + id + "/favorite/undo");
}
public void acceptAnswer(final long answerId, final String site) {
urlEncodedPost("answers/" + answerId + "/accept");
}
public void undoAcceptAnswer(final long answerId, final String site) {
urlEncodedPost("answers/" + answerId + "/accept/undo");
}
private JSONObjectWrapper urlEncodedPost(String restEndPoint) {
Map<String, String> requestHeaders = new HashMap<String, String>();
requestHeaders.put(HttpHeaderParams.CONTENT_TYPE, HttpContentTypes.APPLICATION_FROM_URL_ENCODED);
requestHeaders.put(HttpHeaderParams.ACCEPT, HttpContentTypes.APPLICATION_JSON);
try {
return executeHttpPostRequest(restEndPoint, requestHeaders, null, new UrlEncodedFormEntity(
getBasicNameValuePartListForPost()));
} catch (UnsupportedEncodingException e) {
throw new ClientException(ClientException.ClientErrorCode.INVALID_ENCODING);
}
}
}