package com.door43.translationstudio.rendering; import android.text.Html; import android.text.TextUtils; import com.door43.translationstudio.spannables.ArticleLinkSpan; import com.door43.translationstudio.spannables.PassageLinkSpan; import com.door43.translationstudio.spannables.Span; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Created by joel on 12/2/2015. */ public class HtmlRenderer extends RenderingEngine { private final Span.OnClickListener mLinkListener; private final OnPreprocessLink preprocessCallback; public HtmlRenderer(OnPreprocessLink preprocessor, Span.OnClickListener linkListener) { mLinkListener = linkListener; preprocessCallback = preprocessor; } @Override public CharSequence render(CharSequence in) { CharSequence out = in; out = renderTranslationAcademyAddress(out); out = renderPassageLink(out); // TODO: 12/15/2015 it would be nice if we could pass in a private click listener and interpret the link types before calling the supplied listener. // this will allow calling code to use instance of rather than comparing strings. out = Html.fromHtml(out.toString(), null, new HtmlTagHandler(mLinkListener)); return out; } /** * Renders links to other passages in the project * @param in * @return */ private CharSequence renderPassageLink(CharSequence in) { return renderLink(in, PassageLinkSpan.PATTERN, "p", new OnCreateLink() { @Override public Span onCreate(Matcher matcher) { return new PassageLinkSpan(matcher.group(3), matcher.group(1)); } }); } /** * Renders addresses to translation academy pages as html * Example [[en:ta:vol1:translate:translate_unknown|How to Translate Unknowns]] * @param in * @return */ public CharSequence renderTranslationAcademyAddress(CharSequence in) { return renderLink(in, ArticleLinkSpan.ADDRESS_PATTERN, "ta", new OnCreateLink() { @Override public Span onCreate(Matcher matcher) { String title = matcher.group(4); if(title == null) { title = matcher.group(0); } return ArticleLinkSpan.parse(title, matcher.group(2)); } }); } /** * Renders links to translation academy pages as html * Example <a href="/en/ta/vol1/translate/figs_intro" title="en:ta:vol1:translate:figs_intro">Figures of Speech</a> * @param in * @return */ public CharSequence renderTranslationAcademyLink(CharSequence in) { return renderLink(in, ArticleLinkSpan.LINK_PATTERN, "ta", new OnCreateLink() { @Override public Span onCreate(Matcher matcher) { String title = matcher.group(6); if(title == null) { title = matcher.group(0); } return ArticleLinkSpan.parse(title, matcher.group(3).replace("/", ":")); } }); } /** * A generic rendering method for rendering content links as html * * @param in *@param pattern * @param callback @return */ private CharSequence renderLink(CharSequence in, Pattern pattern, String linkType, OnCreateLink callback) { CharSequence out = ""; Matcher matcher = pattern.matcher(in); int lastIndex = 0; while(matcher.find()) { if(isStopped()) return in; Span link = callback.onCreate(matcher); if(link != null) { link.setOnClickListener(mLinkListener); if (preprocessCallback == null || preprocessCallback.onPreprocess(link)) { // render clickable link CharSequence title = link.getHumanReadable(); if(title == null || title.toString().isEmpty()) { title = link.getMachineReadable(); } String htmlLink = "<app-link href=\"" + link.getMachineReadable() + "\" type=\"" + linkType + "\" >" + title + "</app-link>"; out = TextUtils.concat(out, in.subSequence(lastIndex, matcher.start()), htmlLink); } else { // render as plain text out = TextUtils.concat(out, in.subSequence(lastIndex, matcher.start()), link.getHumanReadable()); } } else { // ignore link out = TextUtils.concat(out, in.subSequence(lastIndex, matcher.end())); } lastIndex = matcher.end(); } out = TextUtils.concat(out, in.subSequence(lastIndex, in.length())); return out; } private interface OnCreateLink { Span onCreate(Matcher matcher); } /** * Used to identify which links to render */ public interface OnPreprocessLink { boolean onPreprocess(Span span); } }