You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
6.4 KiB
194 lines
6.4 KiB
package com.github.jreddit.parser.listing;
|
|
|
|
import static com.github.jreddit.parser.util.JsonUtils.safeJsonToString;
|
|
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
|
|
import org.json.simple.JSONArray;
|
|
import org.json.simple.JSONObject;
|
|
import org.json.simple.parser.JSONParser;
|
|
import org.json.simple.parser.ParseException;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import com.github.jreddit.parser.entity.Comment;
|
|
import com.github.jreddit.parser.entity.Kind;
|
|
import com.github.jreddit.parser.entity.More;
|
|
import com.github.jreddit.parser.entity.Submission;
|
|
import com.github.jreddit.parser.entity.Subreddit;
|
|
import com.github.jreddit.parser.entity.Thing;
|
|
import com.github.jreddit.parser.exception.RedditParseException;
|
|
import com.github.jreddit.parser.util.JsonUtils;
|
|
|
|
public class RedditListingParser {
|
|
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(RedditListingParser.class);
|
|
|
|
protected static final JSONParser JSON_PARSER = new JSONParser();
|
|
|
|
/**
|
|
* Validate that it is indeed the starting of a listing of reddit things.
|
|
*
|
|
* @param response Object returned by JSON parser
|
|
*
|
|
* @throws RedditRequestException If the response is not valid listing of reddit things
|
|
*/
|
|
public void validate(Object response) throws RedditParseException {
|
|
|
|
// Check for null
|
|
if (response == null) {
|
|
throw new RedditParseException();
|
|
}
|
|
|
|
// Check it is a JSON response
|
|
if (!(response instanceof JSONObject)) {
|
|
throw new RedditParseException("not a JSON response");
|
|
}
|
|
|
|
// Cast to JSON object
|
|
JSONObject jsonResponse = ((JSONObject) response);
|
|
|
|
// Check for error
|
|
if (jsonResponse.get("error") != null) {
|
|
throw new RedditParseException(JsonUtils.safeJsonToInteger(jsonResponse.get("error")));
|
|
}
|
|
|
|
// Check that data exists
|
|
if (jsonResponse.get("data") == null && jsonResponse.get("json") == null) {
|
|
throw new RedditParseException("data is missing from listing");
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Parse JSON received from reddit into a list of things.
|
|
* This parser expects the JSON to be of a listing of things, and supports
|
|
* the following things: <i>More</i>, <i>Comment</i>, <i>Submission</i>, and <i>Subreddit</i>.
|
|
*
|
|
* @param jsonText JSON Text
|
|
* @return Parsed list of things
|
|
*
|
|
* @throws ParseException
|
|
*/
|
|
public List<Thing> parseGeneric(String jsonText) throws RedditParseException {
|
|
return parseGeneric(jsonText, "children");
|
|
}
|
|
|
|
/**
|
|
* Parse JSON received from reddit into a list of things.
|
|
* This parser expects the JSON to be of a listing of things, and supports
|
|
* the following things: <i>More</i>, <i>Comment</i>, <i>Submission</i>, and <i>Subreddit</i>.<br>
|
|
* <br>
|
|
* <i>Note: if it encounters an invalid element (e.g. missing kind or data), it will
|
|
* log a warning using SLF4J and would return null.</i>
|
|
*
|
|
* @param jsonText JSON Text
|
|
* @param listingName Name of the listing name within the data
|
|
*
|
|
* @return Parsed list of things
|
|
*
|
|
* @throws ParseException
|
|
*/
|
|
public List<Thing> parseGeneric(String jsonText, String listingName) throws RedditParseException {
|
|
|
|
try {
|
|
|
|
// List of submissions
|
|
List<Thing> things = new LinkedList<Thing>();
|
|
|
|
// Send request to reddit server via REST client
|
|
Object response = JSON_PARSER.parse(jsonText);
|
|
|
|
// Check for reddit error, can throw a RedditError
|
|
validate(response);
|
|
|
|
// Cast to a JSON object
|
|
JSONObject object = (JSONObject) response;
|
|
|
|
// Get the array of children
|
|
JSONArray array = (JSONArray) ((JSONObject) object.get("data")).get(listingName);
|
|
|
|
// Iterate over array of children
|
|
for (Object element : array) {
|
|
|
|
// Get the element
|
|
JSONObject data = (JSONObject) element;
|
|
|
|
// Make sure it is of the correct kind
|
|
String kindData = safeJsonToString(data.get("kind"));
|
|
Object objData = data.get("data");
|
|
|
|
// If no kind is given
|
|
if (kindData == null) {
|
|
LOGGER.warn("Kind data missing, skipping it.");
|
|
|
|
// If no data is given
|
|
} else if (objData == null || !(objData instanceof JSONObject)) {
|
|
LOGGER.warn("Object data missing, skipping it.");
|
|
|
|
} else {
|
|
|
|
// Attempt to match
|
|
Kind kind = Kind.match(kindData);
|
|
|
|
// Parse the thing
|
|
Thing thing = parseThing(kind, ((JSONObject) data.get("data")));
|
|
|
|
// Show warning if failed
|
|
if (thing == null) {
|
|
LOGGER.warn("Encountered invalid kind for a listing (" + kindData + "), skipping it.");
|
|
|
|
} else {
|
|
things.add(thing);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Finally return list of submissions
|
|
return things;
|
|
|
|
} catch (ParseException pe) {
|
|
throw new RedditParseException(pe);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Parse the data into a thing if possible.
|
|
*
|
|
* @param kind Kind of data
|
|
* @param data Data for the thing
|
|
* @return The thing generated from the data, if failed <i>null</i>
|
|
*/
|
|
private Thing parseThing(Kind kind, JSONObject data) {
|
|
|
|
// For a comment
|
|
if (kind == Kind.COMMENT) {
|
|
return new Comment(data);
|
|
|
|
// For a submission
|
|
} else if (kind == Kind.LINK) {
|
|
return new Submission(data);
|
|
|
|
// For a subreddit
|
|
} else if (kind == Kind.SUBREDDIT) {
|
|
return new Subreddit(data);
|
|
|
|
// For a more
|
|
} else if (kind == Kind.MORE) {
|
|
return new More(data);
|
|
|
|
// In all other cases (null, or of a different type)
|
|
} else {
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|