mirror of https://github.com/ThmmyNoLife/mTHMMY
oogee
6 years ago
8 changed files with 398 additions and 22 deletions
@ -0,0 +1,53 @@ |
|||||
|
package gr.thmmy.mthmmy.model; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
|
||||
|
public class BBTag { |
||||
|
private int start, end; |
||||
|
private String name, attribute; |
||||
|
|
||||
|
public BBTag(int start, String name) { |
||||
|
this.start = start; |
||||
|
this.name = name; |
||||
|
} |
||||
|
|
||||
|
public BBTag(int start, String name, String attribute) { |
||||
|
this.start = start; |
||||
|
this.name = name; |
||||
|
this.attribute = attribute; |
||||
|
} |
||||
|
|
||||
|
@NonNull |
||||
|
@Override |
||||
|
public String toString() { |
||||
|
return "start:" + start + ",end:" + end + ",name:" + name; |
||||
|
} |
||||
|
|
||||
|
public int getStart() { |
||||
|
return start; |
||||
|
} |
||||
|
|
||||
|
public void setStart(int start) { |
||||
|
this.start = start; |
||||
|
} |
||||
|
|
||||
|
public int getEnd() { |
||||
|
return end; |
||||
|
} |
||||
|
|
||||
|
public void setEnd(int end) { |
||||
|
this.end = end; |
||||
|
} |
||||
|
|
||||
|
public String getName() { |
||||
|
return name; |
||||
|
} |
||||
|
|
||||
|
public void setName(String name) { |
||||
|
this.name = name; |
||||
|
} |
||||
|
|
||||
|
public String getAttribute() { |
||||
|
return attribute; |
||||
|
} |
||||
|
} |
@ -0,0 +1,58 @@ |
|||||
|
package gr.thmmy.mthmmy.model; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
|
||||
|
public class HtmlTag { |
||||
|
private int start, end; |
||||
|
private String name, attributeKey, attributeValue; |
||||
|
|
||||
|
public HtmlTag(int start, String name) { |
||||
|
this.start = start; |
||||
|
this.name = name; |
||||
|
} |
||||
|
|
||||
|
public HtmlTag(int start, String name, String attributeKey, String attributeValue) { |
||||
|
this.start = start; |
||||
|
this.name = name; |
||||
|
this.attributeKey = attributeKey; |
||||
|
this.attributeValue = attributeValue; |
||||
|
} |
||||
|
|
||||
|
@NonNull |
||||
|
@Override |
||||
|
public String toString() { |
||||
|
return "start:" + start + ",end:" + end + ",name:" + name; |
||||
|
} |
||||
|
|
||||
|
public int getStart() { |
||||
|
return start; |
||||
|
} |
||||
|
|
||||
|
public void setStart(int start) { |
||||
|
this.start = start; |
||||
|
} |
||||
|
|
||||
|
public int getEnd() { |
||||
|
return end; |
||||
|
} |
||||
|
|
||||
|
public void setEnd(int end) { |
||||
|
this.end = end; |
||||
|
} |
||||
|
|
||||
|
public String getName() { |
||||
|
return name; |
||||
|
} |
||||
|
|
||||
|
public void setName(String name) { |
||||
|
this.name = name; |
||||
|
} |
||||
|
|
||||
|
public String getAttributeKey() { |
||||
|
return attributeKey; |
||||
|
} |
||||
|
|
||||
|
public String getAttributeValue() { |
||||
|
return attributeValue; |
||||
|
} |
||||
|
} |
@ -0,0 +1,221 @@ |
|||||
|
package gr.thmmy.mthmmy.utils.parsing; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.graphics.Typeface; |
||||
|
import android.text.Spannable; |
||||
|
import android.text.SpannableStringBuilder; |
||||
|
import android.text.TextUtils; |
||||
|
import android.text.style.StrikethroughSpan; |
||||
|
import android.text.style.StyleSpan; |
||||
|
import android.text.style.URLSpan; |
||||
|
import android.text.style.UnderlineSpan; |
||||
|
|
||||
|
import java.nio.charset.UnsupportedCharsetException; |
||||
|
import java.util.LinkedList; |
||||
|
import java.util.regex.Matcher; |
||||
|
import java.util.regex.Pattern; |
||||
|
|
||||
|
import gr.thmmy.mthmmy.model.BBTag; |
||||
|
import gr.thmmy.mthmmy.model.HtmlTag; |
||||
|
import gr.thmmy.mthmmy.utils.HTMLUtils; |
||||
|
|
||||
|
public class ThmmyParser { |
||||
|
private static final String[] ALL_BB_TAGS = {"b", "i", "u", "s", "glow", "shadow", "move", "pre", "lefter", |
||||
|
"center", "right", "hr", "size", "font", "color", "youtube", "flash", "img", "url" |
||||
|
, "email", "ftp", "table", "tr", "td", "sup", "sub", "tt", "code", "quote", "tex", "list", "li"}; |
||||
|
private static final String[] ALL_HTML_TAGS = {"b", "br", "span", "i", "div", "del", "marquee", "pre", |
||||
|
"hr", "embed", "noembed", "a", "img", "table", "tr", "td", "sup", "sub", "tt", "pre", "ul", "li"}; |
||||
|
|
||||
|
public static SpannableStringBuilder bb2span(String bb) { |
||||
|
SpannableStringBuilder builder = new SpannableStringBuilder(bb); |
||||
|
// store the original indices of the string
|
||||
|
LinkedList<Integer> stringIndices = new LinkedList<>(); |
||||
|
for (int i = 0; i < builder.length(); i++) { |
||||
|
stringIndices.add(i); |
||||
|
} |
||||
|
|
||||
|
BBTag[] tags = getBBTags(bb); |
||||
|
for (BBTag tag : tags) { |
||||
|
int start = stringIndices.indexOf(tag.getStart()); |
||||
|
int end = stringIndices.indexOf(tag.getEnd()); |
||||
|
int startTagLength = tag.getName().length() + 2; |
||||
|
int endTagLength = tag.getName().length() + 3; |
||||
|
switch (tag.getName()) { |
||||
|
case "b": |
||||
|
builder.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
break; |
||||
|
case "i": |
||||
|
builder.setSpan(new StyleSpan(Typeface.ITALIC), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
break; |
||||
|
case "u": |
||||
|
builder.setSpan(new UnderlineSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
break; |
||||
|
case "s": |
||||
|
builder.setSpan(new StrikethroughSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
break; |
||||
|
default: |
||||
|
throw new UnsupportedCharsetException("Tag not supported"); |
||||
|
} |
||||
|
//remove starting and ending tag and and do the same changes in the list
|
||||
|
builder.delete(start, start + startTagLength); |
||||
|
for (int i = start; i < start + startTagLength; i++) { |
||||
|
stringIndices.remove(start); |
||||
|
} |
||||
|
builder.delete(end - startTagLength, end - startTagLength + endTagLength); |
||||
|
for (int i = end - startTagLength; i < end - startTagLength + endTagLength; i++) { |
||||
|
stringIndices.remove(end - startTagLength); |
||||
|
} |
||||
|
} |
||||
|
return builder; |
||||
|
} |
||||
|
|
||||
|
public static SpannableStringBuilder html2span(Context context, String html) { |
||||
|
SpannableStringBuilder builder = new SpannableStringBuilder(html); |
||||
|
// store the original indices of the string
|
||||
|
LinkedList<Integer> stringIndices = new LinkedList<>(); |
||||
|
for (int i = 0; i < builder.length(); i++) { |
||||
|
stringIndices.add(i); |
||||
|
} |
||||
|
|
||||
|
HtmlTag[] tags = getHtmlTags(html); |
||||
|
for (HtmlTag tag : tags) { |
||||
|
int start = stringIndices.indexOf(tag.getStart()); |
||||
|
int end = stringIndices.indexOf(tag.getEnd()); |
||||
|
int startTagLength = tag.getName().length() + 2; |
||||
|
if (tag.getAttributeKey() != null) { |
||||
|
startTagLength += tag.getAttributeKey().length() + tag.getAttributeValue().length() + 4; |
||||
|
} |
||||
|
int endTagLength = tag.getName().length() + 3; |
||||
|
|
||||
|
if (isHtmlTagSupported(tag.getName(), tag.getAttributeKey(), tag.getAttributeValue())) { |
||||
|
switch (tag.getName()) { |
||||
|
case "b": |
||||
|
builder.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
break; |
||||
|
case "i": |
||||
|
builder.setSpan(new StyleSpan(Typeface.ITALIC), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
break; |
||||
|
case "span": |
||||
|
if (tag.getAttributeKey().equals("style") && tag.getAttributeValue().equals("text-decoration: underline;")) { |
||||
|
builder.setSpan(new UnderlineSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
} |
||||
|
break; |
||||
|
case "del": |
||||
|
builder.setSpan(new StrikethroughSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
break; |
||||
|
case "a": |
||||
|
URLSpan urlSpan = new URLSpan(tag.getAttributeValue()); |
||||
|
builder.setSpan(urlSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
HTMLUtils.makeLinkClickable(context, builder, urlSpan); |
||||
|
break; |
||||
|
default: |
||||
|
throw new UnsupportedCharsetException("Tag not supported"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//remove starting and ending tag and and do the same changes in the list
|
||||
|
builder.delete(start, start + startTagLength); |
||||
|
for (int i = start; i < start + startTagLength; i++) { |
||||
|
stringIndices.remove(start); |
||||
|
} |
||||
|
builder.delete(end - startTagLength, end - startTagLength + endTagLength); |
||||
|
for (int i = end - startTagLength; i < end - startTagLength + endTagLength; i++) { |
||||
|
stringIndices.remove(end - startTagLength); |
||||
|
} |
||||
|
} |
||||
|
return builder; |
||||
|
} |
||||
|
|
||||
|
public static BBTag[] getBBTags(String bb) { |
||||
|
Pattern bbtagPattern = Pattern.compile("\\[(.+?)\\]"); |
||||
|
|
||||
|
LinkedList<BBTag> tags = new LinkedList<>(); |
||||
|
Matcher bbMatcher = bbtagPattern.matcher(bb); |
||||
|
while (bbMatcher.find()) { |
||||
|
String startTag = bbMatcher.group(1); |
||||
|
int separatorIndex = startTag.indexOf('='); |
||||
|
String name, attribute = null; |
||||
|
if (separatorIndex > 0) { |
||||
|
attribute = startTag.substring(separatorIndex); |
||||
|
name = startTag.substring(0, separatorIndex); |
||||
|
} else |
||||
|
name = startTag; |
||||
|
|
||||
|
if (name.startsWith("/")) { |
||||
|
//closing tag
|
||||
|
name = name.substring(1); |
||||
|
for (int i = tags.size() - 1; i >= 0; i--) { |
||||
|
if (tags.get(i).getName().equals(name)) { |
||||
|
tags.get(i).setEnd(bbMatcher.start()); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
continue; |
||||
|
} |
||||
|
if (isBBTagSupported(name)) |
||||
|
tags.add(new BBTag(bbMatcher.start(), name, attribute)); |
||||
|
} |
||||
|
// remove parsed tags with no end tag
|
||||
|
for (BBTag bbTag : tags) |
||||
|
if (bbTag.getEnd() == 0) |
||||
|
tags.remove(bbTag); |
||||
|
return tags.toArray(new BBTag[0]); |
||||
|
} |
||||
|
|
||||
|
public static HtmlTag[] getHtmlTags(String html) { |
||||
|
Pattern htmlPattern = Pattern.compile("<(.+?)>"); |
||||
|
|
||||
|
LinkedList<HtmlTag> tags = new LinkedList<>(); |
||||
|
Matcher htmlMatcher = htmlPattern.matcher(html); |
||||
|
while (htmlMatcher.find()) { |
||||
|
String startTag = htmlMatcher.group(1); |
||||
|
int separatorIndex = startTag.indexOf(' '); |
||||
|
String name, attribute = null, attributeValue = null; |
||||
|
if (separatorIndex > 0) { |
||||
|
String fullAttribute = startTag.substring(separatorIndex); |
||||
|
int equalsIndex = fullAttribute.indexOf('='); |
||||
|
attribute = fullAttribute.substring(1, equalsIndex); |
||||
|
attributeValue = fullAttribute.substring(equalsIndex + 2, fullAttribute.length() - 1); |
||||
|
name = startTag.substring(0, separatorIndex); |
||||
|
} else |
||||
|
name = startTag; |
||||
|
|
||||
|
if (name.startsWith("/")) { |
||||
|
//closing tag
|
||||
|
name = name.substring(1); |
||||
|
for (int i = tags.size() - 1; i >= 0; i--) { |
||||
|
if (tags.get(i).getName().equals(name)) { |
||||
|
tags.get(i).setEnd(htmlMatcher.start()); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
continue; |
||||
|
} |
||||
|
if (isHtmlTag(name)) |
||||
|
tags.add(new HtmlTag(htmlMatcher.start(), name, attribute, attributeValue)); |
||||
|
} |
||||
|
// remove parsed tags with no end tag
|
||||
|
for (HtmlTag htmlTag : tags) |
||||
|
if (htmlTag.getEnd() == 0) |
||||
|
tags.remove(htmlTag); |
||||
|
return tags.toArray(new HtmlTag[0]); |
||||
|
} |
||||
|
|
||||
|
private static boolean isHtmlTagSupported(String name, String attribute, String attributeValue) { |
||||
|
return name.equals("b") || name.equals("i") || name.equals("span") || name.equals("del") || name.equals("a"); |
||||
|
} |
||||
|
|
||||
|
public static boolean isBBTagSupported(String name) { |
||||
|
return name.equals("b") || name.equals("i") || name.equals("u") || name.equals("s"); |
||||
|
} |
||||
|
|
||||
|
public static boolean isHtmlTag(String tagName) { |
||||
|
for (String tag : ALL_HTML_TAGS) |
||||
|
if (TextUtils.equals(tag, tagName)) return true; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static boolean containsHtml(String s) { |
||||
|
return getHtmlTags(s).length > 0; |
||||
|
} |
||||
|
} |
Loading…
Reference in new issue