<commit_before>/*
 * Copyright 2018 The twicalico authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.github.moko256.twicalico.text;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.v4.util.ArrayMap;
import android.text.Html;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ClickableSpan;
import android.text.style.DynamicDrawableSpan;
import android.text.style.ImageSpan;
import android.text.style.URLSpan;
import android.view.View;
import android.widget.TextView;

import com.github.moko256.twicalico.GlideApp;
import com.github.moko256.twicalico.GlideRequests;
import com.github.moko256.twicalico.GlobalApplication;
import com.github.moko256.twicalico.SearchResultActivity;
import com.github.moko256.twicalico.ShowUserActivity;
import com.github.moko256.twicalico.cacheMap.StatusCacheMap;
import com.github.moko256.twicalico.entity.Emoji;
import com.github.moko256.twicalico.entity.Type;
import com.github.moko256.twicalico.intent.AppCustomTabsKt;
import com.sys1yagi.mastodon4j.api.exception.Mastodon4jRequestException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import twitter4j.HashtagEntity;
import twitter4j.MediaEntity;
import twitter4j.Status;
import twitter4j.SymbolEntity;
import twitter4j.TwitterException;
import twitter4j.URLEntity;
import twitter4j.User;
import twitter4j.UserMentionEntity;

/**
 * Created by moko256 on 2016/08/06.
 *
 * @author moko256
 */

public class TwitterStringUtils {

    private final static Pattern containsEmoji;

    static {
        containsEmoji = Pattern.compile(":([[A-z]_]+):");
    }

    @NonNull
    public static String plusAtMark(String... strings){
        StringBuilder stringBuilder = new StringBuilder();
        for (String string: strings) {
            stringBuilder.append("@").append(string);
        }
        return stringBuilder.toString();
    }

    public static String convertToSIUnitString(int num){
        if (num == 0) return "0";
        boolean isNegative = (num < 0);
        if (isNegative) num *= -1;

        float k = num / 1000;
        if (k < 1) return (isNegative? "-": "") + String.valueOf(num);

        float m = k / 1000;
        if (m < 1) return (isNegative? "-": "") + String.valueOf(Math.round(k)) + "K";

        float g = m / 1000;
        if (g < 1) return (isNegative? "-": "") + String.valueOf(Math.round(m)) + "M";

        return (isNegative? "-": "") + String.valueOf(Math.round(g)) + "G";
    }

    @NonNull
    public static CharSequence convertToReplyTopString(@NonNull String userScreenName,
                                                       @NonNull String replyToScreenName,
                                                       @NonNull UserMentionEntity[] users){
        StringBuilder userIdsStr = new StringBuilder();

        if (!userScreenName.equals(replyToScreenName)) {
            userIdsStr.append("@").append(replyToScreenName).append(" ");
        }

        for (UserMentionEntity user : users) {
            String screenName = user.getScreenName();
            if (!(screenName.equals(userScreenName) || screenName.equals(replyToScreenName))) {
                userIdsStr.append("@").append(screenName).append(" ");
            }
        }
        return userIdsStr;
    }

    public static String convertErrorToText(@NonNull Throwable e){
        if (e instanceof TwitterException && !TextUtils.isEmpty(((TwitterException) e).getErrorMessage())){
            return ((TwitterException) e).getErrorMessage();
        } else if (e instanceof Mastodon4jRequestException
                && ((Mastodon4jRequestException) e).isErrorResponse()) {
            try {
                return ((Mastodon4jRequestException) e).getResponse().body().string();
            } catch (IOException e1) {
                e1.printStackTrace();
                return e.getMessage();
            }
        } else {
            return e.getMessage();
        }
    }

    public static CharSequence getStatusTextSequence(Status item){

        String postText = item.getText();
        StringBuilder builder = new StringBuilder(postText);

        URLEntity[] urlEntities = item.getURLEntities();

        int tweetLength = postText.codePointCount(0, postText.length());
        int sp = 0;

        for (URLEntity entity : urlEntities) {
            String url = entity.getURL();
            String displayUrl = entity.getDisplayURL();

            int urlLength = url.codePointCount(0, url.length());
            int displayUrlLength = displayUrl.codePointCount(0, displayUrl.length());
            if (entity.getStart() <= tweetLength && entity.getEnd() <= tweetLength) {
                int dusp = displayUrlLength - urlLength;
                builder.replace(postText.offsetByCodePoints(0,entity.getStart()) + sp, postText.offsetByCodePoints(0,entity.getEnd()) + sp, displayUrl);

                sp+=dusp;
            }
        }

        MediaEntity[] mediaEntities = item.getMediaEntities();
        if (mediaEntities.length > 0){
            MediaEntity mediaEntity = mediaEntities[0];
            int result = builder.indexOf(mediaEntity.getURL(), builder.offsetByCodePoints(0, mediaEntity.getStart()));
            if (result != -1){
                builder.replace(result, result + mediaEntity.getURL().length(), "");
            }
        }

        return builder;
    }

    public static void setLinkedSequenceTo(Status item, TextView textView){

        Context context = textView.getContext();

        String tweet = item.getText();

        if (GlobalApplication.clientType == Type.MASTODON){
            Spanned html = Html.fromHtml(tweet);
            if (html.length() >= 2) {
                html = (Spanned) html.subSequence(0, html.length() - 2);
            }
            SpannableStringBuilder builder = convertUrlSpanToCustomTabs(html, context);
            textView.setText(builder);

            Matcher matcher = containsEmoji.matcher(builder);

            int imageSize;
            if (matcher.matches()) {
                imageSize = (int) Math.floor((textView.getTextSize() * 1.15) * 2.0);
            } else {
                imageSize = (int) Math.floor(textView.getTextSize() * 1.15);
            }
            matcher.reset();

            List<Emoji> list = ((StatusCacheMap.CachedStatus) item).getEmojis();

            if (list != null){
                new AsyncTask<Void, Void, Map<String, Drawable>>(){
                    @Override
                    protected Map<String, Drawable> doInBackground(Void... params) {
                        Map<String, Drawable> map = new ArrayMap<>();

                        GlideRequests glideRequests = GlideApp.with(context);
                        for (Emoji emoji : list){
                            try {
                                Drawable value = glideRequests.load(emoji.getUrl()).submit().get();
                                value.setBounds(0, 0, imageSize, imageSize);
                                map.put(emoji.getShortCode(), value);
                            } catch (InterruptedException | ExecutionException e) {
                                e.printStackTrace();
                            }
                        }
                        return map;
                    }

                    @Override
                    protected void onPostExecute(Map<String, Drawable> map) {
                        if (TextUtils.equals(builder, textView.getText())) {
                            while (matcher.find()){
                                String shortCode = matcher.group(1);
                                Drawable drawable = map.get(shortCode);
                                if (drawable != null) {
                                    builder.setSpan(
                                            new ImageSpan(
                                                    drawable,
                                                    DynamicDrawableSpan.ALIGN_BASELINE
                                            ),
                                            matcher.start(), matcher.end(),
                                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
                                    );
                                }
                            }
                            textView.setText(builder);
                        }
                    }
                }.execute();
            }

            return;
        }

        SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(tweet);

        for (SymbolEntity symbolEntity : item.getSymbolEntities()) {
            spannableStringBuilder.setSpan(new ClickableSpan() {
                @Override
                public void onClick(View view) {
                    context.startActivity(SearchResultActivity.getIntent(context, symbolEntity.getText()));
                }
            }, tweet.offsetByCodePoints(0,symbolEntity.getStart()), tweet.offsetByCodePoints(0,symbolEntity.getEnd()), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
        }

        for (HashtagEntity hashtagEntity : item.getHashtagEntities()) {
            spannableStringBuilder.setSpan(new ClickableSpan() {
                @Override
                public void onClick(View view) {
                    context.startActivity(SearchResultActivity.getIntent(context, "#"+hashtagEntity.getText())
                    );<commit_msg>refactor: revert and fix code style<commit_after>/*
 * Copyright 2018 The twicalico authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.github.moko256.twicalico.text;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.v4.util.ArrayMap;
import android.text.Html;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ClickableSpan;
import android.text.style.ImageSpan;
import android.text.style.URLSpan;
import android.view.View;
import android.widget.TextView;

import com.github.moko256.twicalico.GlideApp;
import com.github.moko256.twicalico.GlideRequests;
import com.github.moko256.twicalico.GlobalApplication;
import com.github.moko256.twicalico.SearchResultActivity;
import com.github.moko256.twicalico.ShowUserActivity;
import com.github.moko256.twicalico.cacheMap.StatusCacheMap;
import com.github.moko256.twicalico.entity.Emoji;
import com.github.moko256.twicalico.entity.Type;
import com.github.moko256.twicalico.intent.AppCustomTabsKt;
import com.sys1yagi.mastodon4j.api.exception.Mastodon4jRequestException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import twitter4j.HashtagEntity;
import twitter4j.MediaEntity;
import twitter4j.Status;
import twitter4j.SymbolEntity;
import twitter4j.TwitterException;
import twitter4j.URLEntity;
import twitter4j.User;
import twitter4j.UserMentionEntity;

/**
 * Created by moko256 on 2016/08/06.
 *
 * @author moko256
 */

public class TwitterStringUtils {

    private final static Pattern containsEmoji;

    static {
        containsEmoji = Pattern.compile(":([[A-z]_]+):");
    }

    @NonNull
    public static String plusAtMark(String... strings){
        StringBuilder stringBuilder = new StringBuilder();
        for (String string: strings) {
            stringBuilder.append("@").append(string);
        }
        return stringBuilder.toString();
    }

    public static String convertToSIUnitString(int num){
        if (num == 0) return "0";
        boolean isNegative = (num < 0);
        if (isNegative) num *= -1;

        float k = num / 1000;
        if (k < 1) return (isNegative? "-": "") + String.valueOf(num);

        float m = k / 1000;
        if (m < 1) return (isNegative? "-": "") + String.valueOf(Math.round(k)) + "K";

        float g = m / 1000;
        if (g < 1) return (isNegative? "-": "") + String.valueOf(Math.round(m)) + "M";

        return (isNegative? "-": "") + String.valueOf(Math.round(g)) + "G";
    }

    @NonNull
    public static CharSequence convertToReplyTopString(@NonNull String userScreenName,
                                                       @NonNull String replyToScreenName,
                                                       @NonNull UserMentionEntity[] users){
        StringBuilder userIdsStr = new StringBuilder();

        if (!userScreenName.equals(replyToScreenName)) {
            userIdsStr.append("@").append(replyToScreenName).append(" ");
        }

        for (UserMentionEntity user : users) {
            String screenName = user.getScreenName();
            if (!(screenName.equals(userScreenName) || screenName.equals(replyToScreenName))) {
                userIdsStr.append("@").append(screenName).append(" ");
            }
        }
        return userIdsStr;
    }

    public static String convertErrorToText(@NonNull Throwable e){
        if (e instanceof TwitterException && !TextUtils.isEmpty(((TwitterException) e).getErrorMessage())){
            return ((TwitterException) e).getErrorMessage();
        } else if (e instanceof Mastodon4jRequestException
                && ((Mastodon4jRequestException) e).isErrorResponse()) {
            try {
                return ((Mastodon4jRequestException) e).getResponse().body().string();
            } catch (IOException e1) {
                e1.printStackTrace();
                return e.getMessage();
            }
        } else {
            return e.getMessage();
        }
    }

    public static CharSequence getStatusTextSequence(Status item){

        String postText = item.getText();
        StringBuilder builder = new StringBuilder(postText);

        URLEntity[] urlEntities = item.getURLEntities();

        int tweetLength = postText.codePointCount(0, postText.length());
        int sp = 0;

        for (URLEntity entity : urlEntities) {
            String url = entity.getURL();
            String displayUrl = entity.getDisplayURL();

            int urlLength = url.codePointCount(0, url.length());
            int displayUrlLength = displayUrl.codePointCount(0, displayUrl.length());
            if (entity.getStart() <= tweetLength && entity.getEnd() <= tweetLength) {
                int dusp = displayUrlLength - urlLength;
                builder.replace(postText.offsetByCodePoints(0,entity.getStart()) + sp, postText.offsetByCodePoints(0,entity.getEnd()) + sp, displayUrl);

                sp+=dusp;
            }
        }

        MediaEntity[] mediaEntities = item.getMediaEntities();
        if (mediaEntities.length > 0){
            MediaEntity mediaEntity = mediaEntities[0];
            int result = builder.indexOf(mediaEntity.getURL(), builder.offsetByCodePoints(0, mediaEntity.getStart()));
            if (result != -1){
                builder.replace(result, result + mediaEntity.getURL().length(), "");
            }
        }

        return builder;
    }

    public static void setLinkedSequenceTo(Status item, TextView textView){

        Context context = textView.getContext();

        String tweet = item.getText();

        if (GlobalApplication.clientType == Type.MASTODON){
            Spanned html = Html.fromHtml(tweet);
            if (html.length() >= 2) {
                html = (Spanned) html.subSequence(0, html.length() - 2);
            }
            SpannableStringBuilder builder = convertUrlSpanToCustomTabs(html, context);
            textView.setText(builder);

            Matcher matcher = containsEmoji.matcher(builder);

            int imageSize;
            if (matcher.matches()) {
                imageSize = (int) Math.floor((textView.getTextSize() * 1.15) * 2.0);
            } else {
                imageSize = (int) Math.floor(textView.getTextSize() * 1.15);
            }
            matcher.reset();

            List<Emoji> list = ((StatusCacheMap.CachedStatus) item).getEmojis();

            if (list != null){
                new AsyncTask<Void, Void, Map<String, Drawable>>(){
                    @Override
                    protected Map<String, Drawable> doInBackground(Void... params) {
                        Map<String, Drawable> map = new ArrayMap<>();

                        GlideRequests glideRequests = GlideApp.with(context);
                        for (Emoji emoji : list){
                            try {
                                Drawable value = glideRequests.load(emoji.getUrl()).submit().get();
                                value.setBounds(0, 0, imageSize, imageSize);
                                map.put(emoji.getShortCode(), value);
                            } catch (InterruptedException | ExecutionException e) {
                                e.printStackTrace();
                            }
                        }
                        return map;
                    }

                    @Override
                    protected void onPostExecute(Map<String, Drawable> map) {
                        if (TextUtils.equals(builder, textView.getText())) {
                            while (matcher.find()){
                                String shortCode = matcher.group(1);
                                Drawable drawable = map.get(shortCode);
                                if (drawable != null) {
                                    builder.setSpan(
                                            new ImageSpan(drawable),
                                            matcher.start(), matcher.end(),
                                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
                                    );
                                }
                            }
                            textView.setText(builder);
                        }
                    }
                }.execute();
            }

            return;
        }

        SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(tweet);

        for (SymbolEntity symbolEntity : item.getSymbolEntities()) {
            spannableStringBuilder.setSpan(new ClickableSpan() {
                @Override
                public void onClick(View view) {
                    context.startActivity(SearchResultActivity.getIntent(context, symbolEntity.getText()));
                }
            }, tweet.offsetByCodePoints(0,symbolEntity.getStart()), tweet.offsetByCodePoints(0,symbolEntity.getEnd()), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
        }

        for (HashtagEntity hashtagEntity : item.getHashtagEntities()) {
            spannableStringBuilder.setSpan(new ClickableSpan() {
                @Override
                public void onClick(View view) {
                    context.startActivity(SearchResultActivity.getIntent(context, "#"+hashtagEntity.getText())
                    );
{'fraction_non_alphanumeric': 0.10378426258550268, 'fraction_numerical': 0.011219920279513804, 'mean_word_length': 1.9898484625570105, 'pattern_counts': {'":': 10, '<': 29, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 32, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3318372', 'n_tokens_mistral': 5434, 'n_tokens_neox': 5146, 'n_words': 1127}