package com.dozuki.ifixit.util;
import android.util.Log;
import com.dozuki.ifixit.App;
import com.dozuki.ifixit.R;
import com.dozuki.ifixit.model.Badges;
import com.dozuki.ifixit.model.Comment;
import com.dozuki.ifixit.model.Embed;
import com.dozuki.ifixit.model.Image;
import com.dozuki.ifixit.model.Item;
import com.dozuki.ifixit.model.Video;
import com.dozuki.ifixit.model.VideoEncoding;
import com.dozuki.ifixit.model.VideoThumbnail;
import com.dozuki.ifixit.model.dozuki.Site;
import com.dozuki.ifixit.model.gallery.GalleryEmbedList;
import com.dozuki.ifixit.model.gallery.GalleryVideoList;
import com.dozuki.ifixit.model.guide.Guide;
import com.dozuki.ifixit.model.guide.GuideInfo;
import com.dozuki.ifixit.model.guide.GuideStep;
import com.dozuki.ifixit.model.guide.GuideType;
import com.dozuki.ifixit.model.guide.StepLine;
import com.dozuki.ifixit.model.search.GuideSearchResult;
import com.dozuki.ifixit.model.search.SearchResults;
import com.dozuki.ifixit.model.search.TopicSearchResult;
import com.dozuki.ifixit.model.topic.TopicLeaf;
import com.dozuki.ifixit.model.topic.TopicNode;
import com.dozuki.ifixit.model.user.User;
import com.dozuki.ifixit.model.user.UserImage;
import com.dozuki.ifixit.util.api.ApiError;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class JSONHelper {
private static final String TAG = "JSONHelper";
private static final String INVALID_LOGIN_STRING = "Invalid login";
public static SearchResults parseSearchResults(String json) throws JSONException {
SearchResults search = new SearchResults();
JSONObject response = new JSONObject(json);
search.mLimit = response.getInt("limit");
search.mOffset = response.getInt("offset");
search.mTotalResults = response.getInt("totalResults");
search.mHasMoreResults = response.getBoolean("moreResults");
search.mQuery = response.getString("search");
if (response.has("results")) {
JSONArray resultsArr = response.getJSONArray("results");
int count = resultsArr.length();
for (int i = 0; i < count; i++) {
JSONObject result = resultsArr.getJSONObject(i);
String resultType = result.getString("dataType");
if (resultType.equals("guide")) {
Type guidesType = new TypeToken<GuideInfo>() {}.getType();
GuideInfo gi = new Gson().fromJson(result.toString(), guidesType);
GuideSearchResult gsr = new GuideSearchResult(gi);
search.mResults.add(gsr);
} else if (resultType.equals("wiki")) {
TopicSearchResult tsr = new TopicSearchResult();
tsr.mDisplayTitle = result.getString("display_title");
tsr.mTitle = result.getString("title");
tsr.mText = result.getString("text");
tsr.mNamespace = result.getString("namespace");
tsr.mSummary = result.getString("summary");
tsr.mUrl = result.getString("url");
tsr.mImage = parseImage(result, "image");
search.mResults.add(tsr);
}
}
}
return search;
}
public static ArrayList<Site> parseSites(String json) throws JSONException {
ArrayList<Site> sites = new ArrayList<Site>();
JSONArray jSites = new JSONArray(json);
Site site;
for (int i = 0; i < jSites.length(); i++) {
site = parseSite(jSites.getJSONObject(i));
if (site != null) {
sites.add(site);
}
}
return sites;
}
private static Site parseSite(JSONObject jSite) throws JSONException {
Site site = new Site(jSite.getInt("siteid"));
site.mName = jSite.getString("name");
site.mDomain = jSite.getString("domain");
site.mCustomDomain = jSite.optString("custom_domain", "");
site.mTitle = jSite.getString("title");
site.mTheme = jSite.getString("theme");
site.mPublic = !jSite.getBoolean("private");
site.mDescription = jSite.getString("description");
site.mAnswers = jSite.getBoolean("answers");
site.mStoreUrl = jSite.optString("store", "");
setAuthentication(site, jSite.getJSONObject("authentication"));
return site;
}
public static Site parseSiteInfo(String json) throws JSONException {
JSONObject siteInfoObject = new JSONObject(json);
Site site = parseSite(siteInfoObject);
JSONObject types = (JSONObject) siteInfoObject.get("guide-types");
site.mGuideTypes = new ArrayList<GuideType>();
site.mObjectNamePlural = siteInfoObject.getString("object-name-plural");
site.mObjectNameSingular = siteInfoObject.getString("object-name-singular");
site.setBarcodeScanner(siteInfoObject.getBoolean("feature-mobile-scanner"));
site.mGoogleOAuth2Clientid = siteInfoObject.getString("google-oauth2-clientid");
if (!siteInfoObject.isNull("logo")) {
JSONObject logoImage = siteInfoObject.getJSONObject("logo").getJSONObject("image");
site.mLogo = new Image(logoImage.getInt("id"), logoImage.getString("original"));
}
Iterator<?> keys = types.keys();
while (keys.hasNext()) {
String key = (String) keys.next();
if (types.get(key) instanceof JSONObject) {
site.mGuideTypes.add(parseGuideType(key, (JSONObject)types.get(key)));
}
}
return site;
}
private static void setAuthentication(Site site, JSONObject jAuth) throws JSONException {
site.mStandardAuth = jAuth.has("standard") && jAuth.getBoolean("standard");
site.mSsoUrl = jAuth.has("sso") ? jAuth.getString("sso") : null;
site.mPublicRegistration = jAuth.getBoolean("public-registration");
}
public static ArrayList<String> parseAllTopics(String json) {
ArrayList<String> topics = new ArrayList<String>();
try {
JSONArray topicsJson = new JSONArray(json);
for (int i = 0; i < topicsJson.length(); i++) {
topics.add(topicsJson.getString(i));
}
} catch (JSONException e) {
Log.e(TAG, "Error parsing all topics list: ", e);
}
return topics;
}
private static GuideType parseGuideType(String type, JSONObject object) {
try {
return new GuideType(type, object.getString("title"), object.getString("prompt"));
} catch (JSONException e) {
return new GuideType(type, "", "");
}
}
/**
* Guide parsing
*/
public static Guide parseGuide(String json) throws JSONException {
JSONObject jGuide = new JSONObject(json);
JSONArray jSteps = jGuide.getJSONArray("steps");
JSONArray jTools = jGuide.getJSONArray("tools");
JSONArray jParts = jGuide.getJSONArray("parts");
JSONObject jAuthor = jGuide.getJSONObject("author");
Guide guide = new Guide(jGuide.getInt("guideid"));
guide.setTitle(jGuide.getString("title"));
guide.setTopic(jGuide.getString("category"));
guide.setSubject(jGuide.getString("subject"));
guide.setAuthor(jAuthor.getString("username"));
guide.setTimeRequired(jGuide.getString("time_required"));
guide.setDifficulty(jGuide.getString("difficulty"));
guide.setIntroductionRaw(jGuide.getString("introduction_raw"));
guide.setIntroductionRendered(jGuide.getString("introduction_rendered"));
guide.setIntroImage(parseImage(jGuide, "image"));
guide.setSummary(jGuide.isNull("summary") ? "" : jGuide.getString("summary"));
guide.setRevisionid(jGuide.getInt("revisionid"));
guide.setPublic(jGuide.getBoolean("public"));
guide.setType(jGuide.getString("type"));
guide.setPatrolThreshold(jGuide.getInt("patrol_threshold"));
guide.setConclusion(jGuide.getString("conclusion_rendered"));
guide.setCompleted(jGuide.getBoolean("completed"));
guide.setComments(parseComments(jGuide.getJSONArray("comments")));
guide.setFavorited(jGuide.getBoolean("favorited"));
guide.setModifiedDate(jGuide.getDouble("modified_date"));
guide.setPrereqModifiedDate(jGuide.getDouble("prereq_modified_date"));
if (jGuide.has("can_edit")) {
guide.setCanEdit(jGuide.getBoolean("can_edit"));
}
for (int i = 0; i < jSteps.length(); i++) {
guide.addStep(parseStep(jSteps.getJSONObject(i), i + 1));
}
for (int i = 0; i < jTools.length(); i++) {
guide.addTool(parseTool(jTools.getJSONObject(i)));
}
for (int i = 0; i < jParts.length(); i++) {
guide.addPart(parsePart(jParts.getJSONObject(i)));
}
return guide;
}
private static ArrayList<Comment> parseComments(JSONArray comments) throws JSONException {
ArrayList<Comment> result = new ArrayList<Comment>();
for (int i = 0; i < comments.length(); i++) {
result.add(new Comment(comments.getJSONObject(i)));
}
return result;
}
private static Item parsePart(JSONObject jPart) throws JSONException {
return new Item(
Item.ItemType.PART,
jPart.getString("text"),
jPart.getString("quantity"),
jPart.getString("url"),
jPart.getString("thumbnail"),
jPart.getString("notes"));
}
private static Item parseTool(JSONObject jTool) throws JSONException {
return new Item(
Item.ItemType.TOOL,
jTool.getString("text"),
jTool.getString("quantity"),
jTool.getString("url"),
jTool.getString("thumbnail"),
jTool.getString("notes"));
}
public static GuideStep parseStep(JSONObject jStep, int stepNumber) throws JSONException {
GuideStep step = new GuideStep(stepNumber);
step.setGuideid(jStep.getInt("guideid"));
step.setStepid(jStep.getInt("stepid"));
step.setRevisionid(jStep.getInt("revisionid"));
step.setOrderby(jStep.isNull("orderby") ? stepNumber : jStep.getInt("orderby"));
step.setTitle(jStep.getString("title"));
try {
JSONObject jMedia = jStep.getJSONObject("media");
String type = jMedia.getString("type");
if (type.equals("image")) {
JSONArray jImages = jMedia.getJSONArray("data");
for (int i = 0; i < jImages.length(); i++) {
step.addImage(parseImage(jImages.getJSONObject(i), null));
}
} else if (type.equals("video")) {
JSONObject jVideo = jMedia.getJSONObject("data");
step.addVideo(parseVideo(jVideo));
} else if (type.equals("embed")) {
JSONObject jEmbed = jMedia.getJSONObject("data");
step.addEmbed(new Embed(jEmbed));
}
} catch (JSONException e) {
Image image = new Image();
step.addImage(image);
}
JSONArray jLines = jStep.getJSONArray("lines");
for (int i = 0; i < jLines.length(); i++) {
step.addLine(parseLine(jLines.getJSONObject(i)));
}
if (jStep.has("comments")) {
step.setComments(parseComments(jStep.getJSONArray("comments")));
}
return step;
}
private static Video parseVideo(JSONObject jVideo) throws JSONException {
Video video = new Video();
try {
JSONArray jEncodings = jVideo.getJSONArray("encodings");
int numEncodings = jEncodings.length();
for (int i = 0; i < numEncodings; i++) {
video.addEncoding(parseVideoEncoding(jEncodings.getJSONObject(i)));
}
video.setHeight(jVideo.getInt("width"));
video.setWidth(jVideo.getInt("height"));
video.setDuration(jVideo.getInt("duration"));
video.setFilename(jVideo.getString("filename"));
video.setThumbnail(parseVideoThumbnail(jVideo.getJSONObject("image")));
} catch (JSONException e) {
e.printStackTrace();
Log.e("JSONHelper parseVideo", "Error parsing video API response");
}
return video;
}
private static VideoThumbnail parseVideoThumbnail(JSONObject jVideoThumb) throws JSONException {
Image image = parseImage(jVideoThumb.getJSONObject("image"), null);
String ratio = jVideoThumb.getString("ratio");
int width = jVideoThumb.getInt("width");
int height = jVideoThumb.getInt("height");
return new VideoThumbnail(image.getId(), image.getPath(), width, height);
}
private static VideoEncoding parseVideoEncoding(JSONObject jVideoEncoding) throws JSONException {
return new VideoEncoding(jVideoEncoding.getInt("width"), jVideoEncoding.getInt("height"),
jVideoEncoding.getString("url"), jVideoEncoding.getString("format"));
}
private static StepLine parseLine(JSONObject jLine) throws JSONException {
int lineid = jLine.isNull("lineid") ? 0 : jLine.getInt("lineid");
return new StepLine(lineid, jLine.getString("bullet"),
jLine.getInt("level"), jLine.getString("text_raw"), jLine.getString("text_rendered"));
}
/**
* Topic hierarchy parsing
*/
public static TopicNode parseTopics(String json) throws JSONException {
JSONArray jHierarchy = new JSONArray(json);
ArrayList<TopicNode> topics = parseTopicChildren(jHierarchy);
TopicNode root = new TopicNode();
root.setChildren(topics);
return root;
}
/**
* Reads through the given JSONObject and adds any topics to the given topic.
*/
private static ArrayList<TopicNode> parseTopicChildren(JSONArray jChildren) throws JSONException {
ArrayList<TopicNode> topics = new ArrayList<TopicNode>();
for (int i = 0; i < jChildren.length(); i++) {
JSONObject jTopic = jChildren.getJSONObject(i);
TopicNode topic = new TopicNode(jTopic.getString("title"));
if (jTopic.has("children")) {
topic.setChildren(parseTopicChildren(jTopic.getJSONArray("children")));
}
if (jTopic.has("display_title")) {
topic.setDisplayName(jTopic.getString("display_title"));
}
topics.add(topic);
}
return topics;
}
/**
* Topic leaf parsing
*/
public static TopicLeaf parseTopicLeaf(String json) throws JSONException {
JSONObject jTopic = new JSONObject(json);
JSONArray jGuides = jTopic.getJSONArray("guides");
JSONObject jSolutions = jTopic.getJSONObject("solutions");
JSONObject jInfo = jTopic.getJSONObject("topic_info");
TopicLeaf topicLeaf = new TopicLeaf(jInfo.getString("name"));
for (int i = 0; i < jGuides.length(); i++) {
String guideJson = jGuides.getJSONObject(i).toString();
GuideInfo guide = new Gson().fromJson(guideJson, GuideInfo.class);
topicLeaf.addGuide(guide);
}
topicLeaf.setNumSolutions(Integer.parseInt(jSolutions.getString("count")));
topicLeaf.setSolutionsUrl(jSolutions.getString("url"));
topicLeaf.setDescription(jTopic.getString("description"));
topicLeaf.setImage(parseImage(jTopic.getJSONObject("image"), null));
topicLeaf.setLocale(jTopic.getString("locale"));
topicLeaf.setContentsRaw(jTopic.getString("contents_raw"));
topicLeaf.setContentsRendered(jTopic.getString("contents_rendered"));
topicLeaf.setTitle(jTopic.getString("display_title"));
return topicLeaf;
}
/**
* Parsing list of UserImageInfo.
*/
public static ArrayList<UserImage> parseUserImages(String json) throws JSONException {
JSONArray jImages = new JSONArray(json);
ArrayList<UserImage> userImageList = new ArrayList<UserImage>();
for (int i = 0; i < jImages.length(); i++) {
userImageList.add(parseUserImage(jImages.getJSONObject(i)));
}
return userImageList;
}
private static UserImage parseUserImage(JSONObject jImage) throws JSONException {
JSONObject img = jImage.getJSONObject("image");
int id = img.getInt("id");
int width = jImage.getInt("width");
int height = jImage.getInt("height");
String path = img.getString("original");
String ratio = jImage.getString("ratio");
String markup = jImage.isNull("markup") ? "" : jImage.getString("markup");
// TODO: Add exif.
String exif = "";
return new UserImage(id, path, width, height, ratio, markup, exif);
}
public static GalleryVideoList parseUserVideos(String jVideo) throws JSONException {
JSONArray jImages = new JSONArray(jVideo);
GalleryVideoList userVideoList = new GalleryVideoList();
//for (int i = 0; i < jImages.length(); i++) {
//userVideoList.addItem((parseUserVideoInfo(jImages.getJSONObject(i))));
//}
return userVideoList;
}
public static Video parseUserVideoInfo(JSONObject jVideo) throws JSONException {
/*
UserVideoInfo userVideoInfo = new UserVideoInfo();
userVideoInfo.setItemId(jVideo.getString("imageid"));
userVideoInfo.setGuid(jVideo.getString("guid"));
userVideoInfo.setHeight(jVideo.getString("height"));
userVideoInfo.setWidth(jVideo.getString("width"));
userVideoInfo.setRatio(jVideo.getString("ratio"));
return userVideoInfo; */
return new Video();
}
public static GalleryEmbedList parseUserEmbeds(String jEmbed) throws JSONException {
JSONArray jEmbeds = new JSONArray(jEmbed);
GalleryEmbedList userEmbedList = new GalleryEmbedList();
//for (int i = 0; i < jEmbed.length(); i++) {
//userEmbedList.addItem((parseUserVideoInfo(jEmbeds.getJSONObject(i))));
//}
return userEmbedList;
}
/*
public static UserEmbedInfo parseUserEmbedInfo(JSONObject jEmbed)
throws JSONException {
UserEmbedInfo userEmbedInfo = new UserEmbedInfo();
// userEmbedInfo.setItemId(jEmbed.getString("imageid"));
// userEmbedInfo.setGuid(jEmbed.getString("guid"));
// userEmbedInfo.setHeight(jEmbed.getString("height"));
// userEmbedInfo.setWidth(jEmbed.getString("width"));
// userEmbedInfo.setRatio(jEmbed.getString("ratio"));
return userEmbedInfo;
}
*/
public static Image parseUploadedImage(String image) throws JSONException {
return parseImage(new JSONObject(image), "image");
}
/**
* Login parsing info
*/
public static User parseLoginInfo(String json) throws JSONException {
JSONObject jUser = new JSONObject(json);
User user = new User();
user.setUserid(jUser.getInt("userid"));
user.setUsername(jUser.getString("username"));
if (!jUser.isNull("image"))
user.setAvatar(parseImage(jUser.getJSONObject("image"), null));
user.setAboutRaw(jUser.getString("about_raw"));
user.setAboutRendered(jUser.getString("about_rendered"));
if (!jUser.isNull("summary"))
user.setSummary(jUser.getString("summary"));
if (!jUser.isNull("location"))
user.setLocation(jUser.getString("location"));
if (!jUser.isNull("join_date"))
user.setJoinDate(jUser.getInt("join_date"));
user.setBadges(parseBadges(jUser.getJSONObject("badge_counts")));
user.setReputation(jUser.getInt("reputation"));
user.setCertificationCount(jUser.getInt("certification_count"));
user.setAuthToken(jUser.getString("authToken"));
return user;
}
public static User parseUserLight(JSONObject jUser) throws JSONException {
User user = new User();
user.setUserid(jUser.getInt("userid"));
user.setUsername(jUser.getString("username"));
user.setAvatar(parseImage(jUser, "image"));
if (!jUser.isNull("join_date"))
user.setJoinDate(jUser.getInt("join_date"));
user.setReputation(jUser.getInt("reputation"));
return user;
}
public static Badges parseBadges(JSONObject json) throws JSONException {
int gold = json.getInt("gold");
int silver = json.getInt("silver");
int bronze = json.getInt("bronze");
return new Badges(bronze, silver, gold);
}
/**
* Returns the ApiError contained in the given JSON, or null if one
* does not exist.
* <p/>
* e.g. Returns "Guide not found" for:
* "{"error":true,"msg":"Guide not found"}"
*/
public static ApiError parseError(String json, int code) {
ApiError error = null;
String message, title;
ApiError.Type type;
App app = App.get();
try {
JSONObject jError = new JSONObject(json);
message = jError.getString("message");
type = message.equals(INVALID_LOGIN_STRING) ?
ApiError.Type.INVALID_USER :
ApiError.getByStatusCode(code).mType;
if (type == ApiError.Type.VALIDATION) {
error = JSONHelper.parseValidationError(json);
} else {
title = app.getString(R.string.error); // Default error string
error = new ApiError(title, message, type);
}
} catch (JSONException e) {
Log.e("JSONHelper", "Unable to parse error message");
}
return error;
}
public static ApiError parseValidationError(String json) {
String message;
int index = -1;
ApiError error = null;
try {
JSONObject jError = new JSONObject(json);
JSONArray jErrors = jError.getJSONArray("errors");
message = jError.getString("message") + ".";
for (int i = 0; i < jErrors.length(); i++) {
message += " " + ((JSONObject)jErrors.get(i)).getString("message");
index = ((JSONObject)jErrors.get(i)).optInt("index", -1);
}
error = new ApiError(
App.get().getString(R.string.validation_error_title),
message,
ApiError.Type.VALIDATION,
index);
} catch (JSONException e) {
Log.e("JSONHelper", "Unable to parse error message");
}
return error;
}
public static ArrayList<GuideInfo> parseUserFavorites(String json) {
ArrayList<GuideInfo> result = new ArrayList<GuideInfo>();
try {
JSONArray jFavorites = new JSONArray(json);
int length = jFavorites.length();
for (int i = 0; i < length; i++) {
JSONObject jGuide = jFavorites.getJSONObject(i).getJSONObject("guide");
GuideInfo guide = new Gson().fromJson(jGuide.toString(), GuideInfo.class);
result.add(guide);
}
} catch (JSONException e) {
e.printStackTrace();
}
return result;
}
public static ArrayList<GuideInfo> parseUserGuides(String json) {
return parseGuides(json);
}
public static ArrayList<GuideInfo> parseGuides(String json) {
Type guidesType = new TypeToken<Collection<GuideInfo>>() {}.getType();
Collection<GuideInfo> guideList = new Gson().fromJson(json, guidesType);
return new ArrayList<GuideInfo>(guideList);
}
public static JSONArray createLineArray(ArrayList<StepLine> lines) throws JSONException {
JSONArray array = new JSONArray();
for (StepLine l : lines) {
JSONObject lineObject = createLineObject(l);
array.put(lineObject);
}
return array;
}
public static JSONArray createImageArray(ArrayList<Image> images) throws JSONException {
JSONArray array = new JSONArray();
for (Image image : images) {
array.put(image.getId());
}
return array;
}
public static JSONObject createStepMediaJsonObject(GuideStep step) throws JSONException {
JSONObject jMedia = new JSONObject();
jMedia.put("type", "image");
jMedia.put("data", createImageArray(step.getImages()));
return jMedia;
}
public static JSONObject createLineObject(StepLine l) throws JSONException {
JSONObject lineObject = new JSONObject();
lineObject.put("text", l.getTextRaw());
lineObject.put("bullet", l.getColor());
lineObject.put("level", l.getLevel());
lineObject.put("lineid", (l.getLineId() != null ? l.getLineId() : 0));
return lineObject;
}
public static JSONArray createStepIdArray(ArrayList<GuideStep> steps) throws JSONException {
JSONArray jSteps = new JSONArray();
for (GuideStep step : steps) {
jSteps.put(jSteps.length(), step.getStepid());
}
return jSteps;
}
public static Image parseImage(JSONObject image, String imageFieldName) {
try {
if (imageFieldName != null) {
image = image.optJSONObject(imageFieldName);
}
if (image == null) {
return new Image();
}
return new Image(image.getInt("id"), image.getString("original"));
} catch (JSONException e) {
Log.w(TAG, "Image parsing", e);
return new Image();
}
}
}