Twidere-App-Android-Twitter.../twidere/src/main/java/org/oshkimaadziig/george/androidutils/SpanFormatter.java

115 lines
4.5 KiB
Java

/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* 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/>.
*/
// Uses https://github.com/george-steel/android-utils/commit/289aff11e53593a55d780f9f5986e49343a79e55
package org.oshkimaadziig.george.androidutils;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.SpannedString;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Provides {@link String#format} style functions that work with {@link Spanned} strings and preserve formatting.
*
* @author George T. Steel
*/
public class SpanFormatter {
public static final Pattern FORMAT_SEQUENCE = Pattern.compile("%([0-9]+\\$|<?)([^a-zA-z%]*)([[a-zA-Z%]&&[^tT]]|[tT][a-zA-Z])");
private SpanFormatter() {
}
/**
* Version of {@link String#format(String, Object...)} that works on {@link Spanned} strings to preserve rich text formatting.
* Both the {@code format} as well as any {@code %s args} can be Spanned and will have their formatting preserved.
* Due to the way {@link Spannable}s work, any argument's spans will can only be included <b>once</b> in the result.
* Any duplicates will appear as text only.
*
* @param format the format string (see {@link java.util.Formatter#format})
* @param args the list of arguments passed to the formatter. If there are
* more arguments than required by {@code format},
* additional arguments are ignored.
* @return the formatted string (with spans).
*/
public static SpannedString format(CharSequence format, Object... args) {
return format(Locale.getDefault(), format, args);
}
/**
* Version of {@link String#format(Locale, String, Object...)} that works on {@link Spanned} strings to preserve rich text formatting.
* Both the {@code format} as well as any {@code %s args} can be Spanned and will have their formatting preserved.
* Due to the way {@link Spannable}s work, any argument's spans will can only be included <b>once</b> in the result.
* Any duplicates will appear as text only.
*
* @param locale the locale to apply; {@code null} value means no localization.
* @param format the format string (see {@link java.util.Formatter#format})
* @param args the list of arguments passed to the formatter.
* @return the formatted string (with spans).
* @see String#format(Locale, String, Object...)
*/
public static SpannedString format(Locale locale, CharSequence format, Object... args) {
SpannableStringBuilder out = new SpannableStringBuilder(format);
int i = 0;
int argAt = -1;
while (i < out.length()) {
Matcher m = FORMAT_SEQUENCE.matcher(out);
if (!m.find(i)) break;
i = m.start();
int exprEnd = m.end();
String argTerm = m.group(1);
String modTerm = m.group(2);
String typeTerm = m.group(3);
CharSequence cookedArg;
if (typeTerm.equals("%")) {
cookedArg = "%";
} else if (typeTerm.equals("n")) {
cookedArg = "\n";
} else {
int argIdx;
if (argTerm.equals("")) argIdx = ++argAt;
else if (argTerm.equals("<")) argIdx = argAt;
else argIdx = Integer.parseInt(argTerm.substring(0, argTerm.length() - 1)) - 1;
Object argItem = args[argIdx];
if (typeTerm.equals("s") && argItem instanceof Spanned) {
cookedArg = (Spanned) argItem;
} else {
cookedArg = String.format(locale, "%" + modTerm + typeTerm, argItem);
}
}
out.replace(i, exprEnd, cookedArg);
i += cookedArg.length();
}
return new SpannedString(out);
}
}