move to the builder style comment parser.

refactor-toolbar
Floens 8 years ago
parent a14b407bf9
commit 8b7982fe41
  1. 137
      Clover/app/src/main/java/org/floens/chan/core/site/parser/CommentParser.java
  2. 248
      Clover/app/src/main/java/org/floens/chan/core/site/parser/StyleRule.java
  3. 21
      Clover/app/src/main/java/org/floens/chan/core/site/sites/vichan/ViChanCommentParser.java

@ -21,9 +21,7 @@ import android.graphics.Typeface;
import android.support.annotation.AnyThread;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.text.style.UnderlineSpan;
import org.floens.chan.core.model.Post;
@ -35,11 +33,14 @@ import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.floens.chan.core.site.parser.StyleRule.tagRule;
import static org.floens.chan.utils.AndroidUtils.sp;
@AnyThread
@ -52,6 +53,38 @@ public class CommentParser {
private Pattern quotePattern = Pattern.compile(".*#p(\\d+)");
private Pattern colorPattern = Pattern.compile("color:#([0-9a-fA-F]*)");
private Map<String, List<StyleRule>> rules = new HashMap<>();
public CommentParser() {
rule(tagRule("br").just("\n"));
rule(tagRule("a").action(this::handleAnchor));
rule(tagRule("span").cssClass("deadlink").color(StyleRule.Color.QUOTE).strikeThrough());
rule(tagRule("span").cssClass("spoiler").link(PostLinkable.Type.SPOILER));
rule(tagRule("span").cssClass("fortune").action(this::handleFortune));
rule(tagRule("span").cssClass("abbr").nullify());
rule(tagRule("span").color(StyleRule.Color.INLINE_QUOTE).linkify());
rule(tagRule("table").action(this::handleTable));
rule(tagRule("s").link(PostLinkable.Type.SPOILER));
rule(tagRule("strong").color(StyleRule.Color.QUOTE).bold());
rule(tagRule("pre").cssClass("prettyprint").monospace().size(sp(12f)));
}
public void rule(StyleRule rule) {
List<StyleRule> list = rules.get(rule.tag());
if (list == null) {
list = new ArrayList<>(3);
rules.put(rule.tag(), list);
}
list.add(rule);
}
public void setQuotePattern(Pattern quotePattern) {
this.quotePattern = quotePattern;
}
@ -66,24 +99,23 @@ public class CommentParser {
String tag,
CharSequence text,
Element element) {
List<StyleRule> rules = this.rules.get(tag);
if (rules != null) {
for (int i = 0; i < 2; i++) {
boolean highPriority = i == 0;
for (StyleRule rule : rules) {
if (rule.highPriority() == highPriority && rule.applies(element)) {
return rule.apply(theme, callback, post, text, element);
}
}
}
}
switch (tag) {
case "br":
return "\n";
case "span":
return handleSpan(theme, post, text, element);
case "p":
return appendBreakIfNotLastSibling(
handleParagraph(theme, post, text, element), element);
case "table":
return handleTable(theme, post, text, element);
case "strong":
return handleStrong(theme, post, text, element);
case "a":
return handleAnchor(theme, post, text, element, callback);
case "s":
return handleStrike(theme, post, text, element);
case "pre":
return handlePre(theme, post, text, element);
default:
// Unknown tag, return the text;
return text;
@ -99,10 +131,10 @@ public class CommentParser {
}
private CharSequence handleAnchor(Theme theme,
PostParser.Callback callback,
Post.Builder post,
CharSequence text,
Element anchor,
PostParser.Callback callback) {
Element anchor) {
CommentParser.Link handlerLink = matchAnchor(post, text, anchor, callback);
if (handlerLink != null) {
@ -136,19 +168,14 @@ public class CommentParser {
}
}
public CharSequence handleSpan(Theme theme, Post.Builder post, CharSequence text, Element span) {
SpannableString quote;
private CharSequence handleFortune(Theme theme,
PostParser.Callback callback,
Post.Builder builder,
CharSequence text,
Element span) {
Set<String> classes = span.classNames();
if (classes.contains("deadlink")) {
quote = new SpannableString(span.text());
quote.setSpan(new ForegroundColorSpanHashed(theme.quoteColor), 0, quote.length(), 0);
quote.setSpan(new StrikethroughSpan(), 0, quote.length(), 0);
} else if (classes.contains("fortune")) {
if (classes.contains("fortune")) {
// html looks like <span class="fortune" style="color:#0893e1"><br><br><b>Your fortune:</b>
// manually add these <br>
quote = new SpannableString("\n\n" + span.text());
String style = span.attr("style");
if (!TextUtils.isEmpty(style)) {
style = style.replace(" ", "");
@ -167,30 +194,27 @@ public class CommentParser {
}
if (hexColor >= 0 && hexColor <= 0xffffff) {
quote.setSpan(new ForegroundColorSpanHashed(0xff000000 + hexColor), 0, quote.length(), 0);
quote.setSpan(new StyleSpan(Typeface.BOLD), 0, quote.length(), 0);
text = span(text, new ForegroundColorSpanHashed(0xff000000 + hexColor),
new StyleSpan(Typeface.BOLD));
}
}
} else if (classes.contains("spoiler")) {
PostLinkable pl = new PostLinkable(theme, span.text(), span.text(), PostLinkable.Type.SPOILER);
post.addLinkable(pl);
return span(span.text(), pl);
} else if (classes.contains("abbr")) {
return null;
} else {
quote = new SpannableString(span.text());
quote.setSpan(new ForegroundColorSpanHashed(theme.inlineQuoteColor), 0, quote.length(), 0);
CommentParserHelper.detectLinks(theme, post, span.text(), quote);
}
return quote;
return text;
}
public CharSequence handleParagraph(Theme theme, Post.Builder post, CharSequence text, Element span) {
public CharSequence handleParagraph(Theme theme,
Post.Builder post,
CharSequence text,
Element span) {
return text;
}
public CharSequence handleTable(Theme theme, Post.Builder post, CharSequence text, Element table) {
public CharSequence handleTable(Theme theme,
PostParser.Callback callback,
Post.Builder builder,
CharSequence text,
Element table) {
List<CharSequence> parts = new ArrayList<>();
Elements tableRows = table.getElementsByTag("tr");
for (int i = 0; i < tableRows.size(); i++) {
@ -221,31 +245,6 @@ public class CommentParser {
new AbsoluteSizeSpanHashed(sp(12f)));
}
public CharSequence handleStrong(Theme theme, Post.Builder post, CharSequence text, Element strong) {
return span(text,
new ForegroundColorSpanHashed(theme.quoteColor),
new StyleSpan(Typeface.BOLD));
}
public CharSequence handlePre(Theme theme, Post.Builder post, CharSequence text, Element pre) {
Set<String> classes = pre.classNames();
if (classes.contains("prettyprint")) {
// String linebreakText = CommentParserHelper.getNodeTextPreservingLineBreaks(pre);
return span(text,
new TypefaceSpan("monospace"),
new AbsoluteSizeSpanHashed(sp(12f)));
} else {
return pre.text();
}
}
public CharSequence handleStrike(Theme theme, Post.Builder post, CharSequence text, Element strike) {
PostLinkable pl = new PostLinkable(theme, text.toString(), text, PostLinkable.Type.SPOILER);
post.addLinkable(pl);
return span(text, pl);
}
public Link matchAnchor(Post.Builder post, CharSequence text, Element anchor, PostParser.Callback callback) {
String href = anchor.attr("href");

@ -0,0 +1,248 @@
/*
* Clover - 4chan browser https://github.com/Floens/Clover/
* Copyright (C) 2014 Floens
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.floens.chan.core.site.parser;
import android.graphics.Typeface;
import android.text.SpannableString;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.PostLinkable;
import org.floens.chan.ui.span.AbsoluteSizeSpanHashed;
import org.floens.chan.ui.span.ForegroundColorSpanHashed;
import org.floens.chan.ui.theme.Theme;
import org.jsoup.nodes.Element;
import java.util.ArrayList;
import java.util.List;
public class StyleRule {
public enum Color {
INLINE_QUOTE,
QUOTE
}
public static StyleRule tagRule(String tag) {
return new StyleRule().tag(tag);
}
private String tag;
private List<String> classes;
private List<Action> actions = new ArrayList<>();
private Color color = null;
private boolean strikeThrough = false;
private boolean bold = false;
private boolean monospace = false;
private int size = 0;
private PostLinkable.Type link = null;
private boolean nullify = false;
private boolean linkify = false;
private String justText = null;
public StyleRule tag(String tag) {
this.tag = tag;
return this;
}
public String tag() {
return tag;
}
public StyleRule cssClass(String cssClass) {
if (classes == null) {
classes = new ArrayList<>(4);
}
classes.add(cssClass);
return this;
}
public StyleRule action(Action action) {
actions.add(action);
return this;
}
public StyleRule color(Color color) {
this.color = color;
return this;
}
public StyleRule link(PostLinkable.Type link) {
this.link = link;
return this;
}
public StyleRule strikeThrough() {
strikeThrough = true;
return this;
}
public StyleRule bold() {
bold = true;
return this;
}
public StyleRule monospace() {
monospace = true;
return this;
}
public StyleRule size(int size) {
this.size = size;
return this;
}
public StyleRule nullify() {
nullify = true;
return this;
}
public StyleRule linkify() {
linkify = true;
return this;
}
public StyleRule just(String justText) {
this.justText = justText;
return this;
}
public boolean highPriority() {
return classes != null && !classes.isEmpty();
}
public boolean applies(Element element) {
if (classes == null || classes.isEmpty()) {
return true;
}
for (String c : classes) {
if (element.hasClass(c)) {
return true;
}
}
return false;
}
public CharSequence apply(Theme theme,
PostParser.Callback callback,
Post.Builder post,
CharSequence text,
Element element) {
if (nullify) {
return null;
}
if (justText != null) {
return justText;
}
CharSequence result = text;
for (Action action : actions) {
result = action.execute(theme, callback, post, text, element);
}
List<Object> spansToApply = new ArrayList<>(2);
if (color != null) {
spansToApply.add(new ForegroundColorSpanHashed(getColor(theme, color)));
}
if (strikeThrough) {
spansToApply.add(new StrikethroughSpan());
}
if (bold) {
spansToApply.add(new StyleSpan(Typeface.BOLD));
}
if (monospace) {
spansToApply.add(new TypefaceSpan("monospace"));
}
if (size != 0) {
spansToApply.add(new AbsoluteSizeSpanHashed(size));
}
if (link != null) {
PostLinkable pl = new PostLinkable(theme, result, result, link);
post.addLinkable(pl);
spansToApply.add(pl);
}
if (!spansToApply.isEmpty()) {
result = applySpan(result, spansToApply);
}
if (linkify) {
CommentParserHelper.detectLinks(theme, post, result.toString(), new SpannableString(result));
}
return result;
}
private int getColor(Theme theme, Color color) {
switch (color) {
case INLINE_QUOTE:
return theme.inlineQuoteColor;
case QUOTE:
return theme.quoteColor;
}
return 0;
}
private SpannableString applySpan(CharSequence text, List<Object> spans) {
SpannableString result = new SpannableString(text);
int l = result.length();
for (Object span : spans) {
if (span != null) {
result.setSpan(span, 0, l, 0);
}
}
return result;
}
public interface Action {
CharSequence execute(Theme theme,
PostParser.Callback callback,
Post.Builder post,
CharSequence text,
Element element);
}
}

@ -17,31 +17,18 @@
*/
package org.floens.chan.core.site.sites.vichan;
import android.text.SpannableString;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.site.parser.CommentParser;
import org.floens.chan.core.site.parser.CommentParserHelper;
import org.floens.chan.ui.span.ForegroundColorSpanHashed;
import org.floens.chan.ui.theme.Theme;
import org.jsoup.nodes.Element;
import org.floens.chan.core.site.parser.StyleRule;
import java.util.regex.Pattern;
import static org.floens.chan.core.site.parser.StyleRule.tagRule;
public class ViChanCommentParser extends CommentParser {
public ViChanCommentParser() {
setQuotePattern(Pattern.compile(".*#(\\d+)"));
setFullQuotePattern(Pattern.compile("/(\\w+)/\\w+/(\\d+)\\.html#(\\d+)"));
}
@Override
public CharSequence handleParagraph(Theme theme, Post.Builder post, CharSequence text, Element element) {
if (element.hasClass("quote")) {
SpannableString res = span(text, new ForegroundColorSpanHashed(theme.inlineQuoteColor));
CommentParserHelper.detectLinks(theme, post, res.toString(), res);
return res;
} else {
return text;
}
rule(tagRule("p").cssClass("quote").color(StyleRule.Color.INLINE_QUOTE).linkify());
}
}

Loading…
Cancel
Save