package com.manuelmaly.hn.parser; import android.graphics.Color; import com.manuelmaly.hn.App; import com.manuelmaly.hn.Settings; import com.manuelmaly.hn.model.HNComment; import com.manuelmaly.hn.model.HNPostComments; import com.manuelmaly.hn.util.HNHelper; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.util.ArrayList; public class HNCommentsParser extends BaseHTMLParser<HNPostComments> { @Override public HNPostComments parseDocument(Element doc) throws Exception { if (doc == null) return new HNPostComments(); ArrayList<HNComment> comments = new ArrayList<HNComment>(); Elements tableRows = doc.select("table tr table tr:has(table)"); String currentUser = Settings.getUserName(App.getInstance()); String text = null; String author = null; int level = 0; String timeAgo = null; String url = null; Boolean isDownvoted = false; String upvoteUrl = null; String downvoteUrl = null; boolean endParsing = false; for (int row = 0; row < tableRows.size(); row++) { Element mainRowElement = tableRows.get(row).select("td:eq(2)").first(); Element rowLevelElement = tableRows.get(row).select("td:eq(0)").first(); if (mainRowElement == null) continue; // The not portion of this query is meant to remove the reply link // from the text. As far as I can tell that is the only place // where size=1 is used. If that turns out to not be the case then // searching for u tags is also a pretty decent option - @jmaltz Element mainCommentDiv = mainRowElement.select("div.comment > span").first(); if (mainCommentDiv == null) continue; mainCommentDiv.select("div.reply").remove(); // Parse the class attribute to get the comment color int commentColor = Color.rgb(0, 0, 0); if (mainCommentDiv.attributes().hasKey("class")) { commentColor = getCommentColor(mainCommentDiv.attributes().get("class")); } // In order to eliminate whitespace at the end of multi-line comments, // <p> tags are replaced with double <br/> tags. text = mainCommentDiv.html() .replace("<span> </span>", "") .replace("<p>", "<br/><br/>") .replace("</p>", ""); Element comHeadElement = mainRowElement.select("span.comhead").first(); author = comHeadElement.select("a[href*=user]").text(); timeAgo = comHeadElement.select("a[href*=item").text();//getFirstTextValueInElementChildren(comHeadElement); // if (timeAgoRaw.length() > 0) // timeAgo = timeAgoRaw.substring(0, timeAgoRaw.indexOf("|")); Element urlElement = comHeadElement.select("a[href*=item]").first(); if (urlElement != null) url = urlElement.attr("href"); String levelSpacerWidth = rowLevelElement.select("img").first().attr("width"); if (levelSpacerWidth != null) level = Integer.parseInt(levelSpacerWidth) / 40; Elements voteElements = tableRows.get(row).select("td:eq(1) a"); upvoteUrl = getVoteUrl(voteElements.first()); // We want to test for size because unlike first() calling .get(1) // Will throw an error if there are not two elements if (voteElements.size() > 1) downvoteUrl = getVoteUrl(voteElements.get(1)); comments.add(new HNComment(timeAgo, author, url, text, commentColor, level, isDownvoted, upvoteUrl, downvoteUrl)); if (endParsing) break; } // Just using table:eq(0) would return an extra table, so we use // get(0) instead, which only returns only the one we want Element header = doc.select("body table:eq(0) tbody > tr:eq(2) > td:eq(0) > table").get(0); String headerHtml = null; // Five table rows is what it takes for the title, post information // And other boilerplate stuff. More than five means we have something // Special if (header.select("tr").size() > 5) { HeaderParser headerParser = new HeaderParser(); headerHtml = headerParser.parseDocument(header); } return new HNPostComments(comments, headerHtml, currentUser); } /** * Parses out the url for voting from a given element * @param voteElement The element from which to parse out the voting url * @return The relative url to vote in the given direction for that comment */ private String getVoteUrl(Element voteElement) { if (voteElement != null) { return voteElement.attr("href").contains("auth=") ? HNHelper.resolveRelativeHNURL(voteElement.attr("href")) : null; } return null; } private int getCommentColor(String className) { if ("c5a".equals(className)) { return Color.rgb(0x5A, 0x5A, 0x5A); } else if ("c73".equals(className)) { return Color.rgb(0x73, 0x73, 0x73); } else if ("c82".equals(className)) { return Color.rgb(0x82, 0x82, 0x82); } else if ("c88".equals(className)) { return Color.rgb(0x88, 0x88, 0x88); } else if ("c9c".equals(className)) { return Color.rgb(0x9C, 0x9C, 0x9C); } else if ("cae".equals(className)) { return Color.rgb(0xAE, 0xAE, 0xAE); } else if ("cbe".equals(className)) { return Color.rgb(0xBE, 0xBE, 0xBE); } else if ("cce".equals(className)) { return Color.rgb(0xCE, 0xCE, 0xCE); } else if ("cdd".equals(className)) { return Color.rgb(0xDD, 0xDD, 0xDD); } else { return Color.rgb(0, 0, 0); } } }